02响应式基础

Vue 3 的响应式系统是其核心特性之一,它基于 ES6 的 Proxy 实现,相比 Vue 2 的 Object.defineProperty 有显著改进。

一、核心概念

1.1 什么是响应式?

当数据发生变化时,自动更新依赖该数据的视图或计算。

1.2 响应式原理

Vue 3 使用 Proxy 对象包装原始对象,拦截各种操作(读取、写入、删除等),并在变化时触发更新。

二、核心 API

2.1 ref()

用于创建响应式的基本类型或对象引用。

import { ref } from 'vue'

// 基本类型
const count = ref(0)
console.log(count.value) // 0

// 对象类型
const user = ref({
  name: 'John',
  age: 25
})
console.log(user.value.name) // John

// 模板中自动解套,无需 .value
// <div>{{ count }}</div>

2.2 reactive()

用于创建响应式的对象(包括数组)。

import { reactive } from 'vue'

const state = reactive({
  count: 0,
  user: {
    name: 'John',
    hobbies: ['reading', 'coding']
  }
})

// 直接访问,不需要 .value
console.log(state.count) // 0
state.count++ // 响应式更新

// 数组也是响应式的
state.user.hobbies.push('gaming')

2.3 ref() vs reactive()

微信图片_20260210135052_391_29

三、响应式工具函数

3.1 toRef() 和 toRefs()

import { reactive, toRef, toRefs } from 'vue'

const state = reactive({
  name: 'John',
  age: 25
})

// 将 reactive 的属性转换为 ref
const nameRef = toRef(state, 'name')

// 将整个 reactive 对象的所有属性转换为 ref 对象
const { name, age } = toRefs(state)

// 在组合式函数中返回时很有用
function useFeature() {
  const state = reactive({ x: 0, y: 0 })
  return toRefs(state) // 解构后仍保持响应式
}

3.2 computed()

创建响应式的计算属性。

import { ref, computed } from 'vue'

const count = ref(0)

// 只读计算属性
const doubleCount = computed(() => count.value * 2)

// 可写的计算属性
const writableComputed = computed({
  get: () => count.value * 2,
  set: (val) => {
    count.value = val / 2
  }
})

3.3 watch() 和 watchEffect()

import { ref, watch, watchEffect } from 'vue'

const count = ref(0)
const user = reactive({ name: 'John', age: 25 })

// watch:显式指定监听源
watch(count, (newVal, oldVal) => {
  console.log(`count changed: ${oldVal} -> ${newVal}`)
})

// 监听多个源
watch([count, () => user.name], ([newCount, newName], [oldCount, oldName]) => {
  // 处理变化
})

// watchEffect:自动追踪依赖
watchEffect(() => {
  console.log(`count: ${count.value}, name: ${user.name}`)
  // 自动追踪 count.value 和 user.name
})

四、响应式进阶

4.1 浅层响应式

import { shallowRef, shallowReactive } from 'vue'

// shallowRef:只有 .value 的替换是响应式的
const shallowObj = shallowRef({ deep: { value: 1 } })
shallowObj.value = { deep: { value: 2 } } // 触发更新
shallowObj.value.deep.value = 3 // 不会触发更新

// shallowReactive:只有根层属性是响应式的
const shallowState = shallowReactive({
deep: { value: 1 }
})
shallowState.deep = { value: 2 } // 触发更新
shallowState.deep.value = 3 // 不会触发更新

4.2 只读代理

import { reactive, readonly } from 'vue'

const original = reactive({ count: 0 })
const copy = readonly(original)

original.count++ // 正常工作
copy.count++ // 警告!不可修改

4.3 响应式转换

import { markRaw, isReactive, isRef, unref } from 'vue'

// markRaw:标记对象永远不会被转为响应式
const nonReactive = markRaw({ shouldNotBeReactive: true })

// 类型检查
const obj = reactive({})
console.log(isReactive(obj)) // true
console.log(isRef(ref(0))) // true

// unref:如果是 ref 返回其值,否则返回本身
const value = unref(maybeRef)

五、实际应用示例

5.1 组件中使用

<script setup>
import { ref, reactive, computed } from 'vue'

// 使用 ref 处理基本类型
const count = ref(0)

// 使用 reactive 处理对象
const form = reactive({
  username: '',
  password: ''
})

// 计算属性
const isFormValid = computed(() => {
  return form.username.length > 0 && form.password.length > 0
})

// 方法
function increment() {
  count.value++
}

// 监听
watch(count, (newValue) => {
  console.log('Count changed to:', newValue)
})
</script>

<template>
  <div>
    <p>Count: {{ count }}</p>
    <button @click="increment">Increment</button>
    
    <input v-model="form.username" placeholder="Username">
    <input v-model="form.password" type="password" placeholder="Password">
    
    <button :disabled="!isFormValid">Submit</button>
  </div>
</template>

5.2 组合式函数

// useCounter.js
import { ref, computed } from 'vue'

export function useCounter(initialValue = 0) {
  const count = ref(initialValue)
  
  const double = computed(() => count.value * 2)
  
  function increment() {
    count.value++
  }
  
  function decrement() {
    count.value--
  }
  
  function reset() {
    count.value = initialValue
  }
  
  return {
    count,
    double,
    increment,
    decrement,
    reset
  }
}

// 在组件中使用
import { useCounter } from './useCounter'

const { count, double, increment } = useCounter(10)

六、注意事项

  1. 响应式丢失:解构 reactive 对象会失去响应式,使用 toRefs 解决

  2. 异步更新:响应式更新是异步的,使用 nextTick 访问更新后的 DOM

  3. 大数组性能:对超大数组使用响应式可能影响性能,考虑分页或虚拟滚动

  4. 循环引用:避免在响应式对象中创建循环引用

  5. 避免重复包装:不要将已响应式的对象再次用 reactive() 包装

七、最佳实践

  1. 使用 ref 处理基本类型,reactive 处理对象

  2. 组合式函数返回时使用 toRefs 保持响应式

  3. 合理使用 shallowRef/shallowReactive 优化性能

  4. 复杂的计算使用 computed 缓存结果

  5. 使用 watchEffect 简化依赖追踪逻辑

Vue 3 的响应式系统提供了强大而灵活的工具,理解这些概念和 API 对于编写高效、可维护的 Vue 应用至关重要。

posted @ 2026-02-10 13:58  麻辣~香锅  阅读(3)  评论(0)    收藏  举报