vue function configuration item watch and function $watch source sharing

Keywords: Javascript Vue Attribute

Principle of Vue Two-way List
 
 
Everyone knows that Vue uses the MVVM design mode, uses data-driven to achieve bidirectional binding. If you do not understand the principle of bidirectional binding, you need to supplement the knowledge of bidirectional binding first. In the watch process, you will use the Vue's bidirectional list principle, so review again:
Vue's data is obtained by setting the object's get and set through Object.defineProperty. The data under vue's data corresponds to the only dep object. The dep object stores the watcher corresponding to the changed property, adds a watcher with corresponding processing function to the related property when getting data, and triggers the watcher execution under def object when setting the propertyLogic of
 
 
    // Add getter s and setter s for all properties of data
    function defineReactive( obj,key,val,customSetter,shallow
    ) {
        //
        var dep = new Dep();

        /*....Omit part...*/
        var childOb = !shallow && observe(val);  //Adding a backup dependency dep for an object
        Object.defineProperty(obj, key, {
            enumerable: true,
            configurable: true,
            get: function reactiveGetter() {
                var value = getter ? getter.call(obj) : val;
                if (Dep.target) {
                    dep.depend();  // 
                    if (childOb) {
                        childOb.dep.depend(); //Dependent add watcher for set, array change, etc.
                        if (Array.isArray(value)) {
                            dependArray(value);
                        }
                    }
                }
                return value
            },
            set: function reactiveSetter(newVal) {
                var 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 ("development" !== 'production' && customSetter) {
                    customSetter();
                }
                if (setter) {
                    setter.call(obj, newVal);
                } else {
                    val = newVal;
                }
                childOb = !shallow && observe(newVal);
                dep.notify();//A change triggers the watcher to update
            }
        });
    }

  

When vue is instantiated, initWatch(vm, opts.watch) is called; initialization of watch is done, and the function will eventually call vm.$watch(expOrFn, handler, options) to configure watches. Here we will explain the vm.$watch method
 
 
 Vue.prototype.$watch = function (
            expOrFn,
            cb,
            options
        ) {
            var vm = this;
            if (isPlainObject(cb)) {
                return createWatcher(vm, expOrFn, cb, options)
            }
            options = options || {};
            options.user = true;
            //Add a watcher to the expOrFn that needs to be observed and execute cb if the value of expOrFn changes.
            //The expOrFn is parsed during the instantiation of the watcher and added to the def under the data data data involved in the expOrFn
            var watcher = new Watcher(vm, expOrFn, cb, options);

            //immediate==true executes watch handler immediately
            if (options.immediate) {  
                cb.call(vm, watcher.value);
            }

            //Cancel observation function
            return function unwatchFn() {
                watcher.teardown();
            }
        };

  

Let's look at the process of instantiating a watcher (sharing only watchers that are instances in the observation function)
 
 
 
var Watcher = function Watcher(
        vm,
        expOrFn,
        cb,
        options,
        isRenderWatcher
    ) {
        this.vm = vm;
        if (isRenderWatcher) {
            vm._watcher = this;
        }
        vm._watchers.push(this);
        // options
        if (options) {
            this.deep = !!options.deep; //Whether to observe changes in the object's internal values
            this.user = !!options.user;
            this.lazy = !!options.lazy;
            this.sync = !!options.sync;
        } else {
            this.deep = this.user = this.lazy = this.sync = false;
        }
        this.cb = cb; // Functions executed when observing property changes
        this.id = ++uid$1; // uid for batching
        this.active = true;
        this.dirty = this.lazy; // for lazy watchers
        this.deps = [];
        this.newDeps = [];
        this.depIds = new _Set();
        this.newDepIds = new _Set();
        this.expression = expOrFn.toString();
        // parse expression for getter
        if (typeof expOrFn === 'function') {
            this.getter = expOrFn;
        } else {
            // The data you will need to observe: string | Function | Object | Array, etc. parses as a.b.c and returns the function that accesses the expression  
            this.getter = parsePath(expOrFn);  
            if (!this.getter) {
                this.getter = function () { };
                "development" !== 'production' && warn(
                    "Failed watching path: \"" + expOrFn + "\" " +
                    'Watcher only accepts simple dot-delimited paths. ' +
                    'For full control, use a function instead.',
                    vm
                );
            }
        }
        // this.get() will access data that needs to be observed 
        this.value = this.lazy
            ? undefined
            : this.get(); 
    };

    /**
     * Evaluate the getter, and re-collect dependencies.
     */
    Watcher.prototype.get = function get() {
        //this is the watcher instantiated in the $watch method
        pushTarget(this);speak this Assigned to Dep.target And cached before watcher
        var value;
        var vm = this.vm;
        try {
            //Access the data you need to observe and execute dep.depend() in the getter where you get the data; add the watcher instantiated in the $watch method to the dep under the corresponding data
            value = this.getter.call(vm, vm); 
        } catch (e) {
            if (this.user) {
                handleError(e, vm, ("getter for watcher \"" + (this.expression) + "\""));
            } else {
                throw e
            }
        } finally {
            // "touch" every property so they are all tracked as
            // dependencies for deep watching
            if (this.deep) {
                traverse(value);
            }
            popTarget(); //Assign the previous watcher to Dep.target
            this.cleanupDeps();
        }
        return value
    };

 

This method is executed when the observed object changes
Watcher.prototype.run = function run() { /*....Omitted section.... */ var value = this.get(); //retrieve info value var oldValue = this.value; //Save the old value     this.value = value; this.cb.call(this.vm, value, oldValue); //Execute watch callback /*....Omitted section.... */   };

  

 

this.getter = parsePath(expOrFn); returns a function that accesses the property with parameters such as vm. $watch (info, function (new, old) {console.log (watch success)}; this.getter =function(obj){return obj.info}; and executes value = this.getter.call (vm, call) in the get method of watcher.Vm);, the get method that triggers the attribute, adds the watcher to the def object corresponding to the info attribute, executes traverse(value), accesses the attributes under the info (assuming info is object-only) object in turn, and accesses the info attribute recursively, so that the def object for all attributes under info and info will add the watcher factExample.

When we execute vm.info="change", the set method of info is issued, dep.notify() is executed; the watcher on which the departure info depends executes the run method of watcher, which implements listening

Example

 

Posted by sasi on Thu, 09 May 2019 13:18:38 -0700