developer-plus / interview

https://interview.developer-plus.org
MIT License
9 stars 1 forks source link

Vue 实例挂载的过程中发生了什么? #1

Open Hongbusi opened 2 years ago

alexzhang1030 commented 2 years ago

这里说一下 Vue3 的挂载流程:

默认已通过 createApp() 创建基于 DOM 的渲染器

1. 生成根组件 VNode

调用 createVNode 函数将传入的 rootComponent 根组件转换为 VNode。在 createVNode 函数中,将通过不同的 children 类型打上不同的 ShapeFlag

2. 进行 render

然后调用 renderer 渲染器函数,将根组件的 VNode 进行渲染。这里不使用 Vue 提供的 CustomRenderer 来自定义渲染器,Vue 将会使用内部的 DOM 接口进行挂载。Vue3 挂载流程的重头戏,就是 render 阶段了。

renderer 函数中,会调用 patch 函数。

2.1 patch 函数

在 patch 函数内部,首先会根据 VNode 不同的类型走不同的处理程序。

如果说一个 VNode 是 Fragment 类型,那就走 processFragment 函数,由于 Fragment 类型不渲染父级元素,那么就会直接走处理 children 的流程。

如果说一个 VNode 是 TextNode 类型,那么就说明是字符串,直接创建一个 TextNode 节点,并挂载就可以了。

如果一个 VNode 不是上述的两种类型,那么就要走正常的挂载流程了。正常的挂载流程,会根据组件的 ShapeFlag 分为两种情况:

2.2 初始化

  1. 创建 componentInstance 组件实例
  2. 进入 setupComponent 阶段
    1. 处理 props,将 VNode 的 props 挂载到组件实例上
    2. 处理 slots
    3. 处理 setup 函数,初始化一个有状态的组件,为什么要初始化一个有状态的组件,这是因为 vue 存在 functional component,函数式组件没有状态
  3. 处理 setup 函数:调用 setup 函数,获取返回值,并为返回值包上 proxyRefs
  4. 将 setup 函数返回值挂载到组件实例上
  5. 将 vnode 的 render 函数挂载到 instance 上。如果 vnode 也就是组件没有提供 render 函数,则会通过编译 template 来获取到 render 函数。所以如果一个组件同时提供了 render 函数以及 template,那么 render 函数的优先级会更高
  6. 进入 setupRenderEffect 阶段,调用组件实例的 render 函数,获取其子组件。
  7. 调用 patch 递归处理子组件,此时回到 2.1 阶段

2.3 挂载

  1. 调用 hostCreateElement 创建真实的 dom 节点
  2. 处理 props
  3. 处理 children,如果是 text 节点,直接挂载;如果是 array,循环调用 patch,回到 2.1 阶段
  4. 调用 hostInsert 将节点插入 dom 树中

3. 注意

由于对生命周期钩子研究不深,这里就不提了,如果大家对这个有了解的,可以写个 comment,我来加一下

TickHeart commented 2 years ago

生命周期是在函数单一执行原则的前提下由patch函数衍生出的生命函数,如果想看调用原理还需围绕patch函数去看。 由vue的这个生命周期来看,再次强调了函数单一执行原则的重要性