English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Vue.js Principle Analysis - Detailed Explanation of Observer Module

Introduction

Observer is the most important module in Vue core (I think), which can realize the reactive update of view and data, and relies entirely on the support of observer.

Note:This article is for [email protected]

The code location of observer module in Vue project is src/core/The observer module is divided into these parts:

  1. Observer: The observer of data, keeping the read and write operations of the data object under its supervision
  2. Watcher: The subscriber of data, the change of data will notify Watcher, and then Watcher will perform corresponding operations, such as updating the view
  3. Dep: The link between Observer and Watcher, when data changes, it will be observed by Observer and then notified to Watcher by Dep

The illustration is as follows:

Observer

Observer class is defined in src/core/observer/Let's take a look at the constructor of Observer in index.js first

constructor (value: any) {
 this.value = value
 this.dep = new Dep()
 this.vmCount = 0
 def(value, '__ob__', this)
 if (Array.isArray(value)) {
 const augment = hasProto
 ? protoAugment
 : copyAugment
 augment(value, arrayMethods, arrayKeys)
 this.observeArray(value)
 }
 this.walk(value)
 }
}

value is the data object to be observed. In the constructor, the __ob__ property will be added to value as a flag indicating that the data has been observed by Observer. If value is an array, use observeArray to traverse value and observe each element of value individually. If value is an object, use walk to traverse each key on value and use defineReactive to obtain the set of each key/get control.

Here is an explanation of the functions used above:

  • observeArray: Traverse the array and call observe on each element of the array
  • observe: Checks whether the object has the __ob__ attribute, if it exists, it indicates that the object is already under the observation of Observer, if it does not exist, then new Observer to observe the object (there are some judgment logic, for the sake of understanding, it is not elaborated on)
  • walk: Traverse each key of the object, and call defineReactive on each key data of the object
  • defineReactive: Sets the key attribute of the object through Object.defineProperty, so that it can capture the set of the attribute value/Get action. Generally, it is the instance object of Watcher that performs the get operation, at this time, the instance object of Watcher will be automatically added to the dependency array of Dep instance. When the set is triggered externally, it will notify all dependent watchers for updates through the notify of Dep instance.

If the above text description is not understood, you can see the figure below:

Dep

Dep is the link between Observer and Watcher, and can also be considered as the subscription system serving Observer. Watcher subscribes to the Dep of an Observer, and when the data observed by the Observer changes, it notifies all subscribed Watchers through Dep.

Dep provides several interfaces:

  • addSub: Receives the parameter as a Watcher instance and stores the Watcher instance in the array recording dependencies
  • removeSub: Corresponds to addSub, used to remove the Watcher instance from the array recording dependencies
  • depend: Stores the current watcher instance that needs to be operated on in Dep.target. Calling depend will call the addDep method of the Watcher instance, and the function of addDep can be seen in the introduction of Watcher below
  • notify: Notify all watchers in the dependency array to perform update operations

Watcher

Watcher is used to subscribe to data changes and execute corresponding operations (such as updating the view). The constructor function of Watcher is defined as follows:

constructor (vm, expOrFn, cb, options) {
 this.vm = vm
 vm._watchers.push(this)
 // options
 if (options) {
 this.deep = !!options.deep
 this.user = !!options.user
 this.lazy = !!options.lazy
 this.sync = !!options.sync
 }
 this.deep = this.user = this.lazy = this.sync = false
 }
 this.cb = cb
 this.id = ++uid // 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 = process.env.NODE_ENV !== 'production'
 ? expOrFn.toString()
 : ''
 if (typeof expOrFn === 'function') {
 this.getter = expOrFn
 }
 this.getter = parsePath(expOrFn)
 if (!this.getter) {
 this.getter = function () {}
 process.env.NODE_ENV !== 'production' && warn(
 `Failed watching path: "${expOrFn}" ` +
 'Watcher only accepts simple dot-delimited paths. ' +
 'For full control, use a function instead.',
 vm
 )
 }
 }
 this.value = this.lazy
 ? undefined
 : this.get()
}

In the parameters, vm represents the component instance, expOrFn represents the data field to be subscribed (a string, such as a.b.c) or a function to be executed, cb represents the callback function after the watcher runs, and options is the option object, which includes configurations such as deep, user, lazy, etc.

The watcher instance has these methods:

  • get: Sets Dep.target to the current watcher instance and internally calls this.getter. If at this moment a data object observed by Observer is accessed, the current watcher instance will automatically subscribe to the Dep instance of the data object
  • addDep: Receive the parameter dep (Dep instance), making the current watcher subscribe to dep
  • cleanupDeps: Clear the subscription information recorded by newDepIds and newDep on dep
  • update: Run the watcher immediately or add the watcher to the queue to wait for unified flush
  • run: Run the watcher, call this.get() to get the value, and then trigger the callback
  • evaluate: Call this.get() to get the value
  • depend: Traverse this.deps, making the current watcher instance subscribe to all dep
  • teardown: Remove all subscriptions of the current watcher instance

Array methods

In src/core/observer/In array.js, Vue framework has modified the push, pop, shift, unshift, sort, splice, reverse methods of arrays, which will automatically trigger dep.notify() when calling these methods of the array, solving the problem that the update cannot be triggered after calling these functions to change the array.

There is also an explanation of this in the official Vue documentation: http://cn.vuejs.org/v2/guide/list.html#mutation-methods

Summary

That's all the content of this article. I hope the content of this article can bring some help to everyone's learning or work. If you have any questions, you can leave a message for communication.

Statement: The content of this article is from the Internet, and the copyright belongs to the original author. The content is contributed and uploaded by Internet users spontaneously. This website does not own the copyright, has not been manually edited, and does not assume any relevant legal liability. If you find any content suspected of copyright infringement, please send an email to: notice#oldtoolbag.com (Please replace # with @ when sending an email to report abuse, and provide relevant evidence. Once verified, this site will immediately delete the infringing content.)

You May Also Like