# 全局API
# Vue3入口-createApp
相比较于Vue 2
通过构造函数new Vue()
的方式创建根Vue
实例,Vue 3
改为了通过调用 createApp
返回一个应用实例:
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
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>
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))
}
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)
}
}
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
,其作用相当于Vue2
的new 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
}
}
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.ts
的createApp
函数中重写了app.mount
方法,所以App
的mount
入口在这里:
// @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
}
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.ts
的createAppAPI
中,其主要内容为,将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 内容为开发环境报警
},
})
}
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
← 编译器