vue-next/runtime-core 源码阅读指南详解
写在前面
最近又抽时间把vue-next/runtime-core的源码陆陆续续地看完了,期间整理了很多笔记,但都是碎片化的。本来是想整理一下,写成一篇文章分享出来的,但是感觉最终的成果物只能是一篇篇幅巨长的解析文,就算我一行一行的把源码加上注释,其阅读体验也会很差,因为每个人读代码的习惯不同,思路不同。正所谓抛砖引玉,所以,我觉的写一篇向导文作为这块砖应该是足够了,希望可以帮助到想看源码但觉得无从看起、无从下手的读者。
另一方面,也算是给自己挖一个坑,因为这篇文章中涉及到的很多内容,三言两语肯定是说不清的,这就意味着之后必须要写其他文章来填补这些空白。我会尽可能的将高内聚的模块整理到一起,然后再分享出来,尽量避免陷入罗列代码的境地,从而提高文章质量吧。
阅读笔记我托管在语雀上,不嫌乱的也可以看这里。
准备工作
工欲善其事,必先利其器,要看源码,拿写字板来看肯定是不行的(当然也不排除牛人)。你所需要的就是一个支持代码跳转的编辑器即可,我用的是VSCode,当然了,如果你用VIM、Sublime也是可以的。
另外还需一些储备知识:
- 由于是阅读vue-next的代码,并且是pre-alpha的版本,这就要求你对之前vue有一定的了解,如果是第一次接触,我觉的阅读源码的意义也不是很大
- 需要熟练掌握debug的基础技巧和流程,通过debug的方式来看代码有两个好处
- debug过程有清晰地调用栈记录
- 各种作用域下的变量一目了然
- 需要对typescript有一定掌握程度,最起码给知道interface、enum等概念
如何阅读
一般有三种途径:
- 直接看
- 通过单元测试的可执行代码
- 自己编写的可执行代码
这里推荐第二种方式,因为单元测试是官方团队维护的,质量肯定有保证,二来单元测试一般都很简单,同时带有注释,这有利于我们理解代码。
由于vue-next使用jest进行单元测试,在vscode中安装Jest插件即可,它支持行内debuglens快捷入口,方便直接对某条单元测试进行debug。
不过要注意配置一个自定义选项:
"jest.debugCodeLens.showWhenTestStateIn":[ "fail", "unknown", "pass",//注意这里 ]
这里的"pass"代表即使单元测试通过,也会在上方显示debuglens,默认情况下,单元测试用例通过的话,这个debuglens标识不会显示。
模块职能归纳
runtime-core目录下有多个文件,我暂且把每个文件都当做一个子模块来看待。vue的代码质量还是挺好的,模块与模块之间的耦合性都不是特别高,正因为如此,基本上每个模块都有自己单独对应的单元测试文件。
我在看的时候,基本上就是挨个看这些模块的单元测试,然后调试过程中,会主动的进行一些代码跳转,去看一下具体的实现细节。下面把当前最新代码下该文件目录下的所有模块的职能进行一些总结和归纳。
有一些较独立的模块我还没有看完,但是不影响整体的源码阅读进程,日后对这些独立模块进行单独研究时,才回来补充这些todo就好了。
公共api相关
实现公共api的模块均是以apixxx这样的格式来命名的,如下:
- apiApp.ts:有3个比较重要的接口需要看一下,App、AppConfig和AppContext,如果对于vue2比较熟悉的话,会很容易理解。createApp是一个工厂方法,返回一个符合App接口约束的对象,其内部方法会与一个符合AppContext接口约束的对象进行交互。
- apiCreateComponent.ts:这个文件内部包含多个对于createComponent函数签名的重载声明,其存在目的应该是为了帮助ts提供更好的类型推断以提升开发体验。
- apiInject.ts:组件依赖注入feature的实现逻辑,实现方式很简单,直接与component文件中暴露的currentInstance变量进行交互,实现继承、赋值、取值等逻辑
- apiLifecycle.ts:新的组件声明周期hooks,主要看injectHook方法就可以了,这里的target默认情况下指向currentInstance,之后会在将某个回调逻辑缓存在currentInstance实例的声明周期回调函数数组中
- apiOptions.ts:其中包含对于component如何解析options的实现逻辑,代码比较长同时也比较复杂,耦合性与其他几个文件较高。但其实没有必要直接看完它内部的全部代码,因为options中每一段的解析逻辑互相之间都是独立的,因此可以专门针对某个option单独去看它内部的解析逻辑,我目前只看了data以及lifecycle。
- apiReactivity.ts:就是对reactivity包中的api的重新导出,没有什么额外的东西
- apiWatch.ts:暂时还没仔细看,不过根据名字可知是和watch相关的api,粗略的看了一下,发现耦合性比较低,因此可以日后再看
组件相关
- component.ts:主要包含如何创建一个内部组件实例的逻辑,代码比较长,但是点进去看的话,会发现它其实是在调用其他模块的暴露的api,本身的逻辑还是比较简单的。需要注意的是,这个文件会暴露一对setCurrentInstance和getCurrentInstance方法用来维护currentInstance变量的指向,同时它会在别的模块中被使用到
- componentProxy.ts:声明了renderproxy的实现逻辑,这个proxy主要负责外部如何与内部组件实例进行交互,可以将它看做是一个外部组件实例
- componentProps.ts:主要看resolveProps,实现了如何解析各种形式的props
- componentSlots.ts:主要看resolveChildren,实现了如何解析各种形式的children节点
- componentRendererUtils.ts:一些渲染组件的util方法,按名字了解各个方法的含义即可
- createRenderer.ts:这个和其他文件耦合度较低,可以理解为VNode的渲染器,只要实现了其接口,可以在任何上下文环境中进行渲染,比如小程序、native、canvas或者内存环境,关于如何编写一个renderer,直接看runtime-test或者runtime-dom的代码即可
- directives.ts:指令相关的内部api,当前的代码版本,这部分可能很多todo因此可以日后再回来看看
其他
- errorHandling.ts:错误处理相关,暂时还没仔细看
- scheduler.ts:作业调度器相关,暂时还没仔细看
- shapeFlags.ts:组件本身和children类型的枚举声明及常量
- suspense.ts:suspense相关,暂时还没看,对于其他文件中的suspense的相关逻辑,我完全是按照react中相关概念来理解的,暂时没遇到任何障碍
- warning.ts:警告相关,大部分是一些util方法,按名字理解其含义就好了
推荐的阅读顺序
直接说我自己的阅读顺序,我认为还是比较符合认知习惯的:
- 先看vnode.ts和h.ts等关于vdom的代码了解一下新的VNode的数据结构
- 然后再看apiApp.ts,看挂载过程是怎样把VNode和渲染上下文关联起来的,这个过程中自然会涉及到各种apixxx.ts中的内容,挨个看就好了
- 由于之前看的都是公共api,需要了解实现细节的话,要进一步看component.ts,其中主要包含内部组件实例的数据结构以及创建流程,同样地,打断点一行一行读即可
- 对于一些解析、工具方法,可以放到最后再看其实现细节,打断点的过程中没有必要一探到底,因为有些方法的名字已经可以很明确的说明其背后实现的逻辑是什么了
期间会遇到suspense、lifecycle之类的代码,这类代码也可以当做单独的内容进行阅读,在一开始看的时候,也可以不要太纠结于细节,当对整体流程有一个大概了解之后再回头来看会清晰很多,之后我会专门整理一篇文章介绍这块是如何实现的。
写在最后
虽然vue-next的代码现在还处在初期的阶段,但是整体的阅读体验还是不错的,结构清晰,可读性也比较高,一些关键模块也有注释进行说明,唯一不足的地方在于,很多地方还是借助as关键字来进行类型断言,我觉得这些地方可能有更好的方式实现类型推断吧。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持毛票票。
声明:本文内容来源于网络,版权归原作者所有,内容由互联网用户自发贡献自行上传,本网站不拥有所有权,未作人工编辑处理,也不承担相关法律责任。如果您发现有涉嫌版权的内容,欢迎发送邮件至:czq8825#qq.com(发邮件时,请将#更换为@)进行举报,并提供相关证据,一经查实,本站将立刻删除涉嫌侵权内容。