陈成 (花名:云谦)
蚂蚁金服
杭州
PlainReact
Roof
Roof@0.5
Redux
Dva
代码写哪里?是个问题
代码分散,影响专注力
一遍遍重复地写 showLoading 和 hideLoading
出错处理太繁琐,每个异步 saga 都要 try .. catch
项目太大了,我需要动态加载方案
框架,而非类库
基于 redux, react-router, redux-saga 的轻量级封装
借鉴 elm 的概念,Reducer, Effect 和 Subscription
...
仅有 5 个 API
支持 HMR
支持 SSR (ServerSideRender)
支持 Mobile/ReactNative
支持 TypeScript
支持路由和 Model 的动态加载
完善的语法分析库 dva-ast
...
import dva from'dva';
// 1. 创建 dva 实例
const app = dva();
// 2. 装载插件 (可选)
app.use(require('dva‐loading')());
// 3. 注册 Model
app.model(require('./models/count'));
// 4. 配置路由
app.router(require('./router'));
// 5. 启动应用
app.start('#root');
app = dva(Opts)
app.use(Hooks)
app.model(ModelObject)
app.router(Function)
app.start([HTMLElement], opts)
State
Action
Model
Reducer
Effect
Subscription
Router
RouteComponent
State 表示应用的数据层,由 model 的 state 组成全局的 state
{
todos:[],
visibilityFilter:'SHOW_ALL',
}
全局 State
app.model({namespace:'todos', state: []});
app.model({namespace:'visibilityFilter', state: 'SHOW_ALL'});
Model
Action 表示操作事件,可以是同步,也可以是异步
{
type: String,
payload: Any?,
error? Error,
}
格式
dispatch(Action);
dispatch({ type: 'todos/add', payload: 'Learn Dva' });
触发 action
Model 非 MVC 中的 M,而是领域模型,用于把数据相关的逻辑聚合到一起
包含 5 个 key
namespace
state
reducers
effects
subscriptions
export default {
namespace: 'todos',
state: [],
reducers: {
// 保存 todos
save(state, { payload }) {
return payload;
},
// 添加 todo
add(state, { payload }) {
return [...state, payload];
},
},
effects: {
// 异步获取 todos,然后通过 action 保存
*fetch(action, { call, put }) {
const todos = yield call(service.fetchTodos);
yield put({ type: 'save', payload: todos });
},
},
}
Reducer 是唯一可以修改 state 的地方,接收 state 和 action,返回新的 state 。
(state, action) => newState
格式
// good
function(state, action) {
return state + action.payload;
}
// bad
const foo = 1;
function(state, action) {
return state + action.payload + foo;
}
例子
Effect 用于处理异步逻辑,基于 redux-saga 实现
*(action, effects) => void
格式
*fetch(action, { call, put, select }) {
const todos = yield call(fetchTodos);
yield put({ type: 'save', payload: todos });
},
例子
Subscription 表示订阅,用于订阅一个数据源,然后按需 dispatch action。
({ history, dispatch }) => Function|void
格式
function({ dispatch, history }) {
history.listen(({ pathname }) => {
if (pathname === '/users') {
dispatch({
type: 'users/fetch',
});
}
});
},
例子
Router 表示路由配置信息
({ history, app }) => JSXElement | Object
格式
export default function({ history }){
return(
<Router history={history}>
<Route path="/" component={App} />
</Router>
);
}
例子
RouteComponent 表示 Router 里匹配路径的 Component,通常会绑定 model 的数据
import { connect } from 'dva';
function App() {
return <div>App</div>;
}
function mapStateToProps(state) {
return { todos: state.todos };
}
export default connect(mapStateToProps)(App);
例子
awesome-dva - dva 资源列表
dva-knowledge - 包含使用 dva 所需的所有知识点
视频教程 - 一步步创建一个应用