preact源碼學習(4)

這節開始說回收機制。在這以前把組件的最後一點內容收尾。buildComponentFromVNode至關於updateComponent, 但裏面存在替換操做。在替換過程一堆銷燬函數出現了。javascript

  1. recollectNodeTreejava

  2. unmountComponentnode

  3. removeChildrenreact

  4. removeNode緩存

export function buildComponentFromVNode(dom, vnode, context, mountAll) {
     //取得附上真實DOM上的組件實例
    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;
    }
      //若是能找到這個實例,而且不是在ReactDOM.render過程當中
    if (c && isOwner && (!mountAll || c._component)) {
        setComponentProps(c, props, ASYNC_RENDER, context, mountAll);
        dom = c.base;
    }
    else { 
    //移除舊的實例,建立新的實例
        if (originalComponent && !isDirectOwner) {
            unmountComponent(originalComponent);
            dom = oldDom = null;
        }

        c = createComponent(vnode.nodeName, props, context);
        if (dom && !c.nextBase) {
            c.nextBase = dom;
            oldDom = null;
        }
        setComponentProps(c, props, SYNC_RENDER, context, mountAll);
        dom = c.base;
         
        if (oldDom && dom!==oldDom) {
            oldDom._component = null;//GC
            recollectNodeTree(oldDom, false);
        }
    }

    return dom;
}

unmountComponent是一個遞歸處理子組件的過程dom

export function unmountComponent(component) {
    if (options.beforeUnmount) options.beforeUnmount(component);

    let base = component.base;
     //防止用戶在componentWillUnmount裏進行setState
    component._disable = true;

    if (component.componentWillUnmount) component.componentWillUnmount();

    component.base = null;

//處理高階組件
    let inner = component._component;
    if (inner) {
        unmountComponent(inner);
    }
    else if (base) {
    //若是組件最後生成的是元素節點,而且它上面有ref屬性
        if (base[ATTR_KEY] && base[ATTR_KEY].ref) base[ATTR_KEY].ref(null);

        component.nextBase = base;

        removeNode(base);
        collectComponent(component);//收集元素節點

        removeChildren(base);
    }
    //處理組件自身的ref,如<Tooltip ref={()=>{  console.log(1)}}>
    if (component.__ref) component.__ref(null);
}

removeNode是將節點從它的父節點中分離出來異步

export function removeNode(node) {
    let parentNode = node.parentNode;
    if (parentNode) parentNode.removeChild(node);
}

下面是removeChildren與recollectNodeTree,removeChildren實際上是個二道販子,只是負責遍歷,真正作的事的是recollectNodeTree。函數

export function removeChildren(node) {
    node = node.lastChild;
    while (node) {
        let next = node.previousSibling;
        recollectNodeTree(node, true);
        node = next;
    }
}
//recollectNodeTree用於移除組件與執行元素節點的緩存數據
export function recollectNodeTree(node, unmountOnly) {
    let component = node._component;
    if (component) {
        // if node is owned by a Component, unmount that component (ends up recursing back here)
        unmountComponent(component);
    }
    else {
        // If the node's VNode had a ref function, invoke it with null here.
        // (this is part of the React spec, and smart for unsetting references)
        if (node[ATTR_KEY]!=null && node[ATTR_KEY].ref) node[ATTR_KEY].ref(null);

        if (unmountOnly===false || node[ATTR_KEY]==null) {
            removeNode(node);
        }

        removeChildren(node);
    }
}

至此,銷燬部分已經講完了,還剩下diffAttributes及其內部實現。這個沒有什麼好談,都是巨簡單,裏面滿是if else。性能

總評,preact其實從它這樣體量的代碼,許多狀況是兼顧不及的。惟一稱道的是性能。爲了達成這性能,它儘可能使用異步與_disable來控制組件的更新。爲了確保數據不會亂,它是根據頁面的真實DOM上來提取現有的虛擬DOM樹進行diff。ui

相關文章
相關標籤/搜索