当前位置: 代码网 > it编程>编程语言>Javascript > vue 3 effect作用与原理解析

vue 3 effect作用与原理解析

2025年02月13日 Javascript 我要评论
vue 3 的effect(副作用)是整个响应式系统的核心机制,负责管理依赖追踪和响应式触发。理解其作用和原理对掌握 vue 的底层机制至关重要。一、核心作用1.依赖追踪(dependency tra

vue 3 的 effect(副作用) 是整个响应式系统的核心机制,负责管理依赖追踪和响应式触发。理解其作用和原理对掌握 vue 的底层机制至关重要。

一、核心作用

1. 依赖追踪(dependency tracking)

  • 自动跟踪响应式数据在副作用函数中的使用。

示例代码

import { reactive, effect } from 'vue'
const obj = reactive({ count: 0 })
effect(() => {
  console.log(`count is: ${obj.count}`)
})
  • 当首次执行 effect 时,函数 () => console.log(...) 会被运行。
  • 触发 obj.count 的 get 操作,触发依赖收集(将当前 effect 关联到 obj.count)。

2. 自动响应(automatic re-run)

当响应式数据的依赖变化时,自动重新执行副作用函数:

obj.count++  // 触发依赖更新,控制台打印 "count is: 1"

3. 支撑高级 api

  • computedwatch、组件渲染函数等底层都依赖于 effect 实现。

二、实现原理

1. 核心类:reactiveeffect

vue 3 用 reactiveeffect 类封装副作用逻辑,简化后的源码结构如下:

class reactiveeffect<t = any> {
  // 当前 effect 的所有依赖项(其他响应式对象)
  deps: dep[] = []
  // 构造函数参数
  constructor(
    public fn: () => t,            // 副作用函数
    public scheduler?: () => void  // 调度函数(控制重新执行方式)
  ) {}
  // 运行副作用(触发依赖收集)
  run() {
    activeeffect = this // 标记当前正在运行的 effect
    try {
      return this.fn()
    } finally {
      activeeffect = undefined
    }
  }
  // 停止侦听
  stop() { /* 从所有依赖中移除自身 */ }
}

2. 依赖收集流程(track)

数据结构

type dep = set<reactiveeffect>    // 依赖集合
type targetmap = weakmap<object, map<string, dep>> // 全局依赖存储
  • 触发时机:响应式数据的 get 操作触发时。
  • 流程

    根据响应式对象 (target) 和键 (key) 找到存入 targetmap 的依赖集合 (dep)。

    将当前活跃的 activeeffect 添加到 dep 中。

    同时将 dep 加入 activeeffect.deps(反向记录,用于 cleanup)。

3. 触发更新(trigger)

  • 触发时机:响应式数据的 set 操作时。
  • 流程

    根据 target 和 key 从 targetmap 获取对应的 dep 集合。

    遍历 dep 中所有 effect

    • 如果有 scheduler(如 computed),执行调度器(优化性能)。
    • 否则直接执行 effect.run()

4. 调度器(scheduler)

允许控制 effect 如何重新执行:

effect(() => {
  console.log(obj.count)
}, {
  scheduler(effect) { 
    // 如将 effect 推入微任务队列中异步执行
    queuemicrotask(effect.run) 
  }
})
  • 应用场景:
    • watch 的异步批处理更新。
    • computed 的值懒更新。

三、关键优化设计

1. 嵌套 effect 栈

用栈结构 effectstack 跟踪嵌套的 effect:

function run() {
  if (!effectstack.includes(this)) {
    try {
      effectstack.push((activeeffect = this))
      return this.fn()
    } finally {
      effectstack.pop()
      activeeffect = effectstack[effectstack.length - 1]
    }
  }
}
  • 解决问题:组件嵌套时的依赖关系混乱。

2. cleanup 机制

每次 effect 执行前清理旧依赖:

function run() {
  cleanup(this) // 清理之前收集的旧依赖
  // ...然后重新收集新依赖
}
  • 解决问题:动态分支逻辑导致的无效依赖(如 v-if 切换导致的条件依赖)。

3. lazy 执行

可配置不立即执行 effect:

const runner = effect(fn, { lazy: true })
runner() // 手动执行
  • 应用场景computed 属性初始化时延迟计算。

四、与 vue 各组件的关联

1. 组件渲染

组件 render 函数被包裹在 effect 中:

function setuprendereffect(instance) {
  effect(() => {
    const subtree = instance.render.call(instance.proxy)
    patch(instance.subtree, subtree)
    instance.subtree = subtree
  }, { scheduler: queuejob }) // 异步更新队列
}

2. computed 实现

computed 通过 effect + 调度器实现懒更新:

const computedref = new computedrefimpl(
  getter,
  () => { // 调度器
    if (!this._dirty) {
      this._dirty = true
      trigger(this, 'set', 'value')
    }
  }
)

3. watch api

watch 基于 effect 的调度器实现异步回调:

function watch(source, cb, { flush } = {}) {
  let scheduler
  if (flush === 'sync') {
    scheduler = cb
  } else { // 'post' 或其他默认情况
    scheduler = () => queuepostflushcb(cb)
  }
  effect(() => traverse(source), { scheduler })
}

五、与 vue 2 的对比

特性vue 2 (watcher)vue 3 (effect)
依赖追踪通过遍历数据触发 getter通过 proxy/reflect 自动追踪
更新粒度依赖组件级检查基于精确依赖的靶向更新
性能优化需手写 purecomputed 等内置自动的依赖清理和调度机制
内存管理易产生内存泄漏(旧 dep 引用问题)通过 weakmap 自动释放无用依赖

六、源码流程图解

+---------------------+
|   reactive object   |
+----------+----------+
           │ 访问属性时
           ▼
+---------------------+
|   触发 get 代理     +----→ track(target, key)
+---------------------+         │
           ▲                    ▼ 存储依赖关系
           │          +---------------------+
           +----------+    targetmap        |
                      | (weakmap结构)        |
                      +---------+-----------+
                                │
                                ▼
                      +---------------------+
                      |   depsmap (map)     |
                      | (key → dep set)      |
                      +---------+-----------+
                                │
                                ▼
                      +---------------------+
                      |   dep (set)         |
                      | (存储所有关联的 effect)|
                      +---------------------+

总结

vue 3 的 effect 通过以下机制成为响应式系统的核心:

  • proxy 依赖收集:精确追踪响应式数据的使用。
  • 调度器控制:提供灵活的回调执行方式。
  • 内存安全:通过 weakmap 自动管理依赖。
  • 框架级优化:支持组件渲染、计算属性、watch 等核心功能。

到此这篇关于vue 3 effect作用与原理的文章就介绍到这了,更多相关vue 3 effect作用内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!

(0)

相关文章:

版权声明:本文内容由互联网用户贡献,该文观点仅代表作者本人。本站仅提供信息存储服务,不拥有所有权,不承担相关法律责任。 如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 2386932994@qq.com 举报,一经查实将立刻删除。

发表评论

验证码:
Copyright © 2017-2025  代码网 保留所有权利. 粤ICP备2024248653号
站长QQ:2386932994 | 联系邮箱:2386932994@qq.com