Javascript 虛擬 DOM詳解
什麼是虛擬 dom?
虛擬 dom 本質上就是一個普通的JS對象(mounted 中打印 this. _vnode 就是該對象內容),用於描述視圖的界面結構
在vue中,每個組件都有一個render函數,每個render函數都會返回一個虛擬dom樹,這也就意味著每個組件都對應一棵虛擬DOM樹
vnode 是一個普通的 JS 對象,用於描述界面上應該有什麼,比如:
var vnode = { tag: "h1", children: [ { tag: undefined, text: "第一個vue應用:Hello World"} ] }
上面的對象描述瞭:
有一個標簽名為 h1 的節點,它有一個子節點,該子節點是一個文本,內容為「第一個vue應用:Hello World」
為什麼需要虛擬dom?
在vue中,渲染視圖會調用render函數,這種渲染不僅發生在組件創建時,同時發生在視圖依賴的數據更新時。如果在渲染時,直接使用真實DOM,由於真實DOM的創建、更新、插入等操作會帶來大量的性能損耗,從而就會極大的降低渲染效率。
因此,vue在渲染時,使用虛擬dom來替代真實dom,主要為解決渲染效率的問題。
對比創建js對象和真實 dom 對象效率:
結果:
創建一個真實的 dom 會伴隨著創建許多的屬性
虛擬dom是如何轉換為真實dom的?
在一個組件實例首次被渲染時,它先生成虛擬dom樹,然後根據虛擬dom樹創建真實dom,並把真實dom掛載到頁面中合適的位置,此時,每個虛擬dom便會對應一個真實的dom。如果頁面隻會刷新一次,後續不會有數據更新等問題的情況下,用虛擬 dom 的方式是比直接顯示真實 dom 效率低的。
如果一個組件受響應式數據變化的影響,需要重新渲染時,它仍然會重新調用render函數,創建出一個新的虛擬dom樹,用新樹和舊樹對比,通過對比,找出差異,然後僅更新差異部分的虛擬dom節點,最後,這些更新過的虛擬節點,會去修改它們對應的真實dom
這樣一來,就保證瞭對真實dom達到最小的改動。
模板和虛擬dom的關系
vue框架中有一個compile(編譯)模塊,它主要負責將模板轉換為render函數,而render函數調用後將得到虛擬dom。
編譯的過程分兩步:
1.將模板字符串轉換成為AST(抽象語法樹:用js樹形結構來描述我們原始的代碼;在線工具:https://astexplorer.net/)
2.將AST轉換為render函數
vue 模板並不是真實的 DOM,它會被編譯為虛擬 DOM
<div id="app"> <h1>第一個vue應用:{{title}}</h1> <p>作者:{{author}}</p> </div>
上面的模板會被編譯為類似下面結構的虛擬 DOM
{ tag: "div", children: [ { tag: "h1", children: [ { text: "第一個vue應用:Hello World" } ] }, { tag: "p", children: [ { text: "作者:袁" } ] } ] }
如果使用傳統的引入方式(script src="...vue.js
“),則編譯時間發生在組件第一次加載時,這稱之為運行時編譯。
如果是在vue-cli
的默認配置下,編譯發生在打包時(npm run build
),打包之後就沒有模板隻有 render 函數瞭,這稱之為模板預編譯。
編譯是一個極其耗費性能的操作,預編譯可以有效的提高運行時的性能,而且,由於運行的時候已不需要編譯,vue-cli在打包時會排除掉vue中的compile模塊,以減少打包體積
打包時是否需要包含 compile 模塊,是通過 vue.config.js 中的 runtimeCompiler: true 來控制的,默認 false,不包含。不建議更改該配置
模板的存在,僅僅是為瞭讓開發人員更加方便的書寫界面代碼
vue最終運行的時候,最終需要的是render函數,而不是模板,因此,模板中的各種語法,在虛擬dom中都是不存在的,它們都會變成虛擬dom的配置
在 vue-cli 中如果同時存在 template 和 render, 由於存在一個打包過程,其中的模板預編譯會生成 render 覆蓋原有的 render 函數
在 vue 中如果同時存在 template 和 render,一定是以 render 為準
虛擬 DOM 樹會最終生成為真實的 DOM 樹
vue通過以下邏輯生成vnode tree:
註意:虛擬節點樹必須是單根的
註入
vue會將以下配置註入到vue實例:
data
:和界面相關的數據computed
:通過已有數據計算得來的數據,將來詳細講解methods
:方法
模板中可以使用vue實例中的成員
為瞭防止名稱沖突。因為會將data中數據代理給vue,假如說我們自己寫的data名稱和vue中自帶的屬性沖突瞭,那麼就會覆蓋vue內部的屬性,所以vue會把自己內部的屬性成員名稱前加上 或 , 如 果 加 上 的 是 或_,如果加上的是 或,如果加上的是,代表是我們可以使用的,如果加上的是_,是vue自己內部使用的方法或屬性,我們不需要調用
掛載
將生成的真實 DOM 樹,放置到某個元素位置,稱之為掛載
掛載的方式:
1.通過el:”css選擇器”進行配置
2.通過vue實例.$mount(“css選擇器”)進行配置
完整流程
- 實例被創建: new Vue()
- 註入完成之後才會有響應式,能監聽到數據變化
- 編譯生成虛擬 DOM 樹:首先找 render 函數,沒有就找模板把它生成 render,最後運行 render,生成虛擬 DOM 樹
- 掛載完成:頁面上顯示
總結
本篇文章就到這裡瞭,希望能夠給你帶來幫助,也希望您能夠多多關註WalkonNet的更多內容!