Vue CLI创建的项目的组件的问题

使用Vue CLI生成的项目,有个地方不理解,
在项目默认生成的App.vue中定义的组件,在main.js中加载,为什么在index中没有进行引用(没有相应的标签对),但是还是加载了呢?

App.vue中默认定义的组件如下:

<template>
  <img alt="Vue logo" src="./assets/logo.png">
  <HelloWorld msg="Welcome to Your Vue.js App"/>
template>

通过main.js进行挂载:

import { createApp } from 'vue'
import App from './App.vue'

createApp(App).mount('#app')

首页(index.html)的代码如下(body部分):

  <body>
    <noscript>
      <strong>We're sorry but <%= htmlWebpackPlugin.options.title %> doesn't work properly without JavaScript enabled. Please enable it to continue.strong>
    noscript>
    <div id="app">div>
    
  body>

1)按理说根元素App不应该包含template
2)id为app的元素里面没有引用某个组件啊(里面没有元素内容),但是渲染之后装载了App.vue定义的组件模板
这两个问题求解释,谢谢

第一个问题:首先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源码系列文章