当咱们像上面这样应用 createApp 创立 vue app 实例过程中产生了什么?

const { createApp } = VuecreateApp({    setup() {      return {      }    }}).mount('#app')

咱们一起来看看。


首先, 进入的是 vue导出的 createApp 函数, 它将所有参数都合并为了 args, 并调用了 ensureRenderer 函数并调用了其返回数据上的createApp , 而后将 args 打散传入 。

这里能够看出 ensureRenderer 调用的是 runtime-core 内导出的 createRenderer 办法, createRenderer 接管了 rendererOptions ,

这样做的目标是,一是应用renderer 做了单例缓存, 防止创立多个renderer, 二是vue3 思考到更好的反对多端的渲染,没有强耦合浏览器的 dom 操作, 而是把一些操作的具体实现裸露给开发者本人去实现, 这里传入的 rendererOptions 就是 浏览器 dom 环境的具体实现

import {  createRenderer} from '@vue/runtime-core'const rendererOptions = extend({ patchProp }, nodeOps) // 渲染相干的一些配置,比方更新属性的办法,操作 DOM 的办法let renderer: Renderer<Element | ShadowRoot> | HydrationRendererfunction ensureRenderer() {   return (    renderer ||    (renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))  )}export const createApp = ((...args) => {  const app = ensureRenderer().createApp(...args) //  //...})

createRenderer 办法外部调用的又是 baseCreateRenderer

export function createRenderer<  HostNode = RendererNode,  HostElement = RendererElement>(options: RendererOptions<HostNode, HostElement>) {  return baseCreateRenderer<HostNode, HostElement>(options)}

baseCreateRenderer 外部内容就很多了,这里的options 就是内部传入的平台相干的操作细节 rendererOptions ,而后将 insert、remove 等办法从options 中取出。 在下方实现的渲染相干的一系列办法中调用, 因为内容过长,这里将其省略了。 再往下, 函数的开端呢导出了二个比拟重要的货色, render 办法, createApp 办法, 而后 createApp 办法又是通过 createAppAPI 办法创立的的, 其承受了 render 和 hydrate 。 这也是内部能够调用 ensureRenderer().createApp(...args) 的起因

