I’m here to open a new pit today. I’m in a slump recently. I want to force myself to learn in this way.

This series of articles will be updated from time to time but one will be updated no later than two weeks!

  1. Disclaimer: The analysis version is the current vuE-Next repository master branch code, the code in the article is only the core process code pasted out. The path under the heading is the location of the corresponding method in the source code.

  2. Goal of the day:

  • Understand vuE3 source code structure
  • Understand the vuE3 initialization rendering process

Our code for today

<div id='app'>
  <div>
    <span>{{ count }}</span>
    <button @click='add'>click me add</button>
  </div>
</div>
Copy the code
const {ref , h } = Vue
Vue.createApp({
  setup(p , c){
    const count = ref(0)
    const add = () = > {
      count.value ++
    }
    return {
      count,
      add
    }
  },
}).mount('#app')
Copy the code

To analyze

createApp

Runtime -dom -> index

As we can see from the above code,vue3 initialization needs to go through the createApp method and then mount to the selected container. So let’s look at the implementation of the createApp method

// The entry method
export const createApp = ((. args) = > {
  // Get the app instance
  constapp = ensureRenderer().createApp(... args)const { mount } = app
  app.mount = (containerOrSelector: Element | ShadowRoot | string): any= > {
    // Verify that the container is valid
    const container = normalizeContainer(containerOrSelector)
    if(! container)return
    const component = app._component
    if(! isFunction(component) && ! component.render && ! component.template) {// Append attributes
      component.template = container.innerHTML
    }
    // Empty the contents of the container
    container.innerHTML = ' '
    // Start mounting
    const proxy = mount(container, false, container instanceof SVGElement)

    return proxy
  }

  return app
}) as CreateAppFunction<Element>
Copy the code

We can see that the core purpose of the createApp method is to create an app, after which we add a mount method for the subsequent chain call.

Let’s take a look at the process of creating an app

ensureRenderer

To create a renderer: Runtime-dom -> index

function ensureRenderer() {
  return renderer || (renderer = createRenderer<Node, Element>(rendererOptions))
}
Copy the code

This method attempts to return a renderer. If the renderer doesn’t exist, it creates one. Take a look at rendererOptions passed in at creation time

Here he combines patchProp, forcePatchProp, and nodeOps

We can see that nodeOps is a real DOM operation.

Let’s look at the createRenderer method

createRenderer

Create renderer: Run-time core -> renderer

As you can see, after a few reloads here, you end up with base Reaterenderer

This method is too long to cover all screenshots. In general, the ultimate purpose of this method is to get the results of Render and createAppApi. So let’s take a look at what createApp does.

createAppApi

Generate app instance registration global API: Run-time core -> apiCreateApp

  1. This method creates an empty context for the app
export function createAppContext() :AppContext {
  return {
    app: null as any,
    config: {
      isNativeTag: NO,
      performance: false.globalProperties: {},
      optionMergeStrategies: {},
      errorHandler: undefined.warnHandler: undefined.compilerOptions: {}},mixins: [].components: {},
    directives: {},
    provides: Object.create(null),
    optionsCache: new WeakMap(),
    propsCache: new WeakMap(),
    emitsCache: new WeakMap()}}Copy the code

This structure

  1. A collection of administrative plug-ins is also created

  2. In the app, some global related things are registered (global directives, global components, plug-ins can be seen in this method).

const app: App = (context.app = {
  _uid: uid++,
  _component: rootComponent as ConcreteComponent,
  _props: rootProps,
  _container: null._context: context,
  _instance: null,
  version,
  get config() {
    return context.config
  },
  set config(v) {
    if (__DEV__) {
      warn(
        `app.config cannot be replaced. Modify individual options instead.`)}},use(plugin: Plugin, ... options: any[]) {
    // more ...
    return app
  },
  mixin(mixin: ComponentOptions) {
      // more ...
    returnapp }, component(name: string, component? : Component): any {// more ...
    return app
  },
  directive(name: string, directive? : Directive) {
    // more ...
    returnapp }, mount( rootContainer: HostElement, isHydrate? : boolean, isSVG? : boolean ): any {// more ...
  },
  unmount() {
    // more ...
  },
  provide(key, value) {
    // more ...
    return app
  }
})
Copy the code

This declares a mount method (remember!!). .

And then he sends the app back. So at this point, our app instance is created. As you can see, Vue3 is designed in a way that takes platform-specific operations out of the way, making it extremely friendly to developers of multi-platform frameworks by simply focusing on platform node operations and creating renderers.

Let’s go back to the entry method

Expose the mount method

After obtaining the app instance, Vue caches the mount method in the instance and overrides the mount method on the instance. When we call mount (overwritten mount),

  1. Vue first verifies that the mount target you pass in is a legitimate target.

  2. He will try to get the _component on the instance (the current component is the registered option when createApp is used)

  3. If you find our registration option is not the function | | not render configuration | | no template configuration, he will increase to the component template attribute, this property subsequent use!!!!!!)

  4. Then empty the innerHTML in the container

  5. Execute the previously cached mount method (in the instance)

mount

Mount method: Run-time core -> apiCreateApp -> mount

  1. If the current instance is not already mounted, it creates a vNode. Since it is a reference, the template property we add will also be in the rootComponent

Take a look at the createVNode method

/** * runtime-core -> vnode */

function _createVNode(
  type: VNodeTypes | ClassComponent | typeof NULL_DYNAMIC_COMPONENT, // rootComponent
  props: (Data & VNodeProps) | null = null.// null
  children: unknown = null.// null
  patchFlag: number = 0./ / 0
  dynamicProps: string[] | null = null.// null
  isBlockNode = false // false
) :VNode {
  // If the argument passed is already a vNode
  // There is no need to convert a clone
  // Check whether the child element is a valid child element
  if (isVNode(type)) {
    const cloned = cloneVNode(type, props, true /* mergeRef: true */)
    if (children) {
      normalizeChildren(cloned, children)
    }
    return cloned
  }

  // If it is a class component
  if (isClassComponent(type)) {
    // more
  }

  // Handle class and style in props
  if (props) {
    // more ...
  }

  / / to play tag
  const shapeFlag = isString(type)
    ? ShapeFlags.ELEMENT
    : __FEATURE_SUSPENSE__ && isSuspense(type)
      ? ShapeFlags.SUSPENSE
      : isTeleport(type)
        ? ShapeFlags.TELEPORT
        : isObject(type)
          ? ShapeFlags.STATEFUL_COMPONENT
          : isFunction(type)
            ? ShapeFlags.FUNCTIONAL_COMPONENT
            : 0

  / / vnode
  const vnode: VNode = {
    __v_isVNode: true.__v_skip: true,
    type,
    props,
    key: props && normalizeKey(props),
    ref: props && normalizeRef(props),
    scopeId: currentScopeId,
    slotScopeIds: null.children: null.component: null.suspense: null.ssContent: null.ssFallback: null.dirs: null.transition: null.el: null.anchor: null.target: null.targetAnchor: null,
    shapeFlag,
    patchFlag,
    dynamicProps,
    dynamicChildren: null.appContext: null
  }

  // Check whether the child node is valid
  normalizeChildren(vnode, children)

  // Check suspense's children
  if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
    // more ...
  }

  return vnode
}
Copy the code

Vue3 uses bitwise operations to enumerate element types. Instead of using the number nodeType, the binary number 0000, 0010, 0100, 1000 is used to confirm the nodeType. You only need to compare one bit of the current tag to determine the node type.

  1. After obtaining the vNode, add the current instance context to the VNode

  2. Then determine if fusion is required (no). Call the render method (which is passed in as the parameter when creating the app instance and declared in the renderer)

  3. Set mount state to true after render ends

You can see that the mount method only wants to do two things

  1. Create a vnode

  2. render vnode

render

Renderer -> renderer -> render

  const render: RootRenderFunction = (vnode, container, isSVG) = > {
    if (vnode == null) {
      if (container._vnode) {
        unmount(container._vnode, null.null.true)}}else {
      patch(container._vnode || null, vnode, container, null.null.null, isSVG)
    }
    flushPostFlushCbs()
    container._vnode = vnode
  }
Copy the code

The Render method wants to do two things

  1. patch vnode

  2. flushPostFlushCbs

Today we are going to study the process of patch

 // Remember the input parameter when calling patch
 patch(container._vnode || null, vnode, container, null.null.null, isSVG)
