擴(kuò)展:深入Vue2.x的虛擬DOM diff原理
第一種渲染
1. state 數(shù)據(jù)
2. JSX 模板
3. 數(shù)據(jù) + 模板 結(jié)合 荷憋, 生成真實(shí)的DOM辙纬,來顯示
4. state 發(fā)生改變
5. 數(shù)據(jù) + 模板 結(jié)合 衡楞, 生成真實(shí)的DOM,替換原始的DOM
缺陷:
第一次生成了一個完整的DOM片段
第二次生成了一個完整的DOM片段
第二次的DOM替換第一次的DOM,非常耗性能
第二種渲染
1. state 數(shù)據(jù)
2. JSX 模板
3. 數(shù)據(jù) + 模板 結(jié)合 月劈, 生成真實(shí)的DOM呜袁,來顯示
4. state 發(fā)生改變
*****************************
5. 數(shù)據(jù) + 模板 結(jié)合 敌买, 生成真實(shí)的DOM,并不直接替換原始的DOM
6. 新的DOM(DoucumentFragment(文檔碎片)) 和原始的DOM 做比對阶界, 找差異
7. 找出input 框發(fā)生了變化
8. 只用新的DOM 中的input元素虹钮,替換掉老的DOM中的input元素
注:DoucumentFragment(文檔碎片):還在內(nèi)存里,還沒有掛載到頁面
缺陷:
性能的提升并不明顯
第三種渲染
1. state 數(shù)據(jù)
2. JSX 模板
3. 數(shù)據(jù) + 模板 結(jié)合 膘融, 生成真實(shí)的DOM芙粱,來顯示
<div id='abc'> <span> hello world </span> </div>
**********************
4. 生成虛擬DOM(虛擬DOM就是一個JS對象,用它來描述真實(shí)DOM)(損耗了性能)
['div',{id:'abc},['span',{},'hello world']]
5. state 發(fā)生變化
6. 數(shù)據(jù)+ 模板生成新的虛擬DOM (極大的提升了性能)
['div',{id:'abc},['span',{},'bye bye']]
7. 比較原始虛擬DOM 和新的虛擬DOM的區(qū)別氧映,找到區(qū)別是span中內(nèi)容 (極大的提升了性能)
8. 直接操作DOM 宅倒, 改變span中的內(nèi)容
損耗比較:JS創(chuàng)建一個JS對象,遠(yuǎn)比JS創(chuàng)建一個DOM元素?fù)p耗要小屯耸,因?yàn)镴S創(chuàng)建DOM的時(shí)候需要調(diào)用web application級別的一個api
引入虛擬DOM能提高性能原理:減少了真實(shí)DOM 的創(chuàng)建和JS DOM的對比拐迁,而是去創(chuàng)建JS對象和對比JS對象
實(shí)際上:
1. state 數(shù)據(jù)
2. JSX 模板
3. 數(shù)據(jù) + 模板 , 生成虛擬DOM(虛擬DOM就是一個JS對象,用它來描述真實(shí)DOM)(損耗了性能)
['div',{id:'abc},['span',{},'hello world']]
4. 用虛擬DOM的結(jié)構(gòu)生成真實(shí)的DOM 疗绣, 來顯示
<div id='abc'> <span> hello world </span> </div>
5. state 發(fā)生變化
6. 數(shù)據(jù)+ 模板 生成新的虛擬DOM (極大的提升了性能)
['div',{id:'abc},['span',{},'bye bye']]
7. 比較原始虛擬DOM 和新的虛擬DOM的區(qū)別线召,找到區(qū)別是span中內(nèi)容 (極大的提升了性能)
8. 直接操作DOM , 改變span中的內(nèi)容
優(yōu)點(diǎn):
1.性能提升了
2. 它使得跨端應(yīng)用得以實(shí)現(xiàn)多矮。React Native
render函數(shù)中:JSX → createElement → 虛擬 DOM(JS 對象)→ 真實(shí)的 DOM
虛擬DOM的diff算法
-
setState是個異步函數(shù)缓淹,是為了提高react底層性能
React中如果在短時(shí)間內(nèi)(例如一個函數(shù)體里)連續(xù)執(zhí)行了三次setState操作,React會把三次setState合并成一次setState塔逃,只進(jìn)行一次虛擬DOM的diff比對讯壶,最后只更新一次DOM。
-
同層比對
- React的diff算法核心:同級比較湾盗,一比較一層伏蚊,有差異整體替換。diff算從虛擬DOM樹的根節(jié)點(diǎn)進(jìn)行比較格粪,如果根節(jié)點(diǎn)就存在差異躏吊,就不會再比較下層節(jié)點(diǎn)氛改,則會把原虛擬DOM該層節(jié)點(diǎn)下的DOM全部刪除,用新的虛擬DOM重新生成一遍頁面上綁定的虛擬DOM比伏,再用頁面綁定的虛擬DOM重新生成頁面上的真實(shí)DOM胜卤。
- diff算法,同層比對赁项,算法簡單葛躏,則比對速度很快。所以雖然會浪費(fèi)DOM渲染的性能悠菜,但是它可以大大減少了兩個虛擬DOM比對上花費(fèi)的性能消耗紫新。
-
diff過程
-
key值的作用
- 例子:一個虛擬DOM上的數(shù)組結(jié)構(gòu)做循環(huán),渲染到了頁面上李剖。在沒有key值時(shí),數(shù)據(jù)發(fā)生變化后囤耳,diff算法沒法確定新虛擬DOM和原虛擬DOM元素間的關(guān)系篙顺,好比新的a變了,對應(yīng)老的a到底是數(shù)組中的哪個元素呢充择?diff算法只能寫雙循環(huán)遍歷確定德玫,這樣就會造成很大的性能開銷。而假如椎麦,在對虛擬DOM數(shù)組做循環(huán)時(shí)給每個最外層父節(jié)點(diǎn)設(shè)置key值取名字后宰僧,新a變化后立馬就能抓到對應(yīng)老a來對比,好的观挎,你變化琴儿,那么把a(bǔ)這個節(jié)點(diǎn)樹整顆替換即可,再例如數(shù)組push了新元素z嘁捷,diff算法一看key造成,原來沒它,就生成新DOM樹即可雄嚣。
- 實(shí)例:循環(huán)是用index做key值晒屎。例如
['a','b', 'c']
,循環(huán)拿index做key值缓升,當(dāng)我們刪除a時(shí)鼓鲁,新數(shù)組對應(yīng)的就是['b':0, 'c': 1]
,好的diff算法一上來看到key值港谊,怎么“全變了”骇吭,好吧,沒辦法歧寺,只能去重新這棵節(jié)點(diǎn)數(shù)绵跷!而當(dāng)我們用穩(wěn)定的唯一值做key值時(shí)膘螟,刪除a元素后,diff算法比較后只會把dom樹上的a節(jié)點(diǎn)刪除碾局,節(jié)省了渲染的性能荆残。