當你用vue-cli建立一個工程後,會看到index-html文件裏有一個div,id叫appjavascript
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1.0">
<link rel="icon" href="<%= BASE_URL %>favicon.ico">
<title></title>
</head>
<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">app</div>
<!-- built files will be auto injected -->
</body>
</html>
複製代碼
此時App.vue文件中也有一個div,id也叫apphtml
<template>
<div id="app">
hello world!
</div>
</template>
複製代碼
可是根據規定,id應該具備惟一性,那麼問題來了,咱們在main.js裏面掛載的那個#app是哪一個?最終渲染到頁面上的那個#app又是哪個呢?vue
new Vue({
router,
render: (h: any) => h(App),
}).$mount('#app');
複製代碼
想要知道這些問題的答案,咱們能夠作一個簡單的實驗,好比把這兩個文件中的某一個id改掉,看一下頁面中最終渲染的是哪一個,看一下哪一個文件中的id改變會致使執行$mount('#app')報錯,能夠很輕鬆的得出結論。java
可是本着知其然,知其因此然的態度,咱們打開源碼驗證一下猜測node
爲了方便查找到咱們要看的源碼,因此選擇在瀏覽器打斷點調試。那麼斷點打在哪裏比較合適呢,有兩種思路算法
function removeNode (el) {
const parent = nodeOps.parentNode(el)
// element may have already been removed due to v-html / v-text
if (isDef(parent)) {
nodeOps.removeChild(parent, el)
}
}
複製代碼
咱們將鼠標懸浮在入參el上,能夠看到,要被移除的元素是index.html裏的#app,並且此時App組件中的內容已經渲染到了頁面上,因此另外一個#app很大機率是被append到body裏面的,若是想分析另外一個元素掛載過程,一樣能夠在patch中找到相應的方法打上斷點直接調試 vue-cli
經過調用堆棧,咱們追到了patch方法,這個方法就是大名鼎鼎的diff算法,能夠看到,在調用patch的地方,index中的#app原生dom節點做爲oldNode傳入,vnode則使用已經處理過的App中的#app虛擬dom瀏覽器
接下來,會把老的原生dom節點包裝成虛擬dom,而且建立一個和index的#app相鄰的姊妹元素,用來掛載新的虛擬dom 能夠看到,在這個特殊的時間,頁面中會同時存在兩個#app 至此,oldVnode就完成了使命,準備移除講到這裏,結論基本上已經出來了,那就是在index.html和App.vue中存在兩個#app,通過$mount掛載後,最終渲染在body中的是App.vue中的那個#app,index.html中的#app則會被移除。app
不知道你們有沒有想過做者爲何要這麼設計呢?乍一看有點畫蛇添足的感受。我說一下個人想法。dom
另外,有了完整的調用堆棧信息,想要深挖$mount過程就很容易了