Copy the code

patch

Patch installation method: Run-time core -> Renderer -> Patch

const patch: PatchFn = (
    n1, / / old VNODE
    n2, / / new VNODE
    container, // Mount the node
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    slotScopeIds = null,
    optimized = false
  ) = > {
    // If the old node exists and the types of n1 and n2 are different
    // Uninstall the old VNode
    if(n1 && ! isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense,true)
      n1 = null
    }

    // Get type, ref, shapeFlag from the new vnode
    // Use type to match
    // If type does not match, use shapeFlag to match
    const { type, ref, shapeFlag } = n2
    // We currently pass in the configuration + mount container innerHTML for the user as type in vNode
    // So the shapeFlag condition is matched directly
    switch (type) {
      case Text:
        processText(n1, n2, container, anchor)
        break
      case Comment:
        processCommentNode(n1, n2, container, anchor)
        break
      case Static:
        if (n1 == null) {
          mountStaticNode(n2, container, anchor, isSVG)
        } else if (__DEV__) {
          patchStaticNode(n1, n2, container, isSVG)
        }
        break
      case Fragment:
        processFragment(
          n1,
          n2,
          container,
          anchor,
          parentComponent,
          parentSuspense,
          isSVG,
          slotScopeIds,
          optimized
        )
        break
      default:
        if (shapeFlag & ShapeFlags.ELEMENT) {
          // match element
          processElement(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
        } else if (shapeFlag & ShapeFlags.COMPONENT) {
          // match component
          // The shapeFlag will enter this condition
          processComponent(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
        } else if (shapeFlag & ShapeFlags.TELEPORT) {
          // match teleport
          // more
        } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
          // match suspense
          // more}}/ / deal with ref
    if(ref ! =null&& parentComponent) { setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, ! n2) } }Copy the code

It can be seen that the core goal of patch method is to compare old and new VNode nodes and perform different operations on them. Take a look at the implementation of the processComponent logical branch when you initialize the render

processComponent

Component: Run-time core -> renderer -> 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
  If the old vNode is empty
  if (n1 == null) {
    // If the new vnode is KEPT_ALIVE
    if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) {
      // more
    } else {
      // Mount the node
      mountComponent(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        optimized
      )
    }
  }
  // Compare updates
  else {
    updateComponent(n1, n2, optimized)
  }
}
Copy the code

mountComponent

Initialize render with run-time core -> renderer -> mountComponent

 const mountComponent: MountComponentFn = (
    initialVNode, // Initialize the vNode
    container, // Mount the target
    anchor,
    parentComponent,
    parentSuspense,
    isSVG,
    optimized
  ) = > {
    // Create a component instance
    CreateComponentInstance Initializes a component instance model
    const compatMountInstance =
      __COMPAT__ && initialVNode.isCompatRoot && initialVNode.component
    const instance: ComponentInternalInstance =
      compatMountInstance ||
      (initialVNode.component = createComponentInstance(
        initialVNode,
        parentComponent,
        parentSuspense
      ))

    / /!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
    // Handle prop Slot and setup
    / / first analysis
    if(! (__COMPAT__ && compatMountInstance)) { setupComponent(instance) }/ / after analysis
    setupRenderEffect(
      instance,
      initialVNode,
      container,
      anchor,
      parentSuspense,
      isSVG,
      optimized
    )
  }
Copy the code

setupComponent

Handle registered setup: Run-time core -> Component -> setupComponent

Ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful = ejbateful

Let’s focus on what happens later, when he tries to get the setup execution result and start parsing it by calling the setupStatefulComponent method

setupStatefulComponent

Start processing the registered setup: Run-time core -> Component -> setupStatefulComponent

