vue2.x源码解析 — 响应式原理 (一)

352 阅读3分钟

Observer

export class Observer {
    value: any;
    dep: Dep;
    vmCount: number; // number of vms that has this object as root $data

    constructor(value: any) {
        this.value = value
        this.dep = new Dep()
        this.vmCount = 0

        // 给value添加__ob__属性,值就是本Observer对象,value.__ob__ = this;
        // Vue.$data 中每个对象都有 __ob__ 属性,包括 Vue.$data对象本身
        def(value, '__ob__', this)

        // 判断是否为数组,不是的话调用walk()添加getter和setter
        // 如果是数组,调用observeArray()遍历数组,为数组内每个对象添加getter和setter
        // 并重写push/pop/unshift/shift/splice/sort/reverse方法,当调用这些方法是,主动调用value.__ob__.dep.notify()方法
        if (Array.isArray(value)) {
            const augment = hasProto
                ? protoAugment
                : copyAugment
            augment(value, arrayMethods, arrayKeys)
            this.observeArray(value)
        } else {
            this.walk(value)
        }
    }

    // 遍历每个属性并将它们转换为getter/setter。只有当值类型为对象时才调用此方法。
    walk (obj: Object) {
        const keys = Object.keys(obj)
        for (let i = 0; i < keys.length; i++) {
            defineReactive(obj, keys[i], obj[keys[i]])
        }
    }

    observeArray (items: Array<any>) {
        for (let i = 0, l = items.length; i < l; i++) {
            // 如果是数组继续执行 observe 方法, 其中会继续新建 Observer 对象, 直到穷举完毕执行 walk 方法
            observe(items[i])
        }
    }
}

constructor中,首先将__ob__属性添加到value上,并将该属性指向Observer对象本身,然后判断value的类型,如果是Array,则调用observeArray对数组中对每个值进行处理,否则,即为Object时,直接调用walk方法,来对对象中对每个kv对进行处理。

observe

// 该方法用于观察一个对象,返回与对象相关的Observer对象,如果没有则为value创建一个对应的Observer
export function observe (value: any, asRootData: ?boolean): Observer | void {
    // typeof返回值非object 或 value存在于VNode.prototype的原型链上时
    if (!isObject(value) || value instanceof VNode) {
        return
    }

    let ob: Observer | void
    if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
        ob = value.__ob__
    } else if (
        observerState.shouldConvert &&
        !isServerRendering() &&
        (Array.isArray(value) || isPlainObject(value)) &&
        Object.isExtensible(value) &&   // 是否可扩展,https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/isExtensible
        !value._isVue
    ) {
        ob = new Observer(value)
    }

    if (asRootData && ob) {
        ob.vmCount++
    }

    return ob
}

observe中,判断value.__ob__是否存在,如果不存在,则调用new Observer,将value变成被监听对象。

defineReactive

export function defineReactive (
    obj: Object,
    key: string,
    val: any,
    customSetter?: ?Function,
    shallow?: boolean
) {
    const dep = new Dep()

    // https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/getOwnPropertyDescriptor
    const property = Object.getOwnPropertyDescriptor(obj, key)
    if (property && property.configurable === false) {
        return
    }

    // cater for pre-defined getter/setters
    // 获取已经实现的 getter /setter 方法
    const getter = property && property.get
    const setter = property && property.set

    // 监听key所对应的val的子属性
    let childOb = !shallow && observe(val)

    Object.defineProperty(obj, key, {
        enumerable: true,
        configurable: true,
        get: function reactiveGetter () {
            const value = getter ? getter.call(obj) : val

            // Dep.target 全局变量指向的就是当前正在解析指令的Complie生成的 Watcher,即当前活动的Watcher实例
            // 如果当前有活动的Watcher实例,
            // 会执行到 dep.addSub(Dep.target), 将 Watcher 添加到 Dep 对象的 Watcher 列表中
            if (Dep.target) {
                // 将dep放进当前观察者的deps中,同时,将该观察者放入dep中,等待变更通知
                dep.depend()

                // 为子属性进行依赖收集
                if (childOb) {
                    childOb.dep.depend()
                    if (Array.isArray(value)) {
                        dependArray(value)
                    }
                }
            }
            return value
        },
        set: function reactiveSetter (newVal) {
            const value = getter ? getter.call(obj) : val
            /* eslint-disable no-self-compare */
            if (newVal === value || (newVal !== newVal && value !== value)) {
                return
            }
            /* eslint-enable no-self-compare */
            if (process.env.NODE_ENV !== 'production' && customSetter) {
                customSetter()
            }

            if (setter) {
                setter.call(obj, newVal)
            } else {
                val = newVal
            }

            childOb = !shallow && observe(newVal)

            // 如果数据被重新赋值了, 调用 Dep 的 notify 方法, 遍历dep.subs, 通知所有的 Watcher
            dep.notify()
        }
    })
}

defineReactive中,重写了obj对gettersetter方法,当调用get时,通过dep.depend()将 Watcher 添加到当前 Dep 对象的 Watcher 列表中,并在调用set时,通过dep.notify()来通知所有依赖的Watcher进行更新,因为new Dep()其实是在闭包中调用,所以每个obj.key对应一个自身当dep

src/core/observer/dep.js

// Dep是订阅者Watcher对应的数据依赖
export default class Dep {
    static target: ?Watcher;
    id: number;
    subs: Array<Watcher>;

    constructor() {
        // 每个Dep都有唯一的ID
        this.id = uid++
        // subs用于存放依赖
        this.subs = []
    }

    // 向dep的观察者列表subs添加观察者
    addSub (sub: Watcher) {
        this.subs.push(sub)
    }

    // 从dep的观察者列表subs移除观察者
    removeSub (sub: Watcher) {
        remove(this.subs, sub)
    }

    // 依赖收集:如果当前有观察者,将该dep放进当前观察者的deps中
    // 同时,将当前观察者放入观察者列表subs中
    depend () {
        // 这里添加了Dep.target是否存在的判断,目的是判断是不是Watcher的构造函数调用
        // 也就是说判断他是Watcher的this.get调用的,而不是普通调用
        if (Dep.target) {
            Dep.target.addDep(this)
        }
    }

    notify () {
        // stabilize the subscriber list first
        const subs = this.subs.slice()
        for (let i = 0, l = subs.length; i < l; i++) {
            // 通知所有绑定 Watcher。调用watcher的update()
            subs[i].update()
        }
    }
}