国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

你不知道的Virtual DOM(四):key的作用

DirtyMind / 893人閱讀

摘要:最后里面沒有第四個元素了,才會把蘋果從移除。四總結本文基于上一個版本的代碼,加入了對唯一標識的支持,很好的提高了更新數組元素的效率。

歡迎關注我的公眾號睿Talk,獲取我最新的文章:

一、前言

目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術提高頁面的渲染效率。那么,什么是Virtual DOM?它是通過什么方式去提升頁面渲染效率的呢?本系列文章會詳細講解Virtual DOM的創建過程,并實現一個簡單的Diff算法來更新頁面。本文的內容脫離于任何的前端框架,只講最純粹的Virtual DOM。敲單詞太累了,下文Virtual DOM一律用VD表示。

這是VD系列文章的第四篇,以下是本系列其它文章的傳送門:
你不知道的Virtual DOM(一):Virtual Dom介紹
你不知道的Virtual DOM(二):Virtual Dom的更新
你不知道的Virtual DOM(三):Virtual Dom更新優化
你不知道的Virtual DOM(四):key的作用
你不知道的Virtual DOM(五):自定義組件
你不知道的Virtual DOM(六):事件處理&異步更新

今天,我們繼續在之前項目的基礎上進行優化。用過React或者Vue的朋友都知道在渲染數組元素的時候,編譯器會提醒加上key這個屬性,那么key是用來做什么的呢?

二、key的作用

在渲染數組元素時,它們一般都有相同的結構,只是內容有些不同而已,比如:

  • 商品:蘋果 數量:1
  • 商品:香蕉 數量:2
  • 商品:雪梨 數量:3

可以把這個例子想象成一個購物車。此時如果想往購物車里面添加一件商品,性能不會有任何問題,因為只是簡單的在ul的末尾追加元素,前面的元素都不需要更新:

  • 商品:蘋果 數量:1
  • 商品:香蕉 數量:2
  • 商品:雪梨 數量:3
  • 商品:橙子 數量:2

但是,如果我要刪除第一個元素,根據VD的比較邏輯,后面的元素全部都要進行更新的操作。dom結構簡單還好說,如果是一個復雜的結構,那頁面渲染的性能將會受到很大的影響。

  • 商品:香蕉 數量:2
  • 商品:雪梨 數量:3
  • 商品:橙子 數量:2

有什么方式可以降低這種性能的損耗呢?

最直觀的方法肯定是直接刪除第一個元素然后其它元素保持不變了。但程序沒有這么智能,可以像我們一樣一眼就看出變化。程序能做到的是盡量少的修改元素,通過移動元素而不是修改元素來達到更新的目的。為了告訴程序要怎么移動元素,我們必須給每個元素加上一個唯一標識,也就是key。

  • 商品:蘋果 數量:1
  • 商品:香蕉 數量:2
  • 商品:雪梨 數量:3
  • 商品:橙子 數量:2

當把蘋果刪掉的時候,VD里面第一個元素是香蕉,而dom里面第一個元素是蘋果。當元素有key屬性的時候,框架就會嘗試根據這個key去找對應的元素,找到了就將這個元素移動到第一個位置,循環往復。最后VD里面沒有第四個元素了,才會把蘋果從dom移除。

三、代碼實現

在上一個版本代碼的基礎上,主要的改動點是diffChildren這個函數。原來的實現很簡單,遞歸的調用diff就可以了:

function diffChildren(newVDom, parent) {
    // 獲取子元素最大長度
    const childLength = Math.max(parent.childNodes.length, newVDom.children.length);

    // 遍歷并diff子元素
    for (let i = 0; i < childLength; i++) {
        diff(newVDom.children[i], parent, i);
    }
}

現在,我們要對這個函數進行一個大改造,讓他支持key的查找:

function diffChildren(newVDom, parent) {
    // 有key的子元素
    const nodesWithKey = {};
    let nodesWithKeyCount = 0;

    // 沒key的子元素
    const nodesWithoutKey = [];
    let nodesWithoutKeyCount = 0;

    const childNodes = parent.childNodes,
          nodeLength = childNodes.length;

    const vChildren = newVDom.children,
          vLength = vChildren.length;

    // 用于優化沒key子元素的數組遍歷
    let min = 0;

    // 將子元素分成有key和沒key兩組
    for (let i = 0; i < nodeLength; i++) {
        const child = childNodes[i],
              props = child[ATTR_KEY];

        if (props !== undefined && props.key !== undefined) {
            nodesWithKey[props.key] = child;
            nodesWithKeyCount++;
        } else {
            nodesWithoutKey[nodesWithoutKeyCount++] = child;
        }
    }

    // 遍歷vdom的所有子元素
    for (let i = 0; i < vLength; i++) {
        const vChild = vChildren[i],
              vProps = vChild.props;
        let dom;

        vKey = vProps!== undefined ? vProps.key : undefined;
        // 根據key來查找對應元素
        if (vKey !== undefined) {
            if (nodesWithKeyCount && nodesWithKey[vKey] !== undefined) {
                dom = nodesWithKey[vKey];
                nodesWithKey[vKey] = undefined;
                nodesWithKeyCount--; 
            }
        } 
        // 如果沒有key字段,則找一個類型相同的元素出來做比較
        else if (min < nodesWithoutKeyCount) {
            for (let j = 0; j < nodesWithoutKeyCount; j++) {
                const node = nodesWithoutKey[j];
                if (node !== undefined && isSameType(node, vChild)) {
                    dom = node;
                    nodesWithoutKey[j] = undefined;
                    if (j === min) min++;
                    if (j === nodesWithoutKeyCount - 1) nodesWithoutKeyCount--;
                    break;
                }
            }
        }

        // diff返回是否更新元素
        const isUpdate = diff(dom, vChild, parent);

        // 如果是更新元素,且不是同一個dom元素,則移動到原先的dom元素之前
        if (isUpdate) {
            const originChild = childNodes[i];
            if (originChild !== dom) {
                parent.insertBefore(dom, originChild);
            }
        }
    }

    // 清理剩下的未使用的dom元素
    if (nodesWithKeyCount) {
       for (key in nodesWithKey) {
           const node = nodesWithKey[key];
           if (node !== undefined) {
               node.parentNode.removeChild(node);
           }
       } 
    }
    // 清理剩下的未使用的dom元素
    while (min <= nodesWithoutKeyCount) {
        const node = nodesWithoutKey[nodesWithoutKeyCount--];
        if ( node !== undefined) {
            node.parentNode.removeChild(node);
        }
    }
}

