Vue3 响应式系统——ref 和 reactive

一、Vue3 响应式系统概述

Vue3 响应式包 @vue/reactivity,核心由三部分构成:

Plain 复制代码
数据 (Proxy Object)  ------ 依赖收集 Track  ------ 触发更新 Trigger  ------  Effect 执行更新

核心目标:

  • 拦截读取和设置操作
  • 收集依赖
  • 在数据变化时重新触发相关副作用

主要实现 API:

二、reactive() 执行机制

2.1 核心逻辑(核心源码)

JavaScript 复制代码
function reactive(target) {
  return createReactiveObject(target, false, mutableHandlers)
}

function createReactiveObject(target, isReadonly, baseHandlers) {
  if (!isObject(target)) {
    return target
  }
  if (target already has proxy) return existing proxy
  const proxy = new Proxy(target, baseHandlers)
  cache proxy
  return proxy
}

Vue3 用 ​Proxy 拦截对象操作 ​,比 Vue2 的 Object.defineProperty 更强(能监听属性增删)。

2.2 reactive 的 handler(简化)

JavaScript 复制代码
const mutableHandlers = {
  get(target, key, receiver) {
    const res = Reflect.get(target, key, receiver)
    track(target, key)
    return isObject(res) ? reactive(res) : res
  },
  set(target, key, value, receiver) {
    const oldValue = target[key]
    const result = Reflect.set(target, key, value, receiver)
    if (oldValue !== value) {
      trigger(target, key)
    }
    return result
  }
}

三、依赖收集和触发更新:track()trigger()

Vue 内部维护一个 ​全局的 activeEffect​:

JavaScript 复制代码
let activeEffect = null

function effect(fn) {
  activeEffect = wrappedEffect(fn)
  fn() // 执行一次用于收集依赖
  activeEffect = null
}

每次读取(get)响应式数据时:

JavaScript 复制代码
function track(target, key) {
  if (!activeEffect) return
  const depsMap = targetMap.get(target) || new Map()
  const dep = depsMap.get(key) || new Set()
  dep.add(activeEffect)
}

当数据被设置(set)时:

JavaScript 复制代码
function trigger(target, key) {
  const depsMap = targetMap.get(target)
  const dep = depsMap?.get(key)
  dep?.forEach(effect => effect())
}
  • track 只在 读取时收集依赖
  • trigger 只在 数据修改时触发 effect 重新执行

四、ref() 的设计与区别

4.1 ref 是什么?

ref() 主要用于包装 基本类型 (对于对象引用类型内部直接调用上面的 reactive()):

JavaScript 复制代码
const count = ref(0)

其结构本质上是:

TypeScript 复制代码
interface RefImpl {
  value: T
}

源码核心:

JavaScript 复制代码
function ref(rawValue) {
  return createRef(rawValue)
}

function createRef(rawValue) {
  const refImpl = { 
    _value: convert(rawValue), 
    dep: new Set(), // 区别于reactive引用类型复杂的多层嵌套数据结构封装dep,ref这里直接在实例中存放一个dep来实现
    get value() {
      trackRefValue(refImpl)
      return refImpl._value
    },
    set value(newVal) {
      if (hasChanged(newVal, refImpl._value)) {
        refImpl._value = convert(newVal)
        triggerRefValue(refImpl)
      }
    }
  }
  return refImpl
}

4.2 ref vs reactive 的本质区别

五、template / setup 中的自动 unwrap

在 Vue 模板中:

Plain 复制代码
<p>{{ count }}</p>

如果 count 是一个 ref,它会 ​自动解包 ​,模板中不需要写 .value。这是由编译阶段的 transform 实现的。

六、响应式系统执行流程图(简化)

Plain 复制代码
reactive/ref 数据 -> Proxy getter -> track
                           │
                        effect 注册
                           │
                    数据 setter -> trigger
                           ↓
                    重新执行 effect
相关推荐
To_OC4 小时前
LC 51 N 皇后:我以为难的是回溯,结果栽在了对角线下标
javascript·算法·leetcode
腻害兔4 小时前
【若依项目-产品经理视角】深度拆解 RuoYi-Vue-Pro 商城模块:从商品管理到交易引擎,50 张表撑起一整套电商系统
java·大数据·vue.js·产品经理·ai编程
不好听6135 小时前
从一行 JSX 到屏幕像素:前端开发者必须懂的浏览器渲染管线
前端
恒拓高科WorkPlus5 小时前
企业级内网即时通讯建设模型:BeeWorks内网IM四层架构
前端
前端小李子5 小时前
前端环境变量裸奔?我用 EnvShield 给它穿了件防弹衣
前端
youtootech5 小时前
HarmonyOS《柚兔学伴》项目实战25-我的页面、Web 嵌入与项目总结
前端·华为·harmonyos
小林ixn6 小时前
从零到一理解 React 父子组件通信:手写一个 Todo 应用带你彻底搞懂单向数据流
前端·javascript·react.js
醇氧7 小时前
CountDownLatch / CyclicBarrier / Semaphore 面试高频问答清单
前端·面试·职场和发展
窝子面8 小时前
手搓最简前后端协作-node
javascript·数据库