Javascript 虛擬 DOM詳解
什么是虛擬 dom?
虛擬 dom 本質(zhì)上就是一個(gè)普通的JS對象(mounted 中打印 this. _vnode 就是該對象內(nèi)容),用于描述視圖的界面結(jié)構(gòu)
在vue中,每個(gè)組件都有一個(gè)render函數(shù),每個(gè)render函數(shù)都會(huì)返回一個(gè)虛擬dom樹,這也就意味著每個(gè)組件都對應(yīng)一棵虛擬DOM樹
vnode 是一個(gè)普通的 JS 對象,用于描述界面上應(yīng)該有什么,比如:
var vnode = { tag: "h1", children: [ { tag: undefined, text: "第一個(gè)vue應(yīng)用:Hello World"} ] }
上面的對象描述了:
有一個(gè)標(biāo)簽名為 h1 的節(jié)點(diǎn),它有一個(gè)子節(jié)點(diǎn),該子節(jié)點(diǎn)是一個(gè)文本,內(nèi)容為「第一個(gè)vue應(yīng)用:Hello World」
為什么需要虛擬dom?
在vue中,渲染視圖會(huì)調(diào)用render函數(shù),這種渲染不僅發(fā)生在組件創(chuàng)建時(shí),同時(shí)發(fā)生在視圖依賴的數(shù)據(jù)更新時(shí)。如果在渲染時(shí),直接使用真實(shí)DOM,由于真實(shí)DOM的創(chuàng)建、更新、插入等操作會(huì)帶來大量的性能損耗,從而就會(huì)極大的降低渲染效率。
因此,vue在渲染時(shí),使用虛擬dom來替代真實(shí)dom,主要為解決渲染效率的問題。
對比創(chuàng)建js對象和真實(shí) dom 對象效率:
結(jié)果:
創(chuàng)建一個(gè)真實(shí)的 dom 會(huì)伴隨著創(chuàng)建許多的屬性
虛擬dom是如何轉(zhuǎn)換為真實(shí)dom的?
在一個(gè)組件實(shí)例首次被渲染時(shí),它先生成虛擬dom樹,然后根據(jù)虛擬dom樹創(chuàng)建真實(shí)dom,并把真實(shí)dom掛載到頁面中合適的位置,此時(shí),每個(gè)虛擬dom便會(huì)對應(yīng)一個(gè)真實(shí)的dom。如果頁面只會(huì)刷新一次,后續(xù)不會(huì)有數(shù)據(jù)更新等問題的情況下,用虛擬 dom 的方式是比直接顯示真實(shí) dom 效率低的。
如果一個(gè)組件受響應(yīng)式數(shù)據(jù)變化的影響,需要重新渲染時(shí),它仍然會(huì)重新調(diào)用render函數(shù),創(chuàng)建出一個(gè)新的虛擬dom樹,用新樹和舊樹對比,通過對比,找出差異,然后僅更新差異部分的虛擬dom節(jié)點(diǎn),最后,這些更新過的虛擬節(jié)點(diǎn),會(huì)去修改它們對應(yīng)的真實(shí)dom
這樣一來,就保證了對真實(shí)dom達(dá)到最小的改動(dòng)。
模板和虛擬dom的關(guān)系
vue框架中有一個(gè)compile(編譯)模塊,它主要負(fù)責(zé)將模板轉(zhuǎn)換為render函數(shù),而render函數(shù)調(diào)用后將得到虛擬dom。
編譯的過程分兩步:
1.將模板字符串轉(zhuǎn)換成為AST(抽象語法樹:用js樹形結(jié)構(gòu)來描述我們原始的代碼;在線工具:https://astexplorer.net/)
2.將AST轉(zhuǎn)換為render函數(shù)
vue 模板并不是真實(shí)的 DOM,它會(huì)被編譯為虛擬 DOM
<div id="app"> <h1>第一個(gè)vue應(yīng)用:{{title}}</h1> <p>作者:{{author}}</p> </div>
上面的模板會(huì)被編譯為類似下面結(jié)構(gòu)的虛擬 DOM
{ tag: "div", children: [ { tag: "h1", children: [ { text: "第一個(gè)vue應(yīng)用:Hello World" } ] }, { tag: "p", children: [ { text: "作者:袁" } ] } ] }
如果使用傳統(tǒng)的引入方式(script src="...vue.js
"),則編譯時(shí)間發(fā)生在組件第一次加載時(shí),這稱之為運(yùn)行時(shí)編譯。
如果是在vue-cli
的默認(rèn)配置下,編譯發(fā)生在打包時(shí)(npm run build
),打包之后就沒有模板只有 render 函數(shù)了,這稱之為模板預(yù)編譯。
編譯是一個(gè)極其耗費(fèi)性能的操作,預(yù)編譯可以有效的提高運(yùn)行時(shí)的性能,而且,由于運(yùn)行的時(shí)候已不需要編譯,vue-cli在打包時(shí)會(huì)排除掉vue中的compile模塊,以減少打包體積
打包時(shí)是否需要包含?compile?模塊,是通過?vue.config.js?中的?runtimeCompiler: true?來控制的,默認(rèn) false,不包含。不建議更改該配置
模板的存在,僅僅是為了讓開發(fā)人員更加方便的書寫界面代碼
vue最終運(yùn)行的時(shí)候,最終需要的是render函數(shù),而不是模板,因此,模板中的各種語法,在虛擬dom中都是不存在的,它們都會(huì)變成虛擬dom的配置
在?vue-cli?中如果同時(shí)存在?template?和?render, 由于存在一個(gè)打包過程,其中的模板預(yù)編譯會(huì)生成?render?覆蓋原有的?render?函數(shù)
在 vue 中如果同時(shí)存在?template?和?render,一定是以?render?為準(zhǔn)
虛擬 DOM 樹會(huì)最終生成為真實(shí)的 DOM 樹
vue通過以下邏輯生成vnode tree:
注意:虛擬節(jié)點(diǎn)樹必須是單根的
注入
vue會(huì)將以下配置注入到vue實(shí)例:
data
:和界面相關(guān)的數(shù)據(jù)computed
:通過已有數(shù)據(jù)計(jì)算得來的數(shù)據(jù),將來詳細(xì)講解methods
:方法
模板中可以使用vue實(shí)例中的成員
為了防止名稱沖突。因?yàn)闀?huì)將data中數(shù)據(jù)代理給vue,假如說我們自己寫的data名稱和vue中自帶的屬性沖突了,那么就會(huì)覆蓋vue內(nèi)部的屬性,所以vue會(huì)把自己內(nèi)部的屬性成員名稱前加上 或 , 如 果 加 上 的 是 或_,如果加上的是 或,?如果加上的是,代表是我們可以使用的,如果加上的是_,是vue自己內(nèi)部使用的方法或?qū)傩?,我們不需要調(diào)用
掛載
將生成的真實(shí) DOM 樹,放置到某個(gè)元素位置,稱之為掛載
掛載的方式:
1.通過el:"css選擇器"進(jìn)行配置
2.通過vue實(shí)例.$mount(“css選擇器”)進(jìn)行配置
完整流程
- 實(shí)例被創(chuàng)建: new Vue()
- 注入完成之后才會(huì)有響應(yīng)式,能監(jiān)聽到數(shù)據(jù)變化
- 編譯生成虛擬 DOM 樹:首先找 render 函數(shù),沒有就找模板把它生成 render,最后運(yùn)行 render,生成虛擬 DOM 樹
- 掛載完成:頁面上顯示
總結(jié)
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
layui問題之模擬table表格中的選中按鈕選中事件的方法
今天小編就為大家分享一篇layui問題之模擬table表格中的選中按鈕選中事件的方法,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2019-09-09微信小程序點(diǎn)擊列表跳轉(zhuǎn)到對應(yīng)詳情頁過程解析
這篇文章主要介紹了微信小程序點(diǎn)擊列表跳轉(zhuǎn)到對應(yīng)詳情頁過程解析,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-09-09JavaScript組成、引入、輸出、運(yùn)算符基礎(chǔ)知識講解
JavaScript 被數(shù)百萬計(jì)的網(wǎng)頁用來改進(jìn)設(shè)計(jì)、驗(yàn)證表單、檢測瀏覽器、創(chuàng)建cookies,以及更多的應(yīng)用。這篇文章主要介紹了JavaScript組成、引入、輸出、運(yùn)算符基礎(chǔ)知識講解,需要的朋友可以參考下2016-12-12javascript 不停(setInterval)/延時(shí)(setTimeout)函數(shù)使用實(shí)例
如果想實(shí)現(xiàn)頁面的一些函數(shù)的定時(shí)執(zhí)行,需要用到下面的函數(shù)大家可以看下用法。2009-08-08JavaScript實(shí)現(xiàn)京東快遞單號查詢
這篇文章主要為大家詳細(xì)介紹了JavaScript實(shí)現(xiàn)京東快遞單號查詢,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2020-11-11JS實(shí)現(xiàn)的隨機(jī)排序功能算法示例
這篇文章主要介紹了JS實(shí)現(xiàn)的隨機(jī)排序功能算法,結(jié)合具體實(shí)例形式分析了javascript常用的排序算法實(shí)現(xiàn)技巧,需要的朋友可以參考下2017-06-06