react原理三( diff算法)

前言:

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 />)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容