成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

幫你讀懂preact源碼(二)

Warren / 2544人閱讀

摘要:最后刪除新的樹中不存在的節(jié)點。而中會記錄對其做了相應(yīng)的優(yōu)化,節(jié)點的的情況下,不做移動操作。這種情況,在中得到了優(yōu)化,通過四個指針,在每次循環(huán)中先處理特殊情況,并通過縮小指針范圍,獲得性能上的提升。

上篇文章已經(jīng)介紹過idff的處理邏輯主要分為三塊,處理textNode,element及component,但具體怎么處理component還沒有詳細(xì)介紹,接下來講一下preact是如何處理component的。

組件的diff

通過學(xué)習(xí)元素節(jié)點的diff操作,我們不妨大膽猜測一下,組件是做了如下diff操作:

組件不同類型或者不存在就創(chuàng)建,走相應(yīng)的生命周期鉤子

比較組件的屬性

比較組件的孩子

事實上和我們的猜想很相似,在進(jìn)行下一步之前,我們先了解下preact中的數(shù)據(jù)結(jié)構(gòu):

// 如下JSX

    


// App組件的實例,會有以下屬性

{
    base,   // 對應(yīng)組件渲染的dom
    _component, // 指向Child組件
}

// Child組件有以下屬性

{
    base,    // 與App組件實例指向同一個dom
    _parentComponent,   // 指向App組件
}

// 對應(yīng)的dom節(jié)點,即前文中的base對象

{ 
    _component    // 指向App組件,而不是Child組件
}

然后我們看一下buildComponentFromVNode邏輯:

如果組件類型相同調(diào)用setComponentProps

如果組件類型不同:

回收老的組件

創(chuàng)建新的組件實例

調(diào)用setComponentProps

回收老的dom

返回dom

    function buildComponentFromVNode(dom, vnode, context, mountAll) {
        let c = dom && dom._component,
            originalComponent = c,
            oldDom = dom,
            isDirectOwner = c && dom._componentConstructor === vnode.nodeName, // 組件類型是否變了
            isOwner = isDirectOwner,
            props = getNodeProps(vnode);

        while (c && !isOwner && (c = c._parentComponent)) { // 如果組件類型變了,一直向上遍歷;看類型是否相同
            isOwner = c.constructor === vnode.nodeName;
        }
        // 此時isOwner就代表組件類型是否相同
        // 如果組件類型相同,只設(shè)置屬性;然后將dom指向c.base
        if (c && isOwner && (!mountAll || c._component)) { 
            setComponentProps(c, props, 3, context, mountAll);
            dom = c.base;
        } else {
            if (originalComponent && !isDirectOwner) {   // 組件類型不同就先卸載組件
                unmountComponent(originalComponent);
                dom = oldDom = null;
            }
            // 創(chuàng)建組件的主要邏輯就是return new vnode.nodeName()
            c = createComponent(vnode.nodeName, props, context);
            
            if (dom && !c.nextBase) {
                c.nextBase = dom;
                // passing dom/oldDom as nextBase will recycle it if unused, so bypass recycling on L229:
                oldDom = null;
            }
            setComponentProps(c, props, 1, context, mountAll);
            dom = c.base;

            if (oldDom && dom !== oldDom) {
                oldDom._component = null;
                recollectNodeTree(oldDom, false);
            }
        }
        return dom;
    }

可以看到組件進(jìn)一步diff的核心邏輯在setComponentProps方法中,setComponentProps大致做了兩件事:

調(diào)用渲染前的生命周期鉤子: componentWillMount 與 componentWillReceiveProps

調(diào)用renderComponent

renderComponent主要邏輯為:

調(diào)用shouldComponentUpdate 或 componentWillUpdate生命周期鉤子

調(diào)用組件的render方法

如果render的結(jié)果是一個組件,做類似與buildComponentFromVNode的操作

如果render的結(jié)果是dom節(jié)點,調(diào)用diff操作

替換新的節(jié)點,卸載老的節(jié)點或組件

為組件的base添加組件引用_component

