自己做网站原始代码/2345网址导航桌面版
上两篇文章分析了初次渲染大体流程,以及创建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为undefinedpatch(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 } = n2switch (type) {case Text:processText(n1, n2, container, anchor)breakcase 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.COMPONENTprocessComponent(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 alivemountComponent(n2,container,anchor,parentComponent,parentSuspense,isSVG,optimized)}} else {//进行diff 更新updateComponent(n1, n2, optimized)}}
const mountComponent: MountComponentFn = (initialVNode,container,anchor,parentComponent,parentSuspense,isSVG,optimized) => {//1. 第一件事创建instanceconst 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) || emptyAppContextconst instance: ComponentInternalInstance = {uid: uid++,vnode, //vnode属性type, //type属性parent,appContext, //appContext属性root: null!, // to be immediately setnext: null,subTree: null!, // will be set synchronously right after creationupdate: null!, // will be set synchronously right after creationrender: 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 assetscomponents: null,directives: null,// resolved props and emits optionspropsOptions: normalizePropsOptions(type, appContext),emitsOptions: normalizeEmitsOptions(type, appContext),// emitemit: null as any, // to be set immediatelyemitted: null,// statectx: EMPTY_OBJ,data: EMPTY_OBJ,props: EMPTY_OBJ,attrs: EMPTY_OBJ, //会赋值传过来的style对象slots: EMPTY_OBJ,refs: EMPTY_OBJ,setupState: EMPTY_OBJ,setupContext: null,// suspense relatedsuspense,suspenseId: suspense ? suspense.pendingId : 0,asyncDep: null,asyncResolved: false,// lifecycle hooks// not using enums here because it results in computed propertiesisMounted: 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 属性对象又会指向instanceinstance.ctx = { _: instance }}instance.root = parent ? parent.root : instanceinstance.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) 过程