-
Notifications
You must be signed in to change notification settings - Fork 3.1k
Description
没有新概念,都是旧的。
Why dva ?
经过一段时间的自学或培训,大家应该都能理解 redux 的概念,并认可这种数据流的控制可以让应用更可控,以及让逻辑更清晰。
但随之而来通常会有这样的疑问:概念太多,并且 reducer, saga, action 都是分离的(分文件)。
这带来的问题是:
- 编辑成本高,需要在 reducer, saga, action 之间来回切换
- 不便于组织业务模型 (或者叫 domain model) 。比如我们写了一个 userlist 之后,要写一个 productlist,需要复制很多文件。
还有一些其他的:
- saga 书写太复杂,每监听一个 action 都需要走 fork -> watcher -> worker 的流程
- entry 书写麻烦
- ...
而 dva 正是用于解决这些问题。
What's dva ?
dva 是基于现有应用架构 (redux + react-router + redux-saga 等)的一层轻量封装,没有引入任何新概念,全部代码不到 100 行。( Inspired by elm and choo. )
dva 是 framework,不是 library,类似 emberjs,会很明确地告诉你每个部件应该怎么写,这对于团队而言,会更可控。另外,除了 react 和 react-dom 是 peerDependencies 以外,dva 封装了所有其他依赖。
dva 实现上尽量不创建新语法,而是用依赖库本身的语法,比如 router 的定义还是用 react-router 的 JSX 语法的方式(dynamic config 是性能的考虑层面,之后会支持)。
他最核心的是提供了 app.model
方法,用于把 reducer, initialState, action, saga 封装到一起,比如:
app.model({
namespace: 'products',
state: {
list: [],
loading: false,
},
subscriptions: [
function(dispatch) {
dispatch({type: 'products/query'});
},
],
effects: {
['products/query']: function*() {
yield call(delay(800));
yield put({
type: 'products/query/success',
payload: ['ant-tool', 'roof'],
});
},
},
reducers: {
['products/query'](state) {
return { ...state, loading: true, };
},
['products/query/success'](state, { payload }) {
return { ...state, loading: false, list: payload };
},
},
});
在有 dva 之前,我们通常会创建 sagas/products.js
, reducers/products.js
和 actions/products.js
,然后在这些文件之间来回切换。
介绍下这些 model 的 key :(假设你已经熟悉了 redux, redux-saga 这一套应用架构)
- namespace - 对应 reducer 在 combine 到 rootReducer 时的 key 值
- state - 对应 reducer 的 initialState
- subscription - elm@0.17 的新概念,在 dom ready 后执行,这里不展开解释,详见:A Farewell to FRP
- effects - 对应 saga,并简化了使用
- reducers
How to use
参考 examples:
Roadmap
devtool
热替换支持Router 支持 Dynamic ConfigEffects 需要支持更多的 saga 模式Effects 考虑通过扩展的方式接入 thunk, promise, observable 等方案,基本目的是可以兼容 IE8Component 之间还要传递 dispatch 太麻烦了,考虑下方案单元测试方案More Examples: todolist, users in antd-init, popular productsTo pick up a draggable item, press the space bar. While dragging, use the arrow keys to move the item. Press space again to drop the item in its new position, or press escape to cancel.
FAQ
开发工具层面的支持?
除了热替换还待适配,其他的比如 redux-devtool, css livereload 等都是兼容的。
是否已经可用于生成环境?
可以。
是否包含之前 redux + redux-saga 那套应用架构的所有功能?
是的。
浏览器兼容性?
IE8 不支持,因为使用了 redux-saga 。(后面会考虑以扩展的方式在 effects 层支持 thunk, promise, observable 等)
Activity
ystarlongzi commentedon Jun 24, 2016
被 redux 搞得死去活来的,简直是福音啊,太简洁、优雅啦,大赞!!!
btw,今天无意间在推上看到一老外转发了下,以为还是老外写的,没想到是支付宝的同学,👍
codering commentedon Jun 25, 2016
期待effects的扩展
besteric commentedon Jun 29, 2016
支付宝生产环境有在使用这套架构么?
sorrycc commentedon Jun 29, 2016
@besteric dva 刚出,目前还没来得及应用,但背后的那套应用架构是已经用了有段时间了。
yesmeck commentedon Jul 6, 2016
reducer 的写法是不是可以设计成这样:
这样就可以对 reducer 应用一些高阶方法。
Tinker404 commentedon Jul 7, 2016
好评,写了几个demo就一个问题,model只能用
app.model(Model1); app.model(Model2);
这样的方法来完成组合吗,其实我觉得理想的是
app.model([Model1,Model2])
之类的
JimmyLv commentedon Jul 7, 2016
不用
bindActionCreators
吗?sorrycc commentedon Jul 7, 2016
@yesmeck reducer 高阶用法的具体场景目前是否也就
redo/undo
了? 我不想 dva 过于灵活,这在之后会考虑通过 addon 的方式加入。yesmeck commentedon Jul 7, 2016
我们项目中用了不少,比如我们会把多个 reducer 逻辑类似的部分抽成一个高阶方法,去修饰原有的 reducer,还有能让 reducer 在路由变化的时候重置状态的高阶方法,还有这个 https://github.com/erikras/multireducer
sorrycc commentedon Jul 7, 2016
@Tinker404 感觉分开声明 model 会更清楚,增加删除都比较容易。我会这么写:
sorrycc commentedon Jul 7, 2016
@JimmyLv 个人倾向于不用 actionCreator,而是直接
dispatch
。sorrycc commentedon Jul 7, 2016
@yesmeck ok,我再考虑下看。
感觉这个场景通过在
subscriptions
中订阅路由变化,再通过 action 去重置状态会更合适。或者是用 reducer enhancer 的方法有什么优势吗?yesmeck commentedon Jul 7, 2016
这样的话,需要重置的 reducer 就要每个都写一下重置的逻辑,用高阶方法的话我们现在只要这样:
还有一个场景就是我说的抽取不同 reducer 的相同逻辑。比如有一个产品列表和一个用户列表,它们的 reducer 是这样的:
这里两个 reducer 几乎是一样的,我们就抽出来写一个 list reducer:
然后我们实现一个
composeReducers
来组合这3个reducer:这样,产品列表和用户列表的 reducer 就变成这样了:
list 只是一个例子,实际上在项目中是有不少 reducer 会有相同逻辑的。
76 remaining items