Vue3 中的双向链表依赖管理详解与示例

作者:excel日期:2025/10/3

在 Vue3 的响应式系统中,双向链表是一个非常重要的数据结构。相比 Vue2 使用数组来存放依赖,Vue3 选择链表的原因在于效率更高,尤其是在频繁收集和清理依赖时,链表可以显著优化性能。本文将通过讲解和示例代码,帮助你理解这一点。


为什么要用双向链表

在响应式依赖收集过程中,Vue 需要完成两件事:

  1. 收集依赖:当访问响应式数据时,要记录当前副作用函数(effect)。
  2. 清理依赖:当副作用函数重新运行或失效时,需要把它从依赖集合里移除。

如果依赖集合使用数组:

  • 删除某个依赖需要遍历整个数组才能找到它,复杂度是 O(n)。
  • 当依赖数量多、更新频繁时,性能就会下降。

使用双向链表:

  • 删除节点只需要修改前后两个指针,复杂度是 O(1)。
  • 插入节点同样是 O(1)。
  • 非常适合依赖频繁增删的场景。

双向链表的基本结构

每个链表节点包含:

  • effect:保存副作用函数
  • prev:指向前一个节点
  • next:指向后一个节点

通过 prevnext,节点可以快速被插入或删除。
示例:

1A <-> B <-> C
2删除 B:
3A.next = C
4C.prev = A
5

代码示例

下面的示例展示了如何通过双向链表来管理依赖的添加、删除和触发。

1// 链表节点:存储副作用函数和前后指针
2class EffectNode {
3  constructor(effect) {
4    this.effect = effect; // 要执行的副作用函数
5    this.prev = null;     // 前一个节点
6    this.next = null;     // 后一个节点
7    this.dep = null;      // 当前节点所属的依赖集合
8  }
9}
10
11// 依赖集合(Dep):使用双向链表存储多个 effect
12class Dep {
13  constructor() {
14    this.head = null; // 链表头
15    this.tail = null; // 链表尾
16  }
17
18  // 添加依赖:O(1),插入到链表尾部
19  add(effect) {
20    const node = new EffectNode(effect);
21    node.dep = this;
22    if (!this.head) {
23      // 如果链表为空,头尾都指向当前节点
24      this.head = this.tail = node;
25    } else {
26      // 否则插入到尾部
27      this.tail.next = node;
28      node.prev = this.tail;
29      this.tail = node;
30    }
31    return node; // 返回节点,方便以后删除
32  }
33
34  // 删除依赖:O(1),只需修改相邻节点指针
35  remove(node) {
36    if (node.prev) node.prev.next = node.next;
37    if (node.next) node.next.prev = node.prev;
38    if (node === this.head) this.head = node.next;
39    if (node === this.tail) this.tail = node.prev;
40    // 清理引用,帮助垃圾回收
41    node.prev = node.next = node.dep = null;
42  }
43
44  // 触发依赖:遍历链表,执行所有副作用函数
45  trigger() {
46    let cur = this.head;
47    while (cur) {
48      cur.effect();
49      cur = cur.next;
50    }
51  }
52}
53
54// 模拟响应式数据
55const state = { count: 0 };
56// 每个属性都可能有一个依赖集合,这里用一个 dep 演示
57const dep = new Dep();
58
59// 注册副作用函数,并收集依赖
60function effect(fn) {
61  const runner = () => fn();
62  const node = dep.add(runner);
63  runner(); // 立即执行一次
64  return node; // 返回节点,方便后续删除
65}
66
67// 使用:收集依赖
68const node = effect(() => {
69  console.log("副作用执行: count =", state.count);
70});
71
72// 修改数据时触发依赖
73state.count++;
74dep.trigger(); // 输出: 副作用执行: count = 1
75
76// 删除依赖,再次触发时不会执行
77dep.remove(node);
78state.count++;
79dep.trigger(); // 无输出
80

输出结果

1副作用执行: count = 0
2副作用执行: count = 1
3

在移除依赖后,再次修改数据时不会有输出,说明链表删除操作成功。


总结

  • Vue3 使用双向链表来存储依赖,是为了提升收集和清理的效率。
  • 相比数组,链表的插入和删除复杂度更低,尤其适合依赖数量多且频繁变化的场景。
  • 通过示例可以看到,依赖的收集、触发和清理都能高效完成。

这就是 Vue3 响应式系统比 Vue2 更快的一个重要原因。

本文部分内容借助 AI 辅助生成,并由作者整理审核。


Vue3 中的双向链表依赖管理详解与示例》 是转载文章,点击查看原文


相关推荐


CodeBuddy配套:如何配置AI编程总工程师spec-kit
小虎AI生活2025/10/2

我是小虎,浙江大学计算机本硕,专注AI编程。 如果AI能像总工程师一样,先帮你把图纸画好,再动手干活,那该多爽? AI编程学习群里,有学员在吐槽,AI编程时经常“失忆”,写着着就忘了前面的上下文,让人抓狂 🤯。 这不仅是学员们踩过的坑,也是我自己的惨痛教训。 昨天我也写了一篇文章,介绍我的土办法。 [CodeBuddy实战:防止AI编程跑偏的土办法,能抓老鼠就是好猫!] 今天,我要给你们安利一个刚出炉的神器,它能彻底改变你和AI协作写代码的方式。 而且,我敢说,全网我可能是第一篇教程写C


推荐 6 个本周 yyds 的 GitHub 项目。
喜爱编程2025/10/2

