当前位置: 首页 > news >正文

ref() 与 reactive()

下面,我们来系统的梳理关于 ref() 与 reactive() 的基本知识点:


一、响应式编程核心概念

1.1 什么是响应式编程?

响应式编程是一种声明式编程范式,它使数据变化能够自动传播到依赖它的代码部分。在 Vue 中,响应式系统实现了:

  • 数据驱动视图:数据变化自动更新DOM
  • 依赖追踪:自动跟踪数据依赖关系
  • 高效更新:最小化不必要的DOM操作

1.2 Vue 响应式系统演进

版本响应式实现特点
Vue 2Object.defineProperty拦截getter/setter,不支持数组索引变化
Vue 3Proxy API全面拦截对象操作,支持数组/集合类型

二、reactive() 深度解析

2.1 基本使用

import { reactive } from 'vue'const state = reactive({count: 0,user: {name: 'Alice',profile: {level: 5}}
})// 修改属性
state.count++ // 触发更新
state.user.name = 'Bob' // 深层响应

2.2 实现原理

Get
Set
Delete
reactive对象
Proxy拦截器
操作类型
追踪依赖
触发更新
收集当前effect
通知相关effect

2.3 核心特性

  • 深层响应:嵌套对象自动转为响应式
  • 类型保留:Array/Map/Set等特殊类型保持原型方法
  • 引用稳定:reactive() 返回同一个代理对象
    const obj = {}
    const proxy1 = reactive(obj)
    const proxy2 = reactive(obj)
    console.log(proxy1 === proxy2) // true
    

2.4 局限性

// 1. 原始值无法使用
const count = reactive(0) // 无效// 2. 属性解构丢失响应性
const { count } = state // 非响应式// 3. 新属性添加需要特殊处理
const state = reactive({})
state.newProp = 'value' // 非响应式// 正确添加响应式属性
import { set } from 'vue'
set(state, 'newProp', 'value')

三、ref() 深度解析

3.1 基本使用

import { ref } from 'vue'// 创建ref
const count = ref(0)
const user = ref({ name: 'Alice' })// 模板中使用
// <div>{{ count }}</div> 自动解包.value// JS中访问
console.log(count.value) // 0
count.value++ // 触发更新

3.2 实现原理

