前言
最近一直在用 开发中小型项目,开发起来真的,真的很爽,响应式更新,性能快,样板代码减少(相对 )。所以,想趁 2019 年结束前把 源码研究一遍。MobX
Redux
MobX
技巧
- 由于 的源码很大,因此只会把个人认为比较重要的部分截取说明
MobX
- 阅读的 源码版本@5.15.0
MobX
- 由于本人对 经验尚浅,所以我会将其编译成 阅读
TypeScript
JavaScript
- 下面会用 简称代替
mobx-sourceMobx
如何调试源码
$ git clone https://github.com/mobxjs/mobx.git
$ cd mobx
$ cnpm i
- 查看 ,发现执行脚本有 和 ,我选择的是, 然后在根目录下会生成 和
package.json
quick-build
small-build
small-build
cnpm run small-build
.build.es5
.build.es6
"scripts": { "quick-build": "tsc --pretty", "small-build": "node scripts/build.js" },
- 把 改名为 放到我写好的脚手架中
.build.es6
mobx-source
- 引入绝对路径
import { observable, action } from '../../mobx-source/mobx';
- 然后就可以愉快的调试源码了
function createObservable(v, arg2, arg3) { debugger; ... }
演示
让我们从计数器开始,看看 最基础的使用方式MobX
React
@inject('counterStore') @observer class Index extends Component { constructor(props) { super(props); } render() { const { counterStore } = this.props; return ( <section> <button onClick={() => counterStore.add()}>+</button> <span>count is: {counterStore.obj.count}</span> <button onClick={() => counterStore.reduce()}>-</button> </section> ); } }
MobX
import { observable, action } from '../../mobx-source/mobx'; class CounterStore { @observable obj = { count: 0 }; @action add() { this.obj.count++; } @action reduce() { this.obj.count--; } } export default CounterStore;
界面如下
功能非常简单,实现也非常简单。通过 对 进行了监听,只要 产生了数据变化,就会自动刷新界面。那么, 是如何做到的呢?让我们一步步来分析。observable
count
count
MobX
观察
首先,看入口文件,,发现 等其他方法都是从 引入的。mobx-source -> mobx.js
observable,action,runInAction
internal
export { observable, action, runInAction } from "./internal";
打开internal.js
export * from "./api/action"; export * from "./api/autorun"; export * from "./api/observable";
然后看 这个文件,发现api/observable
export const observable = createObservable;
function createObservable(v, arg2, arg3) { // @observable someProp; if (typeof arguments[1] === "string" || typeof arguments[1] === "symbol") { return deepDecorator.apply(null, arguments); } // it is an observable already, done if (isObservable(v)) return v; // something that can be converted and mutated? const res = isPlainObject(v) ? observable.object(v, arg2, arg3) : Array.isArray(v) ? observable.array(v, arg2) : isES6Map(v) ? observable.map(v, arg2) : isES6Set(v) ? observable.set(v, arg2) : v; }
createObservable
主要做了以下几件事:
1、如果被观察的对象是 或 ,那么执行
deepEnhancer 方法内部会判断当前修改的值类型,来走不同的工厂方法。string
symbol
deepDecorator.apply(null, arguments);
export const deepDecorator = createDecoratorForEnhancer(deepEnhancer);
2、如果第一个参数已经是一个可被观察的对象,那么返回这个对象。
3、对第一个参数进行类型()判断,然后调用不同的工厂方法。object、array、map、set
const observableFactories = { box(value, options) { ... }, array(initialValues, options) { ... }, map(initialValues, options) { ... }, set(initialValues, options) { ... }, object(props, decorators, options) { ... }, ref: refDecorator, shallow: shallowDecorator, deep: deepDecorator, struct: refStructDecorator };
接下来,我们来分析 方法,主要有两个参数,第一个默认为 ,第二个是个函数。,会把上面传的,在此处进行挂载。根据变量不同类型,调用 的不同参数,如 来进行劫持。createDecoratorForEnhancer
true
res.enhancer = enhancer;
deepEnhancer
observable
object, array
export function createDecoratorForEnhancer(enhancer) { invariant(enhancer); const decorator = createPropDecorator(true, (target, propertyName, descriptor, _decoratorTarget, decoratorArgs) => { if (process.env.NODE_ENV !== "production") { invariant(!descriptor || !descriptor.get, `@observable cannot be used on getter (property "${stringifyKey(propertyName)}"), use @computed instead.`); } const initialValue = descriptor ? descriptor.initializer ? descriptor.initializer.call(target) : descriptor.value : undefined; asObservableObject(target).addObservableProp(propertyName, initialValue, enhancer); }); const res = // Extra process checks, as this happens during module initialization typeof process !== "undefined" && process.env && process.env.NODE_ENV !== "production" ? function observableDecorator() { // This wrapper function is just to detect illegal decorator invocations, deprecate in a next version // and simply return the created prop decorator if (arguments.length < 2) return fail("Incorrect decorator invocation. @observable decorator doesn't expect any arguments"); return decorator.apply(null, arguments); } : decorator; res.enhancer = enhancer; return res; }
createPropDecorator
方法创建属性拦截器, 方法为目标对象添加 属性。addHiddenProp
Symbol(mobx pending decorators)
export function createPropDecorator(propertyInitiallyEnumerable, propertyCreator) { return function decoratorFactory() { let decoratorArguments; const decorator = function decorate(target, prop, descriptor, applyImmediately // This is a special parameter to signal the direct application of a decorator, allow extendObservable to skip the entire type decoration part, // as the instance to apply the decorator to equals the target ) { ... if (!Object.prototype.hasOwnProperty.call(target, mobxPendingDecorators)) { const inheritedDecorators = target[mobxPendingDecorators]; addHiddenProp(target, mobxPendingDecorators, Object.assign({}, inheritedDecorators)); } target[mobxPendingDecorators][prop] = { prop, propertyCreator, descriptor, decoratorTarget: target, decoratorArguments }; return createPropertyInitializerDescriptor(prop, propertyInitiallyEnumerable); }; }; }
由于上面我定义的变量是对象,所以 Mobx 会把这个对象拦截,执行observableFactories.object
object(props, decorators, options) { if (typeof arguments[1] === "string") incorrectlyUsedAsDecorator("object"); const o = asCreateObservableOptions(options); if (o.proxy === false) { return extendObservable({}, props, decorators, o); } else { const defaultDecorator = getDefaultDecoratorFromObjectOptions(o); const base = extendObservable({}, undefined, undefined, o); const proxy = createDynamicObservableObject(base); extendObservableObjectWithProperties(proxy, props, decorators, defaultDecorator); return proxy; } },
asCreateObservableOptions
创建一个可观察的对象,由于已经是 了,所以 为 ,则进 , 加工处理下 对象,转成 数据类型,然后看 ,很关键的方法,这个函数内部就是利用 来创建拦截器,对这个对象的属性 方法进行了代理拦截。object
proxy
undefined
else
const base = extendObservable({}, undefined, undefined, o);
o
Symbol
createDynamicObservableObject
Proxy
has, get, set, deleteProperty, ownKeys,preventExtensions
export function createDynamicObservableObject(base) { const proxy = new Proxy(base, objectProxyTraps); base[$mobx].proxy = proxy; return proxy; } const objectProxyTraps = { has(target, name) { ... }, get(target, name) { ... }, set(target, name, value) { ... }, deleteProperty(target, name) { ... }, ownKeys(target) { ... }, preventExtensions(target) { ... } };
extendObservableObjectWithProperties(proxy, props, decorators, defaultDecorator);
,会对对象属性遍历,来创建拦截器,而且这里面会牵扯到一个事务的概念,后面会分析事务。