You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Flux是最早的一个状态管理方案(架构),它的主要核心是Dispatcher、Store、View。主要流程是当用户进行操作的时候,会从组件发出一个 action,这个 action 流到 store 里面,触发 store 对状态进行改动,然后 store 又触发组件基于新的状态重新渲染。其实质就是强制每个数据状态的改变都通过store进行记录。
/** *@param reducer 是一个函数,返回下一个store的的状态 *@param preloadedState是一个初始态的状态 *@param enhancer函数 是redux store的中间件 *@returns store */exportdefaultfunctioncreateStore(reducer,preloadedState,enhancer){// 检测preloadState和enhancer是否传反if(typeofpreloadedState==='function'&&typeofenhancer==='undefined'){enhancer=preloadedStatepreloadedState=undefined}// enhancer中间件if(typeofenhancer!=='undefined'){if(typeofenhancer!=='function'){thrownewError('Expected the enhancer to be a function.')}returnenhancer(createStore)(reducer,preloadedState)}if(typeofreducer!=='function'){thrownewError('Expected the reducer to be a function.')}letcurrentReducer=reducerletcurrentState=preloadedStateletcurrentListeners=[]letnextListeners=currentListenersletisDispatching=false// 如果nextListeners和currentListeners指向同一个栈,浅复制一份// 目的是修改nextListeners不会影响到currentListenersfunctionensureCanMutateNextListeners(){if(nextListeners===currentListeners){nextListeners=currentListeners.slice()}}/** * 通过方法获取state. 在中间件一般都会用到这个函数来获取state */functiongetState(){if(isDispatching){thrownewError('You may not call store.getState() while the reducer is executing. '+'The reducer has already received the state as an argument. '+'Pass it down from the top reducer instead of reading it from the store.')}returncurrentState}/** * 注册listener,同时返回一个取消事件注册的方法。当调用store.dispatch的时候调用listener * 每次调度动作时都会调用它 * 而如果状态树的某些部分可能已经发生了变化那么你可以调用getState()来读取回调中的当前状态树 */functionsubscribe(listener){if(typeoflistener!=='function'){thrownewError('Expected the listener to be a function.')}if(isDispatching){thrownewError('You may not call store.subscribe() while the reducer is executing. '+'If you would like to be notified after the store has been updated, subscribe from a '+'component and invoke store.getState() in the callback to access the latest state. '+'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.')}letisSubscribed=trueensureCanMutateNextListeners()nextListeners.push(listener)/* * 返回删除该listener的函数 * 使用: * const unsubscribe = store.subscribe(() => { // dosomethin }) * unsubscribe() */returnfunctionunsubscribe(){if(!isSubscribed){return}if(isDispatching){thrownewError('You may not unsubscribe from a store listener while the reducer is executing. '+'See https://redux.js.org/api-reference/store#subscribe(listener) for more details.')}isSubscribed=falseensureCanMutateNextListeners()constindex=nextListeners.indexOf(listener)nextListeners.splice(index,1)}}/** * 分发一个action.这是唯一改变state的方法 * reducer函数可以被当前状态树和给以的action对象调用以创建状态集合。它的返回值会作为下一个状态数,会被所有listeners监听 * 基本的实现仅仅支持普通对象的分发,如果你Promise,Observable,thunk或其他东西,那么就要使用中间件去实现了,比如redux-thunk */functiondispatch(action){if(!isPlainObject(action)){thrownewError('Actions must be plain objects. '+'Use custom middleware for async actions.')}if(typeofaction.type==='undefined'){thrownewError('Actions may not have an undefined "type" property. '+'Have you misspelled a constant?')}// 防止多个dipatch同时进行if(isDispatching){thrownewError('Reducers may not dispatch actions.')}try{isDispatching=truecurrentState=currentReducer(currentState,action)}finally{isDispatching=false}// 分发的时候,nextListeners作为新的listenersconstlisteners=(currentListeners=nextListeners)// 遍历调用listenerfor(leti=0;i<listeners.length;i++){constlistener=listeners[i]listener()}returnaction}/** * 替换reducer */functionreplaceReducer(nextReducer){if(typeofnextReducer!=='function'){thrownewError('Expected the nextReducer to be a function.')}currentReducer=nextReducerdispatch({type: ActionTypes.REPLACE})}// 实例化store的时候初始化分发actiondispatch({type: ActionTypes.INIT})return{
dispatch,
subscribe,
getState,
replaceReducer
}}
exportdefaultfunctionapplyMiddleware(...middlewares){// 最终返回一个以createStore为参数的匿名函数// 这个函数返回另一个以reducer, initialState, enhancer为参数的匿名函数returncreateStore=>(...args)=>{conststore=createStore(...args)letdispatch=()=>{thrownewError(`Dispatching while constructing your middleware is not allowed. `+`Other middleware would not be applied to this dispatch.`)}constmiddlewareAPI={getState: store.getState,dispatch: (...args)=>dispatch(...args)}// 每个 middleware 都以 middlewareAPI 作为参数进行注入,返回一个新的链。// 此时的返回值相当于调用 thunkMiddleware 返回的函数: (next) => (action) => {} ,接收一个next作为其参数constchain=middlewares.map(middleware=>middleware(middlewareAPI))// 并将链代入进 compose 组成一个函数的调用链// compose(...chain) 返回形如(...args) => f(g(h(...args))),f/g/h都是chain中的函数对象。// 在目前只有 thunkMiddleware 作为 middlewares 参数的情况下,将返回 (next) => (action) => {}// 之后以 store.dispatch 作为参数进行注入dispatch=compose(...chain)(store.dispatch)return{
...store,
dispatch
}}}
2.5 bindActionCreators
functionbindActionCreator(actionCreator,dispatch){return(...args)=>dispatch(actionCreator(...args))}// bindActionCreators期待一个Object作为actionCreators传入,里面是 key: actionexportdefaultfunctionbindActionCreators(actionCreators,dispatch){// 如果只是传入一个action,则通过bindActionCreator返回被绑定到dispatch的函数if(typeofactionCreators==='function'){returnbindActionCreator(actionCreators,dispatch)}if(typeofactionCreators!=='object'||actionCreators===null){thrownewError(`bindActionCreators expected an object or a function, instead received ${actionCreators===null ? 'null' : typeofactionCreators}. `+`Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?`)}// 遍历并通过bindActionCreator分发绑定至dispatchvarkeys=Object.keys(actionCreators)varboundActionCreators={}for(vari=0;i<keys.length;i++){varkey=keys[i]varactionCreator=actionCreators[key]if(typeofactionCreator==='function'){boundActionCreators[key]=bindActionCreator(actionCreator,dispatch)}}returnboundActionCreators}
一. 为什么我们要数据状态管理,状态管理为什么会复杂?
I. 复杂状态
页面的状态复杂与否决定是否需要数据状态管理。那么什么样的页面状态可以说是复杂呢?
从交互和数据方面来说:
组件来说,非组件状态的共享,包含多方面,相互影响,依赖关系等
更具体来说,因为react dom的树形结构,我们考虑组件的关系,以及其状态的维护与交流来看状态维护与管理的困难程度
自身与子组件的状态维护
简单
简单
一般
复杂或者说麻烦
②context简单
,以下面例子说明自身和兄弟组件的状态管理:
一般
一般
复杂
自身和祖先状态
一般
复杂
复杂
总体上来说,这些状态的关联可以用下面图标表示
面对以上复杂问题,单凭react自身的state和props维护与工作方式,会让页面重构以及问题定位都陷入放飞自我的过程。
以上问题如果说明了我们是需要状态管理,那么我们需要怎样的状态管理,状态管理怎么为我们解决这些状态复杂的问题。这个需要根据我们时候的UI渲染方案的特点,本文就以我们目前使用的React进行探究
II. React特点(v16.4.1)
react是用以创建有相互影响的UI的一个库,在react应用中,表现层响应每一个状态,react保证高效地正确更新状态改变后的组件和让代码更加可预测和调试
每个React元素都是Immutable的,所以一旦创建React Element后就不可以直接改变它了。React DOM创建了一个树形结构的虚拟树,其实根据React的Component互相组合的特点也是可以想象出这个树形结构的虚拟树的。
react Component提供一些钩子函数,叫做生命周期函数,这些生命周期函数反应了React组件的创建过程,包括初始化,装载,更新,卸载的过程。
III. 如果没有状态管理的库,那么也许我们需要的是
对象存储,对象即是View的状态,存储了共享的状态
对象状态的查询,View可以从存储状态读取View所需要的数据和状态
对象状态改变导致对象存储的改变的描述,当数据状态因为页面的交互发生变化时,对应来自对象存储中的数据和状态怎么变化
对象存储变化的时候,怎么通知页面,或者说页面引用的状态怎么保证与对象存储中的状态同步
例子
二. 发展成熟的状态管理方案
React只是一个UI渲染的库,它不是一个架构,它没有明确地说明页面数据必须怎么样流动,但是其state和props的以及render机制说明单向数据流有利于其机制运行
I. 数据驱动
1. Flux
Flux是最早的一个状态管理方案(架构),它的主要核心是Dispatcher、Store、View。主要流程是当用户进行操作的时候,会从组件发出一个 action,这个 action 流到 store 里面,触发 store 对状态进行改动,然后 store 又触发组件基于新的状态重新渲染。其实质就是强制每个数据状态的改变都通过store进行记录。
架构设计思路:
①实例化模块状态类(比如TodoStore),该类继承FluxReduceStore,FluxReduceStore继承FluxStore类。所以必须要实现getInitialState方法和reduce方法。当状态类实例化后该类会向Dispatcher中注册一个回调,这个回调会调用状态类中的reduce方法,然后比较reduce方法返回的state和之前的state做比较,如果不同,会调用该Store中响应
__changeEvent
的事件②创建顶层Container,作用是结合React的渲染机制,使得数据进行单向流动,即使得如果状态集合中如果有状态发生改变,子组件能够进行更新机制。
创建这个Container一般使用createFunctional方法,它接受四个参数,第一个参数是一个纯函数React组件,这个组件是我们代码中的顶层AppContainer; 第二个参数是Store的数组形式;第三个参数是一个函数,这个函数返回我们Container中的state, 这个state会传给我们的AppContainer; 第四个参数是额外的选项,选项包括是否渲染成PureContainer和传递props和context
): ReactClass

