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:{}}); } }) }) },