做者:Horace
ps:新人小白一枚,若有错误,欢迎指出~css
笔者如今是一个大三将要实习的学生,前段时间在掘金看到了一篇文章《vue 248个知识点(面试题)为你保驾护航》,做者在里面写到他在面试一个5年前端工做经验的小伙子中的一些问题,其中有一个问题引发了个人注意,这个问题就是:不错,那我问下你 “vue为何要求组件模板只能有一个根元素?”
html
笔者入坑学习 Vue 也算是有段时间了,文档和一些大佬的项目、文章也多少看了一些,可是彷佛这个问题思考的不多,每次在写项目或是 demo 的时候直接就是代码往上怼,深刻的地方也仅限于了解了路由和模板编译的源码实现,也没有深究过为何只能有一个根元素。这个问题我以为对于刚入门 Vue 学习的朋友来讲是一个很好的问题,在刚开始的时候就要知其因此然才能更好的掌握一门语言。前端
在 google 和掘金简单找了一下,相关的问题资料彷佛不多,也少有人写这个专题,那就很少吹了,今天就由我来带你们分析一下这个问题。如下都是我本身查阅了一些资料以后的我的思考,若是你以为有问题,欢迎在评论区指正和一块儿探讨~vue
首先,我以为这个问题要先从 Vue 的实例开始讲起。Vue 的实例通常都是长成下面这个样子,不一样的只是 id 名的不一样。webpack
<div id="app"></div>
复制代码
var vm = new Vue({
el: '#app',
data: {},
methods: {}
...
})
复制代码
这就是 Vue 实例的基本结构,并不陌生。从这里能够看到,el 的指定是一个 id 为 app 的 div 元素,Vue 实例接管了对它的控制,减小了咱们的 DOM 操做,须要被 vm 控制的元素所有加在它的内部。若是是须要控制不一样的部分,这就须要多个 Vue 的实例来实现。疑问就来了,为何须要不一样的 Vue 实例来接管?git
在 Vue 中指定 el 选项是给 Vue 实例指定一个 SPA 入口,有可能你的页面会长成像下面这样:github
<div id="app"></div>
<div id="app1"></div>
<div id="app2"></div>
复制代码
Vue 实例其实并不知道哪个是入口,它应该接管哪个部分,因此你要给它指定一个惟一的元素做为入口。每个入口能够看做是一个 Vue 的类,Vue 要把这个入口进去的全部东西都取出来进行轮循渲染一遍,再把它从新挂载回页面中的 DOM 里面去。打给比方来讲,一个 Vue 实例只拥有一个钥匙,一个钥匙只能开一把锁,可是页面上有不少把锁,若是你不说清楚它是哪把锁的钥匙,Vue 实例就不知道接下来要怎么作了。web
固然,这只是一个比较浅显的理解。你可能会说,我指定几个入口让 Vue 实例去一个一个试就行了,咱们往下看。面试
“虚拟 DOM”是咱们对由 Vue 组件树创建起来的整个 VNode 树的称呼vue-cli
学习 Vue 不得不说的就是2.0引入的Virtual DOM
,引入虚拟 DOM 后,在框架的内部将虚拟 DOM 树型结构与真实 DOM 作了映射,让咱们不用再命令式的去操做 DOM。
在聊这个话题以前,若是有对于虚拟 DOM 还不是特别了解的,推荐看这篇文章👉《详解Vue中的虚拟DOM》
引用里面的一张图片:
从这张图能够看出来虚拟 DOM 的一个渲染过程,那咱们再回到本文的话题:为何只能由一个根元素?
咱们来看一个例子,假设你的 Vue 实例接管的 DOM 结构长成这个样子:
<div id="app">
<h1 id="h3">My title</h1>
<span>Content</span>
Other text
<!-- annotation text -->
</div>
复制代码
它在浏览器内部的表现是一个这样的 DOM 树:
原谅我画图技术差,不过我想展现的效果达到了。从这能够看出它是一个树的结构,每一个元素、文字、注释都是一个节点,虚拟 DOM 遵循的也是这样的一个树的数据结构。
回到正题,咱们的指定的 el 也就是整个 DOM 结构的根。如今就很好说了,咱们只有指定了惟一的 el 根元素,才能交给 Vue 实例在内部经过 createElement
方法生成一个对应的虚拟 DOM 结构映射真实的 DOM 元素进行操做渲染成真正的 HTML
换句话来讲,能够把 el 对应的元素理解成 Vue 接管部分中的一个顶级标签,就像基本的 HTML 结构中,顶级标签是 <html></html>
,只能有一个这样的标签存在。对应到 Vue 中也是这样,若是你给它两个顶级标签,那么对应的 DOM 结构就没法生成了,这也就解释了以前的疑惑:为何不能指定多个入口让 Vue 实例一个个的试。
不知道我这样的解释有没有说明白这个问题,若是没清楚咱们下面再来看看。
如今实际的项目开发中,使用脚手架 vue-cli 开发居多,咱们来看看。
vue-cli 的形式是单文件组件,一个 .vue 页面的基本结构是这样的:
<template>
<div></div>
</template>
<script>
export default {
}
</script>
<style>
</style>
复制代码
在这里,<template>
标签下也只能有一个根元素 div,这是为何?
在说这个话题以前,咱们须要了解 H5 新标签 <template>
的一些特性,能够参考文档,它保证了内部的内容有效但不会被渲染。vue-cli 本质上是会把 .vue 文件经过 webpack 配置打包成一系列的 js/css 文件注入到一个 html 文件中交给浏览器进行解释执行,咱们看一个打包好的文件目录:
这也就是说,每一个 .vue 文件都会是一个 Vue 的实例,而 <template>
标签中的内容就是 Vue 实例接管造成虚拟 DOM 的那部份内容。若是在 template 下有多个 div,那么虚拟 DOM 树就没办法生成了。
其实这个问题归结到最后,也能够抽象为一个问题:为何抽象出来的 DOM 树只能有一个根?
其实在咱们的学习过程当中,有不少问题都是咱们似懂非懂的,就是知其然不知其因此然。说白了就是知道轮子怎么用,可是不知道轮子内部的运行机制,虽说不要去重复造轮子,但咱们至少要知道轮子是怎么运行起来的。以上都是我我的对这个问题的一些理解,经过这篇文章但愿能够帮助你对这个问题有更深入的认识。若是你以为有什么不对的地方,欢迎直接指正,共同进步!
参考内容
《详解Vue中的虚拟DOM》
《vue2.0的虚拟DOM渲染思路分析》
染陌同窗《说说VNode节点(Vue.js实现)》
官方文档
官方API文档
官方issue相关讨论
我把个人学习记录都记录在了个人 github 而且会持续的更新下去,有兴趣的小伙伴能够看看~
github.com/tearill/Rea…