前言
在Vue2中的响应式数据需要放入 data 函数,Vue2 会遍历 data 中的所有属性,使用的Object.defineProperty 把每个 property 全部转为 getter/setter,getter 用来收集依赖,setter 用来执行 notify,发布更新事件。
而在Vue 3的响应式系统中使用了ref和reactive 这两个核心概念,它们为开发者提供了处理响应式数据的不同方式。理解它们的特性和区别对于构建灵活、高效的Vue应用至关重要
reactive
除了ref外,Vue3还有另一种声明响应式状态的方式,即使用 reactive() API。与将内部值包装在特殊对象中的ref不同,reactive() 将使对象本身具有响应性。
基本用法
<template>
<button @click="addCount">
{{ state.count }}
</button>
</template>
<script setup>
import { reactive } from 'vue'
const state = reactive({ count: 0 })
const addCount = () => {
state.count++
}
</script>
相较于ref,reactive在Javascript中对数据修改时并不需要在.value属性下寻找,另外reactive的参数只能是对象或者数组或者像 Map、Set 这样的集合类型。
Proxy代理
const raw = {}
const proxy = reactive(raw)
// 代理对象和原始对象不是全等的
console.log(proxy === raw) // false
从以上代码可以看出,原始数据和reactive下的数据并不是全等的。因为reactive返回的是一个原始对象的Proxy。
// 在同一个对象上调用 reactive() 会返回相同的代理
console.log(reactive(raw) === proxy) // true
// 在一个代理上调用 reactive() 会返回它自己
console.log(reactive(proxy) === proxy) // true
为保证数据的一致性,代理对象调用reactive()仍会返回自己本身。
<script setup>
import { reactive } from 'vue'
const obj = {
count: 1
}
const proxy = reactive(obj);
proxy.count++;
console.log(proxy.count); // 2
console.log(obj.count); // 2
</script>
从以上代码的运行结果说明 reactive ()代理对象发生改变时,原始数据也会跟着发生变化。
注意事项
- reactive 的参数只能是对象或者数组或者像 Map、Set 这样的集合类型。如果是原始数据类型,控制台会报警告
- 由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失
let state = reactive({ count: 0 })
// 上面的 ({ count: 0 }) 引用将不再被追踪
// (响应性连接已丢失!)
state = reactive({ count: 1 })
- 当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接
const state = reactive({ count: 0 })
// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++
// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)
- 使用 reactive 定义的响应式对象,会深度监听每一层的属性,它会影响到所有嵌套的属性。即嵌套在响应式数据中的对象不论层级都会变成响应式对象。
<script setup>
import { reactive } from 'vue'
let obj = reactive({
name: 'Minato',
a: {
b: {
c: 1
}
}
})
console.log("obj: ", obj) //obj: Proxy(Object){name:'Minato',a:{……}}
console.log("obj.name: ", obj.name) //'Minato'
console.log("obj.a: ", obj.a) //Proxy(Object){b:{……}}
console.log("obj.a.b: ", obj.a.b) //Proxy(Object){c:{……}}
console.log("obj.a.b.c: ",obj.a.b.c) //1
</script>
- 若要避免深层响应式转换,只想保留对这个对象顶层次访问的响应性,我们可以使用 shallowReactive()。
<script setup>
import { shallowReactive } from 'vue'
let obj = shallowReactive({
name: 'Echo',
a: {
b: {
c: 1
}
}
})
console.log("obj: ", obj) //obj: {name:'Minato',a:{……}}
console.log("obj.name: ", obj.name) //'Minato'
console.log("obj.a: ", obj.a) //{b:{……}}
console.log("obj.a.b: ", obj.a.b) //{c:{……}}
console.log("obj.a.b.c: ",obj.a.b.c) //1
</script>