preface
Vue3 took two years to develop, 99 contributors, 2,600 submissions, and 628 PR
Vue3 supports most of the features of 2.
Performance improvements:
1. Reduce package size by 41%
2. Initial render is 55% faster and updates are 133% faster
3. Reduced memory usage by 54%
New Composition API and other features, and better typescript support.
Learning and using Vue3 is imperative.
In this article, we will briefly compare the responsivity principle of Vue2 and Vue3.
The responsive principle of Vue2.0
1. The first Object. DefineProperty
Vue2 uses the native JS API Object. DefineProperty to intercept access to the name attribute of a data Object.
When accessing, the get function is executed. When a property changes, listen for that change, using the set function.
Here’s an example:
const data = {};
let name = 'Vue';
Object.defineProperty(data, 'name', {get: function(){
console.log('get');
return name;
},
set: function (newValue){
console.log('set');
name = newValue;
// Re-render the view}})Copy the code
2. Basic responsive implementation
I have many more, reactive procedures, which look like this:
const data = {
name: 'OrzR3'.age: 30
}
// become responsive data
observer(data);
function observer(target){
if(typeoftarget ! = ='object' || target === null) {return target;
}
for(let key in target){
defineReactive(target, key, target[key])
}
}
function defineReactive(target, key, value){
Object.defineProperty(target, key,{
get(){
return value;
},
set(newValue){
if(newValue ! == value){ value = newValue();console.log('Update view');
}
}
})
}
data.name = 'Test';
// Console prints to update the view
Copy the code
Vue source code is a bit more complex, judging more cases, but the core logic, that’s the logic.
3. Handle complex objects
If the property in the object is still an object, go ahead and call observe, in the set method, listen for the newly set value
const data = {
name: 'OrzR3'.age: 30.friend: {friendName: 'sven'}}// become responsive data
observer(data);
function observer(target){
if(typeoftarget ! = ='object' || target === null) {return target;
}
for(let key in target){
defineReactive(target, key, target[key])
}
}
function defineReactive(target, key, value){
// If the property of the object is still an object, continue to call observe
observer(value);
Object.defineProperty(target, key,{
get(){
return value;
},
set(newValue){
// Listen for the new value
observer(newValue);
if(newValue ! == value){ value = newValue();console.log('Update view');
}
}
})
}
data.name = 'Test';
data.age = { number: 40 }
// Console prints to update the view
Copy the code
4. Handle arrays
The view is updated as it changes based on the index.
With methods like push, you manipulate arrays without updating the view.
The method that needs to override the original array.
const oldArrayProto = Array.prototype;
const newArrProto = Object.create(oldArrayProto);
console.log('old', oldArrayProto);
console.log('new', newArrProto);
['push'.'pop'.'shift'.'unshift'.'splice'].forEach(methodName= >{
newArrProto[methodName] = function(){
console.log('Update view');
oldArrayProto[methodName].call(this. arguments); }})Copy the code
const data = {
name: 'OrzR3'.age: 30.friend: {friendName: 'sven'
},
colors: ['red'.'orange'.'green']}// Save the array prototype
const oldArrayProto = Array.prototype;
const newArrProto = Object.create(oldArrayProto);
console.log('old', oldArrayProto);
console.log('new', newArrProto);
['push'.'pop'.'shift'.'unshift'.'splice'].forEach(methodName= >{
newArrProto[methodName] = function(){
console.log('Update view');
oldArrayProto[methodName].call(this. arguments); }})// become responsive data
observer(data);
function observer(target){
if(typeoftarget ! = ='object' || target === null) {return target;
}
// Determine when to convert data into responsive data
// If the data is an array, modify the prototype to the newly created prototype
if(Array.isArray(target)){
target.__proto__ = newArrProto;
}
for(let key in target){
defineReactive(target, key, target[key])
}
}
function defineReactive(target, key, value){
// If the property of the object is still an object, continue to call observe
observer(value);
Object.defineProperty(target, key,{
get(){
return value;
},
set(newValue){
// Listen for the new value
observer(newValue);
if(newValue ! == value){ value = newValue();console.log('Update view');
}
}
})
}
data.name = 'Test';
data.age = { number: 40 }
// The view is updated as it changes according to the index
data.colors[0] = 'blue';
// With methods like push, arrays are manipulated without updating the view
// Need to override the original array method
data.colors.push('blue');
// Console prints to update the view
Copy the code
disadvantages
-
Object. DefineProperty deep listening, poor performance.
-
Problems with responsive data using Object.defineProperty:
-
If the data is an object and the hierarchy is deep, the depth listening is continued until the property is a common value. When the data is complex, it freezes.
Therefore, use proxy in VUe3 to solve this problem. Listening is enabled only when the proxy uses data.
-
Also, using Object.defineProperty for listening, when data is deleted, new properties are added to the data and the view is not updated.
-
Object.defineproperty has no way to handle data deletion and new attributes.
-
Therefore, when data is deleted, the vue.delete method is used. The vue. set method is used to add attributes to data.
This series of problems has been optimized in Vue3.0.
The principle of Vue3.0 responsiveness
Vue3.0 uses proxy instead of defineProperty in VUe2.0 to monitor mechanism changes and achieve better performance. Makes up for some of the shortcomings of Vue2.0.
For example, use a proxy to implement responsiveness
// proxy The proxy is stored in WeakMap
// toProxy stores the proxy object
const toProxy = new WeakMap(a);// toProxy stores the object before the proxy
const toRaw = new WeakMap(a);function trigger() {
console.log("Trigger view update");
}
function isObject(target) {
return typeof target === "object"&& target ! = =null;
}
function reactive(target) {
if(! isObject(target)) {return target;
}
// If the proxy table already exists, return the result
let proxy = toProxy.get(target);
if (proxy) {
return proxy;
}
// If the object has already been propped, return it unchanged
if (toRaw.get(target)) {
return target;
}
const handlers = {
set(target, key, value, receiver) {
View updates can be triggered directly if private attributes are triggered
if (target.hasOwnProperty(key)) {
trigger();
}
return Reflect.set(target, key, value, receiver);
},
get(target, key, receiver) {
const res = Reflect.get(target, key, receiver);
if (isObject(target[key])) {
// If the property is an object, call recursively
return reactive(res);
}
return res;
},
deleteProperty(target, key) {
return Reflect.deleteProperty(target, key); }};// proxy es6
let observed = new Proxy(target, handlers);
toProxy.set(target, observed); // The original object, the result of the proxy
toRaw.set(observed, target);
return observed;
}
let obj = {
name: "OrzR3".list: [1.2.3]};let p = reactive(obj);
p.name = "sven";
p.list.push(4);
Copy the code
The appendix
WeakMap
WeakMap is an ES6 syntax that represents weakly referenced objects.
In JavaScript, when we create an object, we create a strong reference:
var obj = new Object(); It is only possible to reclaim objects referenced by obj if we manually set obj = null.
And if we can create a weakly referenced object:
Var obj = new WeakObject(); We do nothing but wait quietly for the garbage collection mechanism to execute, and objects referenced by OBj will be collected.
WeakMap saves you the need to manually delete an object’s associated data, so consider using WeakMap when you can’t or don’t want to control the life cycle of the associated data.
Reflect and Proxy
Proxy and Reflect are apis introduced by ES6 for manipulating objects.
The Proxy intercepts operations such as reading and function invocation of the target object, and then processes the operations. It does not operate directly on objects, but rather acts like a proxy mode, operating through objects’ proxy objects, and as it does so, additional operations can be added as needed.
Reflect can be used to get the behavior of a target Object, which is similar to Object but more readable and provides a more elegant way to manipulate objects. Its methods correspond to Proxy.
See documentation for details:
www.runoob.com/w3cnote/es6…