dva實踐
學習react,快速入門的練習
創建引用可以直接使用dva-cli的各項命令快速創建項目.
項目開始前的配置:
-
配置antd
npm i antd --save npm i babel-plugin-inmport --save-dev //按需加載插件
- 在
.roadhogrc
的"exreaBabelPlugins"里加上語句
["import", { "libraryName": "antd", "style": "css" }]
- 在
dva-cli 的常用命令
dva g model users
dva g component Mainlayout/Header
使用封裝的loading
安裝:cnpm i dva-loading --save
切換路由:
dispatch(routerRedux.push({
pathname:'/users',
query:{page},
}))
- ES6 寫法
import React, { Component, PropTypes } from 'react';
import { Popover, Icon } from 'antd';
class PreviewQRCodeBar extends Component { // 組件的聲明方式
constructor(props) { // 初始化的工作放入到構造函數
super(props); // 在 es6 中如果有父類,必須有 super 的調用用以初始化父類信息
this.state = { // 初始 state 設置方式
visible: false,
};
}
// 因為是類,所以屬性與方法之間不必添加逗號
hide() {
this.setState({
visible: false,
});
}
handleVisibleChange(visible) {
this.setState({ visible });
}
render() {
const { dataurl } = this.props;
return (
<Popover
placement="rightTop"
content={<img src={dataurl} alt="二維碼" />}
trigger="click"
visible={this.state.visible}
onVisibleChange={this.handleVisibleChange.bind(this)} // 通過 .bind(this) 來綁定
>
<Icon type="qrcode" />
</Popover>
);
}
}
// 在 react 寫法中,直接通過 propTypes {key:value} 來約定
PreviewQRCodeBar.proptypes = {
dataurl: PropTypes.string.isRequired,
};
// 在 ES6 類聲明中無法設置 props 只能在類的駐外使用 defaultProps 屬性來完成默認值的設定
// 而在 react 中則通過 getDefaultProps(){} 方法來設定
PreviewQRCodeBar.defaults = {
// obj
}
export default PreviewQRCodeBar;
- Stateless 寫法
import React, { PropTypes } from 'react';
// 組件無 state,pure function
const PreviewDevToolWebview = ({ remoteUrl }) => // 箭頭函數,結構賦值
<webview className={devToolWebview.devToolWebview} src={remoteUrl} />;
PreviewDevToolWebview.proptype = {
remoteUrl: PropTypes.string.isRequired,
};
export default PreviewDevToolWebview;
// 此類組件不支持 ref 屬性,沒有組件生命周期的相關的時候和方法,僅支持 propTypes
// 此類組件用以簡單呈現數據
理解dva中的數據流
如何來理解呢?
在 web 應用中,數據的改變通常發生在用戶交互行為或者瀏覽器行為(如路由跳轉等),當此類行為改變數據的時候可以通過 dispatch
發起一個 action,如果是同步行為會直接通過 Reducers
改變 State
,如果是異步行為會先觸發 Effects
然后流向 Reducers
最終改變 State
,所以在 dva 中,數據流向非常清晰簡明,并且思路基本跟開源社區保持一致。
Action
Action 是一個普通 javascript 對象,它是改變 State 的唯一途徑。無論是從 UI 事件、網絡回調,還是 WebSocket 等數據源所獲得的數據,最終都會通過 dispatch 函數調用一個 action,從而改變對應的數據。** 需要注意的是 dispatch
是在組件 connect Models以后,通過 props 傳入的。**
dispatch({
type: 'user/add', // 如果在 model 外調用,需要添加 namespace
payload: {}, // 需要傳遞的信息
});
以上調用函數內的對象就是一個 action。
dispatch 函數
用于觸發 action 的函數,action 是改變 State 的唯一途徑,但是它只描述了一個行為,而 dipatch 可以看作是觸發這個行為的方式,而 Reducer 則是描述如何改變數據的。
dva - Reducer
在 dva 中,reducers 聚合積累的結果是當前 model 的 state 對象。通過 actions 中傳入的值,與當前 reducers 中的值進行運算獲得新的值(也就是新的 state)。需要注意的是 Reducer 必須是純函數。
app.model({
namespace: 'todos', //model 的 namespace
state: [], // model 的初始化數據
reducers: {
// add 方法就是 reducer,可以看到它其實非常簡單就是把老的 state 和接收到的數據處理下,返回新的 state
add(state, { payload: todo }) {
return state.concat(todo);
},
},
};
dva - Effect
Effect 被稱為副作用,在我們的應用中,最常見的就是異步操作,Effects
的最終流向是通過 Reducers
改變 State
。
核心需要關注下 put, call, select。
app.model({
namespace: 'todos',
effects: {
*addRemote({ payload: todo }, { put, call, select }) {
const todos = yield select(state => state.todos); // 這邊的 state 來源于全局的 state,select 方法提供獲取全局 state 的能力,也就是說,在這邊如果你有需要其他 model 的數據,則完全可以通過 state.modelName 來獲取
yield call(addTodo, todo); // 用于調用異步邏輯,支持 promise 。
yield put({ type: 'add', payload: todo }); // 用于觸發 action 。這邊需要注意的是,action 所調用的 reducer 或 effects 來源于本 model 那么在 type 中不需要聲明命名空間,如果需要觸發其他非本 model 的方法,則需要在 type 中聲明命名空間,如 yield put({ type: 'namespace/fuc', payload: xxx });
},
},
});
dva - Subscription
Subscriptions 是一種從 源 獲取數據的方法,它來自于 elm。
Subscription 語義是訂閱,用于訂閱一個數據源,然后根據條件 dispatch 需要的 action。數據源可以是當前的時間、服務器的 websocket 連接、keyboard 輸入、geolocation 變化、history 路由變化等等。
import key from 'keymaster';
...
app.model({
namespace: 'count',
subscriptions: {
keyEvent(dispatch) {
key('?+up, ctrl+up', () => { dispatch({type:'add'}) });
},
}
});
dva - Router
這里的路由通常指的是前端路由,由于我們的應用現在通常是單頁應用,所以需要前端代碼來控制路由邏輯,通過瀏覽器提供的 History API 可以監聽瀏覽器url的變化,從而控制路由相關操作。
dva 實例提供了 router 方法來控制路由,使用的是react-router。
import { Router, Route } from 'dva/router';
app.router(({history}) =>
<Router history={history}>
<Route path="/" component={HomePage} />
</Router>
);
在 dva 中我們通常以頁面維度來設計 Container Components。
所以在 dva 中,通常需要 connect Model的組件都是 Route Components,組織在/routes/
目錄下,而/components/
目錄下則是純組件(Presentational Components)。
** 通過 connect 綁定數據 **
比如:
import { connect } from 'dva';
function App() {}
function mapStateToProps(state, ownProps) { // 該方法名已經非常形象的說明了 connect 的作用在于 State -> Props 的轉換,同時自動注冊一個 dispatch 的方法,用以觸發 action
return {
users: state.users,
};
}
export default connect(mapStateToProps)(App);
然后在 App 里就有了 dispatch
和 users
兩個屬性。