function setupStatefulComponent(instance: ComponentInternalInstance, isSSR: boolean) {
  // At this point instance.type === the innerHTML below the user's registered setup + container
  const Component = instance.type as ComponentOptions

  // 0. Add a cache object to the instance
  instance.accessCache = Object.create(null)
  // 1. Render the global proxy
  instance.proxy = markRaw(new Proxy(instance.ctx, PublicInstanceProxyHandlers))
  // 2. Call setup to get the return stuff
  const { setup } = Component
  / / setup
  if (setup) {
    // Parse the parameters in setup
    // function. Length is the number of parameters in function
    // If the number of parameters in setup > 1
    // Create setup context
    const setupContext = (instance.setupContext =
      setup.length > 1 ? createSetupContext(instance) : null)

    currentInstance = instance

    // Setup is executed here to get the value returned after setup is executed
    // It is worth mentioning that
    When setup is executed, our registered ref Reactive is executed at the same time
    // Const count = ref(0)
    // Count is already wrapped by ref
    const setupResult = callWithErrorHandling(
      setup,
      instance,
      ErrorCodes.SETUP_FUNCTION,
      [__DEV__ ? shallowReadonly(instance.props) : instance.props, setupContext]
    )

    currentInstance = null

    // If return promise
    if (isPromise(setupResult)) {
      // Use this function to save the promise result externally
      const unsetInstance = () = > {
        currentInstance = null
      }
      setupResult.then(unsetInstance, unsetInstance)
    } else {
      // enter the result method
      handleSetupResult(instance, setupResult, isSSR)
    }
  } else {
    finishComponentSetup(instance, isSSR)
  }
}
Copy the code

handleSetupResult

Handle setup execution results: Run-time core -> Component -> handleSetupResult

export function handleSetupResult(instance: ComponentInternalInstance, setupResult: unknown, isSSR: boolean) {
  // Setup returns a user-defined render function
  if (isFunction(setupResult)) {
    // If the current runtime is Node
    if (__NODE_JS__ && (instance.type as ComponentOptions).__ssrInlineRender) {
      instance.ssrRender = setupResult
    } else {
      // Mount renderFunction on the instance
      instance.render = setupResult as InternalRenderFunction
    }
  }
  // composition api
  else if (isObject(setupResult)) {
    // Add the result to the instance's setupState
    instance.setupState = proxyRefs(setupResult)
  }
  // If no value is returned
  else if(__DEV__ && setupResult ! = =undefined) {
   / / warning...
  }
  // Finish processing setup
  finishComponentSetup(instance, isSSR)
}
Copy the code

finishComponentSetup

X optionAPI: Runtime – Core -> Component -> finishComponentSetup

export function finishComponentSetup(instance: ComponentInternalInstance, isSSR: boolean, skipOptions? : boolean) {
  // Component = User registration configuration + mount innerHTML under the target
  const Component = instance.type as ComponentOptions

  / / determine the runtime
  if (__NODE_JS__ && isSSR) {
    // more
  }
  // If the instance has no render function
  / / that
  // setup does not return function
  else if(! instance.render) {// Check if the user has not registered the render function elsewhere
    if(compile && ! Component.render) {/ / get the Component template
      / / that
      // innerHTML in the container
      const template =
        (__COMPAT__ &&
          instance.vnode.props &&
          instance.vnode.props['inline-template']) ||
        Component.template

      if (template) {
        / /... more
        const finalCompilerOptions: CompilerOptions = extend(
         / /... more
        )
        // Execute the compile method (compile method will be analyzed later when analyzing compiler)
        // Get the Render function mounted on the Component
        // The goal is to compile by executing the compile method
        // Get render function
        Component.render = compile(template, finalCompilerOptions)
      }
    }
    // Get render function
    // Attach render to the entire instance
    instance.render = (Component.render || NOOP) as InternalRenderFunction
  }

  // start compatible with 2.x API
  // Setup is not compatible with the 2.x API until the setup parsing is complete
  // support for 2.x options
  if(__FEATURE_OPTIONS_API__ && ! (__COMPAT__ && skipOptions)) { currentInstance = instance applyOptions(instance) currentInstance =null}}Copy the code

One important piece of information we can get from this method is that setup processing timing is very, very early in the lifecycle. The reason why Vue3 is compatible with vue2’s optionAPI is that it first handles setup and then initializes other optionapis. Because the call is early, it can be backward compatible.

Compatible procedures and compiled procedures are later collated together. Understand the whole process first, and then buckle the details.

When we are done executing this method, we return to the mountComponent method. He performs the setupRenderEffect

setupRenderEffect

