render 渲染
先从首次渲染来初探 render 函数。我们在执行 createApp(App).mount('#app') 时,实在上是执行了 createApp 返回的 app 对象中的 mount 函数。同时,我们也知道 mount 函数,挂载组件的时候执行了 render(vnode, rootContainer, isSVG) 方法。
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {
// ...
const app: App = (context.app = {
// ...
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
// ...
if (isHydrate && hydrate) {
// ssr 渲染
} else {
// 普通渲染
render(vnode, rootContainer, isSVG)
}
// ...
} else if (__DEV__) {
// ...
}
},
// ...
)
// ...
// app 对象本体
return app
}
}
render
const render: RootRenderFunction = (vnode, container, isSVG) => {
if (vnode == null) {
// ...
} else {
// 由于是首次渲染,执行 patch 函数
patch(container._vnode || null, vnode, container, null, null, null, isSVG)
}
// ...
}
patch
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
slotScopeIds = null,
optimized = __DEV__ && isHmrUpdating ? false : !!n2.dynamicChildren
) => {
// ...
const { type, ref, shapeFlag } = n2
switch (type) {
// ...
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
/* ... */
} else if (shapeFlag & ShapeFlags.COMPONENT) {
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
slotScopeIds,
optimized
)
} else if (shapeFlag & ShapeFlags.TELEPORT) {
/* ... */
} else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
/* ... */
} else if (__DEV__) {/* ... */}
}
patch 函数会对 vnode 节点(新旧)进行判断,并根据 shapeFlag 调用不同的函数来处理。
Vue3.0 中在 vnode 中新增了 shapeFlag 属性,主要用来定义描述组件的分类
export const enum ShapeFlags {
ELEMENT = 1, // HTML 或 SVG 标签 普通 DOM 元素
FUNCTIONAL_COMPONENT = 1 << 1, // 函数式组件
STATEFUL_COMPONENT = 1 << 2, // 普通有状态组件
TEXT_CHILDREN = 1 << 3, // 子节点为纯文本
ARRAY_CHILDREN = 1 << 4, // 子节点是数组
SLOTS_CHILDREN = 1 << 5, // 子节点是插槽
TELEPORT = 1 << 6, // Teleport
SUSPENSE = 1 << 7, // Supspense
COMPONENT_SHOULD_KEEP_ALIVE = 1 << 8, // 需要被keep-live的有状态组件
COMPONENT_KEPT_ALIVE = 1 << 9, //已经被keep-live的有状态组件
COMPONENT = ShapeFlags.STATEFUL_COMPONENT | ShapeFlags.FUNCTIONAL_COMPONENT // 有状态组件和函数组件都是组件,用component表示
}
processComponent
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
slotScopeIds: string[] | null,
optimized: boolean
) => {
n2.slotScopeIds = slotScopeIds
// 判断 n1(旧节点)是否为空,来决定是挂载还是更新
if (n1 == null) {
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
// keep-alive 组件处理
} else {
// 挂载组件
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else {
// 更新组件
updateComponent(n1, n2, optimized)
}
}
mountComponent
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
// ...
// 创建组件实例
const instance: ComponentInternalInstance =
compatMountInstance || (initialVNode.component = createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
// ...
if (!(__COMPAT__ && compatMountInstance)) {
// ...
// 初始化组件
setupComponent(instance)
// ...
}
// ...
// 渲染组件
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
// ...
}
可以看出 mountComponent 主要做了三件事:
-
创建组件实例 - createComponentInstance;
-
初始化组件 - setupComponent;
-
渲染组件 - setupRenderEffect。