ng老狗的Vue 3 响应式系统源码分享

Ng老狗的Vue 3 响应式系统笔记

响应式库 @vue/reactivity 学习记录

背景

作为一个多年Angular应用的开发者,提到响应式,第一反应就是Rxjs。

相比于Promise的命令式编程分散的信号源, Rxjs的声明式响应数据流,在 用户事件多样且频繁的组件 中,能更优雅地处理异步数据流,往往 一个Observable就能完整的体现整个数据流

初见Vue 3的响应式,是一种既熟悉有新鲜的感觉。

我不再需要各种各样的Observable, Subject, BehaviorSubject 来表达数据流, 而是通过简单的refreactive就能创建响应式数据; 我也不需要去订阅和取消订阅数据流, Vue 3的响应式系统会 自动追踪依赖,并在数据变化时更新。

响应式亦有不同

诚然,Rxjs是一个强大的响应式编程库,适用于复杂的异步数据流处理, 而Vue 3的响应式系统更专注于UI层的数据绑定和状态管理, 在前端开发中,提供了极其便捷和高效的响应式编程体验。

Ps: Angular 16也引入了类似Vue 3的响应式系统,称为Signals。

基础概念

ref 和 reactive 对比 Observable

reactivity中最重要的无疑是 refreactive, 他们类似 Rxjs 中的 BehaviorSubject, 用于收集下游事件,并在更新时 广播 订阅者。

effect, computed 则完成了响应式数据流的蜕变, 自动的依赖收集和触发,彻底摆脱了手动订阅和取消订阅的繁琐。

以自动加法,举一个栗子:

typescript 复制代码
/************* @vue/reactivity ***************/
import { ref, reactive, computed, effect } from '@vue/reactivity';
const a = ref(1);
const b = ref(2);
// 响应式数据流
const sum = computed(() => a.value + b.value);
// 触发
a.value = 3;

console.log(sum.value); // 3 + 2 = 5

/************* Rxjs **************************/
import { BehaviorSubject, combineLatest } from 'rxjs';
import { map } from 'rxjs/operators';
const a$ = new BehaviorSubject(1);
const b$ = new BehaviorSubject(2);
const sum$ = combineLatest([a$, b$]).pipe(
  map(([a, b]) => a + b)
);

// 触发
a$.next(3);
// 特别的数据流只有在订阅时, 才会触发上游计算
sum$.subscribe(console.log); // 3 + 2 = 5

源码阅读

响应式系统的源码并不复杂,核心部分大约几百行代码。 主要包括以下几个模块:

  • 响应式数据创建:refreactive函数
  • 依赖收集和触发:effect函数
  • 计算属性:computed函数
  • 响应式数据的代理和拦截:Proxy对象
  • 响应式数据的只读和深度响应式:readonlyshallowReactive函数 ...等等

其中,依赖收集和触发是响应式系统的核心

一、依赖收集

  • 看懂 effect,就基本了解依赖收集的核心原理
typescript 复制代码
//  例如:
import { effect, ref } from '@vue/reactivity';
const count = ref(0);
effect(() => {
  console.log(`count is: ${count.value}`);
});

在这里如果把 .value 不要看作字段的取值,而是看成 .subscribe(currentEffect) 方法的调用,整个事情就变得很清晰了。

我只需要在 effect 的函数中执行时, 添加一个 currentEffect的全局变量 ,作为上下文, 每次 .subscribe() 的时候,把这个上下文添加到依赖列表中, 就很方便地完成了依赖收集。

computedwatch 也是类似的原理。

二、依赖触发

由于值更新的场景更灵活,依赖触发的设计也就更加复杂些。

Vue中按照 基本数据类型对象类型 分别处理。参考GitHub

当然,非必要的场景下,Vue 也提供了 shallowReactiveshallowRef 来创建浅响应式对象。

三、计算属性

计算属性 computed 的实现也很巧妙。 它本质上是一个带有缓存的 effect。 当计算属性的依赖发生变化时,计算属性会重新计算值, 但只有在访问计算属性的值时才会触发重新计算。

这点和Rxjs中的 shareReplay 操作符有些类似。

四、总结

通过阅读 Vue 3 响应式系统的源码, 对响应式编程的有了新的理解。

核心的依赖清单通过数组管理、 相似的触发逻辑 和响应式数据流的理念,等等保留了发布订阅的稳定设计。

Vue 3 的响应式库,不拘泥于刻板的设计模式, 而是根据实际需求,灵活地设计了响应式数据的创建、依赖收集和触发机制, 从而实现了高效且易用的响应式编程体验。

Vue团队中Anthony Fu在这篇文章中对设计理念有更清晰的阐述,推荐一读。

相关推荐
Zhen (Evan) Wang3 天前
.NET 8 API +Angular 16 + Mysql利用docker compose发布
mysql·.net·angular.js
再花18 天前
在Angular中实现基于nz-calendar的日历甘特图
前端·angular.js
爱学习的小康18 天前
angular MicroApp微服务改造
前端·微服务·angular.js
码界奇点19 天前
基于Spring MVC与AngularJS的API接口管理系统设计与实现
spring·毕业设计·yapi·mvc·angular.js·源代码管理
Highcharts.js23 天前
官方文档|Angular 框架集成 Highcharts Dashboards
前端·javascript·angular.js·highcharts·看板·使用文档·dashboards
聊天QQ:48773927823 天前
基于STM32F103与FPGA的伺服驱动器探索:电流环于FPGA的高效运作
angular.js
Tiam-201624 天前
安装NVM管理多版本node
vue.js·前端框架·node.js·html·es6·angular.js
黑臂麒麟25 天前
DevUI modal 弹窗表单联动实战:表格编辑功能完整实现
前端·javascript·ui·angular.js
黑臂麒麟25 天前
华为云的DevUI&Form组件实战:个人信息编辑表单完整实现
前端·javascript·ui·华为云·angular.js