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