多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] * * * * * # 1 模块功能 # 2 模块实现 ## 2.1 rendering.js ~~~ // roots根节点集合 const roots = new Map(); // componentToDOMNodeMap component与DOM对应集合 export const componentToDOMNodeMap = new Map(); // 查找 export function findDOMNode(domNode) { return componentToDOMNodeMap.get(domNode) || null; } // 渲染组件input到界面元素parentDom中 export function render(input, parentDom) { // 获取parentDom对应root const root = roots.get(parentDom); // 创建元素事件lifecycle const lifecycle = new Lifecycle(); if (isUndefined(root)) { // 组件首次挂载 if (!isInvalidNode(input)) { // 挂载组件inout到parentDom if (!hydrate(input, parentDom, lifecycle)) { mount(input, parentDom, lifecycle, {}, null, false); } // 执行注册的事件 lifecycle.trigger(); // 注册parentDom对应挂载组件input roots.set(parentDom, { input: input }); } } else { // 组件二次渲染 // 获取当前焦点元素 const activeNode = getActiveNode(); // 刷新组件root.input与input, const nextInput = patch(root.input, input, parentDom, lifecycle, {}, null, false); // 执行注册的事件 lifecycle.trigger(); // 检测挂载组件是否为空 if (isNull(input)) { roots.delete(parentDom); } // 更新parent对应的挂载组件nextinput root.input = nextInput; // 恢复焦点元素 resetActiveNode(activeNode); } } ~~~ ## 2.2 mounting.js ~~~ export function mount(input, parentDom, lifecycle, context, instance, isSVG) { // 根据组件input的类型进行mount处理 if (isVPlaceholder(input)) { // VPlaceholder的input,占位挂载 return mountVPlaceholder(input, parentDom); } else if (isVText(input)) { // VText的input,文本挂载 return mountVText(input, parentDom); } else if (isVList(input)) { // VList的input,列表挂载 return mountVList(input, parentDom, lifecycle, context, instance, isSVG); } else if (isVNode(input)) { // VNode的input,单个节点挂载 return mountVNode(input, parentDom, lifecycle, context, instance, isSVG); } else { // bp智能创建节点 const normalisedInput = normalise(input); // 挂载自动创建后节点 if (input !== normalisedInput) { mount(normalisedInput, parentDom, lifecycle, context, instance, isSVG); } else { throw new Error(`Inferno Error: invalid object "${ typeof input }"" passed to mount()`); } } } // Vnode的mount() export function mountVNode(vNode, parentDom, lifecycle, context, instance, isSVG) { // 获取bp const bp = vNode.bp; if (isUndefined(bp)) { // bp为空 return mountVNodeWithoutBlueprint(vNode, parentDom, lifecycle, context, instance, isSVG); } else { if (recyclingEnabled) { const dom = recycle(vNode, bp, lifecycle, context, instance); if (!isNull(dom)) { if (!isNull(parentDom)) { parentDom.appendChild(dom); } return dom; } } // bp挂载 return mountVNodeWithBlueprint(vNode, bp, parentDom, lifecycle, context, instance); } } // VList的mount export function mountVList(vList, parentDom, lifecycle, context, instance, isSVG) { const items = vList.items; const pointer = document.createTextNode(''); const dom = document.createDocumentFragment(); // children处理 mountArrayChildren(items, dom, lifecycle, context, instance, isSVG); // vList.pointer = pointer; vList.dom = dom; dom.appendChild(pointer); if (parentDom) { insertOrAppend(parentDom, dom); } return dom; } // Vtext的mount export function mountVText(vText, parentDom) { const dom = document.createTextNode(vText.text); vText.dom = dom; if (parentDom) { insertOrAppend(parentDom, dom); } return dom; } // Vplaceholder的mount export function mountVPlaceholder(vPlaceholder, parentDom) { const dom = document.createTextNode(''); vPlaceholder.dom = dom; if (parentDom) { insertOrAppend(parentDom, dom); } return dom; } export function handleSelects(node) { if (node.tag === 'select') { selectValue(node); } } // bp.attrs的mount export function mountBlueprintAttrs(node, bp, dom, instance) { handleSelects(node); const attrs = node.attrs; if (isNull(bp.attrKeys)) { const newKeys = Object.keys(attrs); bp.attrKeys = bp.attrKeys ? bp.attrKeys.concat(newKeys) : newKeys; } const attrKeys = bp.attrKeys; mountAttributes(node, attrs, attrKeys, dom, instance); } // bp.events的mount export function mountBlueprintEvents(node, bp, dom) { const events = node.events; if (isNull(bp.eventKeys)) { bp.eventKeys = Object.keys(events); } const eventKeys = bp.eventKeys; mountEvents(events, eventKeys, dom); } // bp的mount function mountVNodeWithBlueprint(node, bp, parentDom, lifecycle, context, instance) { const tag = node.tag; if (isTrue(bp.isComponent)) { return mountComponent(node, tag, node.attrs || {}, node.hooks, node.children, instance, parentDom, lifecycle, context); } const dom = documentCreateElement(bp.tag, bp.isSVG); node.dom = dom; if (isTrue(bp.hasHooks)) { handleAttachedHooks(node.hooks, lifecycle, dom); } if (isTrue(bp.lazy)) { handleLazyAttached(node, lifecycle, dom); } const children = node.children; // bp.childrenType: // 0: no children // 1: text node // 2: single child // 3: multiple children // 4: multiple children (keyed) // 5: variable children (defaults to no optimisation) switch (bp.childrenType) { case 1: appendText(children, dom, true); break; case 2: mount(node.children, dom, lifecycle, context, instance, bp.isSVG); break; case 3: mountArrayChildren(children, dom, lifecycle, context, instance, bp.isSVG); break; case 4: for (let i = 0; i < children.length; i++) { mount(children[i], dom, lifecycle, context, instance, bp.isSVG); } break; case 5: mountChildren(node, children, dom, lifecycle, context, instance, bp.isSVG); break; default: break; } if (isTrue(bp.hasAttrs)) { mountBlueprintAttrs(node, bp, dom, instance); } if (isTrue(bp.hasClassName)) { dom.className = node.className; } if (isTrue(bp.hasStyle)) { patchStyle(null, node.style, dom); } if (isTrue(bp.hasEvents)) { mountBlueprintEvents(node, bp, dom); } if (!isNull(parentDom)) { parentDom.appendChild(dom); } return dom; } // Vnode中bl未定义的mount function mountVNodeWithoutBlueprint(node, parentDom, lifecycle, context, instance, isSVG) { const tag = node.tag; if (isFunction(tag)) { return mountComponent(node, tag, node.attrs || {}, node.hooks, node.children, instance, parentDom, lifecycle, context); } if (!isString(tag) || tag === '') { throw Error('Inferno Error: Expected function or string for element tag type'); } if (tag === 'svg') { isSVG = true; } const dom = documentCreateElement(tag, isSVG); const children = node.children; const attrs = node.attrs; const events = node.events; const hooks = node.hooks; const className = node.className; const style = node.style; node.dom = dom; if (!isNullOrUndefined(hooks)) { handleAttachedHooks(hooks, lifecycle, dom); } if (!isInvalidNode(children)) { mountChildren(node, children, dom, lifecycle, context, instance, isSVG); } if (!isNullOrUndefined(attrs)) { handleSelects(node); mountAttributes(node, attrs, Object.keys(attrs), dom, instance); } if (!isNullOrUndefined(className)) { dom.className = className; } if (!isNullOrUndefined(style)) { patchStyle(null, style, dom); } if (!isNullOrUndefined(events)) { mountEvents(events, Object.keys(events), dom); } if (!isNull(parentDom)) { parentDom.appendChild(dom); } return dom; } // 数组children的mount export function mountArrayChildren(children, parentDom, lifecycle, context, instance, isSVG) { children.complex = false; for (let i = 0; i < children.length; i++) { const child = normaliseChild(children, i); if (isVText(child)) { mountVText(child, parentDom); children.complex = true; } else if (isVPlaceholder(child)) { mountVPlaceholder(child, parentDom); children.complex = true; } else if (isVList(child)) { mountVList(child, parentDom, lifecycle, context, instance, isSVG); children.complex = true; } else { mount(child, parentDom, lifecycle, context, instance, isSVG); } } } // 智能children的mount function mountChildren(node, children, parentDom, lifecycle, context, instance, isSVG) { if (isArray(children)) { mountArrayChildren(children, parentDom, lifecycle, context, instance, isSVG); } else if (isStringOrNumber(children)) { appendText(children, parentDom, true); } else if (!isInvalidNode(children)) { mount(children, parentDom, lifecycle, context, instance, isSVG); } } // ref的mount export function mountRef(instance, value, refValue) { if (!isInvalidNode(instance) && isString(value)) { instance.refs[value] = refValue; } } // events的mount export function mountEvents(events, eventKeys, dom) { for (let i = 0; i < eventKeys.length; i++) { const event = eventKeys[i]; dom[event] = events[event]; } } // component的mount export function mountComponent(parentNode, Component, props, hooks, children, lastInstance, parentDom, lifecycle, context) { props = addChildrenToProps(children, props); let dom; if (isStatefulComponent(Component)) { const instance = new Component(props); // 注册_patch instance._patch = patch; // 注册_componentToDOMNodeMap instance._componentToDOMNodeMap = componentToDOMNodeMap; if (!isNullOrUndefined(lastInstance) && props.ref) { mountRef(lastInstance, props.ref, instance); } const childContext = instance.getChildContext(); if (!isNullOrUndefined(childContext)) { context = Object.assign({}, context, childContext); } instance.context = context; instance._unmounted = false; instance._parentNode = parentNode; if (lastInstance) { instance._parentComponent = lastInstance; } instance._pendingSetState = true; instance.componentWillMount(); let node = instance.render(); if (isInvalidNode(node)) { node = createVPlaceholder(); } instance._pendingSetState = false; dom = mount(node, null, lifecycle, context, instance, false); instance._lastNode = node; instance.componentDidMount(); if (parentDom !== null && !isInvalidNode(dom)) { parentDom.appendChild(dom); } // 添加组件instance与dom的对应关系 componentToDOMNodeMap.set(instance, dom); parentNode.dom = dom; parentNode.instance = instance; } else { if (!isNullOrUndefined(hooks)) { if (!isNullOrUndefined(hooks.componentWillMount)) { hooks.componentWillMount(null, props); } if (!isNullOrUndefined(hooks.componentDidMount)) { lifecycle.addListener(() => { hooks.componentDidMount(dom, props); }); } } /* eslint new-cap: 0 */ let node = Component(props, context); if (isInvalidNode(node)) { node = createVPlaceholder(); } dom = mount(node, null, lifecycle, context, null, false); parentNode.instance = node; if (parentDom !== null && !isInvalidNode(dom)) { parentDom.appendChild(dom); } parentNode.dom = dom; } return dom; } // attr的mount export function mountAttributes(node, attrs, attrKeys, dom, instance) { for (let i = 0; i < attrKeys.length; i++) { const attr = attrKeys[i]; if (attr === 'ref') { mountRef(getRefInstance(node, instance), attrs[attr], dom); } else { patchAttribute(attr, null, attrs[attr], dom); } } } ~~~ ## 2.3 patching.js ~~~ // patch() 对比组件lastInput,nextInput并刷新 export function patch(lastInput, nextInput, parentDom, lifecycle, context, instance, isSVG) { // 修改为组合表 if (lastInput !== nextInput) { // lastInput与nextInput不相等 if (isInvalidNode(lastInput)) { // lastInput无效,重新挂载 mount(nextInput, parentDom, lifecycle, context, instance, isSVG); } else if (isInvalidNode(nextInput)) { // nextinput无效,注销挂载 remove(lastInput, parentDom); } else if (isStringOrNumber(lastInput)) { // lastInput为字符串或者数字 if (isStringOrNumber(nextInput)) { // nextInput也为字符串或数字 // 文本修改 parentDom.firstChild.nodeValue = nextInput; } else { // nextInput不是字符串 // 重新挂载 const dom = mount(nextInput, null, lifecycle, context, instance, isSVG); // 替换掉原有元素 nextInput.dom = dom; replaceNode(parentDom, dom, parentDom.firstChild); } } else if (isStringOrNumber(nextInput)) { // nextInput文本,直接替换文本内容 replaceNode(parentDom, document.createTextNode(nextInput), lastInput.dom); } else { // nextInput为Vlist if (isVList(nextInput)) { if (isVList(lastInput)) { // lastInput也为VList // patchVList() patchVList(lastInput, nextInput, parentDom, lifecycle, context, instance, isSVG); } else { // lastInput不为VList // replaceNode() replaceNode(parentDom, mountVList(nextInput, null, lifecycle, context, instance, isSVG), lastInput.dom); // 注销lastInput unmount(lastInput, null); } } else if (isVList(lastInput)) { replaceVListWithNode(parentDom, lastInput, mount(nextInput, null, lifecycle, context, instance, isSVG)); } else if (isVPlaceholder(nextInput)) { if (isVPlaceholder(lastInput)) { patchVFragment(lastInput, nextInput); } else { replaceNode(parentDom, mountVPlaceholder(nextInput, null), lastInput.dom); unmount(lastInput, null); } } else if (isVPlaceholder(lastInput)) { replaceNode(parentDom, mount(nextInput, null, lifecycle, context, instance, isSVG), lastInput.dom); } else if (isVText(nextInput)) { if (isVText(lastInput)) { patchVText(lastInput, nextInput); } else { replaceNode(parentDom, mountVText(nextInput, null), lastInput.dom); unmount(lastInput, null); } } else if (isVText(lastInput)) { replaceNode(parentDom, mount(nextInput, null, lifecycle, context, instance, isSVG), lastInput.dom); } else if (isVNode(nextInput)) { if (isVNode(lastInput)) { patchVNode(lastInput, nextInput, parentDom, lifecycle, context, instance, isSVG, false); } else { replaceNode(parentDom, mountVNode(nextInput, null, lifecycle, context, instance, isSVG), lastInput.dom); unmount(lastInput, null); } } else if (isVNode(lastInput)) { replaceNode(parentDom, mount(nextInput, null, lifecycle, context, instance, isSVG), lastInput.dom); unmount(lastInput, null); } else { return patch(lastInput, normalise(nextInput), parentDom, lifecycle, context, instance, isSVG); } } } return nextInput; } // TextNode的patch export function patchTextNode(dom, lastChildren, nextChildren) { if (isStringOrNumber(lastChildren)) { dom.firstChild.nodeValue = nextChildren; } else { dom.textContent = nextChildren; } } // Ref的patch function patchRef(instance, lastValue, nextValue, dom) { if (instance) { if (isString(lastValue)) { delete instance.refs[lastValue]; } if (isString(nextValue)) { instance.refs[nextValue] = dom; } } } // Children的patch function patchChildren(lastNode, nextNode, dom, lifecycle, context, instance, isSVG) { const nextChildren = nextNode.children; const lastChildren = lastNode.children; if (lastChildren === nextChildren) { return; } if (isInvalidNode(lastChildren)) { if (isStringOrNumber(nextChildren)) { patchTextNode(dom, lastChildren, nextChildren); } else if (!isInvalidNode(nextChildren)) { if (isArray(nextChildren)) { mountArrayChildren(nextChildren, dom, lifecycle, context, instance, isSVG); } else { mount(nextChildren, dom, lifecycle, context, instance, isSVG); } } } else { if (isInvalidNode(nextChildren)) { removeAllChildren(dom, lastChildren); } else { if (isArray(lastChildren)) { if (isArray(nextChildren)) { nextChildren.complex = lastChildren.complex; if (isKeyed(lastChildren, nextChildren)) { patchKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG, null); } else { patchNonKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG, null); } } else { patchNonKeyedChildren(lastChildren, [nextChildren], dom, lifecycle, context, instance, isSVG, null); } } else { if (isArray(nextChildren)) { let lastChild = lastChildren; if (isStringOrNumber(lastChildren)) { lastChild = createVText(lastChild); lastChild.dom = dom.firstChild; } patchNonKeyedChildren([lastChild], nextChildren, dom, lifecycle, context, instance, isSVG, null); } else if (isStringOrNumber(nextChildren)) { patchTextNode(dom, lastChildren, nextChildren); } else if (isStringOrNumber(lastChildren)) { patch(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG); } else { patchVNode(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG, false); } } } } } // Vnode的patch export function patchVNode(lastVNode, nextVNode, parentDom, lifecycle, context, instance, isSVG, skipLazyCheck) { const lastBp = lastVNode.bp; const nextBp = nextVNode.bp; if (lastBp === undefined || nextBp === undefined) { patchVNodeWithoutBlueprint(lastVNode, nextVNode, parentDom, lifecycle, context, instance, isSVG); } else { patchVNodeWithBlueprint(lastVNode, nextVNode, lastBp, nextBp, parentDom, lifecycle, context, instance, skipLazyCheck); } } // bl的patch export function patchVNodeWithBlueprint(lastVNode, nextVNode, lastBp, nextBp, parentDom, lifecycle, context, instance, skipLazyCheck) { let nextHooks; if (nextBp.hasHooks === true) { nextHooks = nextVNode.hooks; if (nextHooks && !isNullOrUndefined(nextHooks.willUpdate)) { nextHooks.willUpdate(lastVNode.dom); } } const nextTag = nextVNode.tag || nextBp.tag; const lastTag = lastVNode.tag || lastBp.tag; if (lastTag !== nextTag) { if (lastBp.isComponent === true) { const lastNodeInstance = lastVNode.instance; if (nextBp.isComponent === true) { replaceWithNewNode(lastVNode, nextVNode, parentDom, lifecycle, context, instance, false); } else if (isStatefulComponent(lastTag)) { unmountVNode(lastVNode, null, true); const lastNode = lastNodeInstance._lastNode; patchVNodeWithBlueprint(lastNode, nextVNode, lastNode.bp, nextBp, parentDom, lifecycle, context, instance, nextBp.isSVG); } else { unmountVNode(lastVNode, null, true); patchVNodeWithBlueprint(lastNodeInstance, nextVNode, lastNodeInstance.bp, nextBp, parentDom, lifecycle, context, instance, nextBp.isSVG); } } else { replaceWithNewNode(lastVNode, nextVNode, parentDom, lifecycle, context, instance, nextBp.isSVG); } } else if (isNullOrUndefined(lastTag)) { nextVNode.dom = lastVNode.dom; } else { if (lastBp.isComponent === true) { if (nextBp.isComponent === true) { const instance = lastVNode.instance; if (!isNullOrUndefined(instance) && instance._unmounted) { const newDom = mountComponent(nextVNode, lastTag, nextVNode.attrs || {}, nextVNode.hooks, nextVNode.children, instance, parentDom, lifecycle, context); if (parentDom !== null) { replaceNode(parentDom, newDom, lastVNode.dom); } } else { nextVNode.instance = instance; nextVNode.dom = lastVNode.dom; patchComponent(true, nextVNode, nextVNode.tag, lastBp, nextBp, instance, lastVNode.attrs || {}, nextVNode.attrs || {}, nextVNode.hooks, lastVNode.children, nextVNode.children, parentDom, lifecycle, context); } } } else { const dom = lastVNode.dom; const lastChildrenType = lastBp.childrenType; const nextChildrenType = nextBp.childrenType; nextVNode.dom = dom; if (nextBp.lazy === true && skipLazyCheck === false) { const clipData = lastVNode.clipData; if (lifecycle.scrollY === null) { lifecycle.refresh(); } nextVNode.clipData = clipData; if (clipData.pending === true || clipData.top - lifecycle.scrollY > lifecycle.screenHeight) { if (setClipNode(clipData, dom, lastVNode, nextVNode, parentDom, lifecycle, context, instance, lastBp.isSVG)) { return; } } if (clipData.bottom < lifecycle.scrollY) { if (setClipNode(clipData, dom, lastVNode, nextVNode, parentDom, lifecycle, context, instance, lastBp.isSVG)) { return; } } } if (lastChildrenType > 0 || nextChildrenType > 0) { if (nextChildrenType === 5 || lastChildrenType === 5) { patchChildren(lastVNode, nextVNode, dom, lifecycle, context, instance); } else { const lastChildren = lastVNode.children; const nextChildren = nextVNode.children; if (lastChildrenType === 0 || isInvalidNode(lastChildren)) { if (nextChildrenType > 2) { mountArrayChildren(nextChildren, dom, lifecycle, context, instance); } else { mount(nextChildren, dom, lifecycle, context, instance); } } else if (nextChildrenType === 0 || isInvalidNode(nextChildren)) { if (lastChildrenType > 2) { removeAllChildren(dom, lastChildren); } else { remove(lastChildren, dom); } } else { if (lastChildren !== nextChildren) { if (lastChildrenType === 4 && nextChildrenType === 4) { patchKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, instance, nextBp.isSVG, null); } else if (lastChildrenType === 2 && nextChildrenType === 2) { patch(lastChildren, nextChildren, dom, lifecycle, context, instance, true, nextBp.isSVG); } else if (lastChildrenType === 1 && nextChildrenType === 1) { patchTextNode(dom, lastChildren, nextChildren); } else { patchChildren(lastVNode, nextVNode, dom, lifecycle, context, instance, nextBp.isSVG); } } } } } if (lastBp.hasAttrs === true || nextBp.hasAttrs === true) { patchAttributes(lastVNode, nextVNode, lastBp.attrKeys, nextBp.attrKeys, dom, instance); } if (lastBp.hasEvents === true || nextBp.hasEvents === true) { patchEvents(lastVNode.events, nextVNode.events, lastBp.eventKeys, nextBp.eventKeys, dom); } if (lastBp.hasClassName === true || nextBp.hasClassName === true) { const nextClassName = nextVNode.className; if (lastVNode.className !== nextClassName) { if (isNullOrUndefined(nextClassName)) { dom.removeAttribute('class'); } else { dom.className = nextClassName; } } } if (lastBp.hasStyle === true || nextBp.hasStyle === true) { const nextStyle = nextVNode.style; const lastStyle = lastVNode.style; if (lastStyle !== nextStyle) { patchStyle(lastStyle, nextStyle, dom); } } if (nextBp.hasHooks === true && !isNullOrUndefined(nextHooks.didUpdate)) { nextHooks.didUpdate(dom); } setFormElementProperties(nextTag, nextVNode); } } } // 无bp的patch export function patchVNodeWithoutBlueprint(lastNode, nextNode, parentDom, lifecycle, context, instance, isSVG) { const nextHooks = nextNode.hooks; const nextHooksDefined = !isNullOrUndefined(nextHooks); if (nextHooksDefined && !isNullOrUndefined(nextHooks.willUpdate)) { nextHooks.willUpdate(lastNode.dom); } const nextTag = nextNode.tag || ((isNullOrUndefined(nextNode.bp)) ? null : nextNode.bp.tag); const lastTag = lastNode.tag || ((isNullOrUndefined(lastNode.bp)) ? null : lastNode.bp.tag); if (nextTag === 'svg') { isSVG = true; } if (lastTag !== nextTag) { const lastNodeInstance = lastNode.instance; if (isFunction(lastTag)) { if (isFunction(nextTag)) { replaceWithNewNode(lastNode, nextNode, parentDom, lifecycle, context, instance, isSVG); } else if (isStatefulComponent(lastTag)) { unmountVNode(lastNode, null, true); patchVNodeWithoutBlueprint(lastNodeInstance._lastNode, nextNode, parentDom, lifecycle, context, instance, isSVG); } else { unmountVNode(lastNode, null, true); patchVNodeWithoutBlueprint(lastNodeInstance, nextNode, parentDom, lifecycle, context, instance, isSVG); } } else { replaceWithNewNode(lastNodeInstance || lastNode, nextNode, parentDom, lifecycle, context, instance, isSVG); } } else if (isNullOrUndefined(lastTag)) { nextNode.dom = lastNode.dom; } else { if (isFunction(lastTag)) { if (isFunction(nextTag)) { const instance = lastNode._instance; if (!isNullOrUndefined(instance) && instance._unmounted) { const newDom = mountComponent(nextNode, lastTag, nextNode.attrs || {}, nextNode.hooks, nextNode.children, instance, parentDom, lifecycle, context); if (parentDom !== null) { replaceNode(parentDom, newDom, lastNode.dom); } } else { nextNode.instance = lastNode.instance; nextNode.dom = lastNode.dom; patchComponent(false, nextNode, nextNode.tag, null, null, nextNode.instance, lastNode.attrs || {}, nextNode.attrs || {}, nextNode.hooks, lastNode.children, nextNode.children, parentDom, lifecycle, context); } } } else { const dom = lastNode.dom; const nextClassName = nextNode.className; const nextStyle = nextNode.style; nextNode.dom = dom; patchChildren(lastNode, nextNode, dom, lifecycle, context, instance, isSVG); patchAttributes(lastNode, nextNode, null, null, dom, instance); patchEvents(lastNode.events, nextNode.events, null, null, dom); if (lastNode.className !== nextClassName) { if (isNullOrUndefined(nextClassName)) { dom.removeAttribute('class'); } else { dom.className = nextClassName; } } if (lastNode.style !== nextStyle) { patchStyle(lastNode.style, nextStyle, dom); } if (nextHooksDefined && !isNullOrUndefined(nextHooks.didUpdate)) { nextHooks.didUpdate(dom); } setFormElementProperties(nextTag, nextNode); } } } // attr的patch function patchAttributes(lastNode, nextNode, lastAttrKeys, nextAttrKeys, dom, instance) { if (lastNode.tag === 'select') { selectValue(nextNode); } const nextAttrs = nextNode.attrs; const lastAttrs = lastNode.attrs; const nextAttrsIsUndef = isNullOrUndefined(nextAttrs); const lastAttrsIsNotUndef = !isNullOrUndefined(lastAttrs); if (!nextAttrsIsUndef) { const nextAttrsKeys = nextAttrKeys || Object.keys(nextAttrs); const attrKeysLength = nextAttrsKeys.length; for (let i = 0; i < attrKeysLength; i++) { const attr = nextAttrsKeys[i]; const lastAttrVal = lastAttrsIsNotUndef && lastAttrs[attr]; const nextAttrVal = nextAttrs[attr]; if (lastAttrVal !== nextAttrVal) { if (attr === 'ref') { patchRef(instance, lastAttrVal, nextAttrVal, dom); } else { patchAttribute(attr, lastAttrVal, nextAttrVal, dom); } } } } if (lastAttrsIsNotUndef) { const lastAttrsKeys = lastAttrKeys || Object.keys(lastAttrs); const attrKeysLength = lastAttrsKeys.length; for (let i = 0; i < attrKeysLength; i++) { const attr = lastAttrsKeys[i]; if (nextAttrsIsUndef || isNullOrUndefined(nextAttrs[attr])) { if (attr === 'ref') { patchRef(getRefInstance(node, instance), lastAttrs[attr], null, dom); } else { dom.removeAttribute(attr); } } } } } // style的patch export function patchStyle(lastAttrValue, nextAttrValue, dom) { if (isString(nextAttrValue)) { dom.style.cssText = nextAttrValue; } else if (isNullOrUndefined(lastAttrValue)) { if (!isNullOrUndefined(nextAttrValue)) { const styleKeys = Object.keys(nextAttrValue); for (let i = 0; i < styleKeys.length; i++) { const style = styleKeys[i]; const value = nextAttrValue[style]; if (isNumber(value) && !isUnitlessNumber[style]) { dom.style[style] = value + 'px'; } else { dom.style[style] = value; } } } } else if (isNullOrUndefined(nextAttrValue)) { dom.removeAttribute('style'); } else { const styleKeys = Object.keys(nextAttrValue); for (let i = 0; i < styleKeys.length; i++) { const style = styleKeys[i]; const value = nextAttrValue[style]; if (isNumber(value) && !isUnitlessNumber[style]) { dom.style[style] = value + 'px'; } else { dom.style[style] = value; } } const lastStyleKeys = Object.keys(lastAttrValue); for (let i = 0; i < lastStyleKeys.length; i++) { const style = lastStyleKeys[i]; if (isNullOrUndefined(nextAttrValue[style])) { dom.style[style] = ''; } } } } // events的patch export function patchEvents(lastEvents, nextEvents, _lastEventKeys, _nextEventKeys, dom) { const nextEventsDefined = !isNullOrUndefined(nextEvents); const lastEventsDefined = !isNullOrUndefined(lastEvents); if (nextEventsDefined) { if (lastEventsDefined) { const nextEventKeys = _nextEventKeys || Object.keys(nextEvents); for (let i = 0; i < nextEventKeys.length; i++) { const event = nextEventKeys[i]; const lastEvent = lastEvents[event]; const nextEvent = nextEvents[event]; if (lastEvent !== nextEvent) { dom[event] = nextEvent; } } const lastEventKeys = _lastEventKeys || Object.keys(lastEvents); for (let i = 0; i < lastEventKeys.length; i++) { const event = lastEventKeys[i]; if (isNullOrUndefined(nextEvents[event])) { dom[event] = null; } } } else { mountEvents(nextEvents, _nextEventKeys, dom); } } else if (lastEventsDefined) { removeEvents(lastEvents, _nextEventKeys, dom); } } // attr的patch export function patchAttribute(attrName, lastAttrValue, nextAttrValue, dom) { if (attrName === 'dangerouslySetInnerHTML') { const lastHtml = lastAttrValue && lastAttrValue.__html; const nextHtml = nextAttrValue && nextAttrValue.__html; if (isNullOrUndefined(nextHtml)) { throw new Error('Inferno Error: dangerouslySetInnerHTML requires an object with a __html propety containing the innerHTML content'); } if (lastHtml !== nextHtml) { dom.innerHTML = nextHtml; } } else if (strictProps[attrName]) { dom[attrName] = nextAttrValue === null ? '' : nextAttrValue; } else { if (booleanProps[attrName]) { dom[attrName] = nextAttrValue ? true : false; } else { const ns = namespaces[attrName]; if (nextAttrValue === false || isNullOrUndefined(nextAttrValue)) { if (ns !== undefined) { dom.removeAttributeNS(ns, attrName); } else { dom.removeAttribute(attrName); } } else { if (ns !== undefined) { dom.setAttributeNS(ns, attrName, nextAttrValue === true ? attrName : nextAttrValue); } else { dom.setAttribute(attrName, nextAttrValue === true ? attrName : nextAttrValue); } } } } } // component的patch export function patchComponent(hasBlueprint, lastNode, Component, lastBp, nextBp, instance, lastProps, nextProps, nextHooks, lastChildren, nextChildren, parentDom, lifecycle, context) { nextProps = addChildrenToProps(nextChildren, nextProps); if (isStatefulComponent(Component)) { const prevProps = instance.props; const prevState = instance.state; const nextState = instance.state; const childContext = instance.getChildContext(); if (!isNullOrUndefined(childContext)) { context = Object.assign({}, context, childContext); } instance.context = context; let nextNode = instance._updateComponent(prevState, nextState, prevProps, nextProps); if (nextNode === NO_RENDER) { nextNode = instance._lastNode; } else if (isNullOrUndefined(nextNode)) { nextNode = createVPlaceholder(); } patch(instance._lastNode, nextNode, parentDom, lifecycle, context, instance, null, false); lastNode.dom = nextNode.dom; instance._lastNode = nextNode; instance.componentDidUpdate(prevProps, prevState); componentToDOMNodeMap.set(instance, nextNode.dom); } else { let shouldUpdate = true; const nextHooksDefined = (hasBlueprint && nextBp.hasHooks === true) || !isNullOrUndefined(nextHooks); lastProps = addChildrenToProps(lastChildren, lastProps); if (nextHooksDefined && !isNullOrUndefined(nextHooks.componentShouldUpdate)) { shouldUpdate = nextHooks.componentShouldUpdate(lastNode.dom, lastProps, nextProps); } if (shouldUpdate !== false) { if (nextHooksDefined && !isNullOrUndefined(nextHooks.componentWillUpdate)) { nextHooks.componentWillUpdate(lastNode.dom, lastProps, nextProps); } let nextNode = Component(nextProps, context); if (isInvalidNode(nextNode)) { nextNode = createVPlaceholder(); } nextNode.dom = lastNode.dom; patch(instance, nextNode, parentDom, lifecycle, context, null, null, false); lastNode.instance = nextNode; if (nextHooksDefined && !isNullOrUndefined(nextHooks.componentDidUpdate)) { nextHooks.componentDidUpdate(lastNode.dom, lastProps, nextProps); } } } } // VList的patch function patchVList(lastVList, nextVList, parentDom, lifecycle, context, instance, isSVG) { const lastItems = lastVList.items; const nextItems = nextVList.items; const pointer = lastVList.pointer; nextVList.dom = lastVList.dom; nextVList.pointer = pointer; if (!lastItems !== nextItems) { if (isKeyed(lastItems, nextItems)) { patchKeyedChildren(lastItems, nextItems, parentDom, lifecycle, context, instance, isSVG, nextVList); } else { patchNonKeyedChildren(lastItems, nextItems, parentDom, lifecycle, context, instance, isSVG, nextVList); } } } // NonKeyedChildren的patch export function patchNonKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG, parentVList) { let lastChildrenLength = lastChildren.length; let nextChildrenLength = nextChildren.length; let commonLength = lastChildrenLength > nextChildrenLength ? nextChildrenLength : lastChildrenLength; let i = 0; for (; i < commonLength; i++) { const lastChild = lastChildren[i]; const nextChild = normaliseChild(nextChildren, i); patch(lastChild, nextChild, dom, lifecycle, context, instance, isSVG); } if (lastChildrenLength < nextChildrenLength) { for (i = commonLength; i < nextChildrenLength; i++) { const child = normaliseChild(nextChildren, i); insertOrAppend(dom, mount(child, null, lifecycle, context, instance, isSVG), parentVList && parentVList.pointer); } } else if (lastChildrenLength > nextChildrenLength) { for (i = commonLength; i < lastChildrenLength; i++) { remove(lastChildren[i], dom); } } } // VFragment的patch export function patchVFragment(lastVFragment, nextVFragment) { nextVFragment.dom = lastVFragment.dom; } // VText的patch export function patchVText(lastVText, nextVText) { const nextText = nextVText.text; const dom = lastVText.dom; nextVText.dom = dom; if (lastVText.text !== nextText) { dom.nodeValue = nextText; } } // KeyedChildren的patch export function patchKeyedChildren(lastChildren, nextChildren, dom, lifecycle, context, instance, isSVG, parentVList) { let lastChildrenLength = lastChildren.length; let nextChildrenLength = nextChildren.length; let lastEndIndex = lastChildrenLength - 1; let nextEndIndex = nextChildrenLength - 1; let lastStartIndex = 0; let nextStartIndex = 0; let lastStartNode = null; let nextStartNode = null; let nextEndNode = null; let lastEndNode = null; let nextNode; while (lastStartIndex <= lastEndIndex && nextStartIndex <= nextEndIndex) { nextStartNode = nextChildren[nextStartIndex]; lastStartNode = lastChildren[lastStartIndex]; if (nextStartNode.key !== lastStartNode.key) { break; } patchVNode(lastStartNode, nextStartNode, dom, lifecycle, context, instance, isSVG, false); nextStartIndex++; lastStartIndex++; } while (lastStartIndex <= lastEndIndex && nextStartIndex <= nextEndIndex) { nextEndNode = nextChildren[nextEndIndex]; lastEndNode = lastChildren[lastEndIndex]; if (nextEndNode.key !== lastEndNode.key) { break; } patchVNode(lastEndNode, nextEndNode, dom, lifecycle, context, instance, isSVG, false); nextEndIndex--; lastEndIndex--; } while (lastStartIndex <= lastEndIndex && nextStartIndex <= nextEndIndex) { nextEndNode = nextChildren[nextEndIndex]; lastStartNode = lastChildren[lastStartIndex]; if (nextEndNode.key !== lastStartNode.key) { break; } nextNode = (nextEndIndex + 1 < nextChildrenLength) ? nextChildren[nextEndIndex + 1].dom : null; patchVNode(lastStartNode, nextEndNode, dom, lifecycle, context, instance, isSVG, false); insertOrAppend(dom, nextEndNode.dom, nextNode); nextEndIndex--; lastStartIndex++; } while (lastStartIndex <= lastEndIndex && nextStartIndex <= nextEndIndex) { nextStartNode = nextChildren[nextStartIndex]; lastEndNode = lastChildren[lastEndIndex]; if (nextStartNode.key !== lastEndNode.key) { break; } nextNode = lastChildren[lastStartIndex].dom; patchVNode(lastEndNode, nextStartNode, dom, lifecycle, context, instance, isSVG, false); insertOrAppend(dom, nextStartNode.dom, nextNode); nextStartIndex++; lastEndIndex--; } if (lastStartIndex > lastEndIndex) { if (nextStartIndex <= nextEndIndex) { nextNode = (nextEndIndex + 1 < nextChildrenLength) ? nextChildren[nextEndIndex + 1].dom : parentVList && parentVList.pointer; for (; nextStartIndex <= nextEndIndex; nextStartIndex++) { insertOrAppend(dom, mount(nextChildren[nextStartIndex], null, lifecycle, context, instance, isSVG), nextNode); } } } else if (nextStartIndex > nextEndIndex) { while (lastStartIndex <= lastEndIndex) { remove(lastChildren[lastStartIndex++], dom); } } else { let aLength = lastEndIndex - lastStartIndex + 1; let bLength = nextEndIndex - nextStartIndex + 1; const sources = new Array(bLength); // Mark all nodes as inserted. let i; for (i = 0; i < bLength; i++) { sources[i] = -1; } let moved = false; let removeOffset = 0; let lastTarget = 0; let index; let removed = true; let k = 0; if ((bLength <= 4) || (aLength * bLength <= 16)) { for (i = lastStartIndex; i <= lastEndIndex; i++) { removed = true; lastEndNode = lastChildren[i]; if (k < bLength) { for (index = nextStartIndex; index <= nextEndIndex; index++) { nextEndNode = nextChildren[index]; if (lastEndNode.key === nextEndNode.key) { sources[index - nextStartIndex] = i; if (lastTarget > index) { moved = true; } else { lastTarget = index; } patchVNode(lastEndNode, nextEndNode, dom, lifecycle, context, instance, isSVG, false); k++; removed = false; break; } } } if (removed) { remove(lastEndNode, dom); removeOffset++; } } } else { const prevItemsMap = new Map(); for (i = nextStartIndex; i <= nextEndIndex; i++) { prevItemsMap.set(nextChildren[i].key, i); } for (i = lastStartIndex; i <= lastEndIndex; i++) { removed = true; lastEndNode = lastChildren[i]; if (k < nextChildrenLength) { index = prevItemsMap.get(lastEndNode.key); if (index !== undefined) { nextEndNode = nextChildren[index]; sources[index - nextStartIndex] = i; if (lastTarget > index) { moved = true; } else { lastTarget = index; } patchVNode(lastEndNode, nextEndNode, dom, lifecycle, context, instance, isSVG, false); k++; removed = false; } } if (removed) { remove(lastEndNode, dom); removeOffset++; } } } let pos; if (moved) { let seq = lis_algorithm(sources); index = seq.length - 1; for (i = bLength - 1; i >= 0; i--) { if (sources[i] === -1) { pos = i + nextStartIndex; nextNode = (pos + 1 < nextChildrenLength) ? nextChildren[pos + 1].dom : parentVList && parentVList.pointer; insertOrAppend(dom, mount(nextChildren[pos], null, lifecycle, context, instance, isSVG), nextNode); } else { if (index < 0 || i !== seq[index]) { pos = i + nextStartIndex; nextNode = (pos + 1 < nextChildrenLength) ? nextChildren[pos + 1].dom : parentVList && parentVList.pointer; insertOrAppend(dom, nextChildren[pos].dom, nextNode); } else { index--; } } } } else if (aLength - removeOffset !== bLength) { for (i = bLength - 1; i >= 0; i--) { if (sources[i] === -1) { pos = i + nextStartIndex; nextNode = (pos + 1 < nextChildrenLength) ? nextChildren[pos + 1].dom : parentVList && parentVList.pointer; insertOrAppend(dom, mount(nextChildren[pos], null, lifecycle, context, instance, isSVG), nextNode); } } } } } // https://en.wikipedia.org/wiki/Longest_increasing_subsequence function lis_algorithm(a) { let p = a.slice(0); let result = []; result.push(0); let i; let j; let u; let v; let c; for (i = 0; i < a.length; i++) { if (a[i] === -1) { continue; } j = result[result.length - 1]; if (a[j] < a[i]) { p[i] = j; result.push(i); continue; } u = 0; v = result.length - 1; while (u < v) { c = ((u + v) / 2) | 0; if (a[result[c]] < a[i]) { u = c + 1; } else { v = c; } } if (a[i] < a[result[u]]) { if (u > 0) { p[i] = result[u - 1]; } result[u] = i; } } u = result.length; v = result[u - 1]; while (u-- > 0) { result[u] = v; v = p[v]; } return result; } ~~~ ## 2.4 lifecycle.js ~~~ ~~~ ## 2.5 hydration.js ~~~ // child混合 function hydrateChild(child, childNodes, counter, parentDom, lifecycle, context, instance) { const domNode = childNodes[counter.i]; if (isVText(child)) { const text = child.text; child.dom = domNode; if (domNode.nodeType === 3 && text !== '') { domNode.nodeValue = text; } else { const newDomNode = mountVText(text); replaceNode(parentDom, newDomNode, domNode); childNodes.splice(childNodes.indexOf(domNode), 1, newDomNode); child.dom = newDomNode; } } else if (isVPlaceholder(child)) { child.dom = domNode; } else if (isVList(child)) { const items = child.items; // this doesn't really matter, as it won't be used again, but it's what it should be given the purpose of VList child.dom = document.createDocumentFragment(); for (let i = 0; i < items.length; i++) { const rebuild = hydrateChild(normaliseChild(items, i), childNodes, counter, parentDom, lifecycle, context, instance); if (rebuild) { return true; } } // at the end of every VList, there should be a "pointer". It's an empty TextNode used for tracking the VList const pointer = childNodes[counter.i++]; if (pointer && pointer.nodeType === 3) { child.pointer = pointer; } else { // there is a problem, we need to rebuild this tree return true; } } else { const rebuild = hydrateNode(child, domNode, parentDom, lifecycle, context, instance, false); if (rebuild) { return true; } } counter.i++; } // 注释节点清除 function getChildNodesWithoutComments(domNode) { const childNodes = []; const rawChildNodes = domNode.childNodes; let length = rawChildNodes.length; let i = 0; while (i < length) { const rawChild = rawChildNodes[i]; if (rawChild.nodeType === 8) { if (rawChild.data === '!') { const placeholder = document.createTextNode(''); domNode.replaceChild(placeholder, rawChild); childNodes.push(placeholder); i++; } else { domNode.removeChild(rawChild); length--; } } else { childNodes.push(rawChild); i++; } } return childNodes; } function hydrateComponent(node, Component, props, hooks, children, domNode, parentDom, lifecycle, context, lastInstance, isRoot) { // children添加到props props = addChildrenToProps(children, props); if (isStatefulComponent(Component)) { // 组件未挂载 // 创建组件 const instance = node.instance = new Component(props); // 注册组件的_patch instance._patch = patch; // lastInstance,props.ref挂载 if (!isNullOrUndefined(lastInstance) && props.ref) { mountRef(lastInstance, props.ref, instance); } // context合并 const childContext = instance.getChildContext(); if (!isNullOrUndefined(childContext)) { context = Object.assign({}, context, childContext); } // 组件的context,_unmounted,_parentNode instance.context = context; instance._unmounted = false; instance._parentNode = node; if (lastInstance) { instance._parentComponent = lastInstance; } // render()状态控制 instance._pendingSetState = true; // 调用componentWillMount() instance.componentWillMount(); // 调用组件instance.render() let nextNode = instance.render(); // render()状态控制 instance._pendingSetState = false; // render()结果 false,null, if (isInvalidNode(nextNode)) { nextNode = createVPlaceholder(); } // 混合渲染后的的节点 hydrateNode(nextNode, domNode, parentDom, lifecycle, context, instance, isRoot); instance._lastNode = nextNode; // 调用componentDidMount() instance.componentDidMount(); } else { // 已经挂载过 const instance = node.instance = Component(props); // hooks调用 if (!isNullOrUndefined(hooks)) { if (!isNullOrUndefined(hooks.componentWillMount)) { hooks.componentWillMount(null, props); } if (!isNullOrUndefined(hooks.componentDidMount)) { lifecycle.addListener(() => { hooks.componentDidMount(domNode, props); }); } } // 混合节点 return hydrateNode(instance, domNode, parentDom, lifecycle, context, instance, isRoot); } } // 合并属性 function hydrateNode(node, domNode, parentDom, lifecycle, context, instance, isRoot) { // bp:vnode.bp,vnode字符串表示 const bp = node.bp; // tag:vnode.tag,vnode的tag元素 const tag = node.tag || bp.tag; // 是否为Function if (isFunction(tag)) { // 注册domNode到vnode中 node.dom = domNode; // 创建组件 hydrateComponent(node, tag, node.attrs || {}, node.hooks, node.children, domNode, parentDom, lifecycle, context, instance, isRoot); } else { if ( domNode.nodeType !== 1 || tag !== domNode.tagName.toLowerCase() ) { // TODO remake node } else { node.dom = domNode; // hooks处理 const hooks = node.hooks; if ((bp && bp.hasHooks === true) || !isNullOrUndefined(hooks)) { handleAttachedHooks(hooks, lifecycle, domNode); } // children处理 const children = node.children; if (!isNullOrUndefined(children)) { if (isStringOrNumber(children)) { if (domNode.textContent !== children) { domNode.textContent = children; } } else { const childNodes = getChildNodesWithoutComments(domNode); const counter = { i: 0 }; let rebuild = false; if (isArray(children)) { for (let i = 0; i < children.length; i++) { rebuild = hydrateChild(normaliseChild(children, i), childNodes, counter, domNode, lifecycle, context, instance); if (rebuild) { break; } } } else { if (childNodes.length === 1) { rebuild = hydrateChild(children, childNodes, counter, domNode, lifecycle, context, instance); } else { rebuild = true; } } if (rebuild) { // TODO scrap children and rebuild again } } } // className,style处理 const className = node.className; const style = node.style; if (!isNullOrUndefined(className)) { domNode.className = className; } if (!isNullOrUndefined(style)) { patchStyle(null, style, domNode); } // attrs处理 if (bp && bp.hasAttrs === true) { mountBlueprintAttrs(node, bp, domNode, instance); } else { const attrs = node.attrs; if (!isNullOrUndefined(attrs)) { handleSelects(node); mountAttributes(node, attrs, Object.keys(attrs), domNode, instance); } } // events处理 if (bp && bp.hasEvents === true) { mountBlueprintEvents(node, bp, domNode); } else { const events = node.events; if (!isNullOrUndefined(events)) { mountEvents(events, Object.keys(events), domNode); } } } } } const documetBody = isBrowser ? document.body : null; export default function hydrate(node, parentDom, lifecycle) { // 获取parentDom下的带有data-infernoroot属性的元素。 // 使用hydrateNode()进行混合虚拟节点node与元素节点rootNode if (parentDom && parentDom.nodeType === 1) { const rootNode = parentDom.querySelector('[data-infernoroot]'); if (rootNode && rootNode.parentNode === parentDom) { hydrateNode(node, rootNode, parentDom, lifecycle, {}, true); return true; } } // clear parentDom, unless it's document.body if (parentDom !== documetBody) { parentDom.textContent = ''; } else { console.warn('Inferno Warning: rendering to the "document.body" is dangerous! Use a dedicated container element instead.'); } return false; } ~~~ ## 2.6 recycling.js ## 2.7 unmounting.js # 3 模块总结