# 全局API

# Vue3入口-createApp

相比较于Vue 2通过构造函数new Vue()的方式创建根Vue实例,Vue 3改为了通过调用 createApp返回一个应用实例:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')
1
2
3
4

createApp源码位于@vue/runtime-dom/src/index.ts

export const createApp = ((...args) => {
  // 调用render.createApp生成app
  const app = ensureRenderer().createApp(...args)

  // 重新包装app的mount方法
  const { mount } = app
  app.mount = (containerOrSelector: Element | string): any => {
    const container = normalizeContainer(containerOrSelector)
    if (!container) return
    const component = app._component
    if (!isFunction(component) && !component.render && !component.template) {
      component.template = container.innerHTML
    }
    // clear content before mounting
    container.innerHTML = ''
    const proxy = mount(container)
    container.removeAttribute('v-cloak')
    container.setAttribute('data-v-app', '')
    return proxy
  }

  return app
}) as CreateAppFunction<Element>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

ensureRenderer

// lazy create the renderer - this makes core renderer logic tree-shakable
// in case the user only imports reactivity utilities from Vue.
let renderer: Renderer<Element> | HydrationRenderer

let enabledHydration = false

function ensureRenderer() {
  return renderer || (renderer = createRenderer<Node, Element>(rendererOptions))
}
1
2
3
4
5
6
7
8
9

createRenderer位于@vue/runtime-core/renderer.ts,调用了同文件的baseCreateRenderer,其代码如下:

function baseCreateRenderer(
  options: RendererOptions,
  createHydrationFns?: typeof createHydrationFunctions
): any {
  //... 无关代码省略

  return {
    render,
    hydrate,
    createApp: createAppAPI(render, hydrate)
  }
}
1
2
3
4
5
6
7
8
9
10
11
12

因为在createApp中只使用到了render.createApp,所以我们把无关代码省略,可以看到render.createApp是由createAppAPI生成的,代码位于@vue/runtime-core/apiCreateApp.ts第114行,其作用是返回一个createApp方法,用于生成一个AppContext和一个App,其作用相当于Vue2new Vue(),代码如下:

export function createAppAPI<HostElement>(
  render: RootRenderFunction,
  hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
  return function createApp(rootComponent, rootProps = null) {
    // 确保rootProps只能是null或者Object
    if (rootProps != null && !isObject(rootProps)) {
      rootProps = null
    }

    const context = createAppContext()
    const installedPlugins = new Set()

    let isMounted = false

    const app: App = (context.app = {
      // ... app对象内容省略
    })

    return app
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22

# App的mount过程

由于在@vue/runtime-dom/src/index.tscreateApp函数中重写了app.mount方法,所以Appmount入口在这里:

// @vue/runtime-dom/src/index.ts 61行
app.mount = (containerOrSelector: Element | string): any => {
  // 如果是选择器,则返回对应的dom
  const container = normalizeContainer(containerOrSelector)
  if (!container) return
  
  // clear content before mounting
  container.innerHTML = ''

  const proxy = mount(container)
  
  container.removeAttribute('v-cloak')
  container.setAttribute('data-v-app', '')
  return proxy
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

主要做了以下的操作,将选择器转行成DOM,将container清空,调用app原来的mount方法,并对DOM的属性做了一些操作

app原来的mount方法位于@vue/runtime-core/apiCreateApp.tscreateAppAPI中,其主要内容为,将App组件内容生成VNode,将vnode.appContext设置上,调用render(vnode, rootContainer)渲染组件,将闭包里的isMounted设置成true,将rootContainer设置给app._container,将vnode.component!.proxy返回。

function createApp(rootComponent, rootProps = null) {
  // 其他内容省略
  const context = createAppContext()
  let isMounted = false

  const app: App = (context.app = {
    // ... 其他app对象内容省略
    mount(rootContainer: HostElement, isHydrate?: 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

        render(vnode, rootContainer)

        isMounted = true
        app._container = rootContainer
        
        return vnode.component!.proxy
      }
      // else if 内容为开发环境报警
    },
  })
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28