Vue中computed原理分析

1.Computed也是响应式的

Computed是响应式的,读取Computed会触发get,设置Computed会触发set

2.Computed如何控制缓存

计算属性是有缓存的,比如某个计算属性C,它依赖data中的A,如果没有缓存的话,每次读取C时,C都回去读取A,从而触发A的get。多次触发A的get有时候是一个非常消耗性能的操作。所以Computed必须要有缓存。

computed里面控制缓存最重要的一点就是脏数据标记为dirty, dirty是watcher的一个属性。

  • 当dirty为true时,读取computed会重新计算
  • 当dirty为false时,读取computed会使用缓存

3. 依赖的data发生变化,computed是如何更新的

页面P依赖计算属性C, 计算属性C又依赖data里面的A, computed更新步骤如下:

  1. 由于C依赖了A, A可以收集到C的watcher
  2. 当A发生变化时,会将watcher的脏数据标记位dirty设置为true
  3. 并且A会收集到页面P的watcher,A通知P进行更新,从而页面P重新读取计算属性C, 由于此时dirty为true,此时的计算属性会重新计算。
  4. computed更新完毕,重新将脏数据标记位dirty设置为false,如果其依赖的A不发生改变,那下次再进入就会读取缓存。

4. 计算属性C是如何让data中的A收集到页面P的watcher的

这其实是计算属性中一个非常巧妙的操作。来看一下核心的源码(已简化)

