Introduction: This paper is based on Vue 3.0.5 for analysis, mainly for personal learning comb process. It was also the first time I had written a serious article. Correct me if I am wrong.

What does createAppAPI do

The preface raises a question:

  • whyVue3To pass thecreateApp({setup(){}}).mount('#app')This method is implemented functionally, not in the same wayVue2throughnew Vue()The way?

Don’t say much about the source code

createAppAPI

export function createAppAPI<HostElement> (render: RootRenderFunction, hydrate? : RootHydrateFunction) :CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    // omit createApp internal code}}Copy the code

CreateAppAPI is just a wrapper around the implementation of createApp. Moving on to see what the main implementation of the createApp method does.

createApp

// Omit some DEV environment code
return function createApp(rootComponent, rootProps = null) {
  // The main use of the up-down factory function is to initialize some data. See below
  const context = createAppContext()
  const installedPlugins = new Set(a)// mark the mount status
  let isMounted = false

  const app: App = (context.app = {
  _uid: uid++,
  _component: rootComponent as ConcreteComponent,
  _props: rootProps,
  _container: null._context: context,
  version,

  // Omit some code
  // Plug-in installation
  use(plugin: Plugin, ... options: any[]) {
    Vue2 vue.use (vueRouter) * vue3 app. Use (vueRouter) * 1. Global configuration contaminates * 2. Treeshaking optimization * 3. Semantic * /

    // installedPlugins are set key unique

    if(plugin && isFunction(plugin.install)) { installedPlugins.add(plugin) plugin.install(app, ... options) }else if(isFunction(plugin)) { installedPlugins.add(plugin) plugin(app, ... options) }return app
  },
  // global mixin
  mixin(mixin: ComponentOptions) {
    // Core logic
    if(! context.mixins.includes(mixin)) { context.mixins.push(mixin)/ / judge whether the components of the mixed with existing props | emits attribute, if there is not data merging processing (subsequent)
        if (mixin.props || mixin.emits) {
          context.deopt = true}}return app
  },
  // Global component mountcomponent(name: string, component? : Component): any {// No component creation method passed in returns the bound value of undefined or the registered creation method
    if(! component) {return context.components[name]
    }
    // Mount to the component object
    context.components[name] = component
    return app
  },
  // Global custom instruction mount
  directive(name: string, directive? : Directive) {
    // The logic is similar to component mounting
    if(! directive) {return context.directives[name] as any
    }
    context.directives[name] = directive
    return app
  },

  /** * TODO mount phase focus *@param {HostElement} RootContainer Mount element node *@param {boolean} [isHydrate]
    * @return {*}  {*}* /mount(rootContainer: HostElement, isHydrate? : boolean): any {// Check the isMounted mounting status
    if(! isMounted) {//TODO gets the vNode of the entire tree when mounted
      // createApp(base).mount('#app')
      // Virtual DOM creation method
      const vnode = createVNode(
        rootComponent as ConcreteComponent,
        rootProps
      )

      // Store the initialization up and down on the appContext property of the root node
      vnode.appContext = context

      if (isHydrate && hydrate) {
        hydrate(vnode as VNode<Node, Element>, rootContainer as any)
      } else {
        // The vNode passed in by the TODO renderer generates a node to mount to the root node
        render(vnode, rootContainer)
      }
      // Modify tag is mounted
      isMounted = true
      // Bind the root node element
      app._container = rootContainer
      // for devtools and telemetry; (rootContaineras any).__vue_app__ = app
      // Return an instance of Vue
      returnvnode.component! .proxy } },/ / unloading
  unmount() {
    // The first argument to the render function is vnode, which checks that if there is no vnode and _vnode exists on the root instance, unmount it
    if (isMounted) {
      render(null, app._container)
    }
  },
  // global provide
  provide(key, value) {
    /* * Sets a value that can be injected into all application-wide components. Components should use Inject to receive supplied values. * Note * provide and Inject binding are not reactive. This is intentional. * However, if you pass down a responsive object, the property on that object will remain responsive. * /
    context.provides[key as string] = value
    return app
  }
})

return app
}
Copy the code

Vue context creates createAppContext

export function createAppContext() :AppContext {
  return {
    app: null as any,
    config: {
      // const NO = () => false
      isNativeTag: NO,
      performance: false.globalProperties: {},
      optionMergeStrategies: {},
      isCustomElement: NO,
      errorHandler: undefined.warnHandler: undefined
    },
    mixins: [].components: {},
    directives: {},
    provides: Object.create(null)}}Copy the code

conclusion

In createAppAPI we found that its main function is to create an instance of Vue. And bind global data and context to the current Vue instance. The element is eventually mounted by calling the mount method


Follow-up articles:

Vue3 parse series of mount functions

Data responsivity of Vue3 parse series

This is the first article of Vue3 parsing. The Vue3 mount phase will be analyzed later.