美文网首页前端
Vue3和Vue2响应式原理

Vue3和Vue2响应式原理

作者: 咸鱼不咸_123 | 来源:发表于2022-05-14 12:39 被阅读0次

1.响应式原理

1.1 什么是响应式

我们先看一下响应式意味着什么?我们先来看一段代码

  • m有一个初始化的值,有一段代码使用了这个值
  • 那么在m有一个新的值时,这段代码可以自动重新执行
let m=100;


// * 当m发生变化时,这段代码会自动重新执行
// 一段代码
console.log(m);
console.log(m*2);
console.log(m**2);

m=200;

1.2 响应式函数的封装

如果我们想要对obj进行响应式收集,如果obj发生了变化,就执行了一部分代码,但是执行前将需要响应式的代码进行收集,所以就创建了一个函数,来收集当前obj对象的一些响应式收集

  • 在全局中创建了一个变量reactiveFns来收集 obj的响应式函数

    • 因为obj的响应式函数肯定不止一个
  • 声明一个watchFn函数,参数是一个函数,在函数内部用reactiveFns来收集响应式函数

  • 使用watchFn(fn)来收集想要响应式函数

  • 最后将reactiveFns遍历执行内部的响应式函数

// * 封装一个响应式的函数
let reactiveFns=[];
function watchFn(Fn){
  reactiveFns.push(Fn)
}

const obj={
  name:"wjy",
  age:18
}

watchFn(function (){
  const newName=obj.name;
  console.log("你好~何沅洲");
  console.log("Hello World");
  console.log(obj.name);
})

watchFn(function (){
  console.log(obj.name);
})
function bar(){
  console.log("普通的其他的函数");
  console.log("这个函数不需要有任何响应式");
}

obj.name="hyz";
 
reactiveFns.forEach(fn=>fn())

1.3 依赖收集类的封装

前面的设计存在缺陷:

  • 一个数组存取是这个对象的所有的响应式依赖
  • 而在实际中,应该对象的每一个属性都对应一个数组来收集,但如果在全局创建大量的数组来保存,势必不好操作。

所以我们设计了一个依赖收集类Depend:

  • constructor()
    • 初始化一个reactiveFns来存储响应式函数
  • addDepend(reactiveFn)
    • 将响应式函数收集
  • notify()
    • 将对应的响应式函数执行
// * 每个属性对应一个类
class Depend{
  constructor() {
    this.reactiveFns=[];
  }
  addDepend(reactiveFn){
    this.reactiveFns.push(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }
}

将对应的watchFn函数修改:

// * 封装一个响应式的函数
const depend=new Depend();
function watchFn(Fn){
  depend.addDepend(Fn)
}

所有的代码如下:


// * 每个属性对应一个类
class Depend{
  constructor() {
    this.reactiveFns=[];
  }
  addDepend(reactiveFn){
    this.reactiveFns.push(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }
}

// * 封装一个响应式的函数
const depend=new Depend();
function watchFn(Fn){
  depend.addDepend(Fn)
}

const obj={
  name:"wjy",
  age:18
}
 
watchFn(function (){
  const newName=obj.name;
  console.log("你好~何沅洲");
  console.log("Hello World");
  console.log(obj.name);
})

watchFn(function (){
  console.log(obj.name);
})
function bar(){
  console.log("普通的其他的函数");
  console.log("这个函数不需要有任何响应式");
}

obj.name="hyz";
 
depend.notify()

但是在代码最后是手动去调用对象的响应式函数。

1.4 监听对象的变化

在前面设计中存在缺陷:

  • 当对象发生了变化,应该是自动去调用对应的响应式函数,而不是手动去调用

监听对象的变化有两种方式:

  • Proxy
  • Object.defineProperty()

所以我们给obj创建一个Proxy对象(代理对象),使用代理对象代替原来对象的操作,因为代理对象内部有很多捕捉器,可以实时捕捉用户对代理对象操作。

  • 在set捕捉器内部,去调用所有的响应式函数。(在对象内部属性发生变化时去调所有的响应式函数
    • depend.notify()
 // * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    depend.notify(); 
  }
})

所有的代码如下:


