• 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是相同的。

  • 相关阅读:
    L3-1 二叉搜索树的结构 (30 分)
    L3-2 森森快递 (30 分)(贪心+线段树/分块)
    三分(凸函数)
    (三分入门)(凹函数)
    Print Article(斜率DP入门+单调队列)
    PTA 逆散列问题 (30 分)(贪心)
    二叉树遍历相关
    7-5 堆中的路径 (25 分)
    Grouping ZOJ
    D
  • 原文地址:https://www.cnblogs.com/cqhaibin/p/5702059.html
Copyright © 2020-2023  润新知