第一部分:Vue 3 基础入门
第8集:响应式数据初体验
响应式系统是Vue的核心特性之一,它允许我们在数据变化时自动更新视图。Vue 3的响应式系统基于Proxy API实现,提供了更强大、更灵活的响应式能力。在本集中,我们将学习Vue 3响应式系统的基础知识,包括ref和reactive的使用方法和区别。
8.1 响应式系统的基本概念
什么是响应式?
响应式是指当数据发生变化时,视图会自动更新,无需手动操作DOM。这种机制极大地提高了开发效率,让开发者可以专注于数据逻辑,而不是DOM操作。
Vue 3响应式系统的优势
- 基于Proxy API,支持更多的数据类型
- 支持监听数组索引和长度变化
- 支持监听对象的新增和删除属性
- 更好的性能和内存使用
- 提供了更灵活的响应式API
8.2 使用ref创建响应式数据
ref是Vue 3中最常用的响应式API之一,它可以将基本类型或对象转换为响应式数据。
8.2.1 基本使用
语法:
import { ref } from 'vue'
const count = ref(0)示例:
<template>
<div>
<h1>响应式数据初体验</h1>
<p>计数: {{ count }}</p>
<button @click="increment">增加</button>
<button @click="decrement">减少</button>
<p>消息: {{ message }}</p>
<input v-model="message" type="text" placeholder="输入消息">
</div>
</template>
<script setup>
import { ref } from 'vue'
// 创建响应式数据
const count = ref(0)
const message = ref('Hello Vue 3!')
// 方法
function increment() {
// 访问和修改ref的值需要使用.value
count.value++
}
function decrement() {
count.value--
}
</script>8.2.2 注意事项
- 访问和修改值:在
<script setup>中,需要使用.value访问和修改ref的值;在模板中,Vue会自动解包,不需要.value - 类型支持:
ref支持所有JavaScript数据类型,包括基本类型和对象 - 响应式转换:对于对象类型,
ref会递归地将其转换为响应式数据 - 解构问题:直接解构
ref会丢失响应式,需要使用toRefs或toRef
8.3 使用reactive创建响应式数据
reactive用于创建响应式对象,它只能用于对象类型,不能用于基本类型。
8.3.1 基本使用
语法:
import { reactive } from 'vue'
const state = reactive({ count: 0, message: 'Hello' })示例:
<template>
<div>
<h1>使用reactive创建响应式对象</h1>
<p>计数: {{ state.count }}</p>
<button @click="increment">增加</button>
<p>用户信息</p>
<p>姓名: {{ user.name }}</p>
<p>年龄: {{ user.age }}</p>
<input v-model="user.name" type="text" placeholder="输入姓名">
<input v-model.number="user.age" type="number" placeholder="输入年龄">
</div>
</template>
<script setup>
import { reactive } from 'vue'
// 创建响应式对象
const state = reactive({
count: 0
})
const user = reactive({
name: '张三',
age: 25
})
// 方法
function increment() {
// 直接修改属性,不需要.value
state.count++
}
</script>8.3.2 注意事项
- 只能用于对象:
reactive只能用于对象类型(对象、数组、Map、Set等),不能用于基本类型 - 直接修改属性:访问和修改
reactive对象的属性时,不需要使用.value - 响应式转换:
reactive会递归地将对象的所有属性转换为响应式 - 解构问题:直接解构
reactive对象会丢失响应式,需要使用toRefs - 引用问题:不能直接替换
reactive对象,否则会丢失响应式
8.4 ref vs reactive
| 特性 | ref | reactive |
|---|---|---|
| 支持的数据类型 | 所有类型 | 仅对象类型 |
| 访问方式 | 需要.value(脚本中) | 直接访问属性 |
| 解构行为 | 直接解构丢失响应式 | 直接解构丢失响应式 |
| 替换整个对象 | 支持(count.value = 10) | 不支持(会丢失响应式) |
| 数组支持 | 支持 | 支持 |
| 类型推断 | 良好 | 良好 |
| 使用场景 | 基本类型或需要频繁替换的对象 | 复杂对象或状态管理 |
8.5 响应式数据的解构
直接解构响应式对象会丢失响应式,Vue 3提供了toRefs和toRef来解决这个问题。
8.5.1 使用toRefs
toRefs可以将reactive对象转换为包含ref的普通对象,这样解构后的数据仍然保持响应式。
示例:
<template>
<div>
<h1>响应式数据解构</h1>
<p>计数: {{ count }}</p>
<p>消息: {{ message }}</p>
<button @click="increment">增加</button>
<input v-model="message" type="text">
</div>
</template>
<script setup>
import { reactive, toRefs } from 'vue'
// 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue 3!'
})
// 使用toRefs解构,保持响应式
const { count, message } = toRefs(state)
function increment() {
// 解构后的ref仍然需要.value
count.value++
}
</script>8.5.2 使用toRef
toRef用于创建单个属性的ref,它可以从reactive对象中提取单个属性并保持响应式。
示例:
<template>
<div>
<h1>使用toRef提取单个属性</h1>
<p>姓名: {{ name }}</p>
<input v-model="name" type="text">
</div>
</template>
<script setup>
import { reactive, toRef } from 'vue'
const user = reactive({
name: '张三',
age: 25,
email: 'zhangsan@example.com'
})
// 提取单个属性,保持响应式
const name = toRef(user, 'name')
</script>8.6 响应式数据的计算
Vue 3提供了computed函数来创建计算属性,计算属性是基于响应式数据的派生值,它会自动缓存计算结果,只有当依赖的数据变化时才会重新计算。
示例:
<template>
<div>
<h1>计算属性</h1>
<p>原始价格: {{ price }}元</p>
<p>折扣: {{ discount }}%</p>
<input v-model.number="discount" type="number" placeholder="输入折扣">
<p>折后价格: {{ discountedPrice }}元</p>
<p>是否特价: {{ isSpecialPrice ? '是' : '否' }}</p>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
// 原始数据
const price = ref(100)
const discount = ref(20)
// 计算属性
const discountedPrice = computed(() => {
return price.value * (1 - discount.value / 100)
})
// 计算属性的getter和setter
const isSpecialPrice = computed({
get() {
return discountedPrice.value < 80
},
set(value) {
if (value) {
discount.value = 30 // 如果设置为特价,折扣改为30%
} else {
discount.value = 20 // 否则恢复为20%
}
}
})
</script>计算属性的优势:
- 缓存:只有依赖的数据变化时才会重新计算
- 简洁:将复杂的计算逻辑封装起来,使模板更简洁
- 响应式:计算结果会自动响应依赖数据的变化
8.7 响应式数据的侦听
Vue 3提供了watch和watchEffect函数来侦听响应式数据的变化。
8.7.1 使用watch
watch可以监听一个或多个响应式数据的变化,并在变化时执行回调函数。
示例:
<template>
<div>
<h1>侦听响应式数据</h1>
<p>计数: {{ count }}</p>
<button @click="count++">增加</button>
<p>消息: {{ message }}</p>
<input v-model="message" type="text">
<p>用户: {{ user.name }} - {{ user.age }}</p>
<input v-model="user.name" type="text">
<input v-model.number="user.age" type="number">
</div>
</template>
<script setup>
import { ref, reactive, watch } from 'vue'
const count = ref(0)
const message = ref('Hello')
const user = reactive({ name: '张三', age: 25 })
// 监听单个ref
watch(count, (newValue, oldValue) => {
console.log(`count变化了: ${oldValue} -> ${newValue}`)
})
// 监听多个值
watch([count, message], ([newCount, newMessage], [oldCount, oldMessage]) => {
console.log('count或message变化了')
})
// 监听reactive对象的属性
watch(() => user.name, (newName, oldName) => {
console.log(`用户名变化了: ${oldName} -> ${newName}`)
})
// 监听整个reactive对象
watch(user, (newUser, oldUser) => {
console.log('用户对象变化了')
}, {
deep: true, // 深度监听,监听对象的所有属性
immediate: true // 立即执行一次
})
</script>8.7.2 使用watchEffect
watchEffect会自动跟踪依赖,当依赖的数据变化时,会重新执行回调函数。
示例:
<template>
<div>
<h1>使用watchEffect</h1>
<p>计数: {{ count }}</p>
<button @click="count++">增加</button>
<p>消息: {{ message }}</p>
<input v-model="message" type="text">
</div>
</template>
<script setup>
import { ref, watchEffect } from 'vue'
const count = ref(0)
const message = ref('Hello')
// watchEffect会自动跟踪依赖
watchEffect(() => {
console.log(`count: ${count.value}, message: ${message.value}`)
})
</script>**watch vs watchEffect**:
| 特性 | watch | watchEffect |
|---|---|---|
| 依赖跟踪 | 手动指定依赖 | 自动跟踪依赖 |
| 回调时机 | 可以获取新旧值 | 只能获取新值 |
| 立即执行 | 需要设置immediate: true | 默认立即执行 |
| 适用场景 | 需要获取新旧值,或只需要监听特定依赖 | 自动跟踪所有依赖,不需要手动指定 |
8.8 响应式数据的最佳实践
选择合适的API:
- 基本类型使用
ref - 复杂对象使用
reactive - 需要频繁替换的对象使用
ref
- 基本类型使用
避免直接解构响应式对象:
- 使用
toRefs或toRef进行解构 - 或者直接使用
.value访问
- 使用
合理使用计算属性:
- 将复杂的计算逻辑封装为计算属性
- 避免在计算属性中修改数据
- 合理使用getter和setter
谨慎使用深度监听:
- 深度监听会带来性能开销
- 尽量只监听需要的属性
- 考虑使用
watchEffect代替深度监听
注意响应式数据的边界:
- 不要在响应式数据中存储非响应式的复杂对象
- 避免在响应式数据中存储DOM元素
- 合理使用
shallowRef和shallowReactive(用于浅层响应式)
8.9 响应式系统的工作原理
Vue 3的响应式系统基于Proxy API实现,它通过以下步骤工作:
- 创建Proxy:当我们使用
ref或reactive创建响应式数据时,Vue会为数据创建一个Proxy对象 - 收集依赖:当响应式数据被访问时,Vue会收集当前的依赖(通常是组件的渲染函数或计算属性)
- 触发更新:当响应式数据被修改时,Vue会通知所有收集到的依赖,触发它们的重新执行
Proxy的优势:
- 支持监听对象的新增和删除属性
- 支持监听数组索引和长度变化
- 支持Map、Set、WeakMap、WeakSet等数据结构
- 更好的性能和内存使用
本集小结
在本集中,我们学习了Vue 3响应式系统的基础知识:
- 响应式的概念:当数据变化时,视图自动更新
ref的使用:用于创建响应式数据,支持所有数据类型reactive的使用:用于创建响应式对象,仅支持对象类型- **
refvsreactive**:它们的区别和适用场景 - 响应式数据的解构:使用
toRefs和toRef保持响应式 - 计算属性:使用
computed创建基于响应式数据的派生值 - 侦听器:使用
watch和watchEffect侦听响应式数据的变化 - 最佳实践:如何合理使用响应式API
- 工作原理:基于Proxy API的响应式系统
响应式系统是Vue的核心,掌握好响应式API的使用对于开发Vue应用至关重要。在下一集中,我们将学习Vue 3的事件处理与表单绑定,进一步提升我们的Vue开发能力。