// * 每个属性对应一个类
class Depend{
  constructor() {
    this.reactiveFns=[];
  }
  addDepend(reactiveFn){
    this.reactiveFns.push(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }
}

// * 封装一个响应式的函数
const depend=new Depend();
function watchFn(Fn){
  depend.addDepend(Fn)
}

const obj={
  name:"wjy",
  age:18
}
 // * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    depend.notify(); 
  }
})
watchFn(function (){
  // const newName=obj.name;
  console.log("你好~何沅洲");
  console.log("Hello World");
  console.log(obj.name);
})

watchFn(function (){
  console.log(obj.name);
})
function bar(){
  console.log("普通的其他的函数");
  console.log("这个函数不需要有任何响应式");
}

objProxy.name="hyz";
objProxy.name="tqy";
objProxy.name="lzy"
// depend.notify() 

1.5 依赖收集的管理

  1. 使用一个WeakMap对象来存储每个对象。
  2. 为每个对象创建一个Map对象,对象的每个属性对应一个Depend对象,将key为属性,value为Depend对象添加到Map对象内部。
image-20220514004112875.png

所以我们封装了一个获取Depend函数:

  • 在全局中,创建一个变量targetMap(为了保存所有所有对应的映射关系)
// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  const map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  const depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}

修改响应式对象内部的set捕捉器:

  • 在里面获取对应的depend对象,然后再去调用它本身的所有依赖函数
// * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    // depend.notify(); 
    const depend=getDepend(target,key);
    depend.notify();
  }
}) 

所有的代码如下:


// * 每个属性对应一个类
class Depend{
  constructor() {
    this.reactiveFns=[];
  }
  addDepend(reactiveFn){
    this.reactiveFns.push(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }
}

// * 封装一个响应式的函数
const depend=new Depend();
function watchFn(Fn){
  depend.addDepend(Fn)
}


// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  const map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  const depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}
const obj={
  name:"wjy",
  age:18
}
 // * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    // depend.notify(); 
    const depend=getDepend(target,key);
    depend.notify();
  }
})
watchFn(function (){
  const newName=obj.name;
  console.log("你好~何沅洲");
  console.log("Hello World");
  console.log(obj.name);
})

watchFn(function (){
  console.log(obj.name);
})
function bar(){
  console.log("普通的其他的函数");
  console.log("这个函数不需要有任何响应式");
}

objProxy.name="hyz";
objProxy.name="tqy";
objProxy.name="lzy"
// depend.notify() 


// * 什么时候去收集封装依赖最合适呢
const info={
  address:"怀化"
}

// obj对象
// name depend
// age depend
//const objMap=new Map();
//objMap.set("name","nameDepend");
//objMap.set("age","ageDepend");

// info对象
// address depend

// const infoMap=new Map();
// infoMap.set("address","addressDepend")


// const targetMap=new WeakMap();
// targetMap.set(obj,objMap);
// targetMap.set(info,infoMap);

// // * 如果obj.name发生变化了
// targetMap.get(obj).get("name");//* 去找到name的depend
// depend.notify();//* 然后去执行 name的depend


1.6 正确的收集依赖

  • 重构了watchFn函数

    • 在外设置了一个变量activeReactiveFn用来暂存当前需要收集的响应式函数

    • 让函数先执行,在获取obj的key,进行响应式函数收集

    • 在函数执行前,将activeReactiveFn设置为Fn

    • 在函数执行后,将activeReactiveFn设置为null

      // * 封装一个响应式的函数
      let activeReactiveFn=null;
      function watchFn(Fn){
        activeReactiveFn=Fn;
        Fn();
        activeReactiveFn=null;
      }
      
  • 将收集依赖设置在 Proxy对象的get捕捉器里面

    • 先获取target的key属性的依赖收集对象
    • 进行判断,如果不为null,就将这个当前需要收集的响应式函数放入到depend数组中
      get:function(target,key,receiver){
        //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
        const depend=getDepend(target,key);
        // * 2. 给depend对象中添加响应函数
        if(activeReactiveFn){
            depend.addDepend(activeReactiveFn);
        }
        return Reflect.get(target,key,receiver);
      },
    

