Vue 實例
首先,我覺得這個問題要先從 Vue 的實例開始講起。Vue 的實例一般都是長成下面這個樣子,不同的只是 id 名的不同。
<div id="app"></div>
var vm = new Vue({ el: '#app', data: {}, methods: {} ... })
入口問題
在 Vue 中指定 el 選項是給 Vue 實例指定一個 SPA 入口,有可能你的頁面會長成像下面這樣:
<div id="app"></div> <div id="app1"></div> <div id="app2"></div>
虛擬 DOM
學習 Vue 不得不說的就是2.0引入的Virtual DOM
,引入虛擬 DOM 后,在框架的內部將虛擬 DOM 樹型結構與真實 DOM 做了映射,讓我們不用再命令式的去操作 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。
<html></html>
,只能有一個這樣的標簽存在。對應到 Vue 中也是這樣,如果你給它兩個頂級標簽,那么對應的 DOM 結構就無法生成了,這也就解釋了之前的疑惑:為什么不能指定多個入口讓 Vue 實例一個個的試。
vue-cli
現在實際的項目開發中,使用腳手架 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 本身來說,如果說一個組件有多個入口多個根,那不就意味着你的組件還可以進一步拆分成多個組件,進一步組件化,降低代碼之間的耦合程度。