Jinuss's blog Jinuss's blog
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

东流

Web、WebGIS技术博客
首页
  • 源码合集

    • Leaflet源码分析
    • Openlayers源码合集
    • vue3源码
  • HTML
  • CSS
  • 技术文档
  • GitHub技巧
  • 学习
  • 实用技巧
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • reactivity响应式

    • ref
    • reactive
    • 基础对象的代理
    • 数组代理的方法
    • 集合对象的代理
    • Reflect和Proxy详解
    • 依赖的收集与触发
    • EffectScope解析
    • ReactiveEffect类介绍
    • reactive响应式依赖的收集与触发监听
    • 批量更新实现
    • computed
      • 概述
      • 源码解析
        • computed方法
        • ComputedRefImpl类
        • notify
        • getter
        • setter
        • refreshComputed方法
    • 响应式中的watch实现
  • runtime-core运行时核心模块

  • runtime-dom运行时DOM模块

  • 5.18源码学习》
  • reactivity响应式
东流
2025-08-26
目录

computed

# 概述

vue3中,computed函数用于表示计算属性,有惰性求值、响应式追踪依赖的特点。本文将介绍computed的实现原理以及其机制细节。

# 源码解析

computed计算属性和computed方法、ComputedRefImpl类以及refreshComputed方法有关。

# computed方法

computed暴露给外部的就是computed方法,其源码实现如下:

function computed(getterOrOptions, debugOptions, isSSR = false) {
  let getter;
  let setter;
  if (shared.isFunction(getterOrOptions)) {
    getter = getterOrOptions;
  } else {
    getter = getterOrOptions.get;
    setter = getterOrOptions.set;
  }
  const cRef = new ComputedRefImpl(getter, setter, isSSR);
  return cRef;
}
1
2
3
4
5
6
7
8
9
10
11
12

computed方法实现比较简单,需要关注参数getterOrOptions和isSSR,isSSR默认为false,它在服务端渲染会传值为true。debugOptions用以在开发环境调试。

computed会先判断getterOrOptions是否是函数,若是函数,则将其赋值给getter;当然getterOptions也可以是一个包含get和set方法的对象。computed方法返回的是ComputedRefImpl实例,一般我们读取计算属性的值也是读取它的返回值的.value。

# ComputedRefImpl类

ComputedRefImpl用于构造一个计算属性。

ComputedRefImpl的源码实现如下:

class ComputedRefImpl {
    constructor(fn, setter, isSSR) {
        this.fn = getter; //计算函数
        this.setter = setter; // 设置函数(可选)
        this["_value"] = undefined; // 缓存的结果,计算属性的值
        this.dep = new Dep(this); // 依赖收集器(收集依赖此计算属性的副作用effect)
        this["__v_isRef"] = true; // 表示为ref类型
        this["__v_isReadonly"] = undefined; // 只读标记
        this.deps = undefined; //当前计算属性依赖的响应式集合对象链表头
        this.depsTail = undefined; //链表尾
        this.flags = 16; //状态标记
        this.globalVersion = globalVersion - 1;// 全局版本号,用于脏检查
        this.isSSR = undefined; //服务端渲染标记
        this.next = undefined; //用于在effect链表中指向下一个节点
        this.effect = this; // 指向自身
        this["__V_isReadonly"] = !setter; //若无setter,则表示计算属性是只读的
        this.isSSR = isSSR;//ssr标记赋值
    }
    // 依赖变更调用
    notify() {
        this.flags |= 16;
        if (!(this.flags & 8) && activeSub !== this) {
            batch(this, true);
            return true;
        }
    }
    // 计算属性值访问器
    get value() {
        const link = this.dep.track()
        refreshComputed(this);
        if (link) {
            link.version = this.dep.version;
        }
        return this._value;
    }
    // 计算属性值设置器
    set value(newValue) {
        if (this.setter) {
            this.setter(newValue)
        } else {
            warn("Write operation failed: computed value is readonly");
        }
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44

ComputedRefImpl中除了在构造器中定义了相关属性外,就是包含两个属性访问器函数和一个notify方法

# notify

当计算属性依赖的响应式值发生变化时,会调用notify方法.notify方法会先设置this.flags标志位的值,将其第4位置为1,表示有更新请求;然后判断标志位的第3位是否为1并且当前激活订阅(副作用)是不是自身,若条件满足,则调用batch方法,将该计算属性添加到更新队列中,进行批量更新,最后返回true,表示更新已调度;若不满足条件,则返回false,表示更新被跳过。

# getter

getter属性访问器,会在读取计算属性的值时触发。先进行依赖收集,追踪当前正在运行的effect,然后调用refreshComputed方法进行有条件性的重新计算,若当前计算属性被其他effect依赖,则更新依赖的版本,最后返回this._value。

# setter

setter属性设置,一般情况下计算属性只是只读的,若this.setter方法存在,则可以调用该方法设置计算属性的值。

# refreshComputed方法

refreshComputed方法就是用于进行刷新计算属性的值,满足条件就重新进行计算,得到最新的计算属性的值。

refreshComputed的源码实现如下:

function refreshComputed(computed) {
    // 检查更新条件
    if (computed.flags & 4 && !(computed.flags & 16)) {
        return;
    }
    // 清除pending状态
    computed.flags & =-17;

    // 全局版本校验 避免重复计算
    if (computed.globalVersion === globalVersion) {
        return;
    }
    computed.globalVersion = globalVersion;

    // 缓存有效性检查 
    if (!computed.isSSR && computed.flags & 128 && (!computed.deps && !computed._dirty || !isDirty(computed))) {
        return;
    }

    // 标记计算状态
    computed.flags |= 2;
    const dep = computed.dep;
    const prevSub = activeSub;
    const prevShouldTrack = shouldTrack;
    activeSub = computed;
    shouldTrack = true;
    try {
       // 依赖收集准备
        prepareDeps(computed);

        // 执行计算函数
        const value = computed.fn(computed._value);
        
        // 值变化检测
        if (dep.version === 0 || hasChanged(value, computed._value)) {
            computed.flags |= 128; // 标记valid
            computed._value = value;  
            dep.version++; // 触发依赖更新
        }
    } catch (err) {
        dep.version++;
        throw err;
    } finally {
        activeSub = prevSub;
        shouldTrack = prevShouldTrack;
        cleanupDep(computed); // 清理过期依赖
        computed.flags &= -3; // 清除computing状态
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49

refreshComputed方法会先检查flags的值,若是被移除或者没有更新请求,则直接返回;然后修改flags状态,清除pending状态;比较全局版本号和计算属性的版本号,若二者一样,则返回,避免是在计算属性中修改了响应式属性引起的重新计算;修改响应式的版本号;然后做缓存有效性的检查;若是脏数据,则返回;再次标记flags状态,表示是计算中;将当前effect计算属性切换为activeSub,修改shouldTrack为true,调用prepareDeps进行依赖收集,然后执行计算属性的fn,即传入computed的参数函数,得到新的value,比较计算属性的值是否发生改变,赋值this._value,并将其依赖dep的版本递增,如此会触发依赖该计算属性的副作用effect更新;最后恢复activeSub和shouldTrack,清理过期依赖以及清除计算状态。

refreshComputed是计算属性computed的核心方法,依据一些规则判断需要执行fn,获取最新的value以及触发相关依赖。

编辑 (opens new window)
上次更新: 2025/09/11, 06:22:43
批量更新实现
响应式中的watch实现

← 批量更新实现 响应式中的watch实现→

最近更新
01
patchProp属性补丁解析
09-16
02
createApp创建实例解析
09-15
03
渲染器renderer源码解析
09-15
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式