代碼比較長,主要是以下幾個步驟:

將所有dom子元素分為有key和沒key兩組

遍歷VD子元素,如果VD子元素有key,則去查找有key的分組;如果沒key,則去沒key的分組找一個類型相同的元素出來

diff一下,得出是否更新元素的類型

如果是更新元素且子元素不是原來的,則移動元素

最后清理刪除沒用上的dom子元素

diff也要改造一下,如果是新建、刪除或者替換元素,返回false。更新元素則返回true:

function diff(dom, newVDom, parent) {
    // 新建node
    if (dom == undefined) {
        parent.appendChild(createElement(newVDom));
        return false;
    }

    // 刪除node
    if (newVDom == undefined) {
        parent.removeChild(dom);
        return false;
    }

    // 替換node
    if (!isSameType(dom, newVDom)) {
        parent.replaceChild(createElement(newVDom), dom);
        return false;
    }

    // 更新node
    if (dom.nodeType === Node.ELEMENT_NODE) {
        // 比較props的變化
        diffProps(newVDom, dom);

        // 比較children的變化
        diffChildren(newVDom, dom);
    }

    return true;
}

為了看效果,view函數也要改造下:

const arr = [0, 1, 2, 3, 4];

function view() {
    const elm = arr.pop();

    // 用于測試能不能正常刪除元素
    if (state.num !== 9) arr.unshift(elm);

    // 用于測試能不能正常添加元素
    if (state.num === 12) arr.push(9);

    return (
        
Hello World
    { arr.map( i => (
  • 第{i}
  • )) }
); }

通過變換數組元素的順序和適時的添加/刪除元素,驗證了代碼按照我們的設計思路正確運行。

四、總結

本文基于上一個版本的代碼,加入了對唯一標識(key)的支持,很好的提高了更新數組元素的效率。基于當前這個版本的代碼還能做怎樣的優化呢,請看下一篇的內容:你不知道的Virtual DOM(五):自定義組件。

P.S.: 想看完整代碼見這里,如果有必要建一個倉庫的話請留言給我:代碼

文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/97155.html

相關文章

  • 你不知道Virtual DOM(三):Virtual Dom更新優化

    摘要:經過這次優化,計算的時間快了那么幾毫秒。基于當前這個版本的代碼還能做怎樣的優化呢,請看下一篇的內容你不知道的四的作用。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術提高頁面的渲染效率。那么,什...

    xiongzenghui 評論0 收藏0
  • 你不知道Virtual DOM(二):Virtual Dom更新

    摘要:變化的只有種更新和刪除。頁面的元素的數量隨著而變。四總結本文詳細介紹如何實現一個簡單的算法,再根據計算出的差異去更新真實的。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React 和 Vue,都不約而同的借助 Virtual DOM 技術提高頁面的渲染...

    testbird 評論0 收藏0
  • 你不知道Virtual DOM(一):Virtual Dom介紹

    摘要:不同的框架對這三個屬性的命名會有點差別,但表達的意思是一致的。它們分別是標簽名屬性和子元素對象。我們先來看下頁面的更新一般會經過幾個階段。元素有可能是數組的形式,需要將數組解構一層。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約...

    lavor 評論0 收藏0
  • 你不知道Virtual DOM(六):事件處理&異步更新

    摘要:如果列表是空的,則存入組件后將異步刷新任務加入到事件循環當中。四總結本文基于上一個版本的代碼,加入了事件處理功能,同時通過異步刷新的方法提高了渲染效率。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DO...

    caozhijian 評論0 收藏0
  • 你不知道Virtual DOM(五):自定義組件

    摘要:現在流行的前端框架都支持自定義組件,組件化開發已經成為提高前端開發效率的銀彈。二對自定義組件的支持要想正確的渲染組件,第一步就是要告訴某個標簽是自定義組件。下面的例子里,就是一個自定義組件。解決了識別自定義標簽的問題,下一步就是定義標簽了。 歡迎關注我的公眾號睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、...

    lk20150415 評論0 收藏0

發表評論

0條評論

DirtyMind

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<