Vue3响应式原理与依赖收集实战:ref与reactive源码级解析

Vue3响应式系统的核心设计

Vue3的响应式系统基于Proxy实现,替代了Vue2的Object.defineProperty。Proxy可以直接拦截对象的读取、写入、删除、枚举等操作,天然支持数组下标和动态新增属性,这是Vue2需要Vue.set的原因。响应式系统由三个角色组成:reactive/ref创建响应式对象,effect注册副作用函数,依赖收集与触发更新在get/set拦截中完成。

先看一个最小实现,理解响应式工作的链路:读取属性时把当前副作用函数登记到依赖表,修改属性时从依赖表取出函数重新执行。

import { reactive, effect } from 'vue'

const state = reactive({ count: 0 })
effect(() => { console.log(state.count) })
state.count++  // 触发副作用重新执行

ref与reactive的取舍与包装原理

reactive直接代理对象,对基本类型无效;ref则把任意值包进{value},适合单独声明基本类型的响应变量。ref在模板中自动解包,脚本里需要.value。两者混用时,ref放进reactive里自动解包,但要避免在解构时丢失响应性。

const count = ref(0)
const obj = reactive({ list: [] })
// toRefs保留解构后的响应性
const { list } = toRefs(obj)

reactive用Proxy包装,访问属性时触发track,赋值时触发trigger。深层响应通过递归调用reactive实现,浅层版本shallowReactive只代理首层,性能更好,适合列表这种结构。

effect副作用调度与缓存优化

effect是响应系统的执行引擎。它的scheduler调度选项决定了触发时是同步执行还是排队异步执行。Vue的组件渲染与computed都依赖这个机制:批量更新时把任务丢进微任务队列,避免多次渲染。

const runner = effect(
  () => state.count + 1,
  {
    lazy: true,          // 不立即执行
    scheduler: (job) => queueJob(job)  // 调度执行
  }
)
runner() // 手动执行

computed缓存逻辑:依赖未变时不重新计算,直接返回缓存值;依赖变化后置为脏,再次访问才计算。这在昂贵计算场景价值明显,比如过滤大列表。

watch与watchEffect的触发时机控制

watch需要显式指定侦听的源,能拿到新旧值;watchEffect自动收集回调中访问的响应依赖,依赖变化就重新执行。watch适合异步场景,比如搜索防抖;watchEffect适合同步副作用,比如记录日志。

watch(source, (newVal, oldVal) => {
  // 新旧值对比后处理
}, { flush: 'post' })

watchEffect(() => {
  saveToStorage(state.list)  // 自动追踪
})

flush选项控制回调时机:pre在组件渲染前,post在渲染后,sync每次变化立即执行。默认pre。组件卸载时记得停止watch,避免内存泄漏。

响应式性能:shallowRef与数据结构的取舍

大型列表用reactive逐层代理开销大,用shallowRef包裹引用类型,替换整个值时只触发一次更新。依赖图层级深、结构大的对象,可以改用shallow减少代理开销。

import { shallowRef } from 'vue'

const bigList = shallowRef([])
// 整体替换触发更新
bigList.value = newData

Object.freeze冻结的常量对象不需要响应式,跳过代理能省掉一批内存。写响应式代码时先想清楚数据形状,业务数据与临时变量分开,避免把本不该共享的状态放进全局reactive。

原创文章,作者:小编,如若转载,请注明出处:https://www.yunthe.com/vue3-xiang-ying-shi-yuan-li-yu-yi-lai-shou-ji-shi-zhan-ref/

(0)
小编小编
上一篇 1天前
下一篇 1天前

相关推荐