引言
❝为什么要整理这样的文档?答:纯粹是为了消化 redux 的源码
阅读这篇文章,你能了解啥?答:你可以从循序渐进地了解各架构的实现细节以及能力
为什么是谈源码却没有代码?答:通过流程图,渗透到实现的每一个步骤,设计思想是高于代码实现的
小生对 react 其实不是太了解,也是刚开始尝试画流程图,如有不足,请轻喷~
文末放了相关源码的github链接
看完无收获,请疯狂揍我
❞
基本的 Redux
场景一:一个企业级应用必然是多个 view,多个 reducer
❝❞
- 然而,规定一个 react 应用只有一个 store,一个 reducer
- 通过 combineReducers 合并所有 reducers
场景二:如果可以省略 store 和 store.dispatch 和 store.getState 等冗余的逻辑就好啦!
❝❞
- store 是整个应用共享的,可以使用
React.createContext实现- 组件通过 props 派发 action 和订阅 state,使用高阶组件来实现增强原组件的 props
引入 React-Redux
为了解决场景二的问题,引入 React-Redux
原理:使用高阶组件,将全局 store 暴露到 Context 上,所有 React 组件共享一个 store。通过增强组件的 props,使其挂上 dispatch_action 函数和自己组件有关的 state
❝解决问题 1:
❞引入 store的冗余逻辑
使用 React.createContext,将 store 作为全局共享的属性
❝解决问题 2:
❞store.dispatch的冗余逻辑
❝解决问题 3:
❞store.getState的冗余逻辑
组件内部自定义选择器,state => state.counter1
场景三:上述只能进行派发对象形式的动作,如何支持函数形式、Promise 形式
❝❞
- 需要利用 redux 中间件能力
redux-thunk中间件使支持函数形式的 actionredux-promise中间件使支持 Promise 形式的 action
拥有集成中间件能力的 Redux
原理:类似 koa 的compose,其实就是迭代,「中间件可以使 store.dispatch 的功能更加强壮」
用法:applyMiddleware(middlewares)(createStore)(reducer)
场景四:使用 redux 管理 router 状态
❝❞
- 可以从 state 中获取 router 的 location 和 action 属性
- 可以通过 redux 派发一个跳转路由的请求,并且更新 state.router
引入 connected-react-router
将这个库理解成一个完整的 rudex-store 就很明白了,该有的它也得有
- 创建 action
- 创建 reducer
- 创建 middleware(effect)
- 创建 connectedRouter(基于 react-router 实现的强化版 Router 组件)
场景五:上述只能派发同步动作,如何使用 redux 制定业务流程,并且支持异步动作
❝❞
- 提供更丰富的副作用能力,如:异步获取数据和从浏览器中读取缓存
- redux 代码应该是结合业务流程来写,而不是单纯地从 view 到 store
- 对前端而言,主线程是单线程,除了同步代码,管理好异步逻辑特别重要
- 可以随时开启一个任务,暂停或取消它
引入 redux-saga
就是一个使用了 ES6 的 Generator 功能的中间件,所以 saga 充分利用了 Generator 的特性,使得 redux 像注入了灵魂一样,无论是使用还是维护都很有章法
saga 可以是用户自定义的,用户使用 take、put、fork、takeEvery、call、ops、delay、all、cancel 等 effect 去组织自己的业务流程,saga 内部会进行迭代(实现思想类似 Promise.all)
用法:
import createSagaMiddleware from ('redux-saga');
let sagaMiddleware = createSagaMiddleware();
sagaMiddleware.run(...middlewares);
❝场景五:前面引入的库实在太多,用起来不爽,有没有一个库集合了前面的全部功能,简化开发呢?
❞
引入 dva
其实dva并没有多少自己的东西,源码也就百来行。它只是将之前所有的应用架构都集合到了一起,并且重新设计了自己的用法。还有增加了钩子和懒加载的能力...
dva API:
dva core:
- dva model函数(不考虑结合dynamic)的作用
❝prefixNamespace的必要性:用户在进行派发action时,actionType是
❞namespace/add的形式,而reducer或effect定义add函数时,是不会添加namespace/这一串,所以需要dva内部补上
- dva start函数(不考虑结合plugin)的作用
❝增强器enhancers是什么?增强createStore的,applyMiddleware的返回值就是一个enhancer
目前为止,基本的dva已经完成,可以处理app.model下的reducers和effects和subscription,并且结合了connected-react-router来渲染路由
❞
dva plugin:
❝有webpack的插件机制内味了,就是在dva应用运行的流程中,插入一些钩子函数。用户可以利用这些钩子函数或是增强reducer、增强effects、添加额外中间件、添加额外reducer、添加额外createStore增强器、状态变化回调、抛错处理等等
❞
- 请看dva插件有哪些钩子:
- dva start函数(结合plugin)的作用
dva dynamic:
❝懒加载模块的redux_model和component。就是使用Promise.all批量请求models和AsyncComponent,等待懒加载模块都加载回来后,将懒加载的models添加到应用的store中,并且重新渲染AsyncComponent
❞
- dynamic加载模块的流程
- dva model函数(结合dynamic)的作用
至此,关于redux的源码剖析已经基本完成了,可以看得出来,每一个架构的出现都是为了解决之前存在的缺陷。到dva(这里就不展开umi啦)这个阶段,redux已经用起来很有章法且维护成本也降低了不少
内心独白:如此流程化,只是为了让开发者把砖搬得更快更好看吗?To be or bot to be.That is a question...。
redux相关源码仓库
[源码]: https://github.com/reduxjs/redux/tree/master/src redux
[源码]: https://github.com/reduxjs/react-redux/tree/master/src react-redux
[源码]: https://github.com/reduxjs/redux-thunk/tree/master/src redux-thunk
[源码]: https://github.com/redux-utilities/redux-promise/tree/master/src redux-promise
[源码]: https://github.com/supasate/connected-react-router/tree/master/src connected-react-redux
[源码]: https://github.com/redux-saga/redux-saga/tree/master/packages/redux-saga redux-saga
[源码]: https://github.com/dvajs/dva/tree/master/packages/dva-core/src dva
本文使用 mdnice 排版