Add run-time core -> renderer -> setupRenderEffect to the instance

  const setupRenderEffect: SetupRenderEffectFn = (instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized) = > {
    // Add the update method
    // The value is the execution result of effect
    // Effect is used to analyze reactive packages
    // It receives a notification to execute the componentEffect method passed in when it initializes the render
    instance.update = effect(function componentEffect() {
      // Initialize render
      if(! instance.isMounted) {/ / SSR
        if (el && hydrateNode) {
          // more ...
        } else {
          / /!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
          / / build subTree
          const subTree = (instance.subTree = renderComponentRoot(instance))
          / /!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
          // Patch subTree
          patch(
            null,
            subTree,
            container,
            anchor,
            instance,
            parentSuspense,
            isSVG
          )
        }
        // Change the mount state
        instance.isMounted = true
      }
      // Update subsequent analysis
      else {
        // more ...
      }
    }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)
  }
Copy the code

We can see that this method wants to build the subTree and patch the subTree later. Let’s look at the process of building a subTree.

renderComponentRoot

Construct subTree: Run-time core -> RenderUtils -> renderComponentRoot

export function renderComponentRoot(
  instance: ComponentInternalInstance
) :VNode {
  const {
    type: Component,
    vnode,
    proxy,
    withProxy,
    props,
    propsOptions: [propsOptions],
    slots,
    attrs,
    emit,
    render, // Compile the render function
    renderCache,
    data,
    setupState,
    ctx,
    inheritAttrs
  } = instance

  let result

  try {
    let fallthroughAttrs
    if (vnode.shapeFlag & ShapeFlags.STATEFUL_COMPONENT) {
      // Call render first
      // After the call we get the vNode tree of the node under the container
      // Verify that the vNode tree is a valid Vnode
      // Assign to result if a valid vnode is obtainedresult = normalizeVNode( render! .call( proxyToUse, proxyToUse! , renderCache, props, setupState, data, ctx ) ) fallthroughAttrs = attrs }else {
      // If the component is stateless
      // more ...
    }

    let root = result
    let setRoot: ((root: VNode) = > void) | undefined = undefined

    if (vnode.dirs) {
      // merge instructions
      root.dirs = root.dirs ? root.dirs.concat(vnode.dirs) : vnode.dirs
    }
    if (vnode.transition) {
      / / to inherit the transition
      root.transition = vnode.transition
    }

    if (__DEV__ && setRoot) {
      setRoot(root)
    } else {
      result = root
    }
  } catch (err) {
    blockStack.length = 0
    handleError(err, instance, ErrorCodes.RENDER_FUNCTION)
    result = createVNode(Comment)
  }

  return result
}
Copy the code

You can see that this method turns all the nodes under our container into VNodes. After vNode is validated, some attributes are merged and inherited. Finally return the VNode.

Going back to the setupRenderEffect method, subTree means mount the container’s vnodeTree, and we’re going to patch that vnodeTree

We are back to the patch method.

