Vue.js responsive data
What is reactive data?
Reactive data is one of the core features of Vue.js. When the data changes, the view updates automatically, eliminating the need to manually manipulate the DOM. This data-driven approach makes development simpler and more efficient.
Responsive principle
Vue 3 uses Proxy to implement a reactive system, while Vue 2 uses Object.defineProperty.
Vue 3 responsive system
import { reactive, ref } from 'vue'
// 使用 reactive 创建响应式对象
const state = reactive({
count: 0,
message: 'Hello Vue!'
})
// 使用 ref 创建响应式基本类型
const count = ref(0)
The difference between ref and reactive
ref
refBasic types of data (strings, numbers, booleans, etc.) used to create responsiveness.
<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
reactiveUsed to create reactive objects.
<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>
Usage scenarios of responsive data
<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. List data
<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 and toRefs
When you need to convert the properties of a reactive object into independent refs, you can usetoRefandtoRefs。
<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>
Notes on responsive data
1. ref requires .value
When accessing the value of ref in JavaScript, you must use.value:
const count = ref(0)
console.log(count.value) // 正确
console.log(count) // 错误:这是一个 ref 对象
2. Limitations of reactive
reactiveCan only be used for object types, not basic types:
// ❌ 错误
const count = reactive(0)
// ✅ 正确
const count = ref(0)
// 或
const state = reactive({ count: 0 })
3. Deconstruction will lose responsiveness
Destructuring reactive objects directly will lose responsiveness:
const state = reactive({ count: 0 })
// ❌ 失去响应性
let { count } = state
// ✅ 保持响应性
const { count } = toRefs(state)
<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>
Summarize
- Vue 3 使用 Proxy 实现响应式系统
reffor basic types,reactivefor objects
- Accessing ref in JavaScript requires using
.value
- use
toRefsCan maintain responsiveness after deconstruction
- Responsive data allows views to update automatically, simplifying the development process
Next step
Next, learn Computed Properties and Listeners to learn how to perform derived calculations and monitor changes based on responsive data.