Explain in detail the principle of data response to realize vue
- 2021-10-24 18:55:26
- OfStack
This article is mainly for friends who don't know or have not been exposed to vue responsive source code. Its main purpose is to have a basic understanding of vue responsive principle. If asked such questions in the interview, you can know what the interviewer wants you to answer. [PS: If there is anything wrong in the article, please correct it.]
Understanding of response formula
Responsive, as its name implies, is a change in data, which will cause the view to be updated. This article mainly analyzes the implementation of object and array reactive principle in vue 2.0, and we will leave it for the next article to analyze dependency collection and view update.
In vue, when we talk about responsive data, 1 generally refers to data of array type and object type. vue hijacks the attributes of objects by Object. defineProperty method, and arrays are realized by overriding arrays. Let's simply implement 1.
First, we define a data that needs to be intercepted
const vm = new Vue({
data () {
return {
count: 0,
person: { name: 'xxx' },
arr: [1, 2, 3]
}
}
})
let arrayMethods
function Vue (options) { // Here only consider the right data Operation of data
let data = options.data
if (data) {
data = this._data = typeof data === 'function' ? data.call(this) : data
}
observer (data)
}
function observer(data) {
if (typeof data !== 'object' || data === null) {
return data
}
if (data.__ob__) { // Existence __ob__ Property, indicating that it has been intercepted
return data
}
new Observer(data)
}
Please continue to look at the implementation and function of arrayMethods, Observer and __ob__ here
Implement the Observer class
class Observer {
constructor (data) {
Object.defineProperty(data, '__ob__', { // In data Definition above __ob__ Attribute, which is needed in array hijacking
enumerable: false, // Innumerable
configurable: false, // Not configurable
value: this // Value is Observer Instances
})
if (Array.isArray(data)) { // Intercept an array
data.__proto__ = arrayMethods // Prototype inheritance
this.observerArray(data)
} else { // Object to intercept
this.walk(data)
}
}
walk (data) {
const keys = Object.keys(data)
for(let i = 0; i < keys.length; i++) {
const key = keys[i]
defineReactive(data, key, data[key])
}
}
observerArray (data) { // Intercept every 1 Items
data.forEach(value => observer(value))
}
}
Object interception
Several points to be paid attention to in hijacking objects:
Traverse the object. If the value is still an object type, you need to call the observer observation method again If the new value set is an object type, it also needs to be intercepted
// Handle interception of objects
function defineReactive(data, key, value) {
observer(value) // If value Value is still an object type, requiring recursive hijacking
Object.defineProperty(data, key, {
get() {
return value
},
set(newValue){
if (newValue === value) return
value = newValue
observer(newValue) // If you set the newValue Value is also an object type and needs to be hijacked
}
})
}
Hijacking of arrays
Several points to pay attention to when hijacking arrays:
Arrays use the idea of function hijacking (slicing programming) to intercept data The newly added value in the array needs to be intercepted again if it is an object type
const oldArrayPrototype = Array.prototype
arrayMethods = Object.create(oldArrayPrototype)
const methods = ['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'] // Methods that can change the original array
methods.forEach(method => {
arrayMethods[methods] = function (...args) {
const result = oldArrayPrototype[methods].call(this, ...args)
const ob = this.__ob__ // this Is the array that calls the modification method
let inserted; // The collection of new items in the array, which needs to be intercepted again
switch(methods) {
case 'push':
case 'unshift':
inserted = args
case 'splice':
inserted = args.slice(2) // Because splice No. 1 2 The following parameters are added
}
if (inserted) {
ob.observerArray(inserted)
}
return result
}
})
Principle summary
In the interview, if we need to write the responsive principle of vue by hand, the above code is enough. But we learn the source code of vue, if in the interview can give the following to sum up the answer can get the favor of the interviewer.
Responsive principle of vue 2.0 source code:
Because objects are intercepted recursively, the deeper the data hierarchy, the worse the performance Arrays are not intercepted using Object. defineProperty because performance is poor if there are too many array items Only the data defined in data will be intercepted, and the attributes added by vm. newObj = 'xxx' on the instance will not be intercepted later Changing the index and length of the array will not be intercepted, so it will not cause the view to be updated If you need to intercept new attributes on data and change the index and length of the array, you can use the $set method You can use the Object. freeze method to optimize data and improve performance without overriding the set and get methodsvue 3.0 source code responsive principle:
In version 3.0, proxy is used instead of Object. defineProperty, which has 13 interception methods, and does not need to process objects and arrays separately, nor does it need to intercept recursively, which is also the place where its performance is improved the most Simple Implementation of Responsive Principle in vue 3.0
const handler = {
get (target, key) {
if (typeof target[key] === 'object' && target[key] !== null) {
return new Proxy(target[key], handler)
}
return Reflect.get(target, key)
},
set (target, key, value) {
if(key === 'length') return true
console.log('update')
return Reflect.set(target, key, value)
}
}
const obj = {
arr: [1, 2, 3],
count: { num: 1 }
}
// obj Is the target object of the proxy, handler Is a configuration object
const proxy = new Proxy(obj, handler)