// 简化的ref实现
function ref(value) {const refObject = {get value() {track(refObject, 'value') // 依赖收集return value},set value(newVal) {value = newValtrigger(refObject, 'value') // 触发更新}}return refObject
}

3.3 核心特性

  • 包装原始值:使基本类型具有响应性
  • 模板自动解包:在模板中无需.value
  • 响应式替换:整个对象可替换
    const user = ref({ name: 'Alice' })// 替换整个对象
    user.value = { name: 'Bob' } // 保持响应性
    

3.4 ref 的特殊用法

// DOM元素引用
const inputRef = ref(null)onMounted(() => {inputRef.value.focus()
})// 组件引用
<ChildComponent ref="child" />const child = ref(null)
child.value.doSomething()// 函数式ref
<div :ref="(el) => { /* 操作元素 */ }"></div>

四、ref() vs reactive() 对比

4.1 核心差异对比

特性ref()reactive()
包装对象RefImpl 对象Proxy 对象
值访问需要 .value直接访问
原始值支持
深层响应
类型保留完整保留部分特殊处理
解构响应性支持解构属性需要toRefs
整体替换❌ (会破坏响应性)

4.2 使用场景指南

场景推荐说明
基本类型值ref字符串、数字等
复杂对象reactive表单对象、状态对象
组件状态reactive逻辑相关的状态组
模板引用refDOM元素/组件实例
函数参数ref避免响应式对象解构问题
组合函数返回值ref更灵活的响应式值

4.3 性能考量

  • 创建开销:reactive() 的 Proxy 创建比 ref() 稍重
  • 内存占用:ref() 有额外包装对象开销
  • 更新效率:两者更新效率相当
  • 最佳实践
    // 大型对象使用reactive更高效
    const bigData = reactive(largeDataset)// 独立值使用ref
    const loading = ref(false)
    

五、响应式系统高级用法

5.1 响应式转换工具

import { toRef, toRefs, isRef, isReactive, isProxy } from 'vue'const state = reactive({ count: 0 })// 对象属性转为ref
const countRef = toRef(state, 'count')// 整个对象转为ref集合
const refs = toRefs(state) // { count: Ref<number> }// 类型检查
console.log(isRef(countRef)) // true
console.log(isReactive(state)) // true
console.log(isProxy(state)) // true

5.2 浅层响应式

import { shallowReactive, shallowRef } from 'vue'// 浅层reactive:只响应根级属性
const shallowState = shallowReactive({nested: { value: 1 } // 内部非响应
})// 浅层ref:不自动解包内部值
const shallowObj = shallowRef({ count: 0 })
shallowObj.value.count++ // 不会触发更新

5.3 自定义Ref

import { customRef } from 'vue'function useDebouncedRef(value, delay = 200) {let timeoutreturn customRef((track, trigger) => {return {get() {track()return value},set(newValue) {clearTimeout(timeout)timeout = setTimeout(() => {value = newValuetrigger()}, delay)}}})
}// 使用
const text = useDebouncedRef('', 500)

5.4 响应式工具函数

import { markRaw, readonly, watchEffect } from 'vue'// 1. 标记非响应式对象
const nonReactiveObj = markRaw({ shouldNotBeTracked: true })// 2. 创建只读响应式对象
const readOnlyState = readonly(state)// 3. 响应式副作用
watchEffect(() => {console.log(`Count: ${state.count}`) // 自动追踪依赖
})

六、响应式原理深度剖析

6.1 依赖收集与触发

组件渲染 响应式对象 依赖管理器 访问属性 track(收集当前effect) 记录依赖关系 数据变化时 trigger(通知更新) 执行effect更新 组件渲染 响应式对象 依赖管理器

6.2 Proxy 拦截器实现

const reactiveHandlers = {get(target, key, receiver) {track(target, key) // 收集依赖const res = Reflect.get(target, key, receiver)if (isObject(res)) {return reactive(res) // 递归响应式}return res},set(target, key, value, receiver) {const oldValue = target[key]const result = Reflect.set(target, key, value, receiver)if (hasChanged(value, oldValue)) {trigger(target, key) // 触发更新}return result}// 省略deleteProperty/has等其他拦截
}

6.3 响应式系统三大核心

  1. Effect(副作用)

    let activeEffect
    class ReactiveEffect {run() {activeEffect = thisthis.fn() // 执行过程中收集依赖}
    }
    
  2. Dep(依赖集合)

    class Dep {constructor() {this.subscribers = new Set()}depend() {if (activeEffect) this.subscribers.add(activeEffect)}notify() {this.subscribers.forEach(effect => effect.run())}
    }
    
  3. TargetMap(目标映射)

    const targetMap = new WeakMap()function track(target, key) {if (!activeEffect) returnlet depsMap = targetMap.get(target)if (!depsMap) targetMap.set(target, (depsMap = new Map()))let dep = depsMap.get(key)if (!dep) depsMap.set(key, (dep = new Dep()))dep.depend()
    }
    

七、最佳实践与性能优化

7.1 选择指南

场景推荐替代方案原因
独立原始值refreactive更简洁
相关状态组reactive多个ref逻辑聚合
组件propsref-标准做法
全局状态reactiveref结构化数据
需要解构ref + toRefsreactive保持响应性

7.2 性能优化策略

  1. 避免大型响应式对象

    // 避免
    const hugeObj = reactive(/* 大型数据集 */)// 推荐:使用shallowRef或手动管理
    const data = ref({})
    data.value = largeDataset // 替换而非响应式
    
  2. 减少不必要的响应式

    // 静态配置无需响应式
    const config = markRaw({itemsPerPage: 10,maxRetries: 3
    })
    
  3. 合理使用计算属性

    const sortedList = computed(() => [...list.value].sort((a, b) => a.id - b.id)
    )
    
  4. 批量更新优化

    import { nextTick } from 'vue'const updateMultiple = () => {state.a = 1state.b = 2nextTick(() => {// DOM更新后执行})
    }
    

7.3 响应式数据模式

// 组合式函数模式
export function useCounter(initial = 0) {const count = ref(initial)const increment = () => count.value++const decrement = () => count.value--return {count,increment,decrement}
}// 在组件中使用
const { count, increment } = useCounter()

八、常见问题与解决方案

8.1 响应性丢失问题

问题:解构导致响应性丢失

const state = reactive({ count: 0 })
const { count } = state // 失去响应性

解决方案

// 1. 使用toRefs
const { count } = toRefs(state)// 2. 直接访问原对象
state.count// 3. 使用computed
const count = computed(() => state.count)

8.2 循环引用问题

问题:对象循环引用导致无限递归

const obj = reactive({})
obj.self = obj // 循环引用

解决方案

// 1. 避免循环引用
// 2. 使用markRaw切断响应链
obj.self = markRaw(obj)

8.3 数组操作问题

问题:直接索引修改数组不触发更新

const list = reactive([1, 2, 3])
list[0] = 9 // 不会触发更新

解决方案

// 1. 使用变异方法
list.splice(0, 1, 9)// 2. 整个替换
list.value = [9, 2, 3]// 3. 使用Vue.set (Vue 3中为set)
import { set } from 'vue'
set(list, 0, 9)

相关文章:

  • 网站备案 多久seo建站优化推广
  • 济南东易日盛装饰公司seo优
  • 网站要注册为什么网站关键词排名分析
  • 试用型网站大一html网页制作作业
  • 失物招领网站开发项目需求分析怎么找百度客服
  • 17.zwd一起做网站池尾站打开百度网站首页
  • 黑马Day01-03集开始
  • 原子操作(CAS)
  • 《TCP/IP 详解 卷1:协议》第13章:TCP连接管理
  • java-SpringBoot框架开发计算器网页端编程练习项目【web版】
  • 马克思主义基本原理知识笔记
  • MediaMarktSaturn EDI 对接指南:欧洲零售卖场的数字化协同范例
  • 虚幻基础:插槽
  • C++面试6——类和结构体的区别和使用场景
  • 零基础学习RabbitMQ(3)--核心概念
  • 打包上传到Linux部署并启动
  • C++ string类的操作
  • FFMPEG常用函数
  • 应用层协议 HTTP
  • 618风控战升级,瑞数信息“动态安全+AI”利剑出鞘
  • 无人机航电系统之语音通信技术篇
  • elk+filebeat收集springboot项目日志
  • 开疆智能CCLinkIE转ModbusTCP网关连接川崎机器人配置案例
  • 桥头守望者
  • WRF模式与Python融合技术在多领域中的应用及精美绘图;Python助力WRF自动化运行、WRF模式前后处理
  • Android Navigation 架构