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

        // Add the __ob__ attribute to value, which is the Observer object, value.__ob__ = this;
        Each object in Vue.$data has an __ob__ attribute, including the Vue.$data object itself
        def(value, '__ob__'.this)

        If not, call walk() to add getter and setter
        // If it is an array, call observeArray() to traverse the array, adding getters and setters for each object in the array
        / / and re-written push/pop/unshift/shift/splice/sort/reverse method, when call these methods, take the initiative to call value. __ob__. Dep., notify () method
        if (Array.isArray(value)) {
            const augment = hasProto
                ? protoAugment
                : copyAugment
            augment(value, arrayMethods, arrayKeys)
            this.observeArray(value)
        } else {
            this.walk(value)
        }
    }

    // Iterate over each property and convert them to getters/setters. This method is called only if the value type is an object.
    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: Observe: Observe: observe: observe: observe: observe: observe: observe: observe: observe: observe: observe: observe: observe
            observe(items[i])
        }
    }
}
Copy the code

Constructor adds an __ob__ attribute to value and points it to the Observer Object itself, then determines the type of value (if Array) and calls observeArray to process each value in the Array. Otherwise, if Object, Call the walk method directly to process each KV pair in the object.

observe

// This method is used to observe an object, return the Observer associated with the object, or create a corresponding Observer for value if none exists
export function observe (value: any, asRootData: ? boolean) :Observer | void {
    // When typeof returns a value other than Object or value on the prototype chain of vNode. prototype
    if(! isObject(value) || valueinstanceof 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) &&   / / is extensible, 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
}
Copy the code

In observe, check whether value.__ob__ exists. If not, call a new Observer to make value a monitored object.

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
    // Get the implemented getter /setter methods
    const getter = property && property.get
    const setter = property && property.set

    // listen on the child property of val corresponding to key
    letchildOb = ! shallow && observe(val)Object.defineProperty(obj, key, {
        enumerable: true.configurable: true.get: function reactiveGetter () {
            const value = getter ? getter.call(obj) : val

            The dep. target global variable points to the active Watcher instance generated by Complie, which is currently parsing the directive
            // If there is an active Watcher instance,
            // Execute to dep.addSub(dep.target) to add Watcher to the Watcher list of the DEP object
            if (Dep.target) {
                // Put the DEP into the dePS of the current observer, and at the same time, put the observer into the DEP and wait for the change notification
                dep.depend()

                // Dependency collection for child attributes
                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)// If the data is reassigned, call notify of Dep to iterate through dep.subs and notify all watchers
            dep.notify()
        }
    })
}
Copy the code

DefineReactive overwrites obj’s getter and setter methods to add Watcher to the Watcher list of the current DEP object via dep.depend() when get is called, and when set is called, Notifies all dependent watchers to update with dep.notify(), because new dep () is actually called in a closure, so each obj. Key has its own DEP.

src/core/observer/dep.js

// Dep is the data dependency corresponding to subscriber Watcher
export default class Dep {
    statictarget: ? Watcher; id: number; subs:Array<Watcher>;

    constructor() {
        // Each Dep has a unique ID
        this.id = uid++
        // subs is used to store dependencies
        this.subs = []
    }

    // Add an observer to the deP's observer list subs
    addSub (sub: Watcher) {
        this.subs.push(sub)
    }

    // Remove the observer from the deP's observer list subs
    removeSub (sub: Watcher) {
        remove(this.subs, sub)
    }

    // Dependency collection: If there are currently observers, put the DEP into the dePS of the current observer
    // At the same time, put the current observer into the observer list subs
    depend () {
        // Dep. Target is a constructor call from Watcher
        Watcher's this.get call is not a normal call
        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++) {
            // Notify all binding Watcher. Call update() on watcher
            subs[i].update()
        }
    }
}
Copy the code