function createComputedGetter(key) {
    return function() { 
        // 获取到相应 key 的 computed-watcher
        var watcher = this._computedWatchers[key];
        // 如果 computed 依赖的数据变化,dirty 会变成true,
        // 从而重新计算,然后更新缓存值 watcher.value
        if (watcher.dirty) {
            watcher.evaluate();
        }        
        // 这里是 月老computed 牵线的重点,让双方建立关系
        if (Dep.target) {
            watcher.depend();
        }        
        return watcher.value
    }
}
<think>嗯,用户想了解Vue.js中computed属性的实现原理,特别是源码层面的分析。我之前已经整理过相关引用,现在需要结合这些资料来构造一个详细的回答。首先,我应该从Vue 2和Vue 3的不同实现入手,因为引用里提到了两者的源码解析和设计原理。 首先,用户的问题是关于computed的实现原理,需要解释其响应式机制。根据引用[1],Vue 2中计算属性和侦听属性的区别在于计算属性是基于依赖的缓存,而引用[2]提到了Vue3中computed的依赖追踪和副作用触发。同时,引用[3]的书籍可能提供了更系统的框架设计解析。 接下来,我需要理清Vue的响应式系统如何工作。Vue 2使用Object.defineProperty,而Vue 3改用Proxy,这会影响computed的实现方式。计算属性本质是一个getter函数,依赖其他响应式数据,当依赖变化时才会重新计算。 然后,缓存机制是关键点。计算属性只有在相关依赖发生改变时才会重新计算,否则直接返回缓存值。这涉及到依赖收集和派发更新的过程,可能需要结合Watcher和Dep的概念来解释。在Vue 2中,每个计算属性对应一个Watcher,并且这个Watcher是惰性求值的,只有在被访问时才会计算。 另外,Vue 3中的computed通过effect和ReactiveEffect来实现,引用[2]中的代码示例展示了如何利用副作用函数来响应依赖变化。这里需要说明computed内部如何创建effect,以及如何通过调度器(scheduler)来控制重新计算的时机。 还要考虑源码结构,比如在Vue 2中,计算属性在初始化阶段被处理,定义在src/core/instance/state.js中的initComputed函数。而Vue 3的computed API位于packages/reactivity/src/computed.ts,使用ComputedRefImpl类来管理依赖和值。 用户可能还想了解惰性求值和缓存的具体实现细节,比如脏检查机制(dirty标志位)。当依赖变化时,标记为dirty,下次访问时才会重新计算。这可以提升性能,避免不必要的计算。 此外,需要区分computed和watch的不同。计算属性是基于依赖的派生值,而watch是观察特定数据变化执行回调。从源码角度看,computed的Watcher和user Watcher可能有不同的配置选项,比如lazy和dirty的处理。 最后,总结实现原理时,要分点说明响应式依赖、缓存机制、惰性求值,并结合Vue 2和Vue3的不同实现方式。可能还需要提到源码中的关键函数和类,如Vue2的defineComputed、Watcher,Vue3的ComputedRefImpl、track和trigger等。 确保回答结构清晰,逐步深入,并引用提供的资料来支持分析,比如引用[1][2][3]中的内容。同时,生成的相关问题要围绕实现细节、优化、与watch的区别以及应用场景展开,帮助用户进一步学习。</think>### Vue.js computed 属性实现原理分析 Vue 的 **计算属性(computed)** 核心在于**依赖追踪**和**惰性缓存**,其实现机制可分为以下关键环节: --- #### 一、响应式依赖绑定(以 Vue 3 为例) 1. **初始化阶段** ```javascript const sumRes = computed(() => obj.foo + obj.bar) ``` - 通过 `computed` 函数创建 `ComputedRefImpl` 对象[^2]。 - 内部通过 `ReactiveEffect` 包裹计算函数,建立依赖关系: ```typescript // 源码片段(reactivity/src/computed.ts) this.effect = new ReactiveEffect(getter, () => { if (!this._dirty) { this._dirty = true // 标记为"脏数据",触发重新计算 trigger(this, "value" /* TriggerOpTypes.SET */) } }) ``` 2. **依赖收集** - 当首次访问 `sumRes.value` 时,触发计算函数的执行。 - 在计算过程中,访问的响应式变量(如 `obj.foo`)会通过 `track` 函数将当前 `ComputedRefImpl` 对象注册为依赖。 --- #### 二、缓存与惰性计算 1. **缓存机制** - 计算属性内部维护 `_value` 和 `_dirty` 标志位。 - 若依赖未变化(`_dirty` 为 `false`),直接返回缓存值 `_value`。 - 依赖变化时,标记 `_dirty` 为 `true`,但**不立即重新计算**,而是等待下次访问时触发。 2. **性能优化** ```javascript // 示例:多次访问同一个计算属性 console.log(sumRes.value) // 首次计算,标记_dirty=false console.log(sumRes.value) // 直接读取缓存 ``` - **避免重复计算**:依赖未变化时直接返回缓存值。 - **按需更新**:依赖变化后延迟到实际访问时再重新计算[^1]。 --- #### 三、与侦听属性(watch)的本质区别 | **特性** | **计算属性(computed)** | **侦听属性(watch)** | |----------------|------------------------------|-------------------------------| | **触发时机** | 依赖变化后**延迟计算** | 依赖变化后**立即执行回调** | | **返回值** | 必须返回一个值 | 无返回值,用于执行副作用操作 | | **设计目标** | 派生数据(缓存优化) | 响应数据变化(主动监听) | --- #### 四、Vue 2.x 实现差异 - **基于 Watcher 的惰性求值** Vue 2 中每个计算属性对应一个 `Watcher`,配置 `lazy: true` 实现惰性求值: ```javascript // 源码片段(src/core/instance/state.js) function createComputedGetter(key) { return function computedGetter() { const watcher = this._computedWatchers[key] if (watcher.dirty) { watcher.evaluate() // 重新计算 } if (Dep.target) { watcher.depend() // 依赖收集 } return watcher.value } } ``` - **缓存通过 `dirty` 标志控制**,依赖更新时通过响应式系统的 `notify` 方法触发 `dirty` 标记更新。 --- ### 总结 计算属性的核心设计是**将函数式编程与响应式系统结合**,通过依赖追踪实现精准更新,利用缓存机制避免冗余计算。Vue 3 通过 `ReactiveEffect` 和调度器优化了性能,而 Vue 2 则依赖 `Watcher` 和 `Dep` 的经典实现[^2][^3]。 ---
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值