Warehouse address: VUe-next
In Vue2. X, the responsibility of data requires different processing methods for Object and Array types. Object types convert properties to getters/setters via Object.defineProperty. This process requires recursive detection of all Object keys to achieve depth detection.
In order to sense the changes of Array, several methods of changing the contents of Array itself on Array prototype are intercepted. Although the Array can be responded to, there are also some problems, or not convenient enough. Also, the recursive implementation of getter/setter by defineProperty has some performance issues.
Vue3. X implements the Proxy API provided by ES6 in a responsive manner.
proxy
The Proxy API is much more powerful than the old defineProperty API, Proxy can Proxy arrays, and the API provides multiple methods to achieve a lot of functionality.
There are two main attributes: get, set, and some of the more overlooked details.
1.get
.set
- Basic Use of Proxy
var data = {
name: 'Just for fun'.age: Awesome!
}
var proxyData = new Proxy(data, {
get(target, key, receiver) {
// receiver --> proxyData
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result // Return the result
},
set(target, key, val, receiver) {
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // Check whether the configuration is successful
},
deleteProperty(target, key){
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result // Check whether the deletion is successful}})// vue2.x
/* A function that listens for properties in response to changes as soon as new values are assigned */
function defineReactive(obj, key, val){
var dep = new Dep();
Object.defineProperty(obj, key, {
get: function(){
if(Dep.target){
console.log('get')
dep.addSub(Dep.target);
}
return val;
},
set: function(newVal){
if(newVal === val){
return;
}
val = newVal;
// console.log(' ${val} changed ')
// Will be notified as soon as updates are madedep.notify(); }})}// It is much simpler than setting the value of vue2.x
/ / output
proxyData.name = 'Unreliable'The set name is not reliable"Unreliable."
// You can also directly monitor array operations
var data = ['a'.'b'.'c']
/ / output
proxyData.push('d')
get push
get length
set 3 d
set length 4
Copy the code
As you can see from the output, the push operation not only manipulates the current data, but also changes the length property of the array itself. In addition to setting the third subscript of the array to D, the push operation also changes the array’s length to 4. It also triggers get to get the push and Length properties.
2. Multiple triggers
var proxyData = new Proxy(data, {
get(target, key, receiver) {
// receiver --> proxyData
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result // Return the result
},
set(target, key, val, receiver) {
After pushing d above, the length is already 4, no need to set it again
if(val === target[key]){
return true
}
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // Check whether the configuration is successful
},
deleteProperty(target, key){
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result // Check whether the deletion is successful}})/ / output
proxyData.push('d')
get push
get length
set 3 d
Copy the code
3. Deep listening
const data = {
name: 'Just for fun'.age: Awesome!.info: {
title: 'Unreliable'
},
a: {
b: {
c: {
d: 1}}}}function reactive(target = {}) {
if(typeoftarget ! = ='object' || target == null) {return target
}
// Proxy configuration
const proxyConf = {
get(target, key, receiver) {
// receiver --> proxyData
const result = Reflect.get(target, key, receiver)
console.log('get', key)
return result
// Deep monitor
// return reactive(result)
},
set(target, key, val, receiver) {
After pushing d above, the length is already 4, no need to set it again
// if(val === target[key]){
// return true
// }
// const ownKeys = Reflect.ownKeys(target)
// if(ownKeys.includes(key)) {
// // already exists
// }else {
/ / / / new
// }
const result = Reflect.set(target, key, val, receiver)
console.log('set', key, val)
return result // Check whether the configuration is successful
},
deleteProperty(target, key){
const result = Reflect.deleteProperty(target, key)
console.log('delete property', key)
return result // Check whether the deletion is successful}}// Generate a proxy object
const observed = new Proxy(target, proxyConf)
return observed
}
const proxyData = reactive(data)
Copy the code
As can be seen from the output, the proxy represents the outermost data when fetchingproxyData.info
When,info
It is still a normal object with no proxy properties. How to implement internal object proxy, you can seevue3.xThe practice of,vue3
Put the internal object proxy in GET
function createGetter(isReadonly = false, shallow = false) {
return function get(target, key, receiver) {
if (isObject(res)) {
return isReadonly ? readonly(res) : reactive(res)
}
return res
}
}
Copy the code
The following is the data obtained after adding the deep listening, you can see that the INFO has been listened
The advantages and disadvantages
Vue2 uses object.defineProperty as an implementation of the reactive principle and has limitations, such as:
- Deep listening requires one-time recursion
- Vue. Set cannot listen for new attributes in data
- Unable to listen for delete properties, vue.delete is used to overcome the restriction of deleting but not updating views
- Unable to listen on native arrays
Vue3. X:
- Arrays are not intercepted separately
- Inert recursion, when do you use when do you recurse
Reflect
- will
Object
Some methods of the object that are clearly internal to the language (e.gObject.defineProperty
), inReflect
On the object. - Modify some
Object
Method to make it more reasonable - let
Object
Operations become functional behavior.
/ / the old way
'assign' in Object // true
/ / a new way
Reflect.has(Object.'assign') // true
Copy the code