```
Container实例化的时候会实例化一个FluxContainerSubscriptions类
③经过上面两个步骤,flux为应用做了响应数据状态改变的准备。
④当用户触发一些action,比如点击按钮之类的UI操作或者其他触发action的操作。也就是调用了类似这类的函数
实质就是转发中心实例调用
dispatch(action: Object)
方法。该方法会调用所有转发中心里面注册过的函数,我们回忆一下向转发中心里面注册的回调是:所有store初始化注册的函数,Group实例初始化时注册的函数。即是先执行FluxReduceStore
实例的__invokeOnDispatch()
,该方法会调用状态类中的reduce方法,然后比较reduce方法返回的state和之前的state做比较,如果不同把新的state赋值给状态实例的_state
属性,然后执行Group类初始化的时候会向Dispatcher 注册的回调(waitfor + setStateCallback)。state发生改变就会触发react的渲染机制了,这就形成了flux的单数据流解决方案,用下图表示
根据上面的思路和图表,更加细致地看一下这四个核心部分的源码:
1.1 Action
action描述用户行为,比如添加一个Todo的描述,值得注意的是,这个描述是会发给所有Dispatcher注册的回调的
1.2 Dispatcher
转发中心,作用将Action转发到store处理
在分析Dispatcher前,先说一个工具函数invariant,
invariant(condition: boolean, format: string, a: any, b: any, c: any, d: any, e: any, f: any)
。它接受一个返回true或者false的条件,如果false就抛出错误报告,abcdef是打印的参数,format字符串中的的%s字符会被这些参数填充填充然后我们快速看下Dispatcher的源码
1.3 Store
store的作用有如下作用:
我们看一下Flux的store设计
1.4 Views
有两种方式去创建顶层View
下图是完整的flux架构示意图:
2. Redux
Redux是基于观察模式来设计的。
回顾一下观察者模式的实现思路:
subscribers
subscribers
数组中去,比如addSubscriber
subscribers
删除观察者, 比如removeSubscriber
subscribers
中的所有观察者方法,即一个遍历函数调度subscribers
, 比如dispatch
一种方案运行都有它自身的约定原则,redux的几个原则是:
同上面flux介绍,根据一个简单的例子我们先来了解一下
①我们一般会先整理好我们的store集合组成,这里先不关注UI,尽管我们的store的初始化是根据UI上面的状态来进行设置的,如下面两个模块的
reducer
,然后我们会使用combineReducers
方法来合并reducer,这是因为redux方案需要的是一个store②使用createStore构建状态树
③向store添加监听函数,当dispatch函数调用的时候都会重新render
以上就是最简单的redux demo,而在实际开发中,我们一般不会每次都重新render的和直接把所有props传递给组件,也不会手动去添加监听函数,一般引入
react-redux
帮我们处理这些问题,react-redux
就不在这里介绍了redux主要向外暴露五个api:
先介绍下combineReducers
2.1 combineReducers
combineReducers的用法如下,作用是合并reducer为一个,是一个curry化的函数
回到他的源码我们快速过一下:
2.2 createStore
redux的观者者
createStore
实现2.3 compose组合函数
将多个函数组合在一起,从从右到左运行
2.4 applyMiddleware中间件函数
回顾下我们中间件在开发中的用法
为什么要用到thunkMiddleware?
Action 发出以后,Reducer 立即算出 State,这叫做同步。那么Action发出一段时间后,再执行Reducer,就是异步了。
而thunkMiddleware可以解决这个问题。
什么是中间件?
我们把对观察者的扩展叫做中间件
根据Redux的核心部分,我们看哪个地方适合我们扩展
(1)Reducer:纯函数,只承担计算 State 的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。
(2)View:与 State 一一对应,可以看作 State 的视觉层,也不合适承担其他功能。
(3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。
所以相对来说,只有发送 Action 的这个步骤,即store.dispatch()方法,可以添加功能。举例来说,要添加日志功能,把 Action 和 State 打印出来,可以对store.dispatch进行如下改造
code:
2.5 bindActionCreators
3. Flux VS Redux
3.1 相似点
3.2 不同点
3.3 观察者模式和订阅/发布模式的对比
4. Mobx
Mobx的源码比较复杂和多,所以这里主要分析它的原理和主要api
4.1 mobx基本概念(暂不讲)
transaction 事务。表示一组原子性的操作,mobx正是因为此避免不必要的重新计算。主要涉及到startBatch和endBatch两个函数用于开始事务和结束事务
atom。任何能用于存储状态的值在mobx中被称为Atom,它会在
被观察时
和自身发生改变时
发出通知ObservableValue 正是继承自 Atom。可以看到,reportObserverd 和 reportChanged 分别调用了 reportObserved 和 propagateChanged 两个方法,这正是 Observable 用于「通知被观察」和「通知自身变化」的两个函数
可以让用户能够基于它定制一些可观察的数据类型
Derivation。任何 源自状态并且不会再有任何进一步的相互作用的东西就是衍生。 衍生以多种形式存在
4.2 mobx的要点或者说使用步骤
例子:https://codepen.io/farzer/pen/QBaeWB?editors=0011
① 定义状态并使其可观察
② 跟踪变化
③ 创建视图以响应状态的变化,以react为例
observer 函数/装饰器可以用来将 React 组件转变成响应式组件。 它用 mobx.autorun 包装了组件的 render 函数以确保任何组件渲染中使用的数据变化时都可以强制刷新组件
④ 更改状态
5. Mobx VS Redux
5.1 相似点
5.2 不同点
5.3 选择
数据流复杂就使用redux,不复杂使用mobx。
II. 模型驱动
mobx-state-tree
我们在实际react开发中使用mobx方案都是会使用到mobx-react方法结合使用,mobx-react提供了Provider和inject,observer等方法。
而mobx-state-tree的出现就包含了mobx,mobx-react的功能,并且弥补了mobx的跟踪调试差的问题以及快照等功能
III. 不知道应该怎么归类的rxjs,应该更多属于事件驱动吧
rxjs是一种响应式的编程方式,对异步问题的解决非常优雅的,它把一个函数的运行看做一个流,然后通过api对这个流进行阶段操作或者更多细致化的操作,比如一个例子
三. 状态性能优化方案(减少重复渲染,提高性能)
I. Immutable
Immutable解决的是什么问题呢?首先是改变的object对象的时候,层次深的时候复制原来object开销大,其次PureComponent浅比较的时候作比较不优雅。当然其恶心的api却是让人默默继续用回原来的方法
II. Immer
新一代的Immutable Data方案,和Immuatable.js的区别是使用原生的数据结构,这样的话就减少了toJS之类的操作开销
四. 参考
The text was updated successfully, but these errors were encountered: