最近一直在用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