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

資訊專(zhuān)欄INFORMATION COLUMN

你不知道的Virtual DOM(三):Virtual Dom更新優(yōu)化

xiongzenghui / 2028人閱讀

摘要:經(jīng)過(guò)這次優(yōu)化,計(jì)算的時(shí)間快了那么幾毫秒。基于當(dāng)前這個(gè)版本的代碼還能做怎樣的優(yōu)化呢,請(qǐng)看下一篇的內(nèi)容你不知道的四的作用。

歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:

一、前言

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

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

本文基于本系列文章的第二篇,對(duì)VD的比較過(guò)程進(jìn)行優(yōu)化。

二、優(yōu)化一:省略patch對(duì)象,直接更新dom

在上一個(gè)版本的代碼里,我們是通過(guò)在diff過(guò)程中生成patch對(duì)象,然后在利用這個(gè)對(duì)象更新dom。

function tick(element) {
    if (state.num > 20) {
        clearTimeout(timer);
        return;
    }

    const newVDom = view();

    // 生成差異對(duì)象
    const patchObj = diff(preVDom, newVDom);

    preVDom = newVDom;

    // 給dom打個(gè)補(bǔ)丁
    patch(element, patchObj);
}

實(shí)際上這步是多余的。既然在diff的時(shí)候就已經(jīng)知道要如何操作dom了,那為什么不直接在diff里面更新呢?先來(lái)回顧下之前的diff代碼:

function diff(oldVDom, newVDom) {
    // 新建node
    if (oldVDom == undefined) {
        return {
            type: nodePatchTypes.CREATE,
            vdom: newVDom
        }
    }

    // 刪除node
    if (newVDom == undefined) {
        return {
            type: nodePatchTypes.REMOVE
        }
    }

    // 替換node
    if (
        typeof oldVDom !== typeof newVDom ||
        ((typeof oldVDom === "string" || typeof oldVDom === "number") && oldVDom !== newVDom) ||
        oldVDom.tag !== newVDom.tag
    ) {
       return {
           type: nodePatchTypes.REPLACE,
           vdom: newVDom
       } 
    }

    // 更新node
    if (oldVDom.tag) {
        // 比較props的變化
        const propsDiff = diffProps(oldVDom, newVDom);

        // 比較children的變化
        const childrenDiff = diffChildren(oldVDom, newVDom);

        // 如果props或者children有變化,才需要更新
        if (propsDiff.length > 0 || childrenDiff.some( patchObj => (patchObj !== undefined) )) {
            return {
                type: nodePatchTypes.UPDATE,
                props: propsDiff,
                children: childrenDiff
            }   
        }
        
    }
}

diff最終返回的對(duì)象是這個(gè)數(shù)據(jù)結(jié)構(gòu):

{
    type,
    vdom,
    props: [{
               type,
               key,
               value 
            }]
    children
}

現(xiàn)在,我們把生成對(duì)象的步驟省略掉,直接操作dom。這時(shí)候我們需要將父元素,還有子元素的索引傳進(jìn)來(lái)(原patch的邏輯):

