vue createApp的使用:

1
2
3
4
5
6
7
8
9
const HelloVueApp = {
data() {
return {
message: 'Hello Vue!'
}
}
}

Vue.createApp(HelloVueApp).mount('#app')

createApp 做了哪些工作

  1. 调用 ensureRenderer函数,创建基本实例和初始化基础的组件数据
  2. 重写了mount函数,寻找并挂载到指定的DOM元素上
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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
const createApp = ((...args) => {
// 重点看下 ensureRenderer
const app = ensureRenderer().createApp(...args)

if (__DEV__) {
injectNativeTagCheck(app)
injectCompilerOptionsCheck(app)
}

const { mount } = app
// 重写了mount,
// 默认的mount是挂载VNode的,这里第一次是挂在普通的dom元素
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
const container = normalizeContainer(containerOrSelector)
if (!container) return
// 这里是调用该函数的入参一般来说就是App.vue文件
const component = app._component
if (!isFunction(component) && !component.render && !component.template) {
// __UNSAFE__ todo 这是一个什么标记?
// Reason: potential execution of JS expressions in in-DOM template.
// The user must make sure the in-DOM template is trusted. If it's
// rendered by the server, the template should not contain any user data.
// 原因:在 DOM 模板中可能执行 JS 表达式。
// 用户必须确保 in-DOM 模板是可信的。 如果它是
// 由服务器呈现,模板不应包含任何用户数据。
component.template = container.innerHTML
// 2.x compat check
if (__COMPAT__ && __DEV__) {
for (let i = 0; i < container.attributes.length; i++) {
const attr = container.attributes[i]
if (attr.name !== 'v-cloak' && /^(v-|:|@)/.test(attr.name)) {
compatUtils.warnDeprecation(
DeprecationTypes.GLOBAL_MOUNT_CONTAINER,
null
)
break
}
}
}
}

// clear content before mounting
// 清理原来的内容
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>

ensureRenderer 做了什么

单例模式, 调用createRenderer函数

1
2
3
4
5
6
7
8
9
10
11
12
// lazy create the renderer - this makes core renderer logic tree-shakable
// in case the user only imports reactivity utilities from Vue.
// 延迟创建渲染器 - 这使得核心渲染器逻辑可 tree-shakable
// 如果用户只从 Vue 导入反应性实用程序。
let renderer: Renderer<Element | ShadowRoot> | HydrationRenderer

function ensureRenderer() {
return (
renderer ||
(renderer = createRenderer<Node, Element | ShadowRoot>(rendererOptions))
)
}

createRenderer 做了什么

本身只调用了 baseCreateRenderer函数

(个人猜测,未证实)根据注释来看,它的作用估计是为了支持第三方使用vue扩展自己的东西,比入 uni-app?

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
29
30
31
32
33
34
35
36
/**
* The createRenderer function accepts two generic arguments:
* HostNode and HostElement, corresponding to Node and Element types in the
* host environment. For example, for runtime-dom, HostNode would be the DOM
* `Node` interface and HostElement would be the DOM `Element` interface.
*
* Custom renderers can pass in the platform specific types like this:
*
* ``` js
* const { render, createApp } = createRenderer<Node, Element>({
* patchProp,
* ...nodeOps
* })
* ```
*/
/**
* createRenderer 函数接受两个通用参数:
* HostNode和HostElement,分别对应类中的Node和Element类型
* 主机环境。 例如,对于 runtime-dom,HostNode 将是 DOM
* `Node` 接口和 HostElement 将是 DOM `Element` 接口。
*
* 自定义渲染器可以像这样传递特定于平台的类型:
*
* ``` js
* const { render, createApp } = createRenderer<Node, Element>({
* patchProp,
* ...nodeOps
* })
* ```
*/
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}

baseCreateRenderer 做了什么

baseCreateRenderer 这个函数简直可以用庞大来形容(2000+行代码),vnode diff patch均在这个方法中实现,回头我们再来细看实现,现在我们只需要关心他最后返回的什么

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions // ssr才会有这个入参
): any {

// ... 2000+行代码

return {
render, // 内部函数
hydrate, // ssr用到的东西
createApp: createAppAPI(render, hydrate)
}
}

从源码中我们看到 baseCreateRenderer 最终返回 render hydrate createApp 3个函数, 但在 createApp 这个函数中我们本质上只需要返回 createApp 这个函数就好,这里返回了3个,说明其它两个会在别处用到,具体哪里能用到,后面我们再回头看

接着将生成的 render 传给 createAppAPI 这个真正的 createApp 方法,hydrate 为可选参数,ssr 的场景下会用到,这边我们也先跳过

createAppAPI 做了什么

它创建了基础实例的能力,挂在插件,注册组件,注册指令等

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
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
export function createAppAPI<HostElement>(
render: RootRenderFunction<HostElement>,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {
if (!isFunction(rootComponent)) {
rootComponent = { ...rootComponent }
}

if (rootProps != null && !isObject(rootProps)) {
__DEV__ && warn(`root props passed to app.mount() must be an object.`)
rootProps = null
}

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

let isMounted = false
// 定义了一些api,并支持链式操作
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
},

set config(v) {
if (__DEV__) {
warn(
`app.config cannot be replaced. Modify individual options instead.`
)
}
},

use(plugin: Plugin, ...options: any[]) {
// ...
},

mixin(mixin: ComponentOptions) {
// ...
},

component(name: string, component?: Component): any {
// ...
},

directive(name: string, directive?: Directive) {
// ...
},

mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
// ...
},

unmount() {
// ...
},

provide(key, value) {
// ...
}
})

if (__COMPAT__) {
installAppCompatProperties(app, context, render)
}

return app
}
}

export function createAppContext(): AppContext {
return {
app: null as any,
config: {
isNativeTag: NO,
performance: false,
globalProperties: {},
optionMergeStrategies: {},
errorHandler: undefined,
warnHandler: undefined,
compilerOptions: {}
},
mixins: [],
components: {},
directives: {},
provides: Object.create(null),
optionsCache: new WeakMap(),
propsCache: new WeakMap(),
emitsCache: new WeakMap()
}
}