上两篇文章分析了初次渲染大体流程,以及创建vnode过程,都比较简单,这篇文章分析具体渲染vnode的过程。
这个render函数就是调用createAppAPI 传递过来的函数参数。在baseCreateRenderer 函数中能看到这一过程:
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {
//....
//render函数在这里
const render: RootRenderFunction = (vnode, container) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
console.log(`liubbc patch 02`)
//初次渲染的时候container._vnode为undefined
patch(container._vnode || null, vnode, container)
}
flushPostFlushCbs()
//初次渲染后,给container对象增加一个_vnode属性,表示已为这个节点创建了vnode,下次就会进行
//diff更新了
container._vnode = vnode
}
//....
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}
开始patch旅程:
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
optimized = false
) => {
//...
//从vnode中解构出type, ref, shapeFlag 进行不同的处理
const { type, ref, shapeFlag } = n2
switch (type) {
case Text:
processText(n1, n2, container, anchor)
break
case Comment:
processCommentNode(n1, n2, container, anchor)
break
//...
default:
if (shapeFlag & ShapeFlags.ELEMENT) {
processElement(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
} else if (shapeFlag & ShapeFlags.COMPONENT) {
console.log(`liubbc processComponent 01`)
//vnode的shapeFlag 是ShapeFlags.COMPONENT
processComponent(
n1,
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
const processComponent = (
n1: VNode | null,
n2: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
optimized: boolean
) => {
if (n1 == null) {
//初次渲染
if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
;(parentComponent!.ctx as KeepAliveContext).activate(
n2,
container,
anchor,
isSVG,
optimized
)
} else {
console.log(`liubbc mountComponent 01`)
// 不是keep alive
mountComponent(
n2,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
)
}
} else {
//进行diff 更新
updateComponent(n1, n2, optimized)
}
}
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
//1. 第一件事创建instance
const instance: ComponentInternalInstance = (initialVNode.component =
createComponentInstance(
initialVNode,
parentComponent,
parentSuspense
))
//....
//2. 第二件事安装组件
setupComponent(instance)
//...
//3. 第三件事安装副作用函数
setupRenderEffect(
instance,
initialVNode,
container,
anchor,
parentSuspense,
isSVG,
optimized
)
走到这里patch过程就走完了,就能渲染出vnode了。mountComponent函数主要做了3件事,我们一件一件看。先看createComponentInstance过程:
export function createComponentInstance(
vnode: VNode,
parent: ComponentInternalInstance | null,
suspense: SuspenseBoundary | null
) {
const type = vnode.type as ConcreteComponent
// inherit parent app context - or - if root, adopt from root vnode
//如果parent为假appContext则是vnode的属性appContext所指的app对象,这个对象下有mount等方
// 法哦
const appContext =
(parent ? parent.appContext : vnode.appContext) || emptyAppContext
const instance: ComponentInternalInstance = {
uid: uid++,
vnode, //vnode属性
type, //type属性
parent,
appContext, //appContext属性
root: null!, // to be immediately set
next: null,
subTree: null!, // will be set synchronously right after creation
update: null!, // will be set synchronously right after creation
render: null, //render函数,之后会赋值template生成的render函数
proxy: null,
exposed: null,
withProxy: null,
effects: null,
provides: parent ? parent.provides : Object.create(appContext.provides),
accessCache: null!,
renderCache: [],
// local resovled assets
components: null,
directives: null,
// resolved props and emits options
propsOptions: normalizePropsOptions(type, appContext),
emitsOptions: normalizeEmitsOptions(type, appContext),
// emit
emit: null as any, // to be set immediately
emitted: null,
// state
ctx: EMPTY_OBJ,
data: EMPTY_OBJ,
props: EMPTY_OBJ,
attrs: EMPTY_OBJ, //会赋值传过来的style对象
slots: EMPTY_OBJ,
refs: EMPTY_OBJ,
setupState: EMPTY_OBJ,
setupContext: null,
// suspense related
suspense,
suspenseId: suspense ? suspense.pendingId : 0,
asyncDep: null,
asyncResolved: false,
// lifecycle hooks
// not using enums here because it results in computed properties
isMounted: false,
isUnmounted: false,
isDeactivated: false,
bc: null,
c: null,
bm: null,
m: null,
bu: null,
u: null,
um: null,
bum: null,
da: null,
a: null,
rtg: null,
rtc: null,
ec: null
}
if (__DEV__) {
instance.ctx = createRenderContext(instance)
} else {
//instance对象的ctx 属性对象又会指向instance
instance.ctx = { _: instance }
}
instance.root = parent ? parent.root : instance
instance.emit = emit.bind(null, instance)
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsComponentAdded(instance)
}
//最后返回创建的instance对象
return instance
}
createComponentInstance 方法主要是创建了instance对象,这个对象下挂载了很多属性,例如vnode下的type,vnode,appContext, render,props,attrs等,这些属性在以后渲染过程中都会用到,当然其他属性肯定也会用到,不然也不会定义了。
最后把创建的instance对象赋值给initialVNode.component,所以下一件事就是setupComponent(instance)。
下一篇文章再分析第二件事:setupComponent(instance) 过程