购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.3 mounted挂载

3.3.1 涉及文件

mounted挂载涉及文件路径如表3.2所示。

表3.2 mounted挂载涉及文件路径

本节将详细介绍Vue3中mounted挂载时的整体流程,通过该流程了解组件挂载的详细情况。下面对涉及的虚拟DOM和VNode的相关知识进行简单介绍。

mount(挂载)内部将VNode转换为真实的DOM结构再挂载到DOM根节点内,mount的调用在createApp函数内已有简单介绍,对render函数的作用也有一定了解,本节将继续围绕该内容展开介绍。

虚拟DOM本质是通过JavaScript对象描述真实DOM的结构和属性。因为在DOM操作的过程中会涉及页面的重绘和回流,所以引入虚拟DOM减少对DOM对象的操作,降低性能损耗。如果只是对页面内某个极小部分修改就造成整个DOM树的重绘,那么会有极大的性能损耗。虚拟DOM概念从Vue2开始引入,在真实操作DOM前对需要修改的内容进行判断,保证只有DOM结构的最小化修改,减少重绘和回流的次数,以达到节约性能损耗的目的。在Vue3中依然保留虚拟DOM,并且对其进行大幅度优化。

VNode是虚拟DOM的外在表现,对VNode的增、删、改和查可映射为对DOM的增、删、改和查。整个渲染核心均是围绕虚拟DOM展开的,通过VNode的形式呈现。该对象声明位置在$runtime-core_vnode文件内,具体声明及含义可在4.1.2节查看。

对虚拟DOM和VNode有简单了解后,再回到$runtime-core_apiCreateApp文件内查看与mount相关的内容。mount内部首先会判断是否已经挂载,如果已挂载,则结束mount函数执行。如果未挂载,则创建一个VNode,再执行render渲染。mount的挂载本质是调用apiCreateApp()函数,该函数简化后代码如下:

上述代码通过createVNode()函数创建VNode上下文,将创建好的上下文通过render函数进行渲染。此处mounted挂载将分为3个步骤:

(1)根据根组件信息创建根组件VNode,在根节点VNode上存储应用上下文;

(2)从根组件VNode开始递归渲染生成DOM树并挂载到根容器上;

(3)处理根组件挂载后的相关工作并返回根组件的代理。

注: 此render函数是内部渲染函数,内部利用递归的方式将VNode转换为DOM结构使用,与后面将模板转换为VNode的render渲染函数无任何关联。

3.3.2 创建根组件VNode

通过调用createVNode()函数实现对根组件VNode的创建,该函数位于$runtime-core_vnode文件内,实现代码如下:

该函数内部首先判断是否为开发环境,然后根据环境输出代码的执行情况,最后调用_createVNode()函数创建一个VNode并返回。_createVNode()函数的内部实现逻辑主要包括:

(1)对class和style进行标准化;

(2)对VNode的形态类型进行确定(patch时依赖);

(3)通过对象字面量的方式来创建VNode;

(4)标准化子节点;

(5)完成创建后,返回VNode。

注: 此处的标准化操作十分有必要,并且是提升遍历速度的措施之一。此处标准化后,在后续对VNode进行递归遍历时,可以减少判断分支,提高递归效率和减少其他未知情况的影响,进而提升遍历稳定性、遍历速度和代码可读性。

因_createVNode()函数内容较多,此处不便展开,将放到4.1.2节详细介绍,并对较核心的内容进行注解。

3.3.3 递归渲染

完成根组件创建后,开始调用render函数渲染。render函数是以参数的形式传入createAppAPI()函数内,因此在调用createAppAPI()的地方,可以查到对应render函数的实现。代码位于$runtime-core_render文件内,定义在baseCreateRenderer()函数内,精简后的代码如下:

上述代码调用render函数执行卸载和挂载逻辑,因介绍mounted挂载逻辑,此处默认有VNode传入,执行patch函数。

patch函数的主要逻辑代码如下:

整个patch函数主要是判断不同的VNode类型(此处的VNode类型即创建时初始化的类型),并根据不同类型执行不同的分支,实现递归整个VNode的逻辑。Vue3在处理VNode的shapeFlag时采用了位运算的方式,关于位运算的知识,将在7.6节介绍。根据调试情况来看,程序执行逻辑最终会进入processComponent()函数中,该函数具体实现如下:

本节主要关注挂载逻辑,处理mounted挂载的mountComponent()函数,具体实现代码如下:

在上述代码中,mountComponent()函数内部主要完成如下3项工作:

