总体会写得比较乱,同时也比较简单,和读书笔记差很少,基本是边读边写。见谅~javascript
主要三大部分Atom
、Observable
、Derivation
java
Mobx的原子类,可以被观察和通知变化,observableValue继承于Atom。observableValue ---> Atomreact
同时里面有几个比较重要的属性与方法。数组
属性安全
方法ide
Observable是一个工厂函数,让数据变得可观察。这个东西须要和上述的Atom创建联系,即将具体的值与Atom联系起来。从而打通自身可以被观察,同时能通知变化的整个流程。函数
三种可被观察的数据类型:对象,数组,Map,下面简单介绍如何实现。假如都不是,就会提示用户调用observable.box,使其拥有get,set方法,即上述说的observableValue数据类型。优化
部分代码以下:ui
fucntion Observable(v) { // 若是已被观察,直接返回 if (isObservable(v)) return v // 根据其类型分别调用observable.object、observable.array、observable.map const res = isPlainObject(v) ? observable.object(v, arg2, arg3) : Array.isArray(v) ? observable.array(v, arg2) : isES6Map(v) ? observable.map(v, arg2) : v // 返回被观察过的东西 if (res !== v) return res // 都不是,提示用户调用observable.box(value) fail( process.env.NODE_ENV !== "production" && `The provided value could not be converted into an observable. If you want just create an observable reference to the object use 'observable.box(value)'` ) }
重点是observable.object、observable.array、observable.map三者的实现,下面是讨论关于对象的实现方式this
对象(observable.object)
有计算属性时,会新建一个既有observableValue也有derivation属性的computedValue类,存放到adm[$mobx].values里面,key就是computed的key
而后,重点是建立proxy时的handler对象的get和set函数,在有新属性访问时或改变值时会调用get和set函数
(observableValue简称为oV,Object.defineProperty简称为Od)
这样子,整个对象属性的监听流程就创建起来了
Reaction(反应)是一类特殊的Derivation,能够注册响应函数,使之在条件知足时自动执行。使用以下:
// new Reaction(name, onInvalidate) const reaction = new Reaction('name', () => { // do something,即响应函数,发生反作用的地方 console.log('excuted!') }) const ob = observable.object({ name: 'laji', key: '9527' }) reaction.track(() => { // 注册须要被追踪的变量,这里访问了已经被观察的ob对象,因此当ob.name或ob.key发生改变时,上面的响应函数会执行 console.log(`my name is ${ob.name}`) console.log(`${ob.key} hey hey hey!`) }) ob.name = 'mike' // 'excuted!'
让咱们分析一下源码实现,主要有几个重点:
在调用track函数时,这个是重点,会调用trackDerivedFunction(this, fn, undefined)
export function trackDerivedFunction<T>(derivation: IDerivation, f: () => T, context: any) { // 将该 Derivation 的 dependenciesState 和当前全部依赖的 lowestObserverState 设为最新的状态 changeDependenciesStateTo0(derivation) // 创建一个该derivation新的newObserving数组,里面存放的是谁被该derivation注册依赖了 derivation.newObserving = new Array(derivation.observing.length + 100) // 记录新的依赖的数量 derivation.unboundDepsCount = 0 // 每次执行都分配一个全局的 uid derivation.runId = ++globalState.runId // 重点,将当前的derivation分配为全局的globalState.trackingDerivation,这样被观察的 Observable 在其 reportObserved 方法中就能获取到该 Derivation const prevTracking = globalState.trackingDerivation globalState.trackingDerivation = derivation let result // 下面运行存入track的函数,触发被观察变量的get方法 if (globalState.disableErrorBoundaries === true) { result = f.call(context) } else { try { result = f.call(context) } catch (e) { result = new CaughtException(e) } } globalState.trackingDerivation = prevTracking // 比较新旧依赖,更新依赖 bindDependencies(derivation) return result }
能够看到,重点有两个,一个是将当前的derivation分配为全局的globalState.trackingDerivation,一个是下面的更新依赖过程。
接下来,咱们看看触发了被观察变量的get方法,会是怎样的,上面说过,调用get方法会执行reportObserved函数
export function reportObserved(observable: IObservable): boolean { // 拿到刚才被设置到全局的derivation const derivation = globalState.trackingDerivation if (derivation !== null) { if (derivation.runId !== observable.lastAccessedBy) { observable.lastAccessedBy = derivation.runId // 这行是重点,将被观察的变量,放到derivation.newObserving数组中,所以,derivation里就存放了此次访问中被观察的变量 derivation.newObserving![derivation.unboundDepsCount++] = observable if (!observable.isBeingObserved) { observable.isBeingObserved = true observable.onBecomeObserved() } } return true } else if (observable.observers.size === 0 && globalState.inBatch > 0) { queueForUnobservation(observable) } return false }
以后是bindDependencies函数的执行。这里面有两点,不作代码解读了:
此时会调用observable的set函数,而后调用reportChanged,最终会调用一个叫作propagateChanged函数。
export function propagateChanged(observable: IObservable) { // 已经在运行了,直接返回 if (observable.lowestObserverState === IDerivationState.STALE) return observable.lowestObserverState = IDerivationState.STALE // 上面说过,observable(被观察的变量)的observers存放着derivation // 这里就是执行每一个derivation的onBecomeStale函数 observable.observers.forEach(d => { if (d.dependenciesState === IDerivationState.UP_TO_DATE) { if (d.isTracing !== TraceMode.NONE) { logTraceInfo(d, observable) } d.onBecomeStale() } d.dependenciesState = IDerivationState.STALE }) }
onBecomeStale最终会调用derivation里的schedule函数,里面作了两件事:
执行runReactions函数
至此,整个mobx的数据观察与响应流程就都一一解释完整了(autorun,autorunAsync,when等函数都是基于Reaction来实现的,就不做过多解读了)
既然mobx都说了,那就把mobx-react也分析一下吧。其实很简单,只要理解了Reaction与Observable,就很容易明白mobx-react的实现了。
mobx-react的实现主要也是两点
第一点比较简单,实现一个hoc,把observerableStore添加到context上,而后被inject的组件就能够拿到所需的observerableStore
咱们重点看下第二点,实现第二点的主要逻辑,在observer.js
里面的makeComponentReactive函数中,看下面简化版的重点解析
// makeComponentReactive function makeComponentReactive(render) { if (isUsingStaticRendering === true) return render.call(this) // 改造后的render函数 function reactiveRender() { // 防止重复执行响应函数,由于componentWillReact有可能有反作用 isRenderingPending = false // render函数执行后返回的jsx let rendering = undefined // 注册须要被追踪的变量 reaction.track(() => { if (isDevtoolsEnabled) { this.__$mobRenderStart = Date.now() } try { // _allowStateChanges是安全地执行原来的render函数,假如在action外有更改变量的行为,会报错 // 重点是这个,由于render函数被执行了,因此假如里面有被observe过的变量,就能被追踪,更新到依赖该reaction的依赖列表里面 rendering = _allowStateChanges(false, baseRender) } catch (e) { exception = e } if (isDevtoolsEnabled) { this.__$mobRenderEnd = Date.now() } }) return rendering } // ....省略一些代码 // 新建一个Reaction,注册响应函数 const reaction = new Reaction(`${initialName}#${rootNodeID}.render()`, () => { if (!isRenderingPending) { // 正在执行响应函数 isRenderingPending = true // 这里就是执行新的componentWillReact生命周期的地方 if (typeof this.componentWillReact === "function") this.componentWillReact() if (this.__$mobxIsUnmounted !== true) { let hasError = true try { setHiddenProp(this, isForcingUpdateKey, true) // 也是重点,经过forceUpdate,更新组件 if (!this[skipRenderKey]) Component.prototype.forceUpdate.call(this) hasError = false } finally { setHiddenProp(this, isForcingUpdateKey, false) if (hasError) reaction.dispose() } } } }) // 改写原来的render reaction.reactComponent = this reactiveRender[mobxAdminProperty] = reaction this.render = reactiveRender return reactiveRender.call(this) }
能够见到,经过创建一个Reaction,实现了render函数里的被观察的变量收集及响应函数注册。并且在经过forceUpdate从新更新组件后,render函数会被从新执行一遍,从而实时更新被观察的变量。总体的实现仍是巧妙的。
除此以外,还有一些生命周期的优化,对props、state也进行监听等操做,在这里就不一一解读了