图片
而 Vue 因为是直接修改的同一个对象,所以没必要做啥合并,它的 Watcher 执行是异步的,对多次放到队列里的 Watcher 做下去重就行了。
图片
组件间的状态管理
组件内的状态管理就是这样的,利用前端框架自带的 state 机制来管理。
那组件之间呢?一个组件的 state 变了如何联动其他组件变化?
props
通过 props,把当前组件的 state 作为 props 传入其他组件就行了,这样就能联动变化。
但是 props 只能一层层传递,如果组件和想联动变化的组件相隔很多层,传递 props 就很麻烦。
这种情况下前端框架也都提供了解决方案,React 提供了 Context、Vue 提供了 Event Bus。
Context、Event Bus
React 组件可以在 context 中存放 state,当 context 中的 state 变化的时候会直接触发关联组件的渲染。
Vue 可以在一个组件内 emit 一个事件,然后另一个组件 on 这个事件,然后更新自己的 data 来触发渲染。不过这两个 api 在 Vue3 都废弃了。
这种前端框架自带的任意层组件的状态联动方案只能处理简单的场景,复杂的场景还是得用全局状态管理库,比如 Redux、Vuex、Mobx 这些。
为什么这么说呢?
还记得状态管理的两层含义么?状态变化前的异步过程的管理,状态变化后的联动处理。
Context 和 Event Bus 都只做到了状态变化后的联动处理,但是没有对状态变化前的异步过程管理做支持。
比如多个组件都要修改 context 中的值(或者通过 event bus 修改全局状态),这个过程都要执行一段异步逻辑,要做 loading 的展示,那多个组件里怎么复用这段 loading 的逻辑呢?
还有,如果异步过程比较麻烦,需要用 rxjs 这样的库,用 context 和 event bus 的方案怎么和 rxjs 结合呢?
当然,是可以对 context 和 event bus 做一些逻辑复用的封装和一些结合 rxjs 方案之类的封装的,但是比较麻烦。
而且更重要的是如果你想做这些的时候,那也就没必要用 context 和 event bus 了,直接用全局状态管理库就行。
Redux、Mobx、Vuex
redux 就提供了中间件的机制,组件里发送 action 到 store(存放全局 state 的地方),之前会经历层层中间件的处理,在这里就可以做一些可复用的逻辑的封装,比如 loading 的处理,也可以结合 rxjs 这种异步过程处理方案。
redux 里最常用的中间件就是 redux-saga 和 redux-observable 了,这俩都是做异步过程的管理的。
redux-saga 是基于 generator 实现的,不管是同步还是异步,都只要声明式的描述要执行的逻辑就行,由 saga 内部的执行器会去做同步或异步的处理,描述异步逻辑就很简洁,而且 redux-saga 提供了很多内置的逻辑封装。
图片
redux-observable 则是结合 rxjs 的方案了,把 action 变成数据源,经历层层 opreator 的处理,最后传递到 store。可以用 rxjs 生态大量的 oprator,做下组装就行,根本不用自己写异步逻辑的具体实现。
图片
mobx 没有提供中间件机制,它的 action 是执行状态 class 的某个方法,可以用 class 的那套来做封装。
有的同学对这些状态管理库不太熟,简单来介绍下。
我们理清了状态管理的实现只有两种方案,一种是提供 api 做修改,一种是对 state 对象做响应式代理。
前端框架的状态管理是这样,独立的全局状态管理库也同样是这样。
redux 就是提供 api 来修改的方案,通过 reducer 函数来对传入的 action 做处理,返回新的 state。
图片
而且 redux 这种思路是函数式的思想,每个 reducer 都是输入和输出一一对应的纯函数,返回的 state 都是全新的,为了方便创建新的 state,一般会搭配 immutable 库,只要修改属性就会返回新的 state 对象。
mobx 是响应式代理的方案,它对全局 state 做了一层代理(通过 Object.defineProperty),状态的 get 收集依赖,set 的时候触发依赖更新。
图片
所以这种方案很自然的可以把全局 state 组织成一个个 class,是面向对象的思想,可以通过继承等方式实现逻辑复用。
import React, {Component} from 'react';
import ReactDOM from 'react-dom';
import {observer} from 'mobx-react';