Home > Web Front-end > JS Tutorial > body text

Vue source code analysis of Observer implementation process

亚连
Release: 2018-05-28 13:55:29
Original
1446 people have browsed it

This article mainly introduces the Observer implementation process of Vue source code analysis. The main function of Observer is to implement the process of touch -Data (getter) - Collect as Dependency, which is the process of relying on collection. Friends who are interested Let’s learn together

Introduction:

This article is an in-depth review of Vue’s official documentation on responsive principles (https://cn.vuejs.org/v2/guide/reactivity. html) and restore the implementation process through source code.

The responsive principle can be divided into two steps, the process of relying on collection and the process of triggering and re-rendering. There are three very important classes in the dependency collection process, namely Watcher, Dep, and Observer. This article mainly explains Observer.

This article explains the content of Observer that was not covered in the previous article. Let’s first look at this picture on the official website:

The main function of Observer It realizes the process of touch -Data(getter) - Collect as Dependency in the picture above, which is the process of dependency collection.

Let’s take the following code as an example to sort it out:

(Note: Swipe left and right to view the complete code, the same below)

varvm = newVue({
el: '#demo',
data: {
firstName: 'Hello',
fullName: ''
},
watch: {
firstName(val) {
this.fullName = val + 'TalkingData';
},
}
})
Copy after login

In the source code, the process of instantiation by restoring Vue, step by step from the beginning to the source code of the Observer class is as follows (a lot of code that is not discussed in this article is omitted):

// src/core/instance/index.js
functionVue(options) {
if(process.env.NODE_ENV !== 'production'&&
!(thisinstanceofVue)
) {
warn('Vue is a constructor and should be called with the `new` keyword')
}
this._init(options)
}
// src/core/instance/init.js
Vue.prototype._init = function(options?: Object) {
constvm: Component = this
// ...
initState(vm)
// ...
}
// src/core/instance/state.js
exportfunctioninitState(vm: Component) {
// ...
constopts = vm.$options
if(opts.data) {
initData(vm)
}
// ...
}
functioninitData(vm: Component) {
letdata = vm.$options.data
data = vm._data = typeofdata === 'function'
? getData(data, vm)
: data || {}
// ...
// observe data
observe(data, true/* asRootData */)
}
Copy after login

In the initData method, the "observation" of the data in the data item begins, and all data will be made observable. Next, look at the code of the observe method:

// src/core/observer/index.js
functionobserve(value: any, asRootData: ?boolean): Observer| void{
// 如果不是对象,直接返回
if(!isObject(value) || value instanceofVNode) {
return
}
letob: Observer | void
if(hasOwn(value, '__ob__') && value.__ob__ instanceofObserver) {
// 如果有实例则返回实例
ob = value.__ob__
} elseif(
// 确保value是单纯的对象,而不是函数或者是Regexp等情况
observerState.shouldConvert &&
!isServerRendering() &&
(Array.isArray(value) || isPlainObject(value)) &&
Object.isExtensible(value) &&
!value._isVue
) {
// 实例化一个 Observer
ob = newObserver(value)
}
if(asRootData && ob) {
ob.vmCount++
}
returnob
}
Copy after login

The function of the observe method is to create an Observer instance for data and return it. If data has the ob attribute, it means it has been If there is an Observer instance, return the existing instance. Vue's responsive data will have an ob attribute, which stores the Observer instance of the attribute to prevent repeated binding. Let’s look at what happens in the process of new Observer(value):

exportclassObserver{
value: any;
dep: Dep;
vmCount: number; // number of vms that has this object as root $data
constructor(value: any) {
this.value = value
this.dep = newDep()
this.vmCount = 0
def(value, '__ob__', this)
if(Array.isArray(value)) {
// ...
this.observeArray(value)
} else{
this.walk(value)
}
}
walk (obj: Object) {
constkeys = Object.keys(obj)
for(leti = 0; i < keys.length; i++) {
defineReactive(obj, keys[i], obj[keys[i]])
}
}
observeArray (items: Array<any>) {
for(leti = 0, l = items.length; i < l; i++) {
observe(items[i])
}
}
}
Copy after login

As you can see from the source code, there are two main judgments made in the process of instantiating Observer. . If it is an array, call the oberser method again for each item in the array to observe; if it is a non-array object, traverse each attribute of the object and call the defineReactive method on it. The defineReactive method here is the core! Dependency collection is completed by using the Object.defineProperty method to add get/set to each property that needs to be observed. After dependencies are collected, each property will have a Dep to save all Watcher objects. According to the example at the beginning of the article, get/set is added to firstName and fullName respectively, and each of them has a Dep instance to save all the Watcher objects that observe them. The following is the source code of defineReactive:

exportfunctiondefineReactive(
obj: Object,
key: string,
val: any,
customSetter?: ?Function,
shallow?: boolean
) {
constdep = newDep()
// 获取属性的自身描述符
constproperty = Object.getOwnPropertyDeor(obj, key)
if(property && property.configurable === false) {
return
}
// cater for pre-defined getter/setters
// 检查属性之前是否设置了 getter/setter
// 如果设置了,则在之后的 get/set 方法中执行设置了的 getter/setter
constgetter = property && property.get
constsetter = property && property.set
// 通过对属性再次调用 observe 方法来判断是否有子对象
// 如果有子对象,对子对象也进行依赖搜集
letchildOb = !shallow && observe(val)
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get: functionreactiveGetter() {
// 如果属性原本拥有getter方法则执行
constvalue = getter ? getter.call(obj) : val
if(Dep.target) {
// 进行依赖收集
dep.depend()
if(childOb) {
// 如果有子对象,对子对象也进行依赖搜集
childOb.dep.depend()
// 如果属性是数组,则对每一个项都进行依赖收集
// 如果某一项还是数组,则递归
if(Array.isArray(value)) {
dependArray(value)
}
}
}
returnvalue
},
set: functionreactiveSetter(newVal) {
// 如果属性原本拥有getter方法则执行
// 通过getter方法获取当前值,与新值进行比较
// 如果新旧值一样则不需要执行下面的操作
constvalue = 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 !== &#39;production&#39;&& customSetter) {
customSetter()
}
if(setter) {
// 如果属性原本拥有setter方法则执行
setter.call(obj, newVal)
} else{
// 如果原本没有setter则直接赋新值
val = newVal
}
// 判断新的值是否有子对象,有的话继续观察子对象
childOb = !shallow && observe(newVal)
// 通知所有的观察者,更新状态
dep.notify()
}
})
}
Copy after login

According to the Chinese comments in the source code, you should be able to understand what work is done during the execution of defineReactive. In fact, the whole process is recursive, adding getters/setters for each property. For getters/setters, it is also necessary to recurse (judge sub-objects) for each property to complete the observer pattern. For getter, it is used to complete dependency collection, that is, dep.depend() in the source code. For setters, once a piece of data triggers its set method, an update message will be published, notifying all observers of the data that it will also change. That is dep.notify() in the source code.

The above is what I compiled for everyone. I hope it will be helpful to everyone in the future.

Related articles:

vue2.0 Detailed explanation of the difference between asset files and static

##Angular 5.x study notes Router (routing) application

React Router v4 pit guide

The above is the detailed content of Vue source code analysis of Observer implementation process. For more information, please follow other related articles on the PHP Chinese website!

Related labels:
source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template