什么是dva.js?
一个elm-style的轻量级的前端数据流应用框架。 基于: React + redux + redux-saga + react-rotuer + fetch。
特点
- 容易上手,仅6个api
- elm风格:一个新的概念model,由reducers, effects, subscription 组成
- 支持HRM
- 插件系统:如: 使用dva-loading后,可自动处理loading状态
兼容
IE9及以上
版本
- dva@2.x(2017-9)(2018-10-26 dva@2.4.1,2019-11-21 dva@2.6.0-beta.19) 【react16.8.4】
- dva@1.x(2016-9)
根据dvajs的sorrycc的回答:dva@3在计划中,但优先级不高(2019-10的回答)(不知道是否已经开始)
快速上手
安装使用dva-cli
npm install dva-cli -g
dva -v # 0.10.1 【官方现在推荐使用: create-umi】
dva new dva-demo # 创建新项目dva-demo (提示:已经被弃用,推荐使用create-umi)
cd dva-demo
npm start # 启动开发服务器
初始化的项目包含如下功能:
基本的项目初始化目录及文件,开发服务器、构建脚本、数据 mock 服务、代理服务器等。
dva做了什么?
dva提供的功能都在下面代码中实现的:
dva常用的功能 :
// 新建实例
//(见:dva/src/index.js dva-core/drc/index.js)
const app = dva();
// 连接model和ui (见:dva/index.js)
import { connect } from 'dva';
// 插件,如dva-loading等
app.use({});
// model
app.model(require('./models/example').default)
// router
app.router(require('./router').default);
// 启动应用
// 基于redux的createStore新建了store, 以及用applyMiddleware来处理redux-saga及其他中间件。
app.start('#root');
app启动前
新建单个app, 并将models,routers,middlewares初始进app。
// app=dva({}) 在dva-core中新建了一个对象
const app = {
// 将reducers和effects添加namespace为前缀
_models: [prefixNamespace({ ...dvaModel })],
_store: null,
_plugin: plugin,
use: plugin.use.bind(plugin),
// 将传入的model用其namespace前缀后,存到_models中
model,
// 启动
start,
};
// 将router存在_router中
app.router = function router(router){
app._router = router;
};
// app.start()
app.start = function start(container){
// 处理前面接收到的配置, 然后渲染到container中
if (container) {
render(container, store, app, app._router);
}
// 省略...
}
app启动后
提供方法用于:动态挂载model/销毁unmodel,保持store树的干净(只有当前页面相关的的models)。
app.model
挂载model
// app.model的采用注入方式增加store树上的model
app.model = injectModel.bind(app, createReducer, onError, unlisteners);
app.unmodel
销毁model
function unmodel(createReducer, reducers, unlisteners, namespace) {
// Delete reducers
delete store.asyncReducers[namespace];
delete reducers[namespace];
// 为redux新建全局的reducer替换旧的reducer
store.replaceReducer(createReducer());
// 销毁models
app._models = app._models.filter(function (model) {
return model.namespace !== namespace;
});
}
dynamic.js(dva内置)
根据路由变化动态挂载models,使用方法如下:
<Route path="/menu1" exact component={dynamic({
app,
// 有效值:返回array的function
models: () => [import('./models/menu1')],
// 有效值: function
component: () => import('./routes/Menu1'),
})} />
dynamic.js入口:
function dynamic(config) {
const { app, models: resolveModels, component: resolveComponent } = config;
// ...
if (!models || !models.length) {
// 1. require component
} else {
// 1. require并注册models
loadedModels().map(_ => registerModel(app, _))
// 2. require component
}
//...
// 将前面加载的component进行渲染
}
将model注册到app上,cached存储已经动态注册成功的namespce,相同namespace不重复注册:
BUG: app.unmodel销毁model后,cached没有清理,导致相同namespce不能重复注册
const cached = {};
function registerModel(app, model) {
console.log('注册', model)
model = model.default || model;
if (!cached[model.namespace]) {
app.model(model);
cached[model.namespace] = 1;
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。