(1)创建组件上下文实例;

(2)执行组件setup函数;

(3)创建带副作用的render函数。

接下来详细介绍上述3点内容。

3.3.4 创建组件上下文实例

该方法返回通过对象字面量创建的instance。为帮助读者阅读源码及了解相关特性,我们可以通过instance的interface来了解组件实例包含的属性。ComponentInternalInstance的interface定义如下:

因组件属性较多,此处进行简单注解以便于读者理解。许多属性在日常开发过程中均可能涉及,但此处不需要完全记住,对相关属性有了解后,后续若有需要,可以查阅相关文档。

介绍完interface属性定义后,继续回到mountComponent()函数。得到初始化数据后,开始执行setupComponent()函数,该函数主要初始化props和slots,再执行setup和兼容(Vue2)options API的方法,最终得到instance上下文实例。此处简单查看setupComponent()函数的实现,具体解析将放在3.4节中介绍。

setupComponent()代码逻辑如下:

上述代码执行完成后,对于数据的处理包括instance、props、slots和state。继续调用带副作用的render函数,在该函数内创建带副作用的渲染函数并保存在update()方法中,然后调用update()方法。在update()方法内根据挂载情况,判断执行挂载或更新逻辑。主要代码逻辑如下:

setupRenderEffect()函数内保存effect副作用函数,整个副作用函数通过componentUpdateFn()函数实现,完成后将effect.run方法绑定到当前上下文的update()方法上,完成后执行update()方法,触发effect副作用函数的执行(挂载和更新,均会触发)。componentUpdateFn()函数挂载逻辑涉及的代码如下:

ComponentUpdateFn()函数内部主要涉及挂载和更新流程,对挂载逻辑进一步分析可以看到,虽然上述代码较多,但是大部分在处理回调钩子函数。挂载流程实际上主要分为两个步骤:

(1)渲染组件子树;

(2)patch子树。

渲染组件子树时,主要涉及renderComponentRoot()函数,根据代码执行逻辑找到该函数的实现。该函数位于$runtime-core_componentRenderUtils文件内。renderComponentRoot()函数接收一个instance作为参数,返回渲染结果(根组件的VNode)。了解该函数的作用后,再深入查看对应的实现逻辑,主要代码如下:

注: 此处的render渲染函数是组件的渲染函数,用于将template转换为VNode使用,并非3.3.1节中的render分发函数。

上述代码的核心是调用instance的render方法进行处理,通过调用render渲染函数完成转换过程得到VNode后,将执行第(2)步patch子树。

3.3.5 patch子树

patch子树的作用是将VNode通过递归的方式挂载为VNode Tree并转换为渲染函数的过程。在转换过程中,会根据子树的类型进行patch分发,调用不同的逻辑渲染。关于patch函数的具体实现,将会在4.2节着重介绍。

在完成patch子树步骤后,会再次调用patch函数,此时将会得到根组件的真实DOM节点,即id="root"的div元素。根据传入的类型可知,本次patch函数将会分发到processElement()函数内。该函数主要根据判断结果,执行对元素的挂载或更新。具体代码逻辑如下:

此处为首次渲染,会通过mountElement()函数对节点元素进行挂载,涉及代码逻辑如下:

上述代码的主要逻辑如下:

(1)创建el元素;

(2)根据children类型,处理子节点;

(3)处理props;

(4)将元素插入DOM节点内。

mountElement()函数内的其他流程逻辑都较为清晰和简单,下面简单介绍mountChildren()函数的实现。该函数内部通过循环判断children数组,直接调用patch函数递归挂载组件,具体代码实现逻辑如下:

完成渲染后,会返回mount并执行重写mount的方法,将元素挂载到页面上。整个mount方法挂载流程如图3.9所示。

图3.9 mount方法挂载流程

3.3.6 总结

整个mounted挂载过程介绍完成。通过本节的学习,可对Vue3中整个mounted实现过程有深入的认识。本节内涉及的函数较多,函数内实现代码逻辑较多,可以结合流程图帮助理解流程。本节通过重写mount方法,将核心的处理流程和平台的API进行分离,通过重写的方法巧妙地将平台方法和核心方法分离,该写法使得核心方法不局限于Web端,可进行多端扩展。该方法和技巧值得学习,可根据实际情况应用到自身项目内。 ZaOZGSjap/aJ9eiJC2KXXrvo/RcKrAuee/dKg8xUubcUn+qK4qSxD+FiLzSsd/ig

点击中间区域
呼出菜单
上一章
目录
下一章
×