Entrance use:

So let’s start with createApp

CreateApp is exported from the Runtime-DOM module and does the following:

  • callruntime-coreThe modulecreateRendererMethod to generate the renderer and call itcreateAppMethod to generate an APP instance.
  • Take out the originalmountMethod, and then override the above APP instancemountMethod to find the actual DOM Container, empty the container, and call the originalmountMethod to complete the DOM mount
  • Back to APP instance
createApp
/** * createApp function */ export const createApp = ((... args) => { const app = ensureRenderer().createApp(... If (__DEV__) {injectNativeTagCheck(app)} const {mount} = app /** * overrides the mount function  * @param containerOrSelector */ app.mount = (containerOrSelector: Element | ShadowRoot | string): Any => {// Container is the real DOM element const container = normalizeContainer(containerOrSelector) if (! Container) return const Component = app._component // Options of the component // The template of the default component is the content of the mount element. isFunction(component) && ! component.render && ! Component.template) {component.template = container. InnerHTML} // Empty the contents of the container container. InnerHTML = "" const proxy = Mount (container) if (container instanceof Element) {/ / delete elements on v - cloak instructions container. RemoveAttribute (' v - cloak) container.setAttribute('data-v-app', '') } return proxy } return app }) as CreateAppFunction<Element>Copy the code

Got a hit on createRenderer

I said that I called the createRenderer method of the Run-time core module, Generate the renderer and call its createApp method and then we can trace back and see that createRenderer returns baseCreateRenderer, that’s the key code! Let’s see what the baseCreateRenderer method does

Here is a very important part of dome-core, what does baseCreateRenderer have:

  1. acceptoptionsParameters,optionsYou can customize the API for adding, deleting, changing, and checking,runtime-domWhen called, it passes in the DOM add, delete, change, and query API.
  2. There is apatchMethod, which is the chief dispatcher, to determine the type and call different add, delete, change, check apis,
  3. Defines methods for manipulating various types of elements, basically wrapping the options passed in.
  4. definerenderMethod that the method callspatchMethod to mount elements
  5. Returns an object containingcreateAppMethods.

Source:

function baseCreateRenderer( options: RendererOptions, createHydrationFns? Const {insert: hostInsert, remove: typeof createHydrationFunctions): any {// createHydrationFunctions const {insert: hostInsert, remove: hostRemove, patchProp: hostPatchProp, forcePatchProp: hostForcePatchProp, createElement: hostCreateElement, createText: hostCreateText, createComment: hostCreateComment, setText: hostSetText, setElementText: hostSetElementText, parentNode: hostParentNode, nextSibling: hostNextSibling, setScopeId: hostSetScopeId = NOOP, cloneNode: hostCloneNode, insertStaticContent: hostInsertStaticContent } = options const patch = () => {... } const processText = () => {... } const processCommentNode = () => {... } const mountStaticNode = () => {... } const patchStaticNode = () => {... } const moveStaticNode = () => {... } const removeStaticNode = () => {... } const processElement = () => {... } const mountElement = () => {... } const setScopeId = () => {... } const mountChildren = () => {... } const patchElement = () => {... } const patchBlockChildren = () => {... } const patchProps = () => {... } const processFragment = () => {... } const processComponent = () => {... } const mountComponent = () => {... } const updateComponent = () => {... } const setupRenderEffect = () => {... } const updateComponentPreRender = () => {... } const patchChildren = () => {... } const patchUnkeyedChildren = () => {... } const patchKeyedChildren = () => {... } const move = () => {... } const unmount = () => {... } const remove = () => {... } const removeFragment = () => {... } const unmountComponent = () => {... } const unmountChildren = () => {... } const getNextHostNode = () => {... } const render = () => {... } return {render, hydrate, // createApp createApp: createAppAPI(render, hydrate)}}Copy the code

EnsureRenderer ().createApp(… The args method gets an instance of app, which is createApp in the object returned by baseCreateRenderer, a function generated by createAppAPI.

Continue tracking createAppAPI

CreateAppAPI is exported by the Run-time core module, so let’s see what createAppAPI does

  • First, createAppAPI returns a function.

  • Receives a Render method defined in baseCreateRenderer.

  • Function definitions APP, and hang some methods, such as: the mount and unmount to, mixins, component, etc., and returns the APP. Source:

    / * *

    • Back to app instance
    • @param render
    • @param hydrate

    / export function createAppAPI( render: RootRenderFunction, hydrate? : RootHydrateFunction ): CreateAppFunction { /*

    • Receive two parameters
    • RootComponent root component
    • RootProps Props passed to the root component

    */ return function createApp(rootComponent, RootProps = null) {const context = createAppContext() // Return an object // Installed plug-in const installedPlugins = new Set() // Whether to mount let isMounted = false

    Const app = (context.app = {_uid: uid++, // unique id _component: rootComponent as ConcreteComponent, _props: rootProps, _container: null, _context: context,Copy the code

    Version, // vue version get config() {// config is a read-only object, setting config in the development environment will warn return context.config}, use() {… }, mixin() {… }, component() {… }, directive() {… }, mount() {… }, unmount() {… }, provide() {… }})

    return app
    Copy the code

    }}

Mount method of app object created by createAppAPI.

Render (createAppAPI); render (createAppAPI); render (createAppAPI); render (createAppAPI); BaseCreateRenderer method defined in the render method mount source code:

mount( rootContainer: HostElement, isHydrate? : boolean, isSVG? : boolean ): any { if (! isMounted) { const vnode = createVNode( rootComponent as ConcreteComponent, rootProps ) // store app context on the root VNode. // this will be set on the root instance on initial mount. vnode.appContext = context if (isHydrate && hydrate) { hydrate(vnode as VNode<Node, Element>, rootContainer as any) } else { render(vnode, rootContainer, isSVG) } isMounted = true app._container = rootContainer return vnode.component! .proxy } else if (__DEV__) { warn( `App has already been mounted.\n` + `If you want to remount the same app, move your app creation logic ` + `into a factory function and create fresh app instances for each ` + `mount - e.g. \`const createMyApp = () => createApp(App)\`` ) } },Copy the code

The Render method converts a VNode into a real DOM through patch

During patch, different methods are called to process the node depending on the type of the vNode. Here we will focus on processComponent, which performs setup and dependency collection.

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 (n1 == null) { if (n2.shapeFlag & ShapeFlags.COMPONENT_KEPT_ALIVE) { ; (parentComponent! .ctx as KeepAliveContext).activate( n2, container, anchor, isSVG, optimized ) } else { mountComponent( n2, container, anchor, parentComponent, parentSuspense, isSVG, optimized ) } } else { updateComponent(n1, n2, optimized) } }Copy the code

Function: Call mountComponent or updateComponent

mountComponent

const mountComponent: MountComponentFn = ( initialVNode, container, anchor, parentComponent, parentSuspense, isSVG, Optimized) => {// createComponentInstance will be treated as const instance, CTX inconsistencies will be treated as const instance in this method: ComponentInternalInstance = (initialVNode.component = createComponentInstance( initialVNode, parentComponent, parentSuspense )) // ... SetupComponent (instance) setupComponent(instance) setupState setupComponent(instance) setupComponent(instance) If (__FEATURE_SUSPENSE__ && instance.asyncdep) {// Setup is the case for the PROMISE, after the promise state resolve, To perform setupRenderEffect function parentSuspense && parentSuspense. RegisterDep (instance, setupRenderEffect) / /... return } setupRenderEffect( instance, initialVNode, container, anchor, parentSuspense, isSVG, optimized ) }Copy the code

What it does: Generate instance by createComponentInstance. The function finally executes the setupRenderEffect method, which collects dependencies used in vNode

createComponentInstance

The Run-time core module defines the various properties of the component instance and returns them

export function createComponentInstance( vnode: VNode, parent: ComponentInternalInstance | null, suspense: SuspenseBoundary | null ) { // ... const instance: ComponentInternalInstance = {/ / here defines the various attributes, such as: Effects, data, props, etc.} // CTX cannot be used in project development. The production environment does not support if (__DEV__) {instance. CTX = createRenderContext(instance)} else {instance. CTX = {_: instance } } instance.root = parent ? parent.root : instance instance.emit = emit.bind(null, instance) return instance }Copy the code

setupRenderEffect

create reactive effect for rendering

setupRenderEffect: SetupRenderEffectFn = ( instance, initialVNode, container, anchor, parentSuspense, isSVG, Optimized) => {instance.update = effect(function componentEffect() {// New componentEffect(! instance.isMounted) { let vnodeHook: VNodeHook | null | undefined const { el, Props} = initialVNode (props); // render function (ref/reactive); Const subTree = (instance.subTree = renderComponentRoot(instance)) patch(// This patch execution completes the DOM mount null, subTree, container, anchor, instance, parentSuspense, isSVG ) initialVNode.el = subTree.el instance.isMounted = true initialVNode = container = anchor = null as any } else { Let {next, bu, u, parent, vnode} = instance let originNext = next let vnodeHook: VNodeHook | null | undefined const nextTree = renderComponentRoot(instance) const prevTree = instance.subTree instance.subTree = nextTree patch( prevTree, nextTree, // parent may have changed if it's in a teleport hostParentNode(prevTree.el!) ! , // anchor may have changed if it's in a fragment getNextHostNode(prevTree), instance, parentSuspense, isSVG ) next.el = nextTree.el } }, __DEV__ ? createDevEffectOptions(instance) : prodEffectOptions)Copy the code

SetupRenderEffect calls Effect, and only functions executed in Effect rely on collecting renderComponentRoot to create child nodes of the component, which executes the render method of the child component. The render method collects the reactivity data and collects the effect function as a dependency on the change. When effect is executed, the second parameter prodEffectOptions is passed, which contains a scheduler method, This is the scheduler that is called after a dependency update, and the scheduler decides when to perform a DOM update, rather than making changes to the DOM every time a dependency changes.

Initial mount component: 1: generates a component instance when mounting Component, 2: generates a component instance when mounting component. Call setupComponent and the value returned by setup will be stored in instance.setupState. 3: Finally call setupRenderEffect to collect dependencies.