一、在沒有vue
和react
以前,咱們都使用的是原生的javascript
或jq
,咱們都是經過操做dom
,來達到視圖更新的效果。(操做dom->視圖更新)
javascript
二、而在vue
中,咱們只需更改data
中的數據,視圖就會更新,因此在vue
中,簡單說就是數據改變,讓視圖更新。vue
可咱們有沒有想過爲何數據改變,視圖就更新呢?其實vue
更新視圖,也仍是操做了dom
,只不過是在vue
或react
框架內部來完成的。(數據改變->操做dom->視圖更新)java
但這就引出了一個問題,難道vue
每次數據更改,都會操做dom
呢,顯然這不是最優解,因此就推出了虛擬dom
這個東西。node
什麼是虛擬dom
?虛擬dom
就是用js
來模擬dom
結構。react
舉個栗子:git
<div id="app">
<h1>認識虛擬dom</h1>
<ul>
<li>1</li>
<li>2</li>
<li>3</li>
</ul>
</div>
複製代碼
以上例子中的dom結構,若是用js來表達是什麼樣的呢?github
用js來表達dom,他會先聲明一個對象,而後有三個屬性:面試
tag : 元素;
props: 屬性或者事件;
children: 子節點或者內容;
{ tag: 'div',
props: { id: 'app', className: 'container' },
children: [
{ tag: 'h1', children: '認識虛擬dom' },
{ tag: 'ul', props: {}, children: [
{ tag: 'li', props: '1', },
{ tag: 'li', props: '2', },
{ tag: 'li', props: '3', },
]
}
]
}
複製代碼
這樣一來,咱們就用js來表達了dom結構。算法
那咱們要怎麼樣用虛擬dom來計算咱們的變動,來操做真實的dom呢?數組
扯了這麼多,其實就是爲了讓咱們知道虛擬dom,那到底虛擬dom有什麼好處呢?它好在什麼地方呢?彆着急,這就來。
在這以前,先介紹一個東西。Snabbdom
,vue
裏面的虛擬dom就是藉助它來完成的
在官方github
上,咱們能夠看到他下面的例子
主要的目的,就是經過以前那個js對象結構來表示頁面的一些節點,來插入到他的空的容器裏。
var container = document.getElementById('container')
複製代碼
他裏面是用vnode
這個變量來表示的,那個vnode
的結構,就是以前所說的js dom
結構。而後表示出來的節點以後,會經過一個叫Patch
的方法,將虛擬節點vnode塞到某一個容器裏。
咱們能夠照着它這個來寫一個小demo,認識snabbdom
和vnode
的好處:
一、先引入snabbdom
這個庫
const snabbdom = window.snabbdom;
複製代碼
在這以前,偷了一下懶,直接cnd
引入了
<script src="https://lib.baomitu.com/snabbdom/0.7.4/h.js"></script>
<script src="https://lib.baomitu.com/snabbdom/0.7.4/snabbdom-class.js"></script>
<script src="https://lib.baomitu.com/snabbdom/0.7.4/snabbdom-eventlisteners.js"></script>
<script src="https://lib.baomitu.com/snabbdom/0.7.4/snabbdom-props.js"></script>
<script src="https://lib.baomitu.com/snabbdom/0.7.4/snabbdom-style.js"></script>
<script src="https://lib.baomitu.com/snabbdom/0.7.4/snabbdom.js"></script>
複製代碼
二、將vnode(虛擬節點)塞到容器中
const patch = snabbdom.init({ classModule, propsModule, styleModule, eventListenersModule,})
複製代碼
三、建立vnode,也就是虛擬dom,(引入h函數,經過h函數來建立)
const h = snabbdom.h
複製代碼
四、在頁面上聲明一個空的容器,而後在js選中這個容器
<div id="container"></div>//一個空的容器
const container = document.getElementById('container')
複製代碼
五、建立vnode
let vnode = h("ul#list", {}, [ h('li.item', {}, '第一個'), h('li.item', {}, '第二個'), h('li.item', {}, '第三個'),])
複製代碼
六、使用patch函數,將vnode塞到容器中,第一個參數:容器。第二個參數:虛擬節點
patch(container, vnode)
複製代碼
七、能夠看到已經將那幾個元素插入空的container裏了
若是我須要點擊一個按鈕,來改變第二個的內容,須要怎麼作呢?
八、先在頁面上添加一個button
,而後js選中他,添加click監聽事件
<button id="btn">點擊</button>const btn = document.getElementById('btn')btn.addEventListener('click', ()=> { const newVnode = h('ul#list', {}, [ h('li.item', {}, '第一個'), h('li.item', {}, '第二個,我變了'), h('li.item', {}, '第三個'), h('li.item', {}, '第四個'), ]) patch(vnode, newVnode) vnode = newVnode})
複製代碼
值得注意的是,點擊以後,需傳入一個新的vnode
,而後在建立完vnode
以後,再執行patch函數,用新的vnode
來更新老的vnode
,同時將新的vnode
賦值vnode
,方便下次用最新結果比較。
點擊以前:
點擊以後:
能夠看到第二個和第四個已經變了,由於第一個和第三個是沒變了,因此他並無從新渲染那兩個節點。
結論:因此到這咱們就知道了,虛擬dom會去計算你有沒有變化,從而去決定要不要去操做你那個dom
以前說過,虛擬dom
就是計算節點是否變動修改,來判斷是否操做dom
元素來更新視圖。
那麼這個計算,就是用的diff
算法計算的。因此diff
算法是虛擬dom
的核心。
咱們用虛擬dom來表述真實的dom,這樣作的目的,就是爲了計算最小的變化,根據這個最小的變化,來更新真實的dom結構。
如上圖,有兩個虛擬dom,咱們既要用diff算法,來比較更新咱們的虛擬dom。那麼它會怎麼作呢?
遍歷舊的虛擬dom
遍歷新的虛擬dom
從新排序
好比上圖,有一個改變,和一個新增,那麼他會根據這個變化,來從新編排這個虛擬dom。
but……,沒錯,可惡的可是來了,若是咱們只是爲了改一小部分,但是節點數又很是多,有1000個節點,那麼他就會計算1000^3,也就是10億次,顯然這不是咱們的初衷也是不可接受的。
因此vue
和react
內部裏作diff
算法的時候,內部是作過優化的,咱們能夠來see see他們是怎麼作的優化。
本來的diff
算法比較,是先遍歷舊的vnode
,而後遍歷新的vnode
,而後對比,最後再從新編排。
而vue
和react
的是,
只比較同一層級,不作跨級比較。
比較標籤名,若是標籤名不一樣,直接刪除,不會繼續深度比較。
標籤名相同,key相同,就認爲是相同節點,不繼續深度比較。(因此你們知道咱們寫v-for循環的時候,爲啥提示要加上key了吧)
因此經過以上的步驟,vue將diff算法給優化了一波
如今若是有1000個節點,就只需計算1000次了,簡直nice。以下圖:
咱們能夠看到snabbdom/src/package/h.ts
咱們是經過h函數來生產vnode的,能夠看到這個h方法,能接受好幾種狀況的傳參,
再看github上官方的栗子:
能夠看到,這裏傳了sel
data
children
,最後經過
return vnode(sel, data, children, text, undefined)
複製代碼
來返回。咱們執行這個h函數後,而後他往vnode裏面傳遞參數,包括sel, data, children, text, undefined
,咱們能夠進一步看看vnode
函數的代碼:路徑:snabbdom/src/package/vnode.ts
能夠看到這個vnode
函數,接受了一堆參數,那堆參數就是上面傳遞進來的參數。
最終,這個函數會返回一個對象,這個對象就是表示節點的對象。
咱們來看下這個節點對象,須要的組成部分:
sel
: 選擇器,好比div
data
: 好比style
onClick
children
: children
和text
只能有一個,要麼是children
數組包含子元素,要麼text
字符串內容
elm
: 對應的真實的dom
元素,好比將new vnode
替換掉old vnode
,那個old vnode
就是elm
key
: v-for
所需聲明的那個屬性
執行完後,會將這些參數用對象的形式返回回去,返回到h函數那裏,因此咱們執行了h函數以後,就會生成對應的vnode
結構,而後咱們就把vnode
結構,保存在變量中使用。
咱們今天所學的虛擬dom和diff算法,主要圍繞snabbdom來學習虛擬dom,它主要的一些核心方法有這幾個:vnode h patch,若是你們掌握了這些,面試的時候若是有虛擬dom的問題,也應該內心有底了。還有patch函數中的diff算法,還有那個key,你們也應該知道v-for的時候爲何要有了。
最後若是你們有什麼好的建議或想法,也歡迎交流。