最近一直在用Mobx开发中小型项目,开发起来真的,真的很爽,响应式更新,性能快,样板代码减小(相对Redux)。因此,想趁2019年结束前把Mobx源码研究一遍。node
$ 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最基础的使用方式
Reactreact
@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> ); } }
Mobxgit
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是如何作到的呢?让咱们一步步来分析。es6
首先,看入口文件,mobx-source -> mobx.js
,发现observable,action,runInAction
等其余方法都是从internal
引入的。github
export { observable, action, runInAction } from "./internal";
打开internal.js
npm
export * from "./api/action"; export * from "./api/autorun"; export * from "./api/observable";
而后看api/observable
这个文件,发现export const observable = createObservable;
json
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
主要作了如下几件事:api
一、若是被观察的对象是string
或symbol
,那么执行deepDecorator.apply(null, arguments);
export const deepDecorator = createDecoratorForEnhancer(deepEnhancer);
deepEnhancer方法内部会判断当前修改的值类型,来走不一样的工厂方法。app
二、若是第一个参数已是一个可被观察的对象,那么返回这个对象。函数
三、对第一个参数进行类型(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);
,会对对象属性遍历,来建立拦截器,并且这里面会牵扯到一个事务的概念,后面会分析事务。
欢迎关注博客
相关代码已上传至react-scaffolding-mobx