Vue响应式数据

什么是响应式

Vue中的”响应式数据”指的是一种数据变化会自动触发相关依赖更新的机制。这意味着当数据发生变化时,与该数据相关联的视图会自动进行更新,而不需要手动干预。

在Vue中,通过以下两种方式实现数据的响应式:

  1. Object.defineProperty: Vue 2.x 使用 Object.defineProperty 来劫持对象的属性,从而实现对数据的监听。

  2. Proxy: Vue 3.x 引入了 ES6 的 Proxy 对象来实现对对象和数组的监听,相比 Object.defineProperty 具有更强大和灵活的功能。

这种响应式数据的机制是Vue框架的核心之一,它使得开发者在构建应用时可以更方便地管理和更新数据,而不用手动去追踪数据的变化并手动更新视图。

具体而言,当一个数据被定义为响应式时,Vue会追踪这个数据的使用,当数据发生变化时,Vue会自动通知相关的依赖(如视图)进行更新。这使得开发者能够以一种更声明式的方式来处理数据和视图的关系,而不需要手动编写大量的更新逻辑。

响应式数据

在Vue 3中,refreactive 是用于创建响应式数据的两个不同的API。

ref

  • 用途: 用于创建一个包装基本数据类型(如数字、字符串等)的响应式对象。
  • 示例: 点击按钮即可自动更新count值
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<template>
<div>
<div>Count值为:{{ count }}</div>
<button @click="updateInfo">点击修改数据</button>
</div>
</template>

<script setup>
import { ref } from 'vue';
const count = ref('1');

console.log('count', count.value);
const updateInfo = () => {
count.value++;
// console.log('count',count.value);
}

</script>
  • 注意事项: 使用 ref 包装的基本数据类型,需要通过 .value 属性来访问和修改其值。

reactive

  • 用途: 用于创建一个包装对象的响应式对象,这个对象中可以包含多个属性。

  • 示例:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    import { reactive } from 'vue';

    const person = reactive({
    name: 'John',
    age: 25,
    });

    // 读取值
    console.log(person.name); // 输出: John

    // 修改值
    person.age = 26;
  • 注意事项: 使用 reactive 创建的对象的所有属性都是响应式的,不需要额外的 .value

总体而言,ref 适用于包装单一的基本数据类型,而 reactive 适用于包装对象。在实际开发中,选择使用哪个取决于数据的结构和用途。

reactive()局限性

  • 有限的值类型:它只能用于对象类型 (对象、数组和如 Map、Set 这样的集合类型)。它不能持有如 string、number 或 boolean 这样的原始类型。

  • 不能替换整个对象:由于 Vue 的响应式跟踪是通过属性访问实现的,因此我们必须始终保持对响应式对象的相同引用。这意味着我们不能轻易地“替换”响应式对象,因为这样的话与第一个引用的响应性连接将丢失:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
<template>
<div>
<div>state值: {{ state.count }}</div>
<button @click="updateState1">修改属性响应式更新</button><br>
<button @click="updateState">直接赋值更新</button>

</div>
</template>

<script setup>
import { reactive } from 'vue';

let state = reactive({ count: 0 });

const updateState = () => {

// 直接赋值新对象,会失去响应性,视图部分还是0
state = { count: 5 };
console.log('新对象赋值完成',state);
};

const updateState1 = () => {

// 修改对象属性保留响应性,视图部分会自动更新为3
state.count=3;
console.log('修改对象属性完成',state);
};

</script>

  • 对解构操作不友好:当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接:
1
2
3
4
5
6
7
8
9
10
11
const state = reactive({ count: 0 })

// 当解构时,count 已经与 state.count 断开连接
let { count } = state
// 不会影响原始的 state
count++

// 该函数接收到的是一个普通的数字
// 并且无法追踪 state.count 的变化
// 我们必须传入整个对象以保持响应性
callSomeFunction(state.count)

由于这些限制,我们建议使用 ref() 作为声明响应式状态的主要 API。

参考资料