[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 模块总结
- 框架概述
- 框架目录
- 总目录(inferno-master)
- 配置目录(config)
- 示例目录(examples)
- 包目录(packages)
- 源代码目录(src)
- 工具目录(tools)
- 其他文件
- 框架结构
- (0)依赖关系
- (1)Inferno模块
- (2)InfernoDOM模块
- (3)InfernoServer模块
- (4)InfernoComponent模块
- (5)InfernoTestUtils模块
- (6)InfernoCreateElement模块
- (7)InfernoRouter模块
- 框架实现
- (1)Router
- (2)Redux
- (3)Component
- (4)CreateElement
- (5)Core(Vnode)
- (6)Dom(Render)
- (7)Server
- (8)TestUtils
- (9)Utils
- 框架流程
- 框架示例
- 框架更新
- 基础原理
- 框架总结