回顾
Vue3.0 初始化渲染如下
import { createApp } from 'vue'
import App from './App.vue'
createApp(App).mount('#app')
上一篇已经分析过了 createApp 函数的源码,已经知道了执行完 createApp 函数后,得到的 app 对象的具体属性及方法。
接下来继续介绍 app.mount('#app') 方法
mount
export const createApp = ((...args) => {
// ...
const { mount } = app
// 重写 app.mount 方法
app.mount = (containerOrSelector: Element | ShadowRoot | string): any => {
// 本质上调用 document.querySelector 获取 id = '#app' 的 DOM 元素
const container = normalizeContainer(containerOrSelector)
if (!container) return
const component = app._component
// 若根组件非函数对象且未设置 render 和 template 属性,则使用容器的 innerHTML 作为模板的内容
if (!isFunction(component) && !component.render && !component.template) {
component.template = container.innerHTML
if (__COMPAT__ && __DEV__) {
// 向下兼容 ...
}
}
// 清空原本容器(#app)里的内容
container.innerHTML = ''
// 进行 mount 操作
const proxy = mount(container, false, container instanceof SVGElement)
// ...
return proxy
}
return app
}) as CreateAppFunction<Element>
重写 mount 函数,目的是为了跨平台。因为要跨平台,所以原 mount 函数不应该包含任何特定平台相关的逻辑,意味着这些代码的执行逻辑都与平台无关。因此我们需要在外部重写这个方法,来完善 Web 平台下的渲染逻辑。
重写后的 mount 函数,本质还是调用 ensureRenderer().createApp(...args) 函数返回的 mount 方法
做了三件事:
-
标准化 container dom 容器;
-
给 component.template 赋值;
-
执行 mount 进行挂载。
export function createAppAPI<HostElement>(
render: RootRenderFunction,
hydrate?: RootHydrateFunction
): CreateAppFunction<HostElement> {
return function createApp(rootComponent, rootProps = null) {
// ...
const app: App = (context.app = {
// ...
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
// 是否渲染
if (!isMounted) {
// 创建 vnode
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
// 将 context 挂载到根节点
vnode.appContext = context
// ...
if (isHydrate && hydrate) {
// ssr 渲染
} else {
// 普通渲染
render(vnode, rootContainer, isSVG)
}
isMounted = true
// 将容器挂载到 app._container 上
app._container = rootContainer
// ...
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
// 初始化devtools
}
return getExposeProxy(vnode.component!) || vnode.component!.proxy
} else if (__DEV__) {
// ...
}
},
// ...
})
// ...
// app 对象本体
return app
}
简单来说做了三件事:
-
创建根组件对应的VNode对象;
-
设置 VNode 对象上的应用上下文属性;
-
执行渲染操作
到这里,基本结束了初始化 vue3.0 项目代码分析,既分析了 createApp(App).mount('#app') 这行代码具体干了什么。