調(diào)用組件的生命周期鉤子componentDidUpdate,componentDidMount。

至此,我們已經(jīng)大致了解了preact的大致全流程,接下來我們看一下它的diffChildren的算法:

將原始dom的子節(jié)點分為兩部分,有key的放在keyed map里面,沒有key的放在children數(shù)組里面。

遍歷vchildren,通過key找到keyed中的child,如果child不存在,從children中取出相同類型的子節(jié)點

對child與vchild進(jìn)行diff,此時得到的dom節(jié)點就是新的dom節(jié)點

然后與老的dom節(jié)點對應(yīng)的節(jié)點比較,操作dom樹。

最后刪除新的dom樹中不存在的節(jié)點。

function innerDiffNode(dom, vchildren, context, mountAll, isHydrating) {
    let originalChildren = dom.childNodes,
        children = [],
        keyed = {},
        keyedLen = 0,
        min = 0,
        len = originalChildren.length,
        childrenLen = 0,
        vlen = vchildren ? vchildren.length : 0,
        j,
        c,
        f,
        vchild,
        child;

    if (len !== 0) {
        for (var i = 0; i < len; i++) {
            var _child = originalChildren[i],
                props = _child.__preactattr_,
                key = vlen && props ? _child._component ? _child._component.__key : props.key : null;
            if (key != null) {
                keyedLen++;
                keyed[key] = _child;
            } else if (props || (_child.splitText !== undefined ? isHydrating ? _child.nodeValue.trim() : true : isHydrating)) {
                children[childrenLen++] = _child;
            }
        }
    }
    // 遍歷虛擬dom節(jié)點
    // 取child(有key,證明它兩個是要對應(yīng)比較的)
    // 如果child和originchildren[i]比較
    // originchild沒有,多余,否則插入到originchild前面
    if (vlen !== 0) {
        for (var i = 0; i < vlen; i++) {
            vchild = vchildren[i];
            child = null;

            // attempt to find a node based on key matching
            var key = vchild.key;
            if (key != null) {
                if (keyedLen && keyed[key] !== undefined) {
                    child = keyed[key];
                    keyed[key] = undefined;
                    keyedLen--;
                }
            }
            // attempt to pluck a node of the same type from the existing children
            else if (!child && min < childrenLen) {
                for (j = min; j < childrenLen; j++) { //從min往后開始遍歷,如果是相同類型的節(jié)點就拿出來,那個位置設(shè)為undefined
                    if (children[j] !== undefined && isSameNodeType(c = children[j], vchild, isHydrating)) {
                        child = c;
                        children[j] = undefined;
                        if (j === childrenLen - 1) childrenLen--; 
                        if (j === min) min++; 
                        break;
                    }
                }
            }

            // morph the matched/found/created DOM child to match vchild (deep)
            child = idiff(child, vchild, context, mountAll);
            f = originalChildren[i];
            if (child && child !== dom && child !== f) {
                if (f == null) {
                    dom.appendChild(child);
                } else if (child === f.nextSibling) {
                    removeNode(f); 
                } else {
                    dom.insertBefore(child, f);
                }
            }
        }
    }

    // remove unused keyed children:
    // keyedLen標(biāo)識老的集合中還有的元素,但沒在新的集合中使用
    if (keyedLen) {
        for (var i in keyed) {
            if (keyed[i] !== undefined) recollectNodeTree(keyed[i], false);
        }
    }

    // remove orphaned unkeyed children:
    // min代表拿走的元素
    while (min <= childrenLen) {
        if ((child = children[childrenLen--]) !== undefined) recollectNodeTree(child, false);
    }
}

從上面可以看出,preact只處理了常見的使用場景,沒有做特別的優(yōu)化措施,這也導(dǎo)致它在一些情況下的性能比react低,如:從a b到b a。
而react中會記錄lastIndex,對其做了相應(yīng)的優(yōu)化,節(jié)點的Index > lastIndex的情況下,不做移動操作。
但是如果react中有l(wèi)ength > 2,最前面的節(jié)點位置與最后面的節(jié)點位置互換的情況下,由于index一直小于lastIndex,就會失去上述的優(yōu)化效果。
這種情況,在snabbdom中得到了優(yōu)化,snabbdom通過oldStartIdx,oldEndIdx,newStartIdx,newEndIdx四個指針,在每次循環(huán)中先處理特殊情況,并通過縮小指針范圍,獲得性能上的提升。

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

轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/95134.html

相關(guān)文章

  • 幫你讀懂preact源碼(三)

    摘要:對回收的處理在中,回收調(diào)用了兩個方法,節(jié)點的回收一般會調(diào)用,組件的回收會調(diào)用。個人理解從以上源碼閱讀中我們可以看到,最大的性能問題在于遞歸的,中的與也是為了緩解這個問題。為不同類型的更新分配優(yōu)先級。 對回收的處理 在preact中,回收調(diào)用了兩個方法,dom節(jié)點的回收一般會調(diào)用recollectNodeTree,組件的回收會調(diào)用unmountComponent。 preact復(fù)用dom...

    yuanxin 評論0 收藏0
  • 幫你讀懂preact源碼(一)

    摘要:是一個最小的庫,但由于其對尺寸的追求,它的很多代碼可讀性比較差,市面上也很少有全面且詳細(xì)介紹的文章,本篇文章希望能幫助你學(xué)習(xí)的源碼。建議與源碼一起閱讀本文。 作為一名前端,我們需要深入學(xué)習(xí)react的運(yùn)行機(jī)制,但是react源碼量已經(jīng)相當(dāng)龐大,從學(xué)習(xí)的角度,性價比不高,所以學(xué)習(xí)一個react mini庫是一個深入學(xué)習(xí)react的一個不錯的方法。 preact是一個最小的react mi...

    XboxYan 評論0 收藏0
  • 少啰嗦!一分鐘帶你讀懂Java的NIO和經(jīng)典IO的區(qū)別

    摘要:的選擇器允許單個線程監(jiān)視多個輸入通道。一旦執(zhí)行的線程已經(jīng)超過讀取代碼中的某個數(shù)據(jù)片段,該線程就不會在數(shù)據(jù)中向后移動通常不會。 1、引言 很多初涉網(wǎng)絡(luò)編程的程序員,在研究Java NIO(即異步IO)和經(jīng)典IO(也就是常說的阻塞式IO)的API時,很快就會發(fā)現(xiàn)一個問題:我什么時候應(yīng)該使用經(jīng)典IO,什么時候應(yīng)該使用NIO? 在本文中,將嘗試用簡明扼要的文字,闡明Java NIO和經(jīng)典IO之...

    Meils 評論0 收藏0
  • 源碼入手,一文帶你讀懂Spring AOP面向切面編程

    摘要:,,面向切面編程。,切點,切面匹配連接點的點,一般與切點表達(dá)式相關(guān),就是切面如何切點。例子中,注解就是切點表達(dá)式,匹配對應(yīng)的連接點,通知,指在切面的某個特定的連接點上執(zhí)行的動作。,織入,將作用在的過程。因為源碼都是英文寫的。 之前《零基礎(chǔ)帶你看Spring源碼——IOC控制反轉(zhuǎn)》詳細(xì)講了Spring容器的初始化和加載的原理,后面《你真的完全了解Java動態(tài)代理嗎?看這篇就夠了》介紹了下...

    wawor4827 評論0 收藏0
  • 數(shù)據(jù)結(jié)構(gòu)與算法():帶你讀懂冒泡排序(Bubble Sorting)

    摘要:經(jīng)過一次冒泡排序,最終在無序表中找到一個最大值,第一次冒泡結(jié)束。也是我們后面要說的冒泡排序的優(yōu)化。冒泡排序只執(zhí)行第一層循環(huán),而不會執(zhí)行第二層循環(huán)。因此冒泡排序的時間復(fù)雜度是。 showImg(https://user-gold-cdn.xitu.io/2019/6/19/16b6f986df6880f9?w=640&h=142&f=gif&s=17175);showImg(https:...

    chuyao 評論0 收藏0

發(fā)表評論

0條評論

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