你好,我是大圣。在上一讲中,我给你介绍了 Composition API 相比于 Option API 的优点,以及 <script setup> 的语法,这些内容能够给我们后面的开发打下了坚实的基础。

今天我带你深入了解一下 Vue 3 的响应式机制,相信学完今天的内容,你会对响应式机制有更深地体会。我还会结合代码示例,帮你掌握响应式机制的进阶用法,让我们正式开始学习吧!

什么是响应式

响应式一直都是 Vue 的特色功能之一。与之相比,JavaScript 里面的变量,是没有响应式这个概念的。你在学习 JavaScript 的时候首先被灌输的概念,就是代码是自上而下执行的。我们看下面的代码,代码在执行后,打印输出的两次 double 的结果也都是 2。即使我们修改了代码中的 count 的值后,double 的值也不会有任何改变。

let count = 1
let double = count * 2
console.log(double)
count = 2
console.log(double)

double 的值是根据 count 的值乘以二计算而得到的,如果现在我们想让 doube 能够跟着 count 的变化而变化,那么我们就需要在每次 count 的值修改后,重新计算 double。

比如,在下面的代码,我们先把计算 doube 的逻辑封装成函数,然后在修改完 count 之后,再执行一遍,你就会得到最新的 double 值。

let count = 1
// 计算过程封装成函数
let getDouble = n=>n*2 //箭头函数
let double = getDouble(count)
console.log(double)

count = 2
// 重新计算double,这里我们不能自动执行对double的计算
double = getDouble(count)
console.log(double)

实际开发中的计算逻辑会比计算 doube 复杂的多,但是都可以封装成一个函数去执行。下一步,我们要考虑的是,如何让 double 的值得到自动计算。

如果我们能让 getDouble 函数自动执行,也就是如下图所示,我们使用 JavaScript 的某种机制,把 count 包裹一层,每当对 count 进行修改时,就去同步更新 double 的值,那么就有一种 double 自动跟着 count 的变化而变化的感觉,这就算是响应式的雏形了。

Untitled

响应式原理

响应式原理是什么呢?Vue 中用过三种响应式解决方案,分别是 defineProperty、Proxy 和 value setter。我们首先来看 Vue 2 的 defineProperty API,这个函数详细的 API 介绍你可以直接访问MDN 介绍文档(https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty)来了解。

这里我结合一个例子来说明,在下面的代码中,我们定义个一个对象 obj,使用 defineProperty 代理了 count 属性。这样我们就对 obj 对象的 value 属性实现了拦截,读取 count 属性的时候执行 get 函数,修改 count 属性的时候执行 set 函数,并在 set 函数内部重新计算了 double。

let getDouble = n=>n*2
let obj = {}
let count = 1
let double = getDouble(count)

Object.defineProperty(obj,'count',{
    get(){
        return count
    },
    set(val){
        count = val
        double = getDouble(val)
    }
})
console.log(double)  // 打印2
obj.count = 2
console.log(double) // 打印4  有种自动变化的感觉

这样我们就实现了简易的响应式功能,在课程的第四部分,我还会带着你写一个更完善的响应式系统。

但 defineProperty API 作为 Vue 2 实现响应式的原理,它的语法中也有一些缺陷。比如在下面代码中,我们删除 obj.count 属性,set 函数就不会执行,double 还是之前的数值。这也是为什么在 Vue 2 中,我们需要 $delete 一个专门的函数去删除数据。

delete obj.count
console.log(double) // doube还是4