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

資訊專欄INFORMATION COLUMN

幫你讀懂preact源碼(二)

Warren / 2535人閱讀

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

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

組件的diff

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

組件不同類型或者不存在就創建,走相應的生命周期鉤子

比較組件的屬性

比較組件的孩子

事實上和我們的猜想很相似,在進行下一步之前,我們先了解下preact中的數據結構:

// 如下JSX

    


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

{
    base,   // 對應組件渲染的dom
    _component, // 指向Child組件
}

// Child組件有以下屬性

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

// 對應的dom節點,即前文中的base對象

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

然后我們看一下buildComponentFromVNode邏輯:

如果組件類型相同調用setComponentProps

如果組件類型不同:

回收老的組件

創建新的組件實例

調用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就代表組件類型是否相同
        // 如果組件類型相同,只設置屬性;然后將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;
            }
            // 創建組件的主要邏輯就是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;
    }

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

調用渲染前的生命周期鉤子: componentWillMount 與 componentWillReceiveProps

調用renderComponent

renderComponent主要邏輯為:

調用shouldComponentUpdate 或 componentWillUpdate生命周期鉤子

調用組件的render方法

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

如果render的結果是dom節點,調用diff操作

替換新的節點,卸載老的節點或組件

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

調用組件的生命周期鉤子componentDidUpdate,componentDidMount。

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

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

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

對child與vchild進行diff,此時得到的dom節點就是新的dom節點

然后與老的dom節點對應的節點比較,操作dom樹。

最后刪除新的dom樹中不存在的節點。

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節點
    // 取child(有key,證明它兩個是要對應比較的)
    // 如果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往后開始遍歷,如果是相同類型的節點就拿出來,那個位置設為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標識老的集合中還有的元素,但沒在新的集合中使用
    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只處理了常見的使用場景,沒有做特別的優化措施,這也導致它在一些情況下的性能比react低,如:從a b到b a。
而react中會記錄lastIndex,對其做了相應的優化,節點的Index > lastIndex的情況下,不做移動操作。
但是如果react中有length > 2,最前面的節點位置與最后面的節點位置互換的情況下,由于index一直小于lastIndex,就會失去上述的優化效果。
這種情況,在snabbdom中得到了優化,snabbdom通過oldStartIdx,oldEndIdx,newStartIdx,newEndIdx四個指針,在每次循環中先處理特殊情況,并通過縮小指針范圍,獲得性能上的提升。

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

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

相關文章

  • 幫你讀懂preact源碼(三)

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

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

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

    XboxYan 評論0 收藏0
  • 少啰嗦!一分鐘帶你讀懂Java的NIO和經典IO的區別

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

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

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

    wawor4827 評論0 收藏0
  • 數據結構與算法():帶你讀懂冒泡排序(Bubble Sorting)

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

    chuyao 評論0 收藏0

發表評論

0條評論

Warren

|高級講師

TA的文章

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