破题
Context 存储变量
缺点:数据源追溯难,确认变动点难;与组件耦合度高,不利于组件复用和测试。
Flux
Flux 是一种基于 单向数据流 的架构。
- View:视图层,React 组件;
- Store:数据层,维护了数据和数据处理的逻辑;
- Dispatcher:管理数据流动的中央枢纽。每个 Store 提供一个回调。当 Dispatcher 接收一个 Action 时,所有的 Store 接收注册表中的 Action,然后通过回调产生数据。
- Action:事件通知,通常用 type 标记。
流程:Store 存储了视图层所有的数据,当 Store 改变后引起 View 层更新。若在视图层触发 Action,如点击按钮,当前页面数据会发生改变。Action 会被 Dispatcher 进行统一的收发处理,传递给 Store 层。由于 Store 层已注册过相关的 Action 的处理逻辑,处理对应的内部状态变化后,会触发 View 层更新。
优点:单向数据流,解决了 MVC 中数据流向不清问题。
Redux
Redux 三原则:
- 单一数据源:整个应用的 state 被存储在一颗 object tree 中,且这个 object tree 只存在于唯一的一个 Store 中。
- 纯函数 Reducer:即为了描述 Action 是如何改变状态树,编写一个纯函数的 Reducer。
- state 是只读的:改变 state 的唯一方法是触发 Action,Action 是一个用于描述已发生事件的普通对象。
优点:
- 结果可预测
- 代码结构严谨易维护
- 模块分离清晰,且小函数结构易编写单元测试
- Action 的触发方式,可回溯,易定位问题
- 单一数据源使服务端同构变得更为容易
- 社区方案多,生态繁荣
Redux 的调试工具 Redux DevTools,时间回溯。
问:Redux 如何解决副作用???
解决副作用的方案分为两类:
- 在 Dispatch 时有个一个 middleware 中间件层,拦截分发的 Action 并添加额外的复杂行为,还可添加副作用;
流行方案:Redux-thunk,其作用是处理异步 Action,面试中要求独立编写。 ```tsx function createThunkMiddleware(extraArgument) { return ({ dispatch, getState }) => (next) => (Action) => { if (typeof Action === ‘function’) { return Action(dispatch, getState, extraArgument); } return next(Action); } }
const thunk = createThunkMiddleware(); thunk.withExtraArgument = createThunkMiddleware;
export default thunk; ``` 如上代码,Redux-thunk 通过添加中间件来判断 Action 是否为函数:
- 若是函数,则 Dispatch 将当前整个 state 以及额外的参数传入其中;
- 若不是函数,继续流转 Action;
以上是最早最经典的处理 Redux 副作用的方案,也可自定义 Store 的 middleware。
若 Action 是一个数组,或 Promise 该如何处理?Action 可以是 数组,Promise,迭代器,rxjs,如 Redux-Saga,Redux-Promise,Redux-Observable等。
- 允许 Reducer 层直接处理副作用。
Redux Loop 借鉴 Elm,Elm 处理副作用在 update 层,此种设计叫 分形架构。
关于 Redux 的工程化统一解决方案:
- 国外流行 rematch,他提供标准的范式写 Redux,具体案例 ,rematch 的模块更内聚,插件更丰富。
国内流行 Dva 。
MobX
相关API 文档:MobX-01、MobX-02 ;
MobX 是通过监听数据的属性变化,直接在数据上更改来触发 UI 的渲染。
MobX 的 响应式原理 与 Vue 相同。
MobX 的监听方式:MobX 5 之前,
Object.defineProperty
;- MobX 5 之后,
Proxy
。
进阶
手写 Redux,手写时,需注意两个地方:
createStore
,通过createStore
,注入 Reducer 与 middleware,生成 Store 对象;- Store 对象的 getState、subscribe、diapatch 函数。getState 获取当前状态树,subscribe 函数订阅状态树变更,diapatch 函数发送 Action。
在实现上尽量采用 纯函数 实现方案,参考 Redux4.x 。