function diff(oldVDom, newVDom, parent, index=0) {
    // 新建node
    if (oldVDom == undefined) {
        parent.appendChild(createElement(newVDom));
    }

    const element = parent.childNodes[index];

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

    // 替換node
    if (
        typeof oldVDom !== typeof newVDom ||
        ((typeof oldVDom === "string" || typeof oldVDom === "number") && oldVDom !== newVDom) ||
        oldVDom.tag !== newVDom.tag
    ) {
        parent.replaceChild(createElement(newVDom), element);
    }

    // 更新node
    if (oldVDom.tag) {
        // 比較props的變化
        diffProps(oldVDom, newVDom, element);

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

function diffProps(oldVDom, newVDom) {
    const allProps = {...oldVDom.props, ...newVDom.props};

    // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
    Object.keys(allProps).forEach((key) => {
            const oldValue = oldVDom.props[key];
            const newValue = newVDom.props[key];

            // 刪除屬性
            if (newValue == undefined) {
                element.removeAttribute(key);
            } 
            // 更新屬性
            else if (oldValue == undefined || oldValue !== newValue) {
                element.setAttribute(key, newValue);
            }
        }
    )
}

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

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

本質(zhì)上來(lái)說(shuō),這次的優(yōu)化是將patch的邏輯整合進(jìn)diff的過(guò)程中了。經(jīng)過(guò)這次優(yōu)化,JS計(jì)算的時(shí)間快了那么幾毫秒。雖然性能的提升不大,但代碼比原來(lái)的少了80多行,降低了邏輯復(fù)雜度,優(yōu)化的效果還是不錯(cuò)的。

三、優(yōu)化二:VD與真實(shí)dom融合

在之前的版本里面,diff操作針對(duì)的是新舊2個(gè)VD。既然真實(shí)的dom已經(jīng)根據(jù)之前的VD渲染出來(lái)了,有沒(méi)辦法用當(dāng)前的dom跟新的VD做比較呢?

答案是肯定的,只需要按需獲取dom中不同的屬性就可以了。比如,當(dāng)比較tag的時(shí)候,使用的是nodeType和tagName,比較文本的時(shí)候用的是nodeValue。

function tick(element) {
    if (state.num > 20) {
        clearTimeout(timer);
        return;
    }

    const newVDom = view();

    // 比較并更新節(jié)點(diǎn)
    diff(newVDom, element);
    // diff(preVDom, newVDom, element);

    // preVDom = newVDom;
}

function diff(newVDom, parent, index=0) {
    
    const element = parent.childNodes[index];

    // 新建node
    if (element == undefined) {
        parent.appendChild(createElement(newVDom));
        return;
    }

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

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

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

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

// 比較元素類(lèi)型是否相同
function isSameType(element, newVDom) {
    const elmType = element.nodeType;
    const vdomType = typeof newVDom;

    // 當(dāng)dom元素是文本節(jié)點(diǎn)的情況
    if (elmType === Node.TEXT_NODE && 
        (vdomType === "string" || vdomType === "number") &&
        element.nodeValue == newVDom
    ) {
       return true; 
    }

    // 當(dāng)dom元素是普通節(jié)點(diǎn)的情況
    if (elmType === Node.ELEMENT_NODE && element.tagName.toLowerCase() == newVDom.tag) {
        return true;
    }

    return false;
}

為了方便屬性的比較,提高效率,我們將VD的props存在dom元素的__preprops_字段中:

const ATTR_KEY = "__preprops_";

// 創(chuàng)建dom元素
function createElement(vdom) {
    // 如果vdom是字符串或者數(shù)字類(lèi)型,則創(chuàng)建文本節(jié)點(diǎn),比如“Hello World”
    if (typeof vdom === "string" || typeof vdom === "number") {
        return doc.createTextNode(vdom);
    }

    const {tag, props, children} = vdom;

    // 1. 創(chuàng)建元素
    const element = doc.createElement(tag);

    // 2. 屬性賦值
    setProps(element, props);

    // 3. 創(chuàng)建子元素
    children.map(createElement)
            .forEach(element.appendChild.bind(element));

    return element;
}

// 屬性賦值
function setProps(element, props) {
     // 屬性賦值
    element[ATTR_KEY] = props;

    for (let key in props) {
        element.setAttribute(key, props[key]);
    }
}

進(jìn)行屬性比較的時(shí)候再取出來(lái):

// 比較props的變化
function diffProps(newVDom, element) {
    let newProps = {...element[ATTR_KEY]};
    const allProps = {...newProps, ...newVDom.props};

    // 獲取新舊所有屬性名后,再逐一判斷新舊屬性值
    Object.keys(allProps).forEach((key) => {
            const oldValue = newProps[key];
            const newValue = newVDom.props[key];

            // 刪除屬性
            if (newValue == undefined) {
                element.removeAttribute(key);
                delete newProps[key];
            } 
            // 更新屬性
            else if (oldValue == undefined || oldValue !== newValue) {
                element.setAttribute(key, newValue);
                newProps[key] = newValue;
            }
        }
    )

    // 屬性重新賦值
    element[ATTR_KEY] = newProps;
}

通過(guò)這種方式,我們不再需要用變量preVDom將上一次生成的VD存下來(lái),而是直接跟真實(shí)的dom進(jìn)行比較,靈活性更強(qiáng)。

四、總結(jié)

本文基于上一個(gè)版本的代碼,簡(jiǎn)化了頁(yè)面渲染的過(guò)程(省略patch對(duì)象),同時(shí)提供了更靈活的VD比較方法(直接跟dom比較),可用性越來(lái)越強(qiáng)了。基于當(dāng)前這個(gè)版本的代碼還能做怎樣的優(yōu)化呢,請(qǐng)看下一篇的內(nèi)容:你不知道的Virtual DOM(四):key的作用。

P.S.: 想看完整代碼見(jiàn)這里,如果有必要建一個(gè)倉(cāng)庫(kù)的話(huà)請(qǐng)留言給我:代碼

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://specialneedsforspecialkids.com/yun/97149.html

相關(guān)文章

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

    摘要:最后里面沒(méi)有第四個(gè)元素了,才會(huì)把蘋(píng)果從移除。四總結(jié)本文基于上一個(gè)版本的代碼,加入了對(duì)唯一標(biāo)識(shí)的支持,很好的提高了更新數(shù)組元素的效率。 歡迎關(guān)注我的公眾號(hào)睿Talk,獲取我最新的文章:showImg(https://segmentfault.com/img/bVbmYjo); 一、前言 目前最流行的兩大前端框架,React和Vue,都不約而同的借助Virtual DOM技術(shù)提高頁(yè)面的渲染...

    DirtyMind 評(píng)論0 收藏0
  • 你不知道Virtual DOM(二):Virtual Dom更新

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

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

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

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

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

    caozhijian 評(píng)論0 收藏0
  • 你不知道Virtual DOM(一):Virtual Dom介紹

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

    lavor 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<