vue3源码概述
本文选自vue3源码解析第二章
详情见github
本节开始正式进入vue3源码的分析,有了前一节的知识的,我们直接使用demo里面的例子作为用例
源码结构
packages目录下面的包的作用见下表
| 包目录 | 作用 |
|---|---|
| compiler-core | 先忽略 |
| compiler-dom | 先忽略 |
| compiler-sfc | 先忽略 |
| compiler-ssr | 先忽略 |
| dts-test | 直接忽略 |
| reactivity | 响应式相关代码,比如ref等都在这里 |
| reactivity-transform | 先忽略 |
| runtime-core | 渲染的核心代码(于平台无关) |
| runtime-dom | dom渲染有关代码 |
| runtime-test | 直接忽略 |
| server-renderer | 先忽略 |
| sfc-playground | 直接忽略 |
| shared | 公共工具函数 |
| size-check | 直接忽略 |
| template-explorer | 直接忽略 |
| vue | 包导出 |
| vue-compat | 直接忽略 |
是不是感觉代码很多,别急,很多包实际上在代码运行的时候是用不到的,比如先忽略的包很多都是编译阶段使用的包在运行阶段是不会起任何作用的,这个以后在说。
核心包
对于非ssr的项目而言,核心的包只有以下四个
| 包目录 | 作用 |
|---|---|
| reactivity | 响应式相关代码,比如ref等都在这里 |
| runtime-core | 渲染的核心代码(于平台无关) |
| runtime-dom | dom渲染有关代码 |
| shared | 公共工具函数 |
基本数据结构
基本数据结构是在运行中常用的数据结构。
component
常说的组件是import HelloWorld from './helloworld.vue'中的HelloWorld。在使用webpack和vue-loader时,这里导入的组件会被vue-loader自动处理。对于Options API的组件来讲,这里以demo中的App.vue来讲,具有以下结构
对于composite setup的组件来讲,具有一下结构
总结起来就是component是经过编译器处理后得到的产物。具有一定结构
app
一个app是createApp函数的返回值,第一个参数是根组件。第二个参数可选,它是要传递给根组件的 props。根组件就是上面所说的component
app具有以下结构
component instance
component instance表示在运行过程中创建好的component,和component的区分是component只是编译后的一个对象,这个对象不需要vue的创建就可以单独存在,而component instance是vue在渲染过程中创建的内部对象,用以渲染的用途
component instance具有以下结构
vnode
这个不多讲官网文档里面有。vnode是渲染算法的核心结构。type=component的vnode具有以下结构
vnode还有其他很多种结构这里不列出
container
app.mount函数的第一个参数是container,类型为Element
依赖图
对于type=component的vnode来讲,以上数据结构具有如下的关系
图中的
container相关依赖仅为根组件具有,其他component不具有
运行概述
首次渲染
火焰图
从图中可以看出第一次渲染主要分为两步
- 创建
app对象 - 手动调用对象的
mount方法
mount函数是第一次渲染的调用的函数,从图中可以看到mount函数会调用render函数,render函数的核心是patch函数。
这里先给出结论,vue的渲染实际上是递归调用patch函数进行深度优先遍历直到完成渲染。
非首次渲染
图1左边部分位于图2的红色框区域
从图中可以看到非首次渲染也分为两部分
- 触发更新
- 执行更新
执行更新的函数调用时处于一个微任务调用栈里面(图2的run MircoTasks)。
之后调用的核心函数是componentUpdateFn,这个函数在之后也会调用patch方法去递归遍历进行渲染
总结
vue3的渲染无论是首次还是非首次核心都是递归调用patch算法进行深度优先遍历进行渲染