// * 每个属性对应一个类
class Depend{
  constructor() {
    this.reactiveFns=[];
  }
  addDepend(reactiveFn){
    this.reactiveFns.push(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }
}

// * 封装一个响应式的函数
let activeReactiveFn=null;
function watchFn(Fn){
  activeReactiveFn=Fn;
  Fn();
  activeReactiveFn=null;
  
}


// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  let  map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  let  depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}
const obj={
  name:"wjy",
  age:18
}
 // * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
    const depend=getDepend(target,key);
    // * 2. 给depend对象中添加响应函数
    if(activeReactiveFn){
        depend.addDepend(activeReactiveFn);
    }
  
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    // depend.notify(); 
    const depend=getDepend(target,key);
    depend.notify();
  }
})
watchFn(function (){
  console.log("--------------------第一个name函数开始------------");
  console.log("你好~何沅洲");
  console.log("Hello World");
  console.log(objProxy.name);
  console.log("--------------------第一个name函数结束------------");

})

watchFn(function (){
  console.log(objProxy.name,"demo function ------------");
})

watchFn(function(){
  console.log(objProxy.age,"age 发生变化是需要执行的--------1");
})

watchFn(function(){
  console.log(objProxy.age,"age 发生变化是需要执行的--------2");
})
function bar(){
  console.log("普通的其他的函数");
  console.log("这个函数不需要有任何响应式");
};
console.log("-------------------------------------------------------------改变obj的name值-----------------------------------");
objProxy.age=33;

1.7对Depend类的优化 (重构)

我们前面设计存在一些缺陷,例如不应该在Proxy对象内部的get中去操作全局变量,存在一定的不合理性。

我们应该在Depend类中进行收集依赖,而在get捕捉器,只需执行depend.depend()即可收集依赖。

所以进行第一步的Depend类的重构:

添加一个实例方法 depend:

  • 如果activeReactiveFn不为空就去收集依赖

修改了Proxy对象的get方法

// * 当前需要收集的响应式函数
let activeReactiveFn=null;
// * 每个属性对应一个类
class Depend{
 //以上的代码省略,只介绍重要的部分
  depend(){
    if(activeReactiveFn){
      this.reactiveFns.add(activeReactiveFn);
    }
  }
}

const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
    const depend=getDepend(target,key);
    // * 2. 给depend对象中添加响应函数
   depend.depend();
  
    return Reflect.get(target,key,receiver);
  },
    //以下代码暂时省略
})

如果我给watchFn函数添加如下的的一个函数:

watchFn(()=>{
  console.log(objProxy.name,"---------");
  console.log(objProxy.name,"+++++++");
})
console.log("--------------------objProxy发生了改变---------------");
objProxy.name="hyz"
image-20220513231508764.png

在objProxy.name发生变化的时候,执行了两次。

原因是:在新添加的函数内部使用了两次objProxy.name所以就添加了两次。

但实际上,无论在当前函数内部使用多少次name属性,就应该只为name的depend只收集一次当前的响应式函数。

所以:为了在当前属性的depend不应该重复收集响应式函数,我们应该将Depend类的属性设置为Set对象(本质:存放在Set内部的元素是不会重复的。)

  • Set对象和数组对象添加元素方法不一样,应该将push改为add