import { createAppAPI } from './apiCreateApp'function baseCreateRenderer(  options: RendererOptions,  createHydrationFns?: typeof createHydrationFunctions): any {  const {     insert: hostInsert,    remove: hostRemove,    patchProp: hostPatchProp,    createElement: hostCreateElement,    createText: hostCreateText,    createComment: hostCreateComment,    setText: hostSetText,    setElementText: hostSetElementText,    parentNode: hostParentNode,    nextSibling: hostNextSibling,    setScopeId: hostSetScopeId = NOOP,    cloneNode: hostCloneNode,    insertStaticContent: hostInsertStaticContent  } = options      const patch: PatchFn = () => {/*.... */ }  const processText: ProcessTextOrCommentFn = = () => {/*.... */ }  const processCommentNode: ProcessTextOrCommentFn = = () => {/*.... */ }  const mountStaticNode = = () => {/*.... */ }  const patchStaticNode = () => {/*.... */ }  const moveStaticNode = () => {/*.... */ }  const removeStaticNode = () => {/*.... */ }  const processElement = () => {/*.... */ }  const mountElement = () => {/*.... */ }  const setScopeId = () => {/*.... */ }  const mountChildren: MountChildrenFn = () => {/*.... */ }  const patchElement = () => {/*.... */ }  // The fast path for blocks.  const patchBlockChildren: PatchBlockChildrenFn = () => {/*.... */ }  const patchProps = () => {/*.... */ }  const processFragment = () => {/*.... */ }  const processComponent = () => {/*.... */ }  const mountComponent: MountComponentFn = () => {/*.... */ }  const updateComponent = () => {/*.... */ }  const setupRenderEffect: SetupRenderEffectFn = () => {/*.... */ }  const updateComponentPreRender = () => {/*.... */ }  const patchUnkeyedChildren = () => {/*.... */ }  const patchKeyedChildren = () => {/*.... */ }  const move: MoveFn = () => {/*.... */ }  const unmount: UnmountFn = () => {/*.... */ }  const remove: RemoveFn = () => {/*.... */ }  const removeFragment = () => {/*.... */ }  const unmountComponent = () => {/*.... */ }  const unmountChildren: UnmountChildrenFn = () => {/*.... */ }  const getNextHostNode: NextFn = () => {/*.... */ }  const render: RootRenderFunction = () => {/*.... */ }  const internals: RendererInternals = () => {/*.... */ }  return {    render,    hydrate,    createApp: createAppAPI(render, hydrate)  }}

createAppAPI 外部返回了 createApp 函数。 这个函数外部次要是创立了 app 对象根本数据结构,其上有 uuid, use(注册插件) , component (注册子组件), mount 等等属性和办法。 这里要留神的是, mount 办法外部调用了 createAppAPI 接管的 render 渲染器,此时以闭包模式存在, 最初返回了 app对象

export function createAppAPI<HostElement>(  render: RootRenderFunction,  hydrate?: RootHydrateFunction): CreateAppFunction<HostElement> {  return function createApp(rootComponent, rootProps = null) {    const context = createAppContext()    const installedPlugins = new Set()     let isMounted = false     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      },      use(plugin: Plugin, ...options: any[]) {         if (installedPlugins.has(plugin)) {          __DEV__ && warn(`Plugin has already been applied to target app.`)        } else if (plugin && isFunction(plugin.install)) {           installedPlugins.add(plugin)          plugin.install(app, ...options)         } else if (isFunction(plugin)) {           installedPlugins.add(plugin)          plugin(app, ...options)        }        return app      },      mixin(mixin: ComponentOptions) {        if (__FEATURE_OPTIONS_API__) {          if (!context.mixins.includes(mixin)) {            context.mixins.push(mixin)          }        }        return app      },      // 注册组件      component(name: string, component?: Component): any {        if (!component) {           return context.components[name]        }        context.components[name] = component        return app      },      directive(name: string, directive?: Directive) {        if (!directive) {          return context.directives[name] as any        }        context.directives[name] = directive        return app      },      mount(        rootContainer: HostElement,        isHydrate?: boolean,         isSVG?: boolean      ): any {        if (!isMounted) {           const vnode = createVNode(             rootComponent as ConcreteComponent,            rootProps          )          vnode.appContext = context          render(vnode, rootContainer, isSVG)          isMounted = true          app._container = rootContainer           ;(rootContainer as any).__vue_app__ = app  // app 挂到 dom __vue_app__ 属性上          return vnode.component!.proxy        }      },      unmount() {        if (isMounted) {          render(null, app._container)          delete app._container.__vue_app__        }      },      provide(key, value) {        context.provides[key as string] = value        return app      }    })    return app  }}

返回 app后,这时就回到了一开始的 createApp 函数, 将返回的app实例存储在了 app变量内, 并取出 mount 办法,并从新定义了一个 app.mount 办法,对本来的mount 办法进行了一次包装, 包装的目标呢和 renderer 是一样的, 将平台的具体实现抽离,由开发者自行实现,
这里 app.mount 办法所接管的 containerOrSelector 参数就是咱们内部传入的 #app 选择器 , 而后应用 normalizeContainer 对这个选择器进行标准化,返回的是理论的dom, 如果没找到理论的dom的话就间接return 了, 如果发现组件没有提供template 、render ,那就会取 dom 的 innerHTML作为 template , 并在调用mount办法挂载前清空理论dom的 innerHTML , 挂载完后移除了v-cloak 等属性,并将 app返回

export const createApp = ((...args) => {  const app = ensureRenderer().createApp(...args)  const { mount } = app // 取出本来的 mount   app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {    const container = normalizeContainer(containerOrSelector)    if (!container) return    const component = app._component // 取出组件的对象数据    if (!isFunction(component) && !component.render && !component.template) {       component.template = container.innerHTML     }    container.innerHTML = ''     const proxy = mount(container, false, container instanceof SVGElement)    if (container instanceof Element) {       container.removeAttribute('v-cloak')      container.setAttribute('data-v-app', '')     }    return proxy  }  return app}) as CreateAppFunction<Element>

当内部调用mout 办法时, app.mount 天然就被调用了

createApp().mount('#app')

这就又进入到了在 createApp 中定义的最后的 mount , 外部创立了 vNode , 并调用了render 办法进行渲染, 并将 isMounted 批改

  mount(     rootContainer: HostElement,    isHydrate?: boolean,     isSVG?: boolean  ): any {    if (!isMounted) {      const vnode = createVNode(        rootComponent as ConcreteComponent,        rootProps      )      vnode.appContext = context      render(vnode, rootContainer, isSVG)      isMounted = true      app._container = rootContainer           ;(rootContainer as any).__vue_app__ = app        return vnode.component!.proxy    }   }

好了, 这样整个流程就走完了

整顿一下,整个调用过程如下:

createApp -> ensureRenderer -> baseCreateRenderer -> createAppAPI (接管 render办法) -> createApp 创立并返回 app对象 -> 回到 createApp ,包装app.mount -> 内部调用 .mount('#app') -> 触发 app.mount -> mount -> 调用 render 办法渲染