diff了解一下

??emm會(huì)用react斥铺,咱都知道react用virtual dom的形式解耦了視圖層級(jí)和代碼的交互操作怯邪。
??既然托管了view層次的生成次屠,react必然得提供高效的視圖生成馏予,不然如果組件之間的轉(zhuǎn)換卡頓一定是所有人都受不了的掀宋。所以呢鸣个,在渲染html方面,react整了個(gè)diff算法布朦,提升了html重新渲染的速度。
??廢話(huà)不多說(shuō)這里先推薦兩篇文章昼窗,后面寫(xiě)的東西基本都是對(duì)于這兩篇文章的思路(圖片資源也是這里扒下來(lái)的= =)是趴。
??1:從零開(kāi)始實(shí)現(xiàn)一個(gè)React(三):diff算法
??2:React 源碼剖析系列 - 不可思議的 react diff
??首先咱聲明以及明確以下幾點(diǎn)。
??1:react的diff算法對(duì)于新舊兩顆dom樹(shù)只會(huì)對(duì)同一層級(jí)的節(jié)點(diǎn)進(jìn)行比較澄惊,如果類(lèi)型不同唆途,舊的節(jié)點(diǎn)將會(huì)直接刪除重建即使是有跨層級(jí)移動(dòng)的節(jié)點(diǎn)(節(jié)點(diǎn)內(nèi)容相同富雅,但是新的節(jié)點(diǎn)深度和舊節(jié)點(diǎn)深度不同),也是直接刪了重建肛搬,而不會(huì)進(jìn)行節(jié)點(diǎn)移動(dòng)没佑。
??2:新舊兩顆樹(shù)在進(jìn)行比較之后并不會(huì)直接去更新舊樹(shù),而是生成一個(gè)個(gè)patch温赔,然后執(zhí)行方法根據(jù)patch去更新舊樹(shù)蛤奢。(在推薦的第一篇文章中由于并沒(méi)有兩顆virtual dom樹(shù),用的virtual dom直接和html節(jié)點(diǎn)比較陶贼,所以它是是直接更新了舊樹(shù))啤贩。
??3:由于react的virtual dom有多種形式,原生的html element標(biāo)簽拜秧,自定義的component痹屹,字符串,實(shí)際上的dom比較還是比較繁瑣的枉氮,為了簡(jiǎn)化代碼理解志衍,所以這里在后面的代碼中默認(rèn)都是同類(lèi)型的標(biāo)簽數(shù)組。

這里定義簡(jiǎn)單一個(gè)TreeNode

export type ReactNode = {
  tag: string;
  attrs: null | object;
  children: ReactNode[] | string[];
};

然后寫(xiě)diff方法聊替,這里由于咱默認(rèn)沒(méi)有string類(lèi)型以及component類(lèi)型的節(jié)點(diǎn)楼肪,所以我們只需要比較當(dāng)新節(jié)點(diǎn)的tag類(lèi)型和舊節(jié)點(diǎn)的tag類(lèi)型是否一致。不一樣的就刪了重新建立佃牛。

const diffNode = (preNode: ReactNode, currentNode: ReactNode) => {
  
  const preNodeT = preNode as ReactNode;
  const curNode = currentNode as ReactNode;
  // tag類(lèi)型不同時(shí)直接銷(xiāo)毀原node淹辞,創(chuàng)建新node。
  if (!preNode || preNodeT.tag.toLowerCase() !== curNode.tag.toLowerCase()) {
    if (preNodeT) {
      preNode = {
        tag: curNode.tag,
        attrs: curNode.attrs,
        children: curNode.children
      };
    }
  }

  if (
    (preNodeT.children && preNodeT.children.length > 0) ||
    (curNode.children && curNode.children.length > 0)
  ) {
//關(guān)鍵方法是這個(gè)俘侠。
    diffChildren(preNodeT, curNode.children);
  }
};

關(guān)鍵的在同級(jí)比較以及同級(jí)節(jié)點(diǎn)交換的地方象缀,我相信搜索過(guò)diff并且看過(guò)一些diff的文章的人都熟悉這么一張同級(jí)比較的圖。


diff

??如果用上述說(shuō)的不同則刪除的說(shuō)法來(lái)處理同級(jí)比較的話(huà)爷速,react diff算法就沒(méi)啥好講了央星,事實(shí)上,在同級(jí)計(jì)算中用key作為element的唯一標(biāo)識(shí)符惫东,在新舊node的key相同的一些情況下可以直接移動(dòng)節(jié)點(diǎn)而不用刪除再創(chuàng)建節(jié)點(diǎn)莉给。


