Vue.js 最独特的特性之一是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接。
在 Vue.js 中,响应式是指当数据发生变化时,视图会自动更新。这是通过 Vue 的响应式系统实现的,它能够追踪数据的变化并在变化发生时自动更新相关的视图部分。
Vue 3 使用 Proxy 对象来实现响应式,这比 Vue 2 的 Object.defineProperty 更加强大和高效。
在 Vue 3 中,我们可以使用两种主要方式来声明响应式状态:ref 和 reactive。
ref() 用于创建响应式的引用值。它可以包装基本类型(如字符串、数字)和对象类型。
// 导入 ref
import { ref } from 'vue'
// 创建响应式变量
const count = ref(0)
const message = ref('Hello Vue!')
const user = ref({
name: '张三',
age: 25
})
// 访问 ref 的值,需要通过 .value 属性
console.log(count.value) // 0
console.log(message.value) // 'Hello Vue!'
// 修改 ref 的值
count.value++
message.value = 'Hello World!'
user.value.age = 26
// 在模板中,Vue 会自动解包 ref,无需使用 .value
// 模板中直接使用 {{ count }} 而不是 {{ count.value }}
在 JavaScript 中操作 ref 时,需要使用 .value 属性来访问或修改其值。但在模板中,Vue 会自动解包 ref,所以可以直接使用变量名。
reactive() 用于创建响应式的对象。它返回一个对象的响应式代理。
// 导入 reactive
import { reactive } from 'vue'
// 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue!',
user: {
name: '李四',
age: 30
}
})
// 直接访问和修改属性
console.log(state.count) // 0
console.log(state.message) // 'Hello Vue!'
// 修改属性
state.count++
state.message = 'Hello Reactive!'
state.user.age = 31
// 添加新属性
state.newProperty = 'This is new' // 这个新属性也是响应式的
// 在模板中可以直接访问对象的属性
// {{ state.count }}、{{ state.message }}
| 特性 | ref() | reactive() |
|---|---|---|
| 适用类型 | 基本类型和对象类型 | 仅对象类型 |
| 访问方式 | 通过 .value 属性(JS中) |
直接访问属性 |
| 模板使用 | 自动解包,无需 .value |
直接访问属性 |
| 响应性丢失 | 重新赋值仍保持响应性 | 重新赋值会丢失响应性 |
| 解构 | 使用 toRefs 保持响应性 |
直接解构会丢失响应性 |
Vue 提供了一些工具函数来处理响应式数据的转换:
import { reactive, toRef, toRefs, isRef, isReactive, isProxy } from 'vue'
const state = reactive({
count: 0,
name: 'Vue'
})
// toRef - 将 reactive 对象的属性转换为 ref
const countRef = toRef(state, 'count')
countRef.value++ // state.count 也会变为 1
// toRefs - 将 reactive 对象的所有属性转换为 ref 对象
const stateRefs = toRefs(state)
// 现在可以解构而不丢失响应性
const { count, name } = toRefs(state)
// 类型检查
console.log(isRef(countRef)) // true
console.log(isReactive(state)) // true
console.log(isProxy(state)) // true
下面是一个使用 ref 和 reactive 的综合示例:
<template>
<div>
<h2>计数器: {{ count }}</h2>
<button @click="increment">增加</button>
<h3 class="mt-4">用户信息</h3>
<p>姓名: {{ user.name }}</p>
<p>年龄: {{ user.age }}</p>
<button @click="increaseAge">增加年龄</button>
<h3 class="mt-4">待办事项</h3>
<ul>
<li v-for="(todo, index) in todos" :key="index">
{{ todo }}
</li>
</ul>
<input v-model="newTodo" @keyup.enter="addTodo" placeholder="添加新待办">
</div>
</template>
<script setup>
import { ref, reactive } from 'vue'
// 使用 ref 声明响应式基本类型
const count = ref(0)
// 使用 reactive 声明响应式对象
const user = reactive({
name: '王五',
age: 28
})
// 使用 ref 声明响应式数组
const todos = ref(['学习 Vue', '编写代码', '阅读文档'])
const newTodo = ref('')
// 方法
const increment = () => {
count.value++
}
const increaseAge = () => {
user.age++
}
const addTodo = () => {
if (newTodo.value.trim()) {
todos.value.push(newTodo.value)
newTodo.value = ''
}
}
</script>
Vue 3 使用 ES6 的 Proxy 对象来实现响应式。Proxy 可以拦截对象的各种操作,如属性读取、设置、删除等。
// 简化版响应式实现原理
function reactive(target) {
return new Proxy(target, {
get(obj, key) {
console.log(`读取属性: ${key}`)
// 这里可以添加依赖收集逻辑
return obj[key]
},
set(obj, key, value) {
console.log(`设置属性: ${key} = ${value}`)
obj[key] = value
// 这里可以添加触发更新逻辑
return true
}
})
}
// 使用示例
const state = reactive({ count: 0 })
console.log(state.count) // 控制台输出: 读取属性: count
state.count = 1 // 控制台输出: 设置属性: count = 1
Vue.js 的响应式系统是框架的核心特性之一。通过 ref 和 reactive,我们可以轻松创建响应式数据。理解这两种方法的使用场景和区别,对于编写高效、可维护的 Vue 应用至关重要。
在后续章节中,我们将学习计算属性、侦听器等更高级的响应式特性,它们构建在本章所学的基础之上。