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

1. Form input

<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)

Practical example: shopping cart

<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
  • usetoRefsCan 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.