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解析
    • effect解析
    • reactive响应式依赖的收集与触发监听
    • 批量更新实现
      • 概述
      • 批量更新的实现
        • 核心变量
        • 核心方法
        • batch
        • startBatch
        • endBatch
    • ReactiveEffect类介绍
    • computed
  • 5.18源码学习》
  • reactivity响应式
东流
2025-08-25
目录

批量更新实现

# 概述

在vue3响应式系统设计中,批量更新是优化性能的核心机制之一。当短时间内频繁多次修改响应式数据时,批量更新可以避免频繁触发订阅者的更新操作,将这些更新操作合并为一次,从而减少不必要的计算和DOM操作。

批量更新也是利用链表的方式实现。

# 批量更新的实现

# 核心变量

批量更新的实现依赖于3个核心变量:batchDepth、batchedSub和batchedComputed

let batchDepth = 0; // 批量更新的嵌套深度
let batchedSub;// 存储待执行的普通订阅者队列
let batchedComputed; // 存储待执行的计算属性computed订阅者队列(单独处理,有优先级)
1
2
3

# 核心方法

批量更新的实现依赖于3个核心方法:startBatch、endBatch和batch

# batch

batch方法的作用是将订阅者sub加入批量队列中。sub通常是ReactiveEffect实例,该方法不会立即执行更新,只是暂存将sub加入批量队列中,等待endBatch方法调用时统一执行。

batch方法的实现如下:

function batch(sub, isComputed = false) {
  // 标记订阅者为 EffectFlags.NOTIFIED "已加入批量队列"  
  sub.flags |= 8;
  if (isComputed) {
    // 若为计算属性订阅者,则加入计算属性链表中(单独维护)
    sub.next = batchedComputed; // 新订阅者放在链表的头部
    batchedComputed = sub;
    return;
  }
  // 若为普通订阅者,则加入普通订阅者链表中
  sub.next = batchedSub;
  batchedSub = sub;
}
1
2
3
4
5
6
7
8
9
10
11
12
13

# startBatch

startBatch方法的作用是开启批量更新,将batchDepth加1,说明后续的更新操作会被暂存,不立即执行。

function startBatch() {
  batchDepth++;
}
1
2
3

# endBatch

endBatch就是当所有嵌套的批量更新都结束后(batchDepth减为0),执行链表中所有暂存的订阅者更新,并清理状态。

endBatch的源码实现如下:

function endBatch() {
  // 若批量更新仍有嵌套(深度未到0),不执行实际更新   
  if (--batchDepth > 0) {
    return;
  }
  // 处理计算属性订阅者链表
  if (batchedComputed) {
    let e = batchedComputed;
    batchedComputed = void 0; // 清空队列
    while (e) {
      const next = e.next;
      e.next = void 0;// 重置链表指针
      e.flags &= -9;// 清除批量队列的标志
      e = next;
    }
  }
  // 处理普通订阅者队列并执行更新
  let error;
  while (batchedSub) {
    let e = batchedSub;
    batchedSub = void 0; // 清空队列
    while (e) {
      const next = e.next;
      e.next = void 0; // 重置链表指针
      e.flags &= -9; // 清除批量队列的标志
      // 若订阅者标记为需要触发更新,则执行更新,调用订阅者的trigger方法
      if (e.flags & 1) {
        try {
          ;
          e.trigger();
        } catch (err) {
          // 暂存错误  
          if (!error) error = err;
        }
      }
      // 遍历下一个订阅者
      e = next;
    }
  }
  // 处理错误
  if (error) throw error;
}
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
编辑 (opens new window)
上次更新: 2025/08/26, 10:34:53
reactive响应式依赖的收集与触发监听
ReactiveEffect类介绍

← reactive响应式依赖的收集与触发监听 ReactiveEffect类介绍→

最近更新
01
computed
08-26
02
ReactiveEffect类介绍
08-26
03
reactive响应式依赖的收集与触发监听
08-22
更多文章>
Theme by Vdoing | Copyright © 2024-2025 东流 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式