Appearance
响应式状态
状态:指的是页面渲染过程中所需的数据。
响应式状态:指的是状态的变化,会导致页面自动的重新渲染,体现了“数据驱动视图”的编程范式。在 vue3 中声明响应式状态的核心 API 有两个:
- reactive() 函数
- ref() 函数
reactive()
语法格式
reactive() 用来将普通对象包装成响应式对象,从而使得响应式对象中数据的变化会自动触发模板的更新渲染。
语法格式如下:
js
import { reactive } from 'vue'
// 使用 reactive 定义响应式的数据对象
const state = reactive({ count: 0 })
在模板中使用:
html
<p>count 的值是:{{ state.count }}</p>
<button @click="state.count++">+1</button>
复杂对象
reactive() 支持复杂对象(深层嵌套)的响应式更新。
示例代码如下:
js
// 复杂对象
const obj = reactive({
a: {
b: {
c: 1
}
}
})
模板中的代码:
html
<p>a.b.c 的值是:{{obj.a.b.c}}</p>
<button @click="obj.a.b.c++">+1</button>
DOM 更新的时机
当修改了响应式状态时,DOM 会被自动更新。但是需要注意的是,DOM 更新不是同步的。要等待 DOM 更新完成后再执行额外的代码,可以使用 nextTick() 全局 API:
js
// 从 vue 中按需导入 nextTick 函数
import { reactive, nextTick } from 'vue'
// 定义响应式的数据对象
let state = reactive({ count: 0 })
const add = async () => {
state.count++
// 在 nextTick() 之后,才能访问到更新后的 DOM
await nextTick()
console.log(document.querySelector('#counter').textContent)
}
reactive() 的局限性
有限的值类型
reactive() 只能用于对象类型(对象、数组、Map、Set)。不能作用于 string、number 或 boolean 这些值类型的数据。
jslet state = reactive({ count: 0 }) // 下面这种用法是错误的 // 因为 reactive() 不支持值类型: const name = reactive('zs')
不能替换整个对象
直接替换整个响应式对象,会导致响应性的丢失。
jslet state = reactive({ count: 0 }) // 上面的 ({ count: 0 }) 引用将不再被追踪 // (响应性连接已丢失!) state = reactive({ count: 1 })
对解构操作不友好
当我们将响应式对象的原始类型属性解构为本地变量时,或者将该属性传递给函数时,我们将丢失响应性连接。
jsconst state = reactive({ count: 0 }) // 当解构时,count 已经与 state.count 断开连接 let { count } = state // 不会影响原始的 state count++ // 该函数接收到的是一个普通的数字 // 并且无法追踪 state.count 的变化 // 我们必须传入整个对象以保持响应性 callSomeFunction(state.count)
TIP
由于这些限制,Vue3 官方建议使用 ref()
作为声明响应式状态的主要 API。
ref()
语法格式
ref() 支持把值类型的数据包装为响应式的数据对象。ref()
接收参数值,并将其包裹在一个带有 .value
属性的 ref 对象中返回。示例代码如下:
js
// 从 vue 中导入 ref 函数
import { ref } from 'vue'
const app = createApp({
setup() {
// 定义响应式的数据,命名为 count
const count = ref(0)
// count 是一个带有 .value 属性的对象
console.log(count) // { value: 0 }
// ref.value 才是真正的值
console.log(count.value) // 0
// 按钮的点击事件处理函数
// 注意:在 js 中访问 ref 的值时,必须使用 .value
const add = () => (count.value += 2)
return {
count,
add
}
}
})
在模板中使用 ref 时,不需要附加 .value
,因为模板中的 ref 会自动解包:
html
<div id="app">
<!-- 注意:在模板中访问 ref 的值时,会自动解包,不必使用 .value -->
<h3>count 的值是:{{ count }}</h3>
<button @click="count++">+1</button>
<button @click="add">+2</button>
</div>
复杂对象
ref() 支持复杂对象。传递给 ref() 的复杂对象会在内部通过 reactive() 转为响应式数据对象。只不过需要使用 .value 才能访问到响应式对象。
示例代码如下:
js
// 使用 ref() 把对象转为响应式数据
const user = ref({ age: 0 })
console.log(user) // { value: { age: 0 } }
console.log(user.value.age) // 0
在模板中使用:
html
<h3>姓名:{{ user.age }}</h3>
<button @click="user.age++">age+1</button>
正确使用 ref() 的核心口诀
- 在 js 中必须使用 .value 访问 ref 的值;
- 在模板中会自动解包,不必使用 .value;
- Vue3 官方建议使用 ref() 作为声明响应式状态的主要 API,因为它同时支持值类型和对象类型。