在 Vue3 的源码中,@vue/runtime-core 是整个框架的运行时的核心。它不直接操作DOM,通过@vue/runtime-dom实现vnode的创建、对比与更新。
本文主要基于runtime-core的核心流程,包括组件的初始渲染流程与更新等简要实现机制。
一、runtime-core作用
runtime-core = 平台无关的 VNode 调度器 + 组件生命周期管理
1.对比新旧虚拟 DOM,决定如何更新
2.管理组件实例的创建、渲染与更新
二、从 createApp 到 render
在vue日常开发中,入口文件通常会有这段创建应用实例的代码:
javascript
createApp(App).mount('#app')
createApp是vue内部渲染器创建的一个函数,接收一个根组件后进行mount,实现如下:
javascript
createRenderer(options) //创建渲染器
// runtime-core 内部
//options 包含runtime-dom平台操作接口(如 insert, createElement, patchProp)等
//返回 { render, createApp }
export function createRenderer(options) {
return createBaseRenderer(options)
}
export function createBaseRenderer(rendererOptions) { // 渲染时所到的api
const render = (vnode,container) =>{ // 核心渲染方法
// 将虚拟节点转化成真实节点插入到容器中.....
}
return {
createApp:createAppAPI(render)
}
}
//createAppAPI实现
let uid = 0
export function createAppAPI(render) {
const createApp = (rootComponent, rootProps = null) => {
const app = {
_uid: uid++,
_component: rootComponent,
_props: rootProps,
_container: null,
// _context: context,
_instance: null,
mount(rootContainer){
const vnode = createVNode(rootComponent, rootProps)
render(vnode, rootContainer)
app._container = rootContainer
}
}
return app
}
return createApp
}
//createApp 返回应用实例
const app = createApp(rootComponent)
app.mount(container)
//mount 最终调用 render(vnode, container)
//render 启动首次渲染
function render(vnode, container) {
patch(null, vnode, container) // 首次挂载:oldVNode 为 null
}
三、patch 函数
patch(n1, n2, container) 是 runtime-core 实现元素或组件挂载和比对的重要函数
javascript
if (n1 === n2) return // 相同节点,跳过
if (n1 && !isSameVNodeType(n1, n2)) {
// 类型不同:卸载旧节点,挂载新节点
unmount(n1)
n1 = null
}
// 根据 n2 类型分发处理
switch (n2.type) {
case Text: processText(n1, n2, container); break
case Comment: processCommentNode(...); break
default:
if (shapeFlag & ELEMENT) processElement(...)
else if (shapeFlag & COMPONENT) processComponent(...)
}
//关键思想:
n1 == null时挂载(mount)
n1 存在进行更新(update)
四、元素节点和组件处理
javascript
//处理元素节点:processElement
挂载:mountElement
创建真实元素:hostCreateElement
设置属性:hostPatchProp
插入容器:hostInsert
更新:patchElement
所有 hostXXX 操作均由 runtime-dom 提供,runtime-core 只调用接口。
javascript
//组件节点(processComponent)
//挂载(n1 == null)
mountComponent(n2, container) {
const instance = createComponentInstance(n2)
setupComponent(instance) // 执行 setup()
setupRenderEffect(instance) // 创建渲染 effect!
}
//更新(n1 存在):
updateComponent(n1, n2) {
if (shouldUpdateComponent(n1, n2)) {
instance.next = n2 // 标记新 vnode
instance.update() // 触发 effect 重新执行
}
}
五、组件自动更新简化实现版
javascript
//setupRenderEffect是组件实现自动更新的一个重要函数:
function setupRenderEffect(instance) {
instance.update = effect(() => {
if (!instance.isMounted) {
const subTree = instance.render()
patch(null, subTree, container)
instance.isMounted = true
} else {
// 更新:render 新子树与旧子树 diff
const nextTree = instance.render()
patch(instance.subTree, nextTree, container)
instance.subTree = nextTree
}
}, {
scheduler: queueJob // 异步更新队列
})
}
当ref 或 reactive 数据变更时会trigger找到依赖的 effect(即 instance.update),调用scheduler(异步批量更新),effect重新执行后调用render()再进行patch子树
六、总结
runtime-core不直接处理DOM操作,只通过runtime-dom对应方式处理vnode的实现,并贯穿应用实例的创建到卸载整个生命周期流程,本文主要基于runtime-core的核心实现流程,在具体应用包括组件数据渲染,响应式数据更新等一系列流程后续会进行更新。好了,今天就到这里,欢迎有问题的小伙伴一块交流!