Vue3.0 渲染流程(三)

上两篇文章分析了初次渲染大体流程,以及创建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) 过程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值