结合Vue3-demo项目分析源码(一)

Vue3-demo项目即为先前从0搭建简易vue3项目中搭建的demo

分析

入口文件main.js

1
2
3
4
5
import { createApp } from "vue";//引入Vue3源码
import App from "./App";//引入App.vue组件
const app = createApp(App,{msg:"Hello Vue"})//创建App组件,传递msg参数

app.mount("#app")//渲染app,将其挂载到id=app的节点上

通过在浏览器上不同位置打断点,可以按步骤流程分析Vue源码的实现流程。

断点

创建app应用实例

createApp()

创建app对象,即应用实例

查看源码项目vue-next/packages/runtime-dom/src/index.ts

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
export const createApp = ((...args) => {
//创建app对象
const app = ensureRenderer().createApp(...args)

if (__DEV__) {
injectNativeTagCheck(app)
}

const { mount } = app
//重写app的mount方法
app.mount = (containerOrSelector: Element | string): any => {
//根据传递的选择器获取dom元素
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>

其中,创建app的代码为:

1
2
//创建app对象
const app = ensureRenderer().createApp(...args)

查看ensureRenderer()方法定义

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

方法ensureRenderer()通过使用createRenderer<Node, Element>(rendererOptions)方法创建app。

renderer负责Vue3中节点的渲染、对比、更新。

createRenderer()

创建并返回一个节点渲染引擎

查看vue-next/packages/runtime-core/src/renderer.ts中391行

1
2
3
4
5
6
export function createRenderer<
HostNode = RendererNode,
HostElement = RendererElement
>(options: RendererOptions<HostNode, HostElement>) {
return baseCreateRenderer<HostNode, HostElement>(options)
}

该方法调用了baseCreateRenderer()方法,查看其定义

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
//用到的一些定义
type PatchFn = (
n1: VNode | null, // null means this is a mount
n2: VNode,
container: RendererElement,
anchor?: RendererNode | null,
parentComponent?: ComponentInternalInstance | null,
parentSuspense?: SuspenseBoundary | null,
isSVG?: boolean,
optimized?: boolean
) => void

export type MountComponentFn = (
initialVNode: VNode,
container: RendererElement,
anchor: RendererNode | null,
parentComponent: ComponentInternalInstance | null,
parentSuspense: SuspenseBoundary | null,
isSVG: boolean,
optimized: boolean
) => void

export type RootRenderFunction<HostElement = RendererElement> = (
vnode: VNode | null,
container: HostElement
) => void

//baseCreateRenderer方法定义
function baseCreateRenderer(
options: RendererOptions,
createHydrationFns?: typeof createHydrationFunctions
): any {

...
//对比两个节点
const patch: PatchFn = (
n1,
n2,
container,
anchor = null,
parentComponent = null,
parentSuspense = null,
isSVG = false,
optimized = false
) => {
...
}

//渲染组件
const mountComponent: MountComponentFn = (
initialVNode,
container,
anchor,
parentComponent,
parentSuspense,
isSVG,
optimized
) => {
...
}

//更新组件
const updateComponent = (n1: VNode, n2: VNode, optimized: boolean) => {
...
}

//卸载组件
const unmountComponent = (
instance: ComponentInternalInstance,
parentSuspense: SuspenseBoundary | null,
doRemove?: boolean
) => {
...
}

//渲染组件
const render: RootRenderFunction = (vnode, container) => {
if (vnode == null) {
if (container._vnode) {
unmount(container._vnode, null, null, true)
}
} else {
patch(container._vnode || null, vnode, container)
}
flushPostFlushCbs()
container._vnode = vnode
}

//返回renderer对象
return {
render,
hydrate,
createApp: createAppAPI(render, hydrate)
}
}

从最后return可看出,最终的app是使用createAppAPI()方法得出。

createAppAPI()

创建并返回一个app实例

查看文件vue-next/packages/runtime-core/src/apiCreateApp.ts第123行

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
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {

...

//创建应用上下文
const context = createAppContext()

...
//app为方法createApp()最后的返回值,即需要的app实例
const app: App = (context.app = {
_uid: uid++, //唯一标识
_component: rootComponent as ConcreteComponent, //根组件
_props: rootProps, //根属性
_container: null, //根节点
_context: context, //上下文

version, //vue版本号

get config() { //获取当前应用的config对象
return context.config
},

set config(v) {
...
},

//全局插件注册-同webpack.config.js中webpack-chain配置use使用
use(plugin: Plugin, ...options: any[]) {
if (installedPlugins.has(plugin)) {
...
} else if (plugin && isFunction(plugin.install)) {
installedPlugins.add(plugin)
plugin.install(app, ...options)
} else if (isFunction(plugin)) {
installedPlugins.add(plugin)
plugin(app, ...options)
} else if (__DEV__) {
warn(
`A plugin must either be a function or an object with an "install" ` +
`function.`
)
}
return app
},

//全局mixin注册
mixin(mixin: ComponentOptions) {
...
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) {
...
context.directives[name] = directive
return 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

// HMR root reload
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer)
}
}

if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer)
}
isMounted = true
app._container = rootContainer
// for devtools and telemetry
;(rootContainer as any).__vue_app__ = app

if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
devtoolsInitApp(app, version)
}

return vnode.component!.proxy
} else if (__DEV__) {
warn(
`App has already been mounted.\n` +
`If you want to remount the same app, move your app creation logic ` +
`into a factory function and create fresh app instances for each ` +
`mount - e.g. \`const createMyApp = () => createApp(App)\``
)
}
},

//卸载当前app
unmount() {
...
render(null, app._container)
...
},

//全局provide注册
provide(key, value) {
...
// TypeScript doesn't allow symbols as index type
// https://github.com/Microsoft/TypeScript/issues/24587
context.provides[key as string] = value
return app
}
})
//返回app对象
return app
}
}

由此可得,最终返回的app组成如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
export interface App<HostElement = any> {
version: string //vue版本
config: AppConfig //全局配置
use(plugin: Plugin, ...options: any[]): this //全局插件注册
mixin(mixin: ComponentOptions): this //全局mixin注册
component(name: string): Component | undefined //全局组件注册
component(name: string, component: Component): this
directive(name: string): Directive | undefined //全局指令注册
directive(name: string, directive: Directive): this
mount( //渲染app方法
rootContainer: HostElement | string,
isHydrate?: boolean
): ComponentPublicInstance
unmount(rootContainer: HostElement | string): void //卸载组件方法
provide<T>(key: InjectionKey<T> | string, value: T): this //全局provide注册

// internal, but we need to expose these for the server-renderer and devtools
_uid: number //唯一标识
_component: ConcreteComponent //根组件
_props: Data | null //根属性
_container: HostElement | null //根节点
_context: AppContext //上下文节点
}

故回到main.js文件,创建完app实例,则调用了app的mount方法开始渲染对象

1
2
3
4
import { createApp } from "vue";
import App from "./App"; // 引入 App.vue 组件
const app = createApp(App, { msg: "hello Vue 3" }); // 创建 App 根组件
app.mount("#app"); // 渲染 App

附录-Vue3流程图

vue流程图

  • 版权声明: 本博客所有文章除特别声明外,均采用 Apache License 2.0 许可协议。转载请注明出处!
  • © 2020-2024 Aweso Lynn
  • PV: UV: