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