Vue.js 响应式数据
什么是响应式数据?
响应式数据是 Vue.js 的核心特性之一。当数据发生变化时,视图会自动更新,无需手动操作 DOM。这种数据驱动的方式让开发变得更加简单和高效。
响应式原理
Vue 3 使用 Proxy 来实现响应式系统,而 Vue 2 使用的是 Object.defineProperty。
Vue 3 响应式系统
javascript
import { reactive, ref } from 'vue'
// 使用 reactive 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue!'
})
// 使用 ref 创建响应式基本类型
const count = ref(0)ref 和 reactive 的区别
ref
ref 用于创建响应式的基本类型数据(字符串、数字、布尔值等)。
vue
<script setup>
import { ref } from 'vue'
const count = ref(0)
const message = ref('Hello')
function increment() {
count.value++ // 注意:需要使用 .value 访问
}
</script>
<template>
<div>
<p>计数:{{ count }}</p> <!-- 模板中自动解包,不需要 .value -->
<p>消息:{{ message }}</p>
<button @click="increment">增加</button>
</div>
</template>reactive
reactive 用于创建响应式对象。
vue
<script setup>
import { reactive } from 'vue'
const state = reactive({
count: 0,
user: {
name: '张三',
age: 25
}
})
function updateUser() {
state.user.name = '李四'
state.count++
}
</script>
<template>
<div>
<p>计数:{{ state.count }}</p>
<p>用户:{{ state.user.name }},年龄:{{ state.user.age }}</p>
<button @click="updateUser">更新</button>
</div>
</template>响应式数据的使用场景
1. 表单输入
vue
<script setup>
import { ref } from 'vue'
const username = ref('')
const password = ref('')
function handleSubmit() {
console.log('用户名:', username.value)
console.log('密码:', password.value)
}
</script>
<template>
<form @submit.prevent="handleSubmit">
<input v-model="username" placeholder="用户名" />
<input v-model="password" type="password" placeholder="密码" />
<button type="submit">提交</button>
</form>
</template>2. 列表数据
vue
<script setup>
import { reactive } from 'vue'
const todos = reactive([
{ id: 1, text: '学习 Vue', done: false },
{ id: 2, text: '写代码', done: false }
])
function toggleTodo(id) {
const todo = todos.find(t => t.id === id)
if (todo) {
todo.done = !todo.done
}
}
function addTodo(text) {
todos.push({
id: Date.now(),
text,
done: false
})
}
</script>
<template>
<ul>
<li v-for="todo in todos" :key="todo.id">
<input type="checkbox" :checked="todo.done" @change="toggleTodo(todo.id)" />
<span :class="{ done: todo.done }">{{ todo.text }}</span>
</li>
</ul>
</template>
<style scoped>
.done {
text-decoration: line-through;
color: #999;
}
</style>toRef 和 toRefs
当需要将响应式对象的属性转换为独立的 ref 时,可以使用 toRef 和 toRefs。
vue
<script setup>
import { reactive, toRef, toRefs } from 'vue'
const state = reactive({
count: 0,
message: 'Hello'
})
// 转换单个属性
const count = toRef(state, 'count')
// 转换所有属性
const { message } = toRefs(state)
// 现在可以独立使用这些 ref
count.value++
message.value = 'Hi'
</script>响应式数据的注意事项
1. ref 需要 .value
在 JavaScript 中访问 ref 的值时,必须使用 .value:
javascript
const count = ref(0)
console.log(count.value) // 正确
console.log(count) // 错误:这是一个 ref 对象2. reactive 的限制
reactive 只能用于对象类型,不能用于基本类型:
javascript
// ❌ 错误
const count = reactive(0)
// ✅ 正确
const count = ref(0)
// 或
const state = reactive({ count: 0 })3. 解构会失去响应性
直接解构 reactive 对象会失去响应性:
javascript
const state = reactive({ count: 0 })
// ❌ 失去响应性
let { count } = state
// ✅ 保持响应性
const { count } = toRefs(state)实战示例:购物车
vue
<script setup>
import { reactive, computed } from 'vue'
const cart = reactive({
items: [
{ id: 1, name: '苹果', price: 5, quantity: 2 },
{ id: 2, name: '香蕉', price: 3, quantity: 3 }
]
})
const total = computed(() => {
return cart.items.reduce((sum, item) => {
return sum + item.price * item.quantity
}, 0)
})
function updateQuantity(id, quantity) {
const item = cart.items.find(i => i.id === id)
if (item) {
item.quantity = quantity
}
}
function removeItem(id) {
const index = cart.items.findIndex(i => i.id === id)
if (index > -1) {
cart.items.splice(index, 1)
}
}
</script>
<template>
<div class="cart">
<h2>购物车</h2>
<div v-for="item in cart.items" :key="item.id" class="cart-item">
<span>{{ item.name }}</span>
<span>¥{{ item.price }}</span>
<input
type="number"
:value="item.quantity"
@input="updateQuantity(item.id, $event.target.value)"
min="1"
/>
<button @click="removeItem(item.id)">删除</button>
</div>
<div class="total">
<strong>总计:¥{{ total }}</strong>
</div>
</div>
</template>
<style scoped>
.cart {
max-width: 600px;
margin: 0 auto;
padding: 20px;
}
.cart-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
border-bottom: 1px solid #eee;
}
.cart-item input {
width: 60px;
padding: 5px;
}
.total {
margin-top: 20px;
text-align: right;
font-size: 1.2em;
}
</style>总结
- Vue 3 使用 Proxy 实现响应式系统
ref用于基本类型,reactive用于对象- 在 JavaScript 中访问 ref 需要使用
.value - 使用
toRefs可以保持解构后的响应性 - 响应式数据让视图自动更新,简化了开发流程
下一步
接下来学习 计算属性和侦听器,了解如何基于响应式数据进行派生计算和监听变化。