const diffChildren =  (preChildren: ReactNode[], currentChildren: ReactNode[]) => {

  const preKeyArray:string[] = preChildren.map(node => {
    return node.attrs['key'];
  });

  if (currentChildren && currentChildren.length > 0) {
    //  lastIndex 作為舊樹(shù)中已經(jīng)比較過(guò)的節(jié)點(diǎn)最右的位置,當(dāng)當(dāng)前訪(fǎng)問(wèn)節(jié)點(diǎn)在舊樹(shù)中的位置比之前比較過(guò)的節(jié)點(diǎn)都大時(shí)廉沮,
    //說(shuō)明這個(gè)節(jié)點(diǎn)并不會(huì)影響其他節(jié)點(diǎn)的位置颓遏,放著就好不用操作。
    let lastIndex = 0;
    // 新樹(shù)是用來(lái)遍歷的滞时,preIndex 就是作為舊樹(shù)的地址下標(biāo)叁幢。
    let preIndex = 0;
    currentChildren.forEach((currentNode,currentIndex) => {
        const currentKey = currentNode["key"];
        // 新樹(shù)的key同時(shí)存在于舊樹(shù)
        if (preKeyArray.includes(currentKey)) {
          const preIndex = preKeyArray.indexOf(currentKey)
          // 僅當(dāng)當(dāng)前舊樹(shù)中的節(jié)點(diǎn)位置比lastIndex小的時(shí)候需要移動(dòng)節(jié)點(diǎn)
          // 因?yàn)檫@意味著這個(gè)節(jié)點(diǎn)的新位置將會(huì)破壞舊位置的順序,所以需要進(jìn)行移動(dòng)
          if (preIndex < lastIndex) {
            moveChild(preIndex,currentIndex)
          }
          lastIndex = Math.max(preIndex, lastIndex);
        }
        // 新樹(shù)的key不存在舊樹(shù)的時(shí)候
        else{
          // 舊樹(shù)同位置節(jié)點(diǎn)存在則刪除舊節(jié)點(diǎn)并且創(chuàng)建節(jié)點(diǎn)
          // 否則直接創(chuàng)建節(jié)點(diǎn)
          if (preChildren[currentIndex]) {
            lastIndex = Math.max(currentIndex, lastIndex);
            removeChild(preChildren[currentIndex])
          }
          createChild(currentNode,preIndex);
        }
        // 遍歷整棵樹(shù)坪稽。
        diffNode(preChildren[preIndex],currentChildren[currentIndex])
        preIndex++;
        
    });
// 這里注意了曼玩,在上述操作后鳞骤,你是沒(méi)法刪除舊節(jié)點(diǎn)存在而新節(jié)點(diǎn)不存的節(jié)點(diǎn)的,最后遍歷一遍刪除黍判。
// 上面放的那個(gè)文章1就忘了刪除操作豫尽。
 preChildren.forEach(item=>{
      if (!currentKeyArray.includes(item.attrs['key'])) {
        removeChild(item)
      } 
    });
  }
};

??基本的diff大概就是這樣了,雖然這里還有很多類(lèi)似與removeChild顷帖,createChild之類(lèi)的方法沒(méi)有實(shí)現(xiàn)美旧,但說(shuō)實(shí)話(huà)要完全實(shí)現(xiàn)的話(huà)還是比較復(fù)雜的,咱也不可能真就復(fù)制一份react代碼唄窟她。而且現(xiàn)在react開(kāi)始使用fiber結(jié)構(gòu)(就是說(shuō)這個(gè)方法過(guò)時(shí)了= =)陈症,上面文章2的代碼都得去react之前的release去找了...
??但是(震聲)!咱也不是白看的diff啊震糖,咱至少知道了key有啥用不是录肯。
??寫(xiě)代碼要是細(xì)一點(diǎn),就該把所有的位置有可能變化的列表數(shù)據(jù)都給他添上唯一性ID吊说,即使后臺(tái)給的數(shù)據(jù)论咏,我們也可以在獲得數(shù)據(jù)的同時(shí)自己給他加一個(gè)不變的唯一性id,不加的話(huà)颁井,這可都是新建以及銷(xiāo)毀的開(kāi)銷(xiāo)是吧厅贪。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市雅宾,隨后出現(xiàn)的幾起案子养涮,更是在濱河造成了極大的恐慌,老刑警劉巖眉抬,帶你破解...
    沈念sama閱讀 219,427評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件贯吓,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡蜀变,警方通過(guò)查閱死者的電腦和手機(jī)悄谐,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,551評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)库北,“玉大人爬舰,你說(shuō)我怎么就攤上這事『撸” “怎么了情屹?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,747評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)杂腰。 經(jīng)常有香客問(wèn)我屁商,道長(zhǎng),這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,939評(píng)論 1 295
  • 正文 為了忘掉前任蜡镶,我火速辦了婚禮,結(jié)果婚禮上恤筛,老公的妹妹穿的比我還像新娘官还。我一直安慰自己,他們只是感情好毒坛,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,955評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布望伦。 她就那樣靜靜地躺著,像睡著了一般煎殷。 火紅的嫁衣襯著肌膚如雪屯伞。 梳的紋絲不亂的頭發(fā)上,一...
    開(kāi)封第一講書(shū)人閱讀 51,737評(píng)論 1 305
  • 那天豪直,我揣著相機(jī)與錄音劣摇,去河邊找鬼。 笑死弓乙,一個(gè)胖子當(dāng)著我的面吹牛末融,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播暇韧,決...
    沈念sama閱讀 40,448評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼勾习,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了懈玻?” 一聲冷哼從身側(cè)響起巧婶,我...
    開(kāi)封第一講書(shū)人閱讀 39,352評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎涂乌,沒(méi)想到半個(gè)月后艺栈,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,834評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡骂倘,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,992評(píng)論 3 338
  • 正文 我和宋清朗相戀三年眼滤,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片历涝。...
    茶點(diǎn)故事閱讀 40,133評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡诅需,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出荧库,到底是詐尸還是另有隱情堰塌,我是刑警寧澤,帶...
    沈念sama閱讀 35,815評(píng)論 5 346
  • 正文 年R本政府宣布分衫,位于F島的核電站场刑,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚪战。R本人自食惡果不足惜牵现,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,477評(píng)論 3 331
  • 文/蒙蒙 一铐懊、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧瞎疼,春花似錦科乎、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 32,022評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至太抓,卻和暖如春空闲,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背走敌。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,147評(píng)論 1 272
  • 我被黑心中介騙來(lái)泰國(guó)打工碴倾, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人悔常。 一個(gè)月前我還...
    沈念sama閱讀 48,398評(píng)論 3 373
  • 正文 我出身青樓影斑,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親机打。 傳聞我的和親對(duì)象是個(gè)殘疾皇子矫户,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,077評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容