01 微软开源的文档转换工具 MarkItDown 用于将各种常见格式的文档转换为 Markdown 格式。 包括 PDF、PPT、Word、Excel、图片、音频、HTML、JSON等,甚至还能处理 ZIP 压缩包内的多个文件、YouTube 视频转录文本以及电子书 EPub 等。 它尤其适合需要将文档内容提取为结构化文本,并用于大模型处理或文本分析任务的情景。 其实仅限这个场景,因为项目官方说可以保留表格、排版啥的,实测并没有。。。 PDF 文件转换,左边屏是源文


【数据挖掘】基于随机森林回归模型的二手车价格预测分析(数据集+源码)
码银10/2/2025

本研究运用随机森林回归模型对汽车价格进行预测。通过对包含多种汽车属性的数据集进行预处理,包括对分类变量的独热编码,将其划分为训练集与测试集。利用训练集数据拟合随机森林模型,并使用测试集数据进行预测与评估。同时,借助多种可视化手段深入分析模型性能与数据特征。数据源:https://www.kaggle.com/datasets/vrajesh0sharma7/used-car-price-prediction选择随机森林回归模型作为预测工具,并设定决策树数量为100(),同时固定随机种子(


iOS 26 系统流畅度剖析:Liquid Glass 动画表现 + 用户反馈
代码背锅人日志9/30/2025

本文聚焦 iOS 26 系统流畅度,结合用户反馈、Liquid Glass 视觉变革与性能挑战,介绍如何用 KeyMob + Instruments 记录帧率 /卡顿 /动画延迟,并给出实战流程与优化建议,帮助开发者评估新版系统中界面的真实流畅性。


优先级队列(堆)-1046.最后一块砖的重量-力扣(LeetCode)
1白天的黑夜19/30/2025

个人主页:1白天的黑夜1-CSDN博客专栏:力扣刷题录_1白天的黑夜1的博客-CSDN博客、企鹅程序员:Linux 系统与网络编程_1白天的黑夜1的博客-CSDN博客目录一、题目解析1、选出两块最重的石头意为第一重和第二重或同样重2、如果只剩一块石头,返回石头的重量;如果没有石头返回0二、算法原理解法:优先级队列解法步骤:三、代码示例一、题目解析优先级队列就是堆,而堆又有大根堆和小根堆,本题需要用到的就是大根堆这里模板参数Compare的缺省值为less,也就是按照从根往下,根比孩子大;而小根堆则是grea


从 “Hello AI” 到企业级应用:Spring AI 如何重塑 Java 生态的 AI 开发
草莓熊Lotso2025/10/4

🔥个人主页:@草莓熊Lotso 🎬作者简介:C++研发方向学习者 📖个人专栏: 《C语言》 《数据结构与算法》《C语言刷题集》《Leetcode刷题指南》 ⭐️人生格言:生活是默默的坚持,毅力是永久的享受。 前言:当大模型浪潮席卷软件开发领域时,Java 开发者常常面临一个困境:一边是 PyTorch、LangChain 等 Python 生态的 AI 工具链蓬勃发展,一边是企业现有系统中大量的 Spring 技术栈难以快速接入 AI 能力。而 Spring AI 的出现


Android Jetpack 核心组件实战:ViewModel + LiveData + DataBinding 详解
马 孔 多 在下雨2025/10/5

Android Jetpack 核心组件实战:ViewModel + LiveData + DataBinding 详解 在 Android 开发中,我们经常会遇到屏幕旋转数据丢失、UI 与逻辑耦合紧密、数据更新无法自动同步 UI 等问题。Google 推出的 Jetpack 架构组件可以很好地解决这些问题,本文将对 ViewModel、LiveData 和 DataBinding 三个核心组件进行讲解,从基础概念到实战案例,完整讲解这三个组件的使用方法与联动逻辑。 一、ViewModel:


v你真的会记笔记吗?AI的答案可能让你意外
万少 VIP.5 如鱼得水2025/10/7

这段时间我在准备一个行业调查,调研资料几乎全来自视频会议、线上讲座和播客。 内容是很丰富,但问题也随之而来:一个小时的视频回放,想找个观点得快进倒退十几次,遇到灵感还得赶紧切出去做笔记,效率低到崩溃。 看不完,根本看不完…… 正好我朋友是一个AI发烧友,他就推荐我用了一个专注做AI笔记的工具。 坦白讲,最开始我没抱太大期待,心想不就是转写嘛。但真用了两周后,我发现它完全改变了我的学习和工作流。 这个工具叫Ai好记: 网址:aihaoji.com/zh?utm_sour… 输入口令【万少】可以


JAVA算法练习题day34
QiZhang6032025/10/8

43.验证二叉搜索树 要知道二叉搜索树的中序遍历结果是升序序列 # Definition for a binary tree node. # class TreeNode(object): # def __init__(self, val=0, left=None, right=None): # self.val = val # self.left = left # self.right = right class Solution(o


面试真实经历某节跳动大厂Java和算法问答以及答案总结(一)
360_go_php2025/10/10

Java面试问题与解答 常见的GC回收器 - Serial GC: 适合单线程环境,暂停时间较长。 - Parallel GC: 多线程垃圾回收,适合多核处理器,停顿时间较短。 - CMS (Concurrent Mark-Sweep): 适合响应时间要求高的应用,通过多线程并发清除垃圾。 - G1 GC: 适用于大内存系统,目标是尽量减少GC停顿时间,分区回收。​编辑 SpringMVC的请求过程 - 流程: 用户发起请求 → 前端控制器(DispatcherServlet)接收请求

首页编辑器站点地图

Copyright © 2025 聚合阅读

License: CC BY-SA 4.0