vue 為什么只能有一個根元素


我們在初學Vue時,第一個上手的例子基本都是 new Vue({el:’#app’}),但是為什么Vue實例只能掛載在一個div上呢?同樣的當我們開始寫第一個Vue頁面的時候,我們試圖在template標簽下寫兩個div,Vue提醒我們只能寫一個元素,但是為什么只能有一個元素呢?很多時候我們都已經習以為常,但是卻說不上來為什么。

  筆者入坑Vue也有一段時間了,對Vue也算了解,Vuex、Vue-Router也用了不少;但是前幾天一看到這個面試問題卻感覺一下子回答不上了,想來每次寫代碼也都是拿來就用,也沒有仔細的思考過里面的原因;每每報錯了就換一種寫法,能用就行,僅此而已。

  這個問題要從兩個方面來說:

  1. new Vue({el:’#app’})
  2. 單文件組件中,template下的元素div

Vue實例

  當我們實例化Vue的時候,填寫一個el選項,來指定我們的SPA入口:

let vm = new Vue({ el:'#app', data:{ msg: 'Hi boy' } }) <body> <div id='app'>{{msg}}</div> </body> 

  如果我們把代碼改造一下,變成兩個入口。

let vm = new Vue({
    el:'.app',
    data:{ msg: 'Hi boy' }
})

<body>
    <div class='app'>{{msg}}</div>
    <div class='app'>{{msg}}</div>
</body>

  這時候會發現只有第一個div被渲染出來,而第二個div還是原封不動。我們簡單來看一下Vue的源碼是如何實現的

Vue.prototype.$mount = function (
  el?: string | Element,
  hydrating?: boolean
): Component {
  el = el && query(el)
  /* istanbul ignore if */
  if (el === document.body || el === document.documentElement) {
    process.env.NODE_ENV !== 'production' && warn(
      `Do not mount Vue to <html> or <body> - mount to normal elements instead.`
    )
    return this
  }
  //以下省略無關代碼
  //...
}

  可以看到掛載函數傳了一個el參數,這個參數可以是string類型,也可以是一個element元素,也就是dom節點。最重要的是el = el && query(el)這一行代碼,那就繼續看一下query函數是做什么的:

/**
 * Query an element selector if it's not an element already.
 */
export function query (el: string | Element): Element {
  if (typeof el === 'string') {
    const selected = document.querySelector(el)
    if (!selected) {
      process.env.NODE_ENV !== 'production' && warn(
        'Cannot find element: ' + el
      )
      return document.createElement('div')
    }
    return selected
  } else {
    return el
  }
}

  首先query函數判斷是否是string類型,如果是string類型,就通過querySelector函數獲取頁面中的元素,但是querySelector僅僅返回匹配指定選擇器的第一個元素,所以這就解釋了為什么第二個div會原封不動。

  Vue其實並不知道哪一個才是我們的入口,因為對於一個入口來講,這個入口就是一個Vue類,Vue需要把這個入口里面的所有東西拿來渲染、處理,最后再重新插入到dom中。如果同時設置了多個入口,那么vue就不知道哪一個才是這個

單文件組件

  當我們在vue-cli腳手架搭建的vue開發環境下使用單文件組件時,一般會這么寫:

<template>
  <div class="box">
    這里是頁面內容
  </div>
</template>

  如果我們嘗試在template標簽下寫兩個div,那么編輯器會提示我們The template root requires exactly one element。那這里為什么template下也必須有且只能有一個div呢?

這里我們要先看一看template這個標簽,這個標簽是HTML5出來的新標簽,它有三個特性:

  1. 隱藏性:該標簽不會顯示在頁面的任何地方,即便里面有多少內容,它永遠都是隱藏的狀態;
  2. 任意性:該標簽可以寫在頁面的任何地方,甚至是head、body、sciprt標簽內;
  3. 無效性:該標簽里的任何HTML內容都是無效的,不會起任何作用;

但是我們可以通過innerHTML來獲取到里面的內容。

知道了這個,我們再來看.vue的單文件組件。其實本質上,一個單文件組件會被各種各樣的loader處理成為.js文件(因為當你import一個單文件組件並打印出來的時候,是一個vue實例),通過template的任意性我們知道,template包裹的HTML可以寫在任何地方,那么對於一個.vue來講,這個template里面的內容就是會被vue處理為虛擬dom並渲染的內容,導致結果又回到了開始 :既然一個.vue單文件組件是一個vue實例,那么這個實例的入口在哪里?

如果在template下有多個div,那么該如何指定這個vue實例的根入口?
為了讓組件能夠正常的生成一個vue實例,那么這個div會被自然的處理成程序的入口。

通過這個‘根節點’,來遞歸遍歷整個vue‘樹’下的所有節點,並處理為vdom,最后再渲染成真正的HTML,插入在正確的位置。

 

轉載自:https://zhuanlan.zhihu.com/p/111691226

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM