一、vue2响应式原理
vue2的响应式是通过Object.definProperty方法,对data中的属性进行递归处理,为每一个属性加入getter和setter方法,在getter中注入依赖收集的逻辑,在setter中定义发布者逻辑,每次属性改变时,通知所有在getter中收集的依赖项进行更新
const target = {
name:"default name"
}
let name=target.name
const handler = {
get(){
// 订阅者逻辑
return name;
},
set(newVal){
// 发布者逻辑
name=newVal;
}
}
Object.defineProperty(target,handler);
// vue 模板
<template>
// 触发订阅者逻辑,收集div依赖
<div v-text="name"></div>
</tamplate>
// vue javascript
this.name="update"; //触发搜集者逻辑
二、vue3响应式原理
vue3使用的式es6的Proxy 对象,它仍然遵循发布者和订阅者的设计模式,在组件或者式html元素使用响应式对象时,将其收集为依赖。等到响应式对象发生改变时通知所有的依赖项进行更新
Proxy 对象
const obj = {
name: "default name",
};
const handle = {
get(obj, field) {
console.log("get", obj, field);
return obj[field];
},
set(obj, field, value) {
console.log("get", obj, field);
obj[field] = value;
},
deleteProperty(obj, field) {
delete obj[field];
console.log("del", obj, field);
},
};
const proxy1 = new Proxy(obj, handle);
console.log("读取", proxy1.filed);
proxy1.name = "updated name";
delete proxy1.name;
console.log("源数据", obj);
proxy对象和源对象obj是相互独立的两个对象,proxy只做拦截作用
在你未定义set时,他会自动做一个分发代理,即
const obj = {
name:"default name"
}
const handler = {}
const proxy1 = new Proxy(obj,handler)
proxy1.name="updated name";
console.log(obj.name); // updated name
定义handler.set
之后,你需要手动去改变obj的值,即
const obj = {
name:"default name"
}
const handler = {
set(target,prop,value){
target[prop] = value; // 需要手动修改obj的值
}
}
三、对比vue2,vue3响应式的优势
vue2数组中新增属性不会有响应式
data(){
return {
list:[1,2],
objList:[{name:1}]
}
}
this.list[1]='9'; // 基本类型数组,改变原有的元素不会触发视图更新
this.objList[0].name="update";// 引用类型数,改变原有元素属性,触发更新
this.objList[1]={name:2}; // 引用类型数组,新增元素不会触发更新
vue2对象属性的新增、删除无法监听
data(){
return {
obj:{name:"name"}
}
}
delete this.obj.name; // 不触发视图更新
this.obj.age="age"; // 不触发视图更新
在vue2中要使属性变成响应式只能是放在data或者调用$set
data(){
return {
name:'name',
list:[]
}
}
this.$set(this.list,0,1)
vue2 无法处理map、set中属性改变的响应式
data(){
return {
map:new Map([['key0','val1']])
set:new Set(['value1','value2'])
}
}
this.map.set('key1','val1'); // 不触发更新
this.map.delete('key1');// 不触发更新
this.set.add('value0');// 不触发更新
this.set.delete('value0');// 不触发更新
需要递归data属性,如果data对象层级复杂,将会比较消耗性能