前言:
React基于兩個假設(shè):
1、兩個相同的組件產(chǎn)生類似的DOM結(jié)構(gòu),不同組件產(chǎn)生不同DOM結(jié)構(gòu)。
2、對于同一層次的一組子節(jié)點(diǎn),它們可以通過唯一的id區(qū)分。
舉個例子:(創(chuàng)建100個li)
a. 傳統(tǒng)js優(yōu)化后的寫法:循環(huán)拼接100個,然后一次性觸發(fā)dom操作。
var str = '';
for (var i = 0; i < 100; i++) {
str+='<li>'+i+'</li>';
};
document.getElementById('ul1').appendChild(str);
b. react通過深度優(yōu)先遍歷(DFS),循環(huán)創(chuàng)建createElement,createTextNode,搞毛呀這豈不是比上一種方法慢多了,所以才要引出我們的主角diff算法。
// 核心代碼
Element.prototype.render = function(){
var el = document.createElement(this.tagName),
props = this.props,
propName,
propValue;
for(propName in props){
propValue = props[propName];
el.setAttribute(propName, propValue);
}
this.children.forEach(function(child){
var childEl = null;
if(child instanceof Element){
childEl = child.render();
}else{
childEl = document.createTextNode(child);
}
el.appendChild(childEl);
});
return el;
};
那為什么還用虛擬dom呢?
1)虛擬dom + diff提高渲染速度(其實(shí)瀏覽器也不傻,他會等一段時間再統(tǒng)一處理dom,所以虛擬dom多快,主要取決瀏覽器牛不牛逼,越牛逼虛擬dom+diff算法優(yōu)勢越?。?br> 2)我們終于可以解脫操作dom的時代了,我們可以直接用數(shù)據(jù)控制渲染我們的頁面,開發(fā)效率大大提高。
2)現(xiàn)在我們可以控制頁面是否渲染,比如shouldcomponentupdate(想讓他渲染就渲染,多happy)。
主角diff算法
1. 介紹diff算法前,我們對比傳統(tǒng)算法和diff算法。
傳統(tǒng)算法:通過循環(huán)遞歸對節(jié)點(diǎn)進(jìn)行依次對比。
diff算法:比較真實(shí)dom與虛擬dom,得出差異dom patch,只更新改變的部分。
2. diff原則
一:對比當(dāng)前真實(shí)的DOM和虛擬DOM,在對比過程中直接更新真實(shí)DOM。
二:只對比同一層級的變化實(shí)現(xiàn)。
3. 實(shí)現(xiàn):
有的框架會選擇保存上次渲染的虛擬DOM,然后對比虛擬DOM前后的變化;我們選選擇直接對比虛擬DOM和真實(shí)DOM,一邊對比一邊更新;
虛擬DOM的結(jié)構(gòu)可以分為三種,分別表示文本、原生DOM節(jié)點(diǎn)以及組件。
// 原生DOM節(jié)點(diǎn)的vnode
{
tag: 'div',
attrs: {
className: 'container'
},
children: []
}
// 文本節(jié)點(diǎn)的vnode
"hello,world"
// 組件的vnode
{
tag: ComponentConstrucotr,
attrs: {
className: 'container'
},
children: []
}
對比類型:
1)對比文本節(jié)點(diǎn):如果當(dāng)前的DOM就是文本節(jié)點(diǎn),則直接更新內(nèi)容,否則就新建一個文本節(jié)點(diǎn),并移除掉原來的DOM("hello p" => "hello ge p")。
2)對比元素節(jié)點(diǎn):新舊節(jié)點(diǎn)不同就是,增新刪舊(<div> => <p>);只新增;只刪除。
3)對比屬性節(jié)點(diǎn):舊的屬性刪掉,新的屬性添加(<img src='' id=''/> => <img src='' className=''/>)。
4)對比子節(jié)點(diǎn):舊的屬性刪掉,新的屬性添加(<img src='' id=''/> => <img src='' className=''/>)。
????4.1 為啥對比子節(jié)點(diǎn)?子節(jié)點(diǎn)是一個數(shù)組,它們可能改變了順序,或者?數(shù)量有所變化,我們很難確定要和虛擬DOM對比的是哪一個。
????4.2 給節(jié)點(diǎn)設(shè)一個key值,重新渲染時對比key值相同的節(jié)點(diǎn)。新增dom.appendChild(child);刪除removeNode( f );插入dom.insertBefore( child, f )
5)對比組件:
????5.1 如果組件類型沒有變化,則重新set props。(<AComponent props={oldProps} /> => <AComponent props={newProps} />)
????5.2 如果組件類型變化,則移除掉原來組件,并渲染新的組件。(<AComponent /> => <BComponent />)