Vue.js响应式基础

Vue.js 最独特的特性之一是其非侵入性的响应式系统。数据模型仅仅是普通的 JavaScript 对象。而当你修改它们时,视图会进行更新。这使得状态管理非常简单直接。

什么是响应式?

在 Vue.js 中,响应式是指当数据发生变化时,视图会自动更新。这是通过 Vue 的响应式系统实现的,它能够追踪数据的变化并在变化发生时自动更新相关的视图部分。

核心概念

Vue 3 使用 Proxy 对象来实现响应式,这比 Vue 2 的 Object.defineProperty 更加强大和高效。

声明响应式状态

在 Vue 3 中,我们可以使用两种主要方式来声明响应式状态:refreactive

使用 ref()

ref() 用于创建响应式的引用值。它可以包装基本类型(如字符串、数字)和对象类型。

Composition API示例: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() 用于创建响应式的对象。它返回一个对象的响应式代理。

Composition API示例: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() 比较

特性 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 的综合示例:

完整示例Vue 组件中的响应式数据

<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
                        

最佳实践

推荐做法
  • 使用 ref 处理基本类型数据
  • 使用 reactive 处理复杂的对象结构
  • 在组合式函数中返回响应式对象时,使用 toRefs 保持响应性
  • 避免直接重新赋值 reactive 对象,这会丢失响应性
  • 使用 computed 创建基于响应式数据的计算属性
常见问题
  • 响应式丢失:解构 reactive 对象或重新赋值会导致响应性丢失
  • 深层响应式:reactive 是深层的,会影响所有嵌套属性
  • 数组处理:使用响应式数组时,某些变异方法可能需要特别注意

总结

Vue.js 的响应式系统是框架的核心特性之一。通过 refreactive,我们可以轻松创建响应式数据。理解这两种方法的使用场景和区别,对于编写高效、可维护的 Vue 应用至关重要。

在后续章节中,我们将学习计算属性、侦听器等更高级的响应式特性,它们构建在本章所学的基础之上。

测试你的理解

思考题
  1. 什么时候使用 ref,什么时候使用 reactive?
  2. 如何在解构 reactive 对象时保持响应性?
  3. Vue 3 的响应式实现与 Vue 2 有什么主要区别?
@