// * 当前需要收集的响应式函数
let activeReactiveFn=null;
// * 每个属性对应一个类
class Depend{
  constructor() {
    // * 解决方案是将原来的数组类型转化为set类型
    this.reactiveFns=new Set();
  }
  addDepend(reactiveFn){
    this.reactiveFns.add(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }

  depend(){
    if(activeReactiveFn){
      this.reactiveFns.add(activeReactiveFn);
    }
  }
}

再执行一次:结果恢复我想看到的了,无论在当前函数内部使用多少次name属性,就应该只为name的depend只收集一次当前的响应式函数。

image-20220513232357915.png

总的代码如下:


// * 当前需要收集的响应式函数
let activeReactiveFn=null;
// * 每个属性对应一个类
class Depend{
  constructor() {
    // * 解决方案是将原来的数组类型转化为set类型
    this.reactiveFns=new Set();
  }
  addDepend(reactiveFn){
    this.reactiveFns.add(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }

  depend(){
    if(activeReactiveFn){
      this.reactiveFns.add(activeReactiveFn);
    }
  }
}

// * 封装一个响应式的函数
function watchFn(Fn){
  activeReactiveFn=Fn;
  Fn();
  activeReactiveFn=null;
  
}


// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  let  map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  let  depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}
const obj={
  name:"wjy",
  age:18
}
 // * 监听对象的属性变化 : Proxy(vue3)和Object.defineProperty(vue2)
const objProxy=new Proxy(obj,{
  get:function(target,key,receiver){
    //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
    const depend=getDepend(target,key);
    // * 2. 给depend对象中添加响应函数
   depend.depend();
  
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    // * 获取对应对象的key的依赖收集
    const depend=getDepend(target,key);
    // * 依次执行依赖收集的响应式函数
    depend.notify();
  }
})

watchFn(()=>{
  // * 这里面有两个地方用到了name,所以如果objProxy.name发生了变化,,这里其实应该是只要执行一次的,但是实际上原来设计会执行两次
  console.log(objProxy.name,"---------");
  console.log(objProxy.name,"+++++++");
})
console.log("--------------------objProxy发生了改变---------------");
objProxy.name="hyz"

1.8 创建响应式对象(Vue3)

但如果我们有新的对象要进行响应式处理呢?那新创建一个 Proxy对象,然后在写Proxy对象内部写一大串同样的代码,如果有很多对象需要进行收集,那代码的数量就大大增加了,阅读性差。就比如以下,以下代码还是只是新增了一个响应式对象

// * 但如果我们有新的对象要进行响应式处理呢?那新创建一个 Proxy对象,然后在写Proxy对象内部写一大串同样的代码,如果有很多对象需要进行收集,那代码的数量就大大增加了,阅读性差。
const info={
  address:"广州市",
  height:1.88
}


const infoProxy=new Proxy(info,{
  get:function(target,key,receiver){
    //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
    const depend=getDepend(target,key);
    // * 2. 给depend对象中添加响应函数
   depend.depend();
    return Reflect.get(target,key,receiver);
  },
  set:function(target,key,newValue,receiver){
    Reflect.set(target,key,newValue,receiver);
    // * 获取对应对象的key的依赖收集
    const depend=getDepend(target,key);
    // * 依次执行依赖收集的响应式函数
    depend.notify();
  }
})


watchFn(()=>{
  console.log("info",infoProxy.address,new Date());
})
infoProxy.address="怀化";

所以我决定封装一个函数:创建响应式对象,并返回

// * 封装一个响应式对象的函数
function reactive(obj){
  return new Proxy(obj,{
    get:function(target,key,receiver){
      //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
      const depend=getDepend(target,key);
      // * 2. 给depend对象中添加响应函数
     depend.depend();
    
      return Reflect.get(target,key,receiver);
    },
    set:function(target,key,newValue,receiver){
      Reflect.set(target,key,newValue,receiver);
      // * 获取对应对象的key的依赖收集
      const depend=getDepend(target,key);
      // * 依次执行依赖收集的响应式函数
      depend.notify();
    }
  })
}

之前的代码又可以简写为下面的样子:


// * 当前需要收集的响应式函数
let activeReactiveFn=null;
// * 每个属性对应一个类
class Depend{
  constructor() {
    // * 解决方案是将原来的数组类型转化为set类型
    this.reactiveFns=new Set();
  }
  addDepend(reactiveFn){
    this.reactiveFns.add(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }

  depend(){
    if(activeReactiveFn){
      this.reactiveFns.add(activeReactiveFn);
    }
  }
}

// * 封装一个响应式的函数
function watchFn(Fn){
  activeReactiveFn=Fn;
  Fn();
  activeReactiveFn=null;
  
}



// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  let  map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  let  depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}

// * 封装一个响应式对象的函数
function reactive(obj){
  return new Proxy(obj,{
    get:function(target,key,receiver){
      //* 1. 在这里能拿到target,还能拿到对应的key,获取对应的depend
      const depend=getDepend(target,key);
      // * 2. 给depend对象中添加响应函数
     depend.depend();
    
      return Reflect.get(target,key,receiver);
    },
    set:function(target,key,newValue,receiver){
      Reflect.set(target,key,newValue,receiver);
      // * 获取对应对象的key的依赖收集
      const depend=getDepend(target,key);
      // * 依次执行依赖收集的响应式函数
      depend.notify();
    }
  })
};
const obj=reactive({
  name:"wjy",
  age:18
})


// * 但如果我们有新的对象要进行响应式处理呢?那新创建一个 Proxy对象,然后在写Proxy对象内部写一大串同样的代码,如果有很多对象需要进行收集,那代码的数量就大大增加了,阅读性差。
const info=reactive({
  address:"广州市",
  height:1.88
})




watchFn(()=>{
  console.log("info",info.address,new Date());
})
info.address="怀化";


1.9 创建响应式对象(Vue2)

将reactive函数进行改造,使用Object.definedProperty去截取对象的get和set操作。以便在里面进行相关操作。


// * 封装一个响应式对象的函数
function reactive(obj){
  // {name:"wjy",age:18}
  // * 根据对象的所有key定义属性描述符
  Object.keys(obj).forEach(key=>{
    let value= obj[key];
    Object.defineProperty(obj,key,{
      get:function(){
        // * 获取当前对象的key的收集对象
        const depend=getDepend(obj,key);
        // * 收集当前响应式依赖
        depend.depend();
        return value;
      },
      set:function(v){
        value=v;
        // * 获取当前对象的key的收集对象
        const depend=getDepend(obj,key);
        // * 去调用收集对象的响应式依赖,因为值发生了变化
        depend.notify();
      }
    })
  })
  return obj

};

所有代码:


// * 当前需要收集的响应式函数
let activeReactiveFn=null;
// * 每个属性对应一个类
class Depend{
  constructor() {
    // * 解决方案是将原来的数组类型转化为set类型
    this.reactiveFns=new Set();
  }
  addDepend(reactiveFn){
    this.reactiveFns.add(reactiveFn)
  }
  notify(){
    this.reactiveFns.forEach(fn=>fn())
  }