const patch: PatchFn = (
    n1, // null
    n2, // subTree
    container, // Mount the node
    anchor = null,
    parentComponent = null,
    parentSuspense = null,
    isSVG = false,
    slotScopeIds = null,
    optimized = false
  ) = > {
    // If the old node exists and the types of n1 and n2 are different
    // Uninstall the old VNode
    if(n1 && ! isSameVNodeType(n1, n2)) { anchor = getNextHostNode(n1) unmount(n1, parentComponent, parentSuspense,true)
      n1 = null
    }
    / / this time
    // Type is the child element type. In this case, type is div
    // shapeFlag is still a component type flag
    const { type, ref, shapeFlag } = n2
    switch (type) {
      case Text:
        // more ...
        break
      case Comment:
        // more ...
        break
      case Static:
        // more ...
        break
      case Fragment:
        // more ...
        break
      default:
        if (shapeFlag & ShapeFlags.ELEMENT) {
          // At this point we enter the condition
          processElement(
            n1,
            n2,
            container,
            anchor,
            parentComponent,
            parentSuspense,
            isSVG,
            slotScopeIds,
            optimized
          )
        } else if (shapeFlag & ShapeFlags.COMPONENT) {
          // more
        } else if (shapeFlag & ShapeFlags.TELEPORT) {
          // match teleport
          // more
        } else if (__FEATURE_SUSPENSE__ && shapeFlag & ShapeFlags.SUSPENSE) {
          // match suspense
          // more}}/ / deal with ref
    if(ref ! =null&& parentComponent) { setRef(ref, n1 && n1.ref, parentSuspense, n2 || n1, ! n2) } }Copy the code

processElement

Element: Runtime -core -> renderer -> processElement

 const processElement = (
    n1: VNode | null,
    n2: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) = > {
    isSVG = isSVG || (n2.type as string) === 'svg'
    // There is no direct mount on the old node
    if (n1 == null) {
      mountElement(
        n2,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
    // There is a comparison update
    else {
      patchElement(
        n1,
        n2,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
Copy the code

mountElement

Handle child elements inserted into the target node: Runtime-core -> renderer -> mountElement

  const mountElement = (
    vnode: VNode,
    container: RendererElement,
    anchor: RendererNode | null,
    parentComponent: ComponentInternalInstance | null,
    parentSuspense: SuspenseBoundary | null,
    isSVG: boolean,
    slotScopeIds: string[] | null,
    optimized: boolean
  ) = > {
    let el: RendererElement
    let vnodeHook: VNodeHook | undefined | null
    const { type, props, shapeFlag, transition, patchFlag, dirs } = vnode
    // Go this way!
    // Call hostCreateElement to create the real DOM
    el = vnode.el = hostCreateElement(
      vnode.type as string,
      isSVG,
      props && props.is,
      props
    )
    // If the child node is text
    if (shapeFlag & ShapeFlags.TEXT_CHILDREN) {
      // operate dom assignment text
      hostSetElementText(el, vnode.children as string)
    }
    // The child node is an array
    else if (shapeFlag & ShapeFlags.ARRAY_CHILDREN) {
      // Mount the child node
      mountChildren(
        vnode.children as VNodeArrayChildren,
        el, / /!!!!!!!!!!
        null, parentComponent, parentSuspense, isSVG && type ! = ='foreignObject', slotScopeIds, optimized || !! vnode.dynamicChildren ) }// If the current element has props
    if (props) {
      / / traverse the props
      for (const key in props) {
        if(! isReservedProp(key)) {// Patch elements with attributes
          hostPatchProp(
            el,
            key,
            null,
            props[key],
            isSVG,
            vnode.children as VNode[],
            parentComponent,
            parentSuspense,
            unmountChildren
          )
        }
      }
    }
    // Insert the current operation node under the corresponding container
    hostInsert(el, container, anchor)
  }
Copy the code

mountChildren

Mount child nodes: Runtime-core -> Renderer -> mountChildren

 const mountChildren: MountChildrenFn = (
    children,
    container,
    anchor,
    parentComponent,
    parentSuspense,
    isSVG,
    slotScopeIds,
    optimized,
    start = 0
  ) = > {
    for (let i = start; i < children.length; i++) {
      const child = (children[i] = optimized
        ? cloneIfMounted(children[i] as VNode)
        : normalizeVNode(children[i]))
      // Recursive patch subelement
      patch(
        null,
        child,
        container,
        anchor,
        parentComponent,
        parentSuspense,
        isSVG,
        slotScopeIds,
        optimized
      )
    }
  }
Copy the code

Here we can see a detail. Every time he mounts, the container is a real DOM generated from the parent node’s type. After mountChildren is finished, it will insert the corresponding node under the parent element. Since it is recursive, the nodes analyzed first are mounted later. When the outermost node is mounted. All nodes are mounted.

At this point, the process of initializing the render is more or less complete.

To summarize the initial render process

Create renderer –> Create app instance –> call mount method –> Create vNode (based on the option passed in at initialization) –> call Render –> Patch –> processComponent –> MountComponent –> call setup to get its result –> call compile to get innerHTML’s render function from the container –> vue2. X compatible API –> add update to the instance Method: build subTree > processElement > mountElement > recursive patch sub-element > add props > insert to the parent node

In general, the process is quite complicated. If there is any dispute in the process, please point it out in the comments section. I will update the article in time. At the same time the code word is not easy, hope more praise attention support ~

Additional analysis is required

  1. compile
  2. Backward compatibility details
  3. Reactive related
  4. Update the related