ko源码讲解(2) subscribable和dependencyDetection

@songhlc 2018-07-10 12:33:11发表于 yonyouyc/blog

observable中如何使用subscribable

// src/subscribables/observable.js 55行
if (ko.utils.canSetPrototype) {
    ko.utils.setPrototypeOf(observableFn, ko.subscribable['fn']);
}

进入 src/subscribables/subscribable.js

ko.subscribable['fn'] = ko_subscribable_fn;

var ko_subscribable_fn = {
    init: function(instance) {
        instance._subscriptions = { "change": [] };
        instance._versionNumber = 1;
    },
    subscribe: function (callback, callbackTarget, event) {
    },
    "notifySubscribers": function (valueToNotify, event) {
    },
    getVersion: function () {
        return this._versionNumber;
    },
    hasChanged: function (versionToCheck) {
        return this.getVersion() !== versionToCheck;
    },
    updateVersion: function () {
        ++this._versionNumber;
    },
    limit: function(limitFunction) {
    },
    hasSubscriptionsForEvent: function(event) {
    },
    getSubscriptionsCount: function (event) {
    },
    isDifferent: function(oldValue, newValue) {
        return !this['equalityComparer'] || !this['equalityComparer'](oldValue, newValue);
    },
    toString: function() {
      return '[object Object]'
    },
    extend: applyExtenders
};

我们先回顾一下subscribe如何使用

var vm = ko.observable('ttt')
// 获取newValue
vm.subscribe(function (newVal) {
    console.log(newVal)
})
// 获取oldValue
vm.subscribe(function (oldValue) {
    console.log(oldValue)
},null, "beforeChange")

然后我们来重点看一下里面的subscribe方法的实现

// 三个参数:回调函数、回调函数的上下文,回调事件
subscribe: function (callback, callbackTarget, event) {
    var self = this;
    // 这里defaultEvent默认是change
    event = event || defaultEvent;
    // 确认是否要使用bind重新设置上下文
    var boundCallback = callbackTarget ? callback.bind(callbackTarget) : callback;
    // 创建一个ko.subscription对象(ko.subscription的内容大家自己看代码即可)
    // 三个参数:target,callback,disposeCallback(dispose表示不再监听)
    var subscription = new ko.subscription(self, boundCallback, function () {
        ko.utils.arrayRemoveItem(self._subscriptions[event], subscription);
        if (self.afterSubscriptionRemove)
            self.afterSubscriptionRemove(event);
    });

    if (self.beforeSubscriptionAdd)
        self.beforeSubscriptionAdd(event);
    // self._subscriptions存储了当前所有的监听事件
    if (!self._subscriptions[event])
        self._subscriptions[event] = [];
    self._subscriptions[event].push(subscription);
    return subscription;
},

通过上面的代码我们了解到所有监听事件都注册到了self._subscriptions[event]之中。

我们再回到observable.js之中

var observableFn = {
    'equalityComparer': valuesArePrimitiveAndEqual,
    peek: function() { return this[observableLatestValue]; },
    valueHasMutated: function () {
        this['notifySubscribers'](this[observableLatestValue], 'spectate');
        this['notifySubscribers'](this[observableLatestValue]);
    },
    valueWillMutate: function () { this['notifySubscribers'](this[observableLatestValue], 'beforeChange'); }
};

上面我们发现在valueWillMutate和valueHasMutated中都调用了notifySubscribers方法,参数不同,分为beforeChange、spectate、null(也就是默认的change)

我们再看一下subscribable下的notifySubscribers方法

hasSubscriptionsForEvent: function(event) {
        return this._subscriptions[event] && this._subscriptions[event].length;
    },
"notifySubscribers": function (valueToNotify, event) {
        event = event || defaultEvent;
        // 默认change 改变version
        if (event === defaultEvent) {
            this.updateVersion();
        }
        //判断_subscriptions里是否有监听事件
        if (this.hasSubscriptionsForEvent(event)) {
            var subs = event === defaultEvent && this._changeSubscriptions || this._subscriptions[event].slice(0);
            try {
                ko.dependencyDetection.begin(); // Begin suppressing dependency detection (by setting the top frame to undefined)
                for (var i = 0, subscription; subscription = subs[i]; ++i) {
                    // 先判断是否已经设置为dispose了
                    if (!subscription._isDisposed)
                // _callback 是在subscrtion里定义的 subscription._callback(valueToNotify);
                }
            } finally {
                ko.dependencyDetection.end(); // End suppressing dependency detection
            }
        }
    },

总结

1.通过subscribe注册callback
2.所有subscribe写入_subscription[event]之中
3.重新赋值的时候在valueWillMutated和valueHasMutated当中触发notifySubscribers方法
4.notifySubscribers中循环调用_subscription中的callback方法