第一个问题:首先template只是一种声明式的模板语法,关于组件的写法,可以不使用template,你可以使用h渲染函数或jsx都ok。template的写法会更接近html,而且在编译时,会进行一些编译优化。那么App可以不包含template吗?可以,如果App根组件不是函数式组件或不存在template和渲染函数,那么会将#app的innerHTML属性作为template。下面是mount过程的部分源码
const component = app._component
if (!isFunction(component) && !component.render && !component.template) {
// 将container.innerHTML作为根组件的template属性
component.template = container.innerHTML
//...
}
第二个问题:#app为什么没有显示引用组件。在main.js中会执行一个mount方法,这个mount方法会将App渲染出来的dom整个挂在到#app上。下面是mount方法的源码,其中的render就是渲染dom并挂载到#app的核心
mount(
rootContainer: HostElement,
isHydrate?: boolean,
isSVG?: boolean
): any {
if (!isMounted) {
if (__DEV__ && (rootContainer as any).__vue_app__) {
warn(
`There is already an app instance mounted on the host container.\n` +
` If you want to mount another app on the same host container,` +
` you need to unmount the previous app by calling \`app.unmount()\` first.`
)
}
const vnode = createVNode(
rootComponent as ConcreteComponent,
rootProps
)
vnode.appContext = context
if (__DEV__) {
context.reload = () => {
render(cloneVNode(vnode), rootContainer, isSVG)
}
}
if (isHydrate && hydrate) {
hydrate(vnode as VNode<Node, Element>, rootContainer as any)
} else {
render(vnode, rootContainer, isSVG)
}
isMounted = true
app._container = rootContainer
;(rootContainer as any).__vue_app__ = app
if (__DEV__ || __FEATURE_PROD_DEVTOOLS__) {
app._instance = vnode.component
devtoolsInitApp(app, version)
}
return getExposeProxy(vnode.component!) || 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)\``
)
}
}
如果你对vue3源码感兴趣,可以关注我,目前我正在更新vue3源码系列文章