这节开始说回收机制。在这以前把组件的最后一点内容收尾。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