你的位置:首页 > Java教程

[Java教程]knockout源码分析之computed(依赖属性)


一、序列图

二、主要代码文件

1、dependentObservable.js:主要包含ko.computed相关方法的处理
2、dependencyDetection.js:主要包含依赖的监控上下文对象。

三、主要逻辑

1、首先为某个属性定义 一个computed对象,如下源码:

var vModel = function(){    this.fName = ko.observable('fName'),    this.lName= ko.observable('lName'),    this.name= ko.computed(function () { //监控依赖对象      return this.fName() + '-' + this.lName();    },this);  };

2、当代码在执行ko.computed方法,求值方法被作为参数传入,并赋值给options的read属性
3、创建一个state字面量对象,其中包含read、write属性,如下代码:
var state = {    latestValue: undefined,    isStale: true,    isBeingEvaluated: false,    suppressDisposalUntilDisposeWhenReturnsFalse: false,    isDisposed: false,    pure: false,    isSleeping: false,    readFunction: options["read"],    evaluatorFunctionTarget: evaluatorFunctionTarget || options["owner"],    disposeWhenNodeIsRemoved: options["disposeWhenNodeIsRemoved"] || options.disposeWhenNodeIsRemoved || null,    disposeWhen: options["disposeWhen"] || options.disposeWhen,    domNodeDisposalCallback: null,    dependencyTracking: {},    dependenciesCount: 0,    evaluationTimeoutInstance: null  };

4、生成computedObservable对象(function),然后将state附加到_state属性上,则扩展为发布/订阅对象。
5、扩展computedFn所有方法和属性到computedObservable对象上
// Inherit from 'subscribable'  if (!ko.utils.canSetPrototype) {    // 'subscribable' won't be on the prototype chain unless we put it there directly    ko.utils.extend(computedObservable, ko.subscribable['fn']);  }  ko.subscribable['fn'].init(computedObservable); //执行发布/订阅对象的init方法,用于初始化发布/订阅对象。  // Inherit from 'computed'  ko.utils.setPrototypeOfOrExtend(computedObservable, computedFn);

6、然后执行computedObservable的evaluateImmediate方法,此方法中最重的三点:
   6.1、在evaluateImmediate_CallReadWithDependencyDetection方法中,创建了依赖监控对象,并添加到依赖监控上下文中
var isInitial = state.pure ? undefined : !state.dependenciesCount,  // If we're evaluating when there are no previous dependencies, it must be the first time      dependencyDetectionContext = {        computedObservable: computedObservable,        disposalCandidates: state.dependencyTracking,        disposalCount: state.dependenciesCount      };    ko.dependencyDetection.begin({      callbackTarget: dependencyDetectionContext,      callback: computedBeginDependencyDetectionCallback,      computed: computedObservable,      isInitial: isInitial    });

    6.2、然后调用evaluateImmediate_CallReadThenEndDependencyDetection方法,参数传递的state(在ko.computed方法中定义的)、dependencyDetectionContext(依赖监控对象)
    6.3、其中用到了try catch finall方式,确保ko.dependencyDetection.end方法的执行
try {      var readFunction = state.readFunction;      return state.evaluatorFunctionTarget ? readFunction.call(state.evaluatorFunctionTarget) : readFunction();    } finally {      ko.dependencyDetection.end();      // For each subscription no longer being used, remove it from the active subscriptions list and dispose it      if (dependencyDetectionContext.disposalCount && !state.isSleeping) {        ko.utils.objectForEach(dependencyDetectionContext.disposalCandidates, computedDisposeDependencyCallback);      }      state.isStale = false;    }

7、在执行ko.computed的readFunction方法时,其中就执行了ko.observable方法(执行的是read),这时就会去调用ko.dependencyDetection.registerDependency方法(参数为此函数对象)
function observable() {    if (arguments.length > 0) {      // Write      // Ignore writes if the value hasn't changed      if (observable.isDifferent(observable[observableLatestValue], arguments[0])) {        observable.valueWillMutate();        observable[observableLatestValue] = arguments[0];        observable.valueHasMutated();      }      return this; // Permits chained assignments    }    else {      debugger;      // Read      ko.dependencyDetection.registerDependency(observable); //执行依赖      return observable[observableLatestValue];    }  }

8、在ko.dependencyDetection中的registerDependency方法内,首先会判断ko.observable是否为订阅对象,如果是则执行begin加入的callbak函数.
registerDependency: function (subscribable) { //注入到相关依赖属性      if (currentFrame) {        if (!ko.isSubscribable(subscribable))          throw new Error("Only subscribable things can act as dependencies");        currentFrame.callback.call(currentFrame.callbackTarget, subscribable, subscribable._id || (subscribable._id = getId()));      }    }

9、执行evaluateImmediate方法后,然后注册Dom移除回调事件。
if (state.disposeWhenNodeIsRemoved && computedObservable.isActive()) {    ko.utils.domNodeDisposal.addDisposeCallback(state.disposeWhenNodeIsRemoved, state.domNodeDisposalCallback = function () {      computedObservable.dispose();    });  }

10、返回computedObservable对象

四、补充说明

1、ko.dependencyDetection中有ignore方法,他主要实现的是一个异步锁,让callbcak处于锁的状态执行

ignore: function (callback, callbackTarget, callbackArgs) { //按顺序s执行依赖,但不触发订阅。      try {        begin();        return callback.apply(callbackTarget, callbackArgs || []);      } finally {        end();      }    }

2、ko.computed 与 ko.dependentObservable是相同的。