dva 是一個基於 redux 和 redux-saga 的數據流方案.前端
由於它簡化了react引入redux的過程。java
redux:react
開發時,咱們須要action,reducer等文件,而且須要自行分類,不太清晰。webpack
dva:web
開發時,把 store 及 saga 統一爲一個 model 的概念, 寫在一個 js 文件裏面,分類清晰。
增長了一個 Subscriptions, 用於收集其餘來源的 action。redux
HelloWorldModel.js服務器
export default { namespace: 'HelloWorldModel', state: {str:"hi"}, reducers: { }, };
這是react組件的建立:react-router
class HelloWorld extends Component { constructor() { super() this.state = { } } sayHi () { alert('this.props.HelloWorldModel.str) } render () { return ( <div onClick={this.sayHi.bind(this)}>Hello World</div> ) } } export default connect(({ HelloWorldModel }) => ({ HelloWorldModel, }))(HelloWorld);
函數式組件app
const HelloWorld = (props) => { const sayHi = (event) => alert(this.props.HelloWorldModel.str) return ( <div onClick={sayHi}>Hello World</div> ) } export default connect(({ HelloWorldModel }) => ({ HelloWorldModel, }))(HelloWorld);
頁面的數據分爲2大類:
1.頁面屬性:Modal打開或者關閉,與用戶行爲相關,與後臺數據無關
2.頁面狀態:Table展現的數據內容,與用戶行爲無關,與後臺數據相關
我一般在react組件中直接使用state做爲頁面屬性,將頁面狀態存在model中,
而函數式組件,則將頁面屬性和狀態所有存放在model中。dom
上面是react組件和函數式組件的寫法區別,
經過react組件寫法聲明的組件繼承了「react.Component」的一些屬性及方法,如生命週期鉤子函數等。
而函數式組件只是個返回dom節點的方法,因此不具有生命週期函數。
由於dva的數據流是單向的,
用戶行爲=》視圖=》model.Effect=>model.Reducer=>store變化=>視圖更新,
react組件能夠在「componentWillReceiveProps」鉤子中決定是否容許當前組件內的數據流動,
函數式組件只能被動接收數據流動。
如上述代碼,頁面與model的綁定,是經過connect方法實現的,在connect方法中,入參能夠抓取到全部導入的model文件,咱們能夠在每一個組件中(須要關聯model的)「重載」connect方法,只獲取咱們當前頁面所需的model,展現在頁面上。而頁面的事件則可使用dispatch觸發model的Effect事件與服務端交互以後執行reducer,store(model數據)變化後,頁面由於connect了model,至關於訂閱了model的變化,數據流入,頁面更新。
一些公用的狀態是能夠抽離到同一個model中的,如登陸態,上一次查詢參數等。
dva提供dynamic方法支持動態加載組件和model,
import dynamic from 'dva/dynamic'; const UserPageComponent = dynamic({ app, models: () => [ import('./models/users'), ], component: () => import('./routes/UserPage'), });
經過動態加載後,build出來的文件將index.js切割成index.js(體積變小) + 1.async.js等等,
可是index.js默認是經過"./"來加載1.async.js的,對於前端靜態資源與服務端應用不在同一臺服務器的狀況,如
java boot應用 發佈在http://xxx.com/a =》 a.ftl 中引用 dva build以後的靜態資源,而靜態資源發佈倒是在另外一臺服務器上http://zzz.com/dvaBuild/dist,
index.js仍是去訪問默認的"./1.async.js" 其實訪問的是http://xxx.com/a/1.async.js,...。
能夠直接在頁面script標籤中,
window.__webpack_public_path__ = 'http://zzz.com/dvaBuild/dist';
1.是否登錄
在GlobalModel中保存着登陸態,路由中使用AuthComponent替換Route,在渲染路由組件的時候就會判斷是否登錄,若是沒有登錄渲染正在登錄loading頁面,等待第三方登錄接口判斷已經登錄,頁面會自動更新爲路由組件。
這是第三方登錄,若是是內部登錄使用react-router跳轉。
AuthComponent
@connect(({GlobalModel}) => GlobalModel) export default class AuthComponent extends PureComponent { render() { const { component: Component, isLogin, ...rest } = this.props; return ( <Route {...rest} render={props => isLogin == 1 ? ( <Component {...props}/> ) : ( <Logining></Logining> ) } /> ) } }
GlobalModel
effects: { *CheckLogin({payload,callback}, {call,select,put}) { const loginfoResult = yield call(getLoginInfo); if(loginfoResult && loginfoResult.ResponseStatus && loginfoResult.ResponseStatus.Ack == "Success" && loginfoResult.userBasicInfoDTO){ if(loginfoResult.userBasicInfoDTO && loginfoResult.userBasicInfoDTO.userAccount && checkPermission(loginfoResult.userBasicInfoDTO.userAccount)){ yield put({type:"save",loginInfo:loginfoResult}) callback(); }else{ yield put( routerRedux.push('/NoPermission') ); //沒有權限 } }else{ window.location.href = getLoginUrl(); } },
須要在各個路由頁面綁定的model的路由切換訂閱方法中作登錄判斷,
頁面切換就要判斷是否登錄或鑑權
subscriptions: { setup({ dispatch, history }) { // eslint-disable-line return history.listen((location) => { dispatch({ type: "GlobalModel/onShow", callback: () => { dispatch({type:"BaseInfoQuery",payload:{}}); } }) }) },