  depend(){
    if(activeReactiveFn){
      this.reactiveFns.add(activeReactiveFn);
    }
  }
}

// * 封装一个响应式的函数
function watchFn(Fn){
  activeReactiveFn=Fn;
  Fn();
  activeReactiveFn=null;
  
}



// * 封装一个获取depend的函数
const targetMap=new WeakMap();
function getDepend(target,key){
  // * 根据target对象获取map的过程
  let  map=targetMap.get(target);
  if(!map){//第一次在使用的需要去创建一个
    map=new Map();
    targetMap.set(target,map);
  }
  
  // * 根据key获取depend对象
  let  depend=map.get(key);
  if(!depend){ //* 第一次可能没有depend
   depend=new Depend();
   map.set(key,depend);
  }
  return depend;
}

// * 封装一个响应式对象的函数
function reactive(obj){
  // {name:"wjy",age:18}
  // * 根据对象的所有key定义属性描述符
  Object.keys(obj).forEach(key=>{
    let value= obj[key];
    Object.defineProperty(obj,key,{
      get:function(){
        // * 获取当前对象的key的收集对象
        const depend=getDepend(obj,key);
        // * 收集当前响应式依赖
        depend.depend();
        return value;
      },
      set:function(v){
        value=v;
        // * 获取当前对象的key的收集对象
        const depend=getDepend(obj,key);
        // * 去调用收集对象的响应式依赖,因为值发生了变化
        depend.notify();
      }
    })
  })
  return obj

};
const obj=reactive({
  name:"wjy",
  age:18
})


// * 但如果我们有新的对象要进行响应式处理呢?那新创建一个 Proxy对象,然后在写Proxy对象内部写一大串同样的代码,如果有很多对象需要进行收集,那代码的数量就大大增加了,阅读性差。
const info=reactive({
  address:"广州市",
  height:1.88
})




watchFn(()=>{
  console.log("info",info.address,new Date());
})

info.address="怀化市"

2.总结

vue3和vue2的响应式原理.png

相关文章

网友评论

    本文标题:Vue3和Vue2响应式原理

    本文链接:https://www.haomeiwen.com/subject/jhnpurtx.html