Vue课代表,今天复习响应式(一)

648 阅读3分钟

背景

终于到年末了,拿出那篇从年初看到年末的响应式原理,拍拍灰尘,叹一口气,终于有时间来复(预)习了!

Vue 的Object.defineProperty和Proxy

题外话

这里肯定很多同学要插嘴了,别人写了那么多,你凭什么来凑热闹?

温故而知新嘛,说不定这里有不知道的呢?(弱小、可怜、又无助)

基本原理

众所周知,响应式的基本原理很简单,Vue2使用了Object.defineProperty,Vue3使用了Proxy对象。

那好奇的小明要问了:这两个是啥?为什么它们能够实现响应式?

Object.defineProperty

在伟大的MDN中有这样一段话,介绍了defineProperty函数的关键作用:

defineProperty介绍

image.png

同时他的使用也极为简单,通过传递一个对象,一个对象中属性的key,就可以让这个属性存在响应式被监听。

image.png

好奇的狗子来问了:什么被监听?什么响应式?我不明白!

举个栗子

...
<span>{{ name }}</span>
...
data() {
    return {
        name: 'xxx'
    };
}
...

在Vue对象中data返回的对象就是defineProperty中第一个参数,name是第二个参数,由此vue实现了最基础的响应式原理。一旦name被改变了,defineProperty中的set函数就被触发,由此才能进入下一步依赖触发

Proxy

在Vue3.x中,尤大改进了Vue2的响应式痛点,例如:复杂对象内部的增删改监听不到。

这时,好奇的小张要问了:为什么在Vue2中数组和对象中的变动无法做到响应式?

因为defineProperty中只有getset可以使用,没有createdelete!所以数组内部的操作还有对象中的操作其实是无法监听到的。

由此,尤大在多年前找浏览器API时发现了Proxy(我猜的)。

它的使用也比较简单,只需要传一个需要代理的对象一个通常以函数作为属性的对象,据伟大的MDN上记载:

image.png

Proxy的set、get和deleteProperty函数

那Proxy是如何比Object.defineProperty更强的呢?这就要聊到它在Vue3中的应用,set支持监听对象属性的新增和修改,get支持监听对象属性的调用,所以依赖收集和依赖触发都能正常使用并且比Vue2更好用。

举个set的栗子:

let handler = {
  set: function(obj, prop, value) {
    console.log(`Property ${prop} is set to ${value}`);
    obj[prop] = value;
    return true;
  }
};

let p = new Proxy({}, handler);
p.newProp = 123;  // 输出:Property newProp is set to 123

deleteProperty函数我们直接看源码(2024-2),我加了一些注释方便解读:

deleteProperty(target: object, key: string | symbol): boolean {
    // 检查目标对象是否有这个属性
    const hadKey = hasOwn(target, key)
    // 获取旧值
    const oldValue = (target as any)[key]
    // 使用Reflect API删除属性,并获取操作是否成功的结果
    const result = Reflect.deleteProperty(target, key)
    // 如果属性删除成功并且目标对象原本就有这个属性
    if (result && hadKey) {
      // 触发响应系统的更新
      // target - 被操作的响应式对象
      // TriggerOpTypes.DELETE - 定义了需要触发效果的操作类型,在这里是删除操作
      // key - 被删除的属性名,用于定位目标对象中的特定响应式属性
      // undefined - 新值,在这里是undefined
      // oldValue - 被删除的属性的旧值
      trigger(target, TriggerOpTypes.DELETE, key, undefined, oldValue)
    }
    // 返回操作是否成功的结果
    return result
  }

通过源码来看,其实理解起来还是非常简单的,核心就是找到属性,删除属性,调用trigger函数,开始触发之前收集的依赖。

至于Reflect的故事,等我写完再补。

第一节总结

我们大概聊了一下Vue的两个主要实现,口也渴了,后面的慢慢讲,放假咯~。

内容如有不妥,欢迎指正。

Vue课代表,今天复习响应式(二)

Vue课代表,今天复习响应式(三)

课后问题

为什么Vue2和Vue3删除属性都用的Object.defineProperty,Vue2咋监听不到?

image.png