这个问题还能怎么理解?
关键词:初始化、挂载、DOM、VNode、createApp、创建实例
- Vue实例挂载的时候都发生了什么?
- Vue如何创建实例的?
- Vue createApp都发生了什么?
- Vue是怎么将组件渲染成DOM的?
- Vue初始化的时候都做了什么?
一. 着重了解什么?
- 延时创建渲染器
- 便于 tree-shaking 去除渲染相关的核心代码
- 重写 app.mount 方法
二. vnode渲染需要解决什么问题?
三. 延时创建渲染器
只有在 createApp() 时才才会去通过 ensureRenderer 方法去创建渲染器。所谓渲染器就是 app.mount() 相关的逻辑。好处是可以在只使用vue响应式内容时,自动将渲染相关逻辑进行tree shaking。
const app = ensureRenderer().createApp(...args)
所谓渲染器,就是 ensureRenderer().createApp(...args) 返回复写前的app.mount,它只有 模板 -> vnode -> 渲染vnode 逻辑。
四. 重写 app.mount 方法
因为不同平台的代码mount逻辑不同,这里需要根据不同平台作区分,比如:weex、小程序和web就不一样。但无论什么平台,本质都是 模板 -> vnode -> 渲染vnode。重写只是在渲染器外添加区分平台,拿到container和template的逻辑。
重写的内容补充了什么?
渲染器是 createApp() 创建根实例时用到的,所以渲染器的第一步是创建根实例vnode。
五. 创建根组件vnode
其实就是根据组件类型,拼出 vnode 对象。最后序列化一下子节点。把子节点整理成数组的形式。
六. 根节点vnode渲染成dom
这一步主要就是通过 render 渲染器实现,本质就是对根节点子树vnode调用patch。
七. Patch 将组件的vnode渲染成dom
Patch 的功能就是将 vnode 渲染成dom。同时兼备首次渲染,新老vnode更新,组件卸载的能力。会根据vnode的类型分情况处理。
如果新老vnode type不同,则直接销毁原有。
只是判断新老 vnode.type 和 vnode.key 是否都严格相等。
function isSameVNodeType (n1, n2) {
// n1 和 n2 节点的 type 和 key 都相同,才是相同节点
return n1.type === n2.type && n1.key === n2.key
}
八. processComponent 创建组件
processComponent 即负责首次挂载组件么,也负责组件更新。
因为组件vnode本质上只是一个占位符,需要创建这个组件实例,然后挂载这个组件的dom。创建组件的过程需要相比挂载普通dom多出 创建组件实例、设置组件实例、创建组件的副作用渲染函数。
- 创建组件实例
- 设置组件实例:执行setup函数,处理props、插槽...
- 创建组件的副作用渲染函数(和 vnode -> dom 渲染相关的就在这儿)
这个副作用渲染函数负责组建的首次挂载过程,也负责组件更新的过程,他就是被响应式变量更新时派发的render effect,下面是render effect的核心流程:
九. processElement 创建普通DOM元素
processElement 即负责首次渲染DOM,也负责更新dom,diff算法的逻辑就在更新dom这里。
根据 vnode 创建 dom 元素的过程就是简单的一系列原生操作。
如果有子节点,就会 mountChildren 去用 patch 方法挂载子节点。
所以说,节点创建和挂载的过程是一个深度优先遍历的过程。父先创建,子后创建,子先挂载,父后挂载。mounted生命周期即使如此。
补充:mountElement会先通过document.createElement创建当前节点,然后递归子节点去patch,等子节点都patch完成,最外层就会插入到container上。