responsive

The goal of data responsiveness: Functions such as the Render function are run when the responsive data object itself or properties change.

Vue implementation of responsive implementation, specific is dependent on several components:

  1. Observer
  2. Dep
  3. Watcher
  4. Scheduler

The implementation of these modules is in the Vue2 source code./ SRC /core/observer.

Observer

An Observer is a class that does nothing more than convert an ordinary object into a responsive object.

The Observer converts each property of an Object to a property with a getter and setter via Object.defineProperty, so that vue has a chance to do something else when the property is accessed or set.

The Observer prototype defines two methods, observeArray and Walk, to convert arrays and general objects into responsive forms, respectively.

There are some differences between array responsivity and object responsivity in Vue2. If an item in an array is a primitive value type, it cannot be triggered by assignment. While some of the prototype methods of arrays are responsive, Vue2 quietly modifies the prototype methods of arrays.

In the array module, Object. Create (Array.prototype) creates an Object arrayMethods, and rewrites some methods of array to achieve data responsiveness. Modify the implicit stereotype of the array in our data to arrayMethods when instantiating the Observer.

Vue2 is based on ES5, which does not provide object. setPrototypeOf methods. Vue2 can only perform different operations for __proto__ and un__proto__ respectively. Browsers that implement __proto__ directly modify array prototypes to inherit from arrayMethods, while browsers that don’t implement __proto__ define arrayMethods’ methods directly into arrays.

/* @flow */
function copyAugment (target: Object, src: Object, keys: Array<string>) {
    for (let i = 0, l = keys.length; i < l; i++) {
        const key = keys[i]
        def(target, key, src[key])
    }
}
Copy the code

Observe, the most important function in the Observer module, observes data, does nothing with its original value, and instantiates an Observer for an object that has not implemented a response.

The primitive values in the array are not implemented responsively. The object is implemented because observeArray traverses the array and calls observe on each item.

One important function that the Observer relies on is defineReactive, which is used to implement a generic object into reactive mode by adding getters and setters for each property of the object through defineProperty. The observe function is used to recurse to achieve a deeply responsive implementation.

Each responsive object, array, has a corresponding Observer instance, which is accessible through __ob__. This is because there is a def(value, ‘__ob__’, this) in the Observer constructor.

Because the traversal can only traverse the current properties of the object, it cannot detect future dynamically added or deleted properties. The Observer module defines two functions set and del to solve this problem:

function set (target: Array<any> | Object, key: any, val: any) :any {
    if (Array.isArray(target)) {
        target.splice(key, 1, val)
        return val
    }
    const ob = (target: any).__ob__
    defineReactive(ob.value, key, val)
    ob.dep.notify()
    return val
}

function del (target: Array<any> | Object, key: any) {
    if (Array.isArray(target)) {
        target.splice(key, 1)
        return
    }
    const ob = (target: any).__ob__
    delete target[key]
    ob.dep.notify()
}
Copy the code

For objects, after deleting/adding attributes, an update is dispatched via __ob__.notify.

For arrays, you can simply add/modify/delete and distribute updates by calling the rewritten prototype method splice, because arrayMethods does the same thing with ob.dp.notify.

Dep

There are two unsolved problems, namely what to do when a property is read, and what to do when a property changes, and that needs to be solved by Dep.

Dep stands for Dependency.

Dep is also a class that deals with what needs to be done to read properties and change properties.

Vue2 creates one for each property in the reactive object, the object itself, and the array itselfDepInstance.

Each Dep instance has the ability to do two things:

  • Record dependencies: Who is using my corresponding data
  • Distribute updates: When my data changes, notify those functions that use me to update it

When reactive data is read, it does dependency collection; When reactive data is changed, it sends an update.

These things are done in defineReactive:

  • defineReactiveThe first line of the function is going to beconst dep = new Dep()
  • ObserverExists in the constructor this.dep = new Dep()
  • getterindep.depend()Collect rely on
  • setindep.notify()Distributed update

Watcher

So here’s another question, how does Dep know who’s using me?

To solve this problem, you need to use another class, Watcher.

When a function (computed, watch, render) uses responsive data, responsive data has no way of knowing which function uses its own.

Vue2 solves this problem in a clever way. Instead of executing functions directly, we hand them over to something called a Watcher.

A Watcher is an object. Every function that uses reactive data should create a Watcher and execute that function.

Before it executes, a global variable records that watcher is currently responsible for executing the function, and then executes the function. During the execution of the function, the getter is executed if the responsive data is used, and the indirect dep.depend is executed. The dependency is the corresponding Watcher instance of the function.

For every responsive data, there is a DEP. For every responsive data, there may be functions that depend on it. For every function that depends on responsive data, there is a Watcher. Each DEP has a property subs that records the watcher of the function on which the responsive data corresponding to the DEP is dependent. Calling dep.depend adds the current watcher to the subs.

The current running watcher is logged by the dep. target global variable.

For each Vue component instance, there is at least one Watcher that records the render function of that component.

When the data changes, setter methods of reactive objects are run to distribute updates through Dp. notify, and update methods are run for each watcher in the subs. The run method is called, and the function corresponding to the watcher is called in the run method.

notify () {
    // stabilize the subscriber list first
    const subs = this.subs.slice()
    for (let i = 0, l = subs.length; i < l; i++) {
        subs[i].update()
    }
}
Copy the code

Scheduler

If Watcher re-runs the corresponding function after the Dep informs watcher, it may cause the function to run frequently, resulting in inefficiency.

This is obviously not appropriate, so instead of executing the corresponding function immediately after watcher is notified of the update, it actually hands itself off to something called a scheduler, via queueWatcher(this).

update () {
    /* istanbul ignore else */
    if (this.lazy) {
        this.dirty = true
    } else if (this.sync) {
        this.run()
    } else {
        queueWatcher(this)}}Copy the code

The scheduler is implemented through the Scheduler module, which maintains an execution queue in which the same watcher exists only once.

The scheduler module has a flushSchedulerQueue function to flush the execution queue, which is passed to nextTick in the next-tick module

There is also a constant MAX_UPDATE_COUNT = 100 in the Scheduler module.

The next-tick module maintains a task queue, and the nextTick method will put the tasks to be executed into a micro-queue, which is generally implemented using Promise. MutationObserver, setImmediate, setTimeout(flushCallbacks, 0) are then used.

So Vue updates (execution of the render function) are asynchronous.

NextTick is exposed to developers via this.$nextTick. If you use nextTick before the data update, you get the data before the update, and if you use nextTick after the data update, you get the changed value.

The overall process