Vue 3.0 源码逐行解析:响应式模块

Posted 前端技术优选

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Vue 3.0 源码逐行解析:响应式模块相关的知识,希望对你有一定的参考价值。

https://juejin.im/post/5da91fcff265da5b852928e1

前言

vue 3.0 源码刚出没多久 本人平常开发惯用vue 之前看过 vue 2.0 的一些源码 这次准备做一次逐行分析 3.0 的源码 欢迎大家在下方留言 大家一起讨论。

vue 3.0 源码特色:

  • 结构清晰


    通过yarn的工作区方式,简单来说就是功能模块按照package.json下的workspace字段,解耦划分成一个个文件夹(自带package.json,不懂的跳转:yarn 文档),每个功能可以自己 yarn dev 编译出相应模块文件,换句话说 vue 3.0 的响应式模块 完全可以抽离出来用作其他项目 这是之前vue 2.0 做不到的

    Vue 3.0 源码逐行解析(一):响应式模块(1)

  • Typescript重构
    之前vue 2.0 是用 Flow 来做静态类型代码检查,后面发现这个团队不靠谱 Flow项目烂尾了,根本拼不过微软大大的TS,现在TS的前端市场占比也很高,因此本次也是直接狠下心用Typescript重构了。

    之前本人有个项目用了vue+TS,但总觉得自己的使用很初级,高阶使用 如范型之类的,都没有落地接触过,这个源码中有非常多的TS最佳实践,高阶TS使用。所以看这个源码还能同时很好地学习 TS ,一举两得。


  • Proxy改造
    看过2.0源码的或者稍微了解过vue响应式原理的都知道2.0使用的是Object.defineProperty做数据劫持的。这次也是直接改造成Proxy(ES6语法),性能不像Object.defineProperty需要一上来就遍历所有,性能更加优异,下面会继续讨论这块的。

vue 3.0 源码如何有效地看呢:

  1. 必看
    主目录的package.json,每个项目应该首先看这个文件。因为它可以让我们了解源码支持哪些scripts,比如 test 我们可以yarn test跑一下测试脚本试试。workspace字段代表工作区的内容即功能模块代码位置,就不多说了。

    还有一些前端工程化发布流程相关的加餐知识,学到了也可以应用到自己的项目中,这块可以下节附赠给大家。


  2. 先关注某个功能模块的主流程
    前面说了 workspace 的概念,我们可以从其中一个功能模块开始,看主流程文件,中间遇到函数回调,先看字面意思,看完主流程再去一个个切进去深入看。如:先从 reactivity 文件夹下的 reactive.ts 响应式主流程看起。


  3. 语法文档
    因为有很多 TS / ES6 的语法,建议配合着 TS 文档及 ES6 语法来看,遇到不会的语法赶紧查


  4. yarn dev
    调试源码,编译生成相应文件,自己写一个html引入来测试,这块下节给大家说一下

正文

先看这块,我将源码的注释翻译了一下,感觉还是不够立体,需要品味一番
Vue 3.0 源码逐行解析(一):响应式模块(1)


语法上看,用到了 TS 的范型接口的概念

  • 什么是泛型,这是一种 OOP 后端语言常用的概念,即下图中的<T>,可以从字面的意思推导出:泛指的,不确定的类型。传进来什么类型后续就用什么类型,比如这里Set如果传入Number类型,add方法的入参也必须是Number类型

  • 为什么说是接口,因为 TS 核心对 ES6 的 Set Map WeakSet WeakMap都做了封装 写入 TS 声明文件(.d.ts),这样 TS 就能对这些类型进行类型推断,静态检查

Vue 3.0 源码逐行解析(一):响应式模块(1)

语义上看,主要是为了构造 targetMap 这个存储依赖关系的WeakMap
这里用WeakMap的原因在于,WeakMap键名所引用的对象都是弱引用,即垃圾回收机制不将该引用考虑在内,不影响引用计数,只要其他引用都清除,垃圾回收自动回收掉该对象内存,正好适合该场景:你需要存储proxy化的target对象,一旦其他target对象引用清除,这边targetMap也需要自动清除该对象,有助于防止内存泄漏。

目的就是为了构造出下方这种格式,具体 Dep 的泛型 ReactiveEffect 是什么需要到 effect.ts再看,现在关注这个结构。
{
  target:{
    key: Dep  
  }   
}

再看下一块

Vue 3.0 源码逐行解析(一):响应式模块(1)

这边源码注释翻译就是这4个weakmap存储着原始数据到观察者的映射 这里注意这个<->符号代表的是双向的,即原始数据 -> 观察者,观察者 -> 原始数据。
这边为什么要存储双向的,应该还是从 通过牺牲空间复杂度来换取更高效的时间复杂度考虑,因为这样存储获取的时间复杂度是O(1),并且还能获得一些其余的好处比如一些自带的性能极佳的方法


Vue 3.0 源码逐行解析(一):响应式模块(1)

这边主要是做了一些预备操作,比如声明集合的类型,正则表达式等,以及是否可观察的函数,这边我已经注释了,对主流程没有太大影响。



接下来属于核心流程

Vue 3.0 源码逐行解析(一):响应式模块(1)
Vue 3.0 源码逐行解析(一):响应式模块(1)

对外暴露的reactive方法是proxy化,这边对只读数据进行了特殊处理readonly函数,这里还需要对值做判断,如果这个值是响应式的proxy直接返回响应式版本。其实最后的核心proxy化步骤都是createReactiveObject这个函数


Vue 3.0 源码逐行解析(一):响应式模块(1)

这边是proxy化的核心流程,其实看着还是比较简单,主要是proxy的handler这一块下一节会深入进去。



Vue 3.0 源码逐行解析(一):响应式模块(1)

最后是一些对外暴露的函数,字面意思应该都看得懂,这里注意获取原始数据这里,最后直接或操作符了observed,有两种可能,一种是非可观察的白名单即上文所提到的,一种是基本数据类型



尾言


这节主要讲了响应式的核心文件及主流程,内容并不复杂,有问题可以下方留言,互相讨论。下节会深入到文件effect.ts,这块也是源码中理解的难点,并且会附送源码中的一些工程化的实践。

Vue 3.0 源码逐行解析(一):响应式模块(1)


在看点这里


以上是关于Vue 3.0 源码逐行解析:响应式模块的主要内容,如果未能解决你的问题,请参考以下文章

Vue 3.0 公开代码后,引发国外一场撕逼大战!

vue系列---响应式原理实现及Observer源码解析

Part3-5-3 Vue.js 3.0 响应式系统原理

vuex源码分析(二)——双向数据绑定

Vue 响应式原理解析

前端攻城狮该了解的 Vue.2x 响应式原理