Comments (6)
1.1.1的节点排序算法
function updateChildren(lastVnode, nextVnode, parentNode, context, mountQueue) {
let lastChildren = lastVnode.vchildren,
nextChildren = flattenChildren(nextVnode), //nextVnode.props.children;
childNodes = parentNode.childNodes,
hashcode = {},
hasExecutor = mountQueue.executor;
if (nextChildren.length == 0) {
lastChildren.forEach(function(el) {
var node = el._hostNode;
if (node) {
removeDOMElement(node);
}
disposeVnode(el);
});
return;
}
lastChildren.forEach(function(el, i) {
let key = el.type + (el.key || "");
if (el._disposed) {
return;
}
let list = hashcode[key];
el._index = i;
if (list) {
list.push(el);
} else {
hashcode[key] = [el];
}
});
nextChildren.forEach(function(el) {
let key = el.type + (el.key || "");
let list = hashcode[key];
if (list) {
let old = list.shift();
if (old) {
el.old = old;
if (!list.length) {
delete hashcode[key];
}
}
}
});
var removed = [];
for (let i in hashcode) {
let list = hashcode[i];
if (Array.isArray(list)) {
removed.push.apply(removed, list);
}
}
removed.sort(function(a, b) {
return a._index - b._index;
});
var queue = hasExecutor ? mountQueue : [];
nextChildren.forEach(function(el, index) {
let old = el.old,
ref,
dom;
removeNodes(removed, true);
if (old) {
delete el.old;
if (el === old && old._hostNode && !contextHasChange) {
//cloneElement
dom = old._hostNode;
if (dom !== childNodes[index]) {
parentNode.replaceChild(dom, childNodes[index]);
return;
}
} else {
dom = updateVnode(old, el, lastVnode, context, queue);
if (!dom) {
dom = createDOMElement({ vtype: "#comment", text: "placeholder" });
replaceChildDeday(
[old, el, lastVnode, context, queue],
dom,
parentNode
);
}
}
} else {
dom = mountVnode(null, el, lastVnode, context, queue);
}
ref = childNodes[index];
if (dom !== ref) {
insertDOM(parentNode, dom, ref);
}
if (!hasExecutor && queue.length) {
clearRefsAndMounts(queue);
}
});
removeNodes(removed);
}
function removeNodes(removed, one) {
while (removed.length) {
let removedEl = removed.shift();
let node = removedEl._hostNode;
if (node) {
removeDOMElement(node);
}
disposeVnode(removedEl);
if (one) {
break;
}
}
}
function replaceChildDeday(args, dom1, parentNode) {
setTimeout(function() {
var dom2 = updateVnode.apply(null, args);
parentNode.replaceChild(dom2, dom1);
});
}
function insertDOM(parentNode, dom, ref) {
if (!dom) {
return console.warn("元素末初始化"); // eslint-disable-line
}
if (!ref) {
parentNode.appendChild(dom);
} else {
parentNode.insertBefore(dom, ref);
}
}
from anu.
国庆假期对1.1.2的节点排序进行大调整
function diffChildren(lastVnode, nextVnode, parentNode, context, updateQueue) {
let lastChildren = parentNode.vchildren,
nextChildren = flattenChildren(nextVnode),
nextLength = nextChildren.length,
lastLength = lastChildren.length,
dom;
//如果旧数组长度为零, 直接添加
if (nextLength && !lastLength) {
emptyElement(parentNode);
return mountChildren(parentNode, nextChildren, lastVnode, context, updateQueue);
}
if (nextLength === lastLength && lastLength === 1) {
lastChildren[0]._hostNode = parentNode.firstChild;
return alignVnode(lastChildren[0], nextChildren[0], lastVnode, context, updateQueue);
}
let maxLength = Math.max(nextLength, lastLength),
insertPoint = parentNode.firstChild,
removeHits = {},
fuzzyHits = {},
actions = [],
i = 0,
hit,
nextChild,
lastChild;
//第一次循环,构建移动指令(actions)与移除名单(removeHits)与命中名单(fuzzyHits)
if (nextLength) {
actions.length = nextLength;
while (i < maxLength) {
nextChild = nextChildren[i];
lastChild = lastChildren[i];
if (nextChild && lastChild && isSameNode(lastChild, nextChild)) {
// 如果能直接找到,命名90%的情况
actions[i] = [lastChild, nextChild];
removeHits[i] = true;
} else {
if (nextChild) {
hit = nextChild.type + (nextChild.key || "");
if (fuzzyHits[hit] && fuzzyHits[hit].length) {
var oldChild = fuzzyHits[hit].shift();
// 如果命中旧的节点,将旧的节点移动新节点的位置,向后移动
actions[i] = [oldChild, nextChild, "moveAfter"];
removeHits[oldChild._i] = true;
}
}
if (lastChild) {
//如果没有命中或多了出来,那么放到命中名单中,留给第二轮循环使用
lastChild._i = i;
hit = lastChild.type + (lastChild.key || "");
let hits = fuzzyHits[hit];
if (hits) {
hits.push(lastChild);
} else {
fuzzyHits[hit] = [lastChild];
}
}
}
i++;
}
}
for (let j = 0, n = actions.length; j < n; j++) {
let action = actions[j];
if (!action) {
nextChild = nextChildren[j];
hit = nextChild.type + (nextChild.key || "");
if (fuzzyHits[hit] && fuzzyHits[hit].length) {
lastChild = fuzzyHits[hit].shift();
action = [lastChild, nextChild, "moveAfter"];
}
}
if (action) {
lastChild = action[0];
nextChild = action[1];
dom = lastChild._hostNode;
if (action[2]) {
parentNode.insertBefore(dom, insertPoint);
}
insertPoint = updateVnode(lastChild, nextChild, lastVnode, context, updateQueue);
if (!nextChild._hostNode) {
nextChildren[j] = lastChild;
}
removeHits[lastChild._i] = true;
} else {
//为了兼容 react stack reconciliation的执行顺序,添加下面三行,
//在插入节点前,将原位置上节点对应的组件先移除
var removed = lastChildren[j];
if (removed && !removed._disposed && !removeHits[j]) {
disposeVnode(removed);
}
//如果找不到对应的旧节点,创建一个新节点放在这里
dom = mountVnode(null, nextChild, lastVnode, context, updateQueue);
parentNode.insertBefore(dom, insertPoint);
insertPoint = dom;
}
insertPoint = insertPoint.nextSibling;
}
parentNode.vchildren = nextChildren;
//移除
lastChildren.forEach(function(el, i) {
if (!removeHits[i]) {
var node = el._hostNode;
if (node) {
removeDOMElement(node);
}
disposeVnode(el);
}
});
}
from anu.
1.1.4的diffChildren
function diffChildren(parentVnode, nextChildren, parentNode, context) {
let lastChildren = parentVnode.vchildren || emptyArray, //parentNode.vchildren,
nextLength = nextChildren.length,
insertPoint = parentNode.firstChild,
lastLength = lastChildren.length;
//optimize 1: 如果旧数组长度为零, 只进行添加
if (!lastLength) {
emptyElement(parentNode);
return mountChildren(parentNode, nextChildren, parentVnode, context);
}
//optimize 2: 如果新数组长度为零, 只进行删除
if (!nextLength) {
return lastChildren.forEach(function(el) {
removeElement(el._hostNode);
disposeVnode(el);
});
}
//optimize 3: 如果1vs1, 不用进入下面复杂的循环
if (nextLength === lastLength && lastLength === 1) {
if (insertPoint) {
lastChildren[0]._hostNode = insertPoint;
}
return alignVnode(lastChildren[0], nextChildren[0], parentVnode, context);
}
let mergeChildren = [], //确保新旧数组都按原顺数排列
fuzzyHits = {},
i = 0,
hit,
oldDom,
dom,
nextChild;
lastChildren.forEach(function(lastChild) {
hit = genkey(lastChild);
mergeChildren.push(lastChild);
let hits = fuzzyHits[hit];
if (hits) {
hits.push(lastChild);
} else {
fuzzyHits[hit] = [lastChild];
}
});
while (i < nextLength) {
nextChild = nextChildren[i];
nextChild._new = true;
hit = genkey(nextChild);
if (fuzzyHits[hit] && fuzzyHits[hit].length) {
var oldChild = fuzzyHits[hit].shift();
// 如果命中旧节点,置空旧节点,并在新位置放入旧节点(向后移动)
var lastIndex = mergeChildren.indexOf(oldChild);
if (lastIndex !== -1) {
mergeChildren[lastIndex] = fakeLastNode;
// mergeChildren.splice(lastIndex, 1);
}
nextChild._new = oldChild;
}
mergeChildren.splice(i, 0, nextChild);
i++;
}
for (var j = 0, n = mergeChildren.length; j < n; j++) {
let nextChild = mergeChildren[j];
if (nextChild._new) {
var lastChild = nextChild._new;
delete nextChild._new;
if (dom) {
insertPoint = dom.nextSibling;
}
if (lastChild === true) {
//新节点有两种情况,命中位置更后方的旧节点或就地创建实例化
// console.log("添加",getName(nextChild.type));
dom = mountVnode(null, nextChild, parentVnode, context);
insertElement(parentNode, dom, insertPoint);
} else {
oldDom = lastChild._hostNode;
if (oldDom !== insertPoint) {
insertElement(parentNode, oldDom, insertPoint);
}
//console.log("更新",getName(nextChild.type));
dom = alignVnode(lastChild, nextChild, parentVnode, context);
}
} else {
if (nextChild._hostNode) {
//console.log("移除",getName(nextChild.type));
removeElement(nextChild._hostNode);
disposeVnode(nextChild);
}
}
}
}
from anu.
1.1.5-pre2的diffChildren
function diffChildren(lastChildren, nextChildren, parentVnode, parentContext, updateQueue) {
let nextLength = nextChildren.length,
parentNode = parentVnode.stateNode,
lastLength = lastChildren.length;
var allChildNodes = parentVnode.childNodes;
console.log("lastChildren",lastChildren);
var lastFragments = getFragments(lastChildren);
// if(lastFragments)
// var stateIndex = allChildNodes.indexOf(lastFragments[0])
// stateIndex = stateIndex = -1 ? 0: stateIndex
parentVnode.childNodes = [];
//得到要更新旧节点
//optimize 1: 如果旧数组长度为零, 只进行添加
if (!lastLength) {
emptyElement(parentNode);
return mountChildren(parentVnode, nextChildren, parentContext, updateQueue);
}
//optimize 2: 如果新数组长度为零, 只进行删除
if (!nextLength && lastLength) {
disposeVnode(lastChildren);
return;
}
//optimize 3: 如果1vs1, 不用进入下面复杂的循环
if (nextLength === lastLength && lastLength === 1) {
var lastChild = lastChildren[0],
nextChild = nextChildren[0];
if (parentNode.firstChild && parentVnode.vtype === 1) {
lastChild.stateNode = parentNode.firstChild;
}
return alignVnode(lastChild, nextChild, parentContext, updateQueue);
}
//从这里开始非常复杂的节点排序算法
//step1: 构建模糊匹配对象fuzzyHits,以虚拟DOM的key/type为键名,并记录它的旧位置
let fuzzyHits = {},
hit,
i = 0;
lastChildren.forEach(function(lastChild) {
hit = genkey(lastChild);
let hits = fuzzyHits[hit];
if (hits) {
hits.push(lastChild);
} else {
fuzzyHits[hit] = [lastChild];
}
});
//step2: 碰撞检测,并筛选离新节点最新的节点,执行null ref与updateComponent
var React15 = false;
var priorityQueue = [],
reorderQueue = [];
while (i < nextLength) {
nextChild = nextChildren[i];
hit = genkey(nextChild);
let fLength = fuzzyHits[hit] && fuzzyHits[hit].length,
hitVnode = null;
if (fLength) {
let fnodes = fuzzyHits[hit];
React15 = true;
if (fLength > 1) {
hitVnode = getNearestNode(fnodes, i, nextChild);
} else {
hitVnode = nextChild._hit = fnodes[0];
delete fuzzyHits[hit];
}
if (hitVnode) {
lastChildren[hitVnode.index] = null;
if (hitVnode.index !== nextChild.index) {
reorderQueue.push(nextChild);
}
if (hitVnode.vtype > 1) {
var updater = hitVnode.stateNode.updater;
receiveComponent(hitVnode, nextChild, parentContext, updater._ending ? updateQueue : priorityQueue);
} else {
Refs.detachRef(hitVnode, nextChild);
}
}
}
i++;
}
drainQueue(priorityQueue);
//step3: 移除没有命中的虚拟DOM,执行它们的钩子与ref
if (React15) {
disposeChildren(lastChildren);
}
//step4: 更新元素,调整位置或插入新元素
var child = nextChildren[0];
alignVnode(child._hit, child, parentContext, updateQueue, true);
//React的怪异行为,如果没有组件发生更新,那么先执行添加,再执行移除
if (!React15) {
disposeChildren(lastChildren);
}
}
from anu.
1.1.5-pre3
diff.js
import { options, innerHTML, emptyObject, showQueue, toLowerCase, emptyArray, toArray, deprecatedWarn } from "./util";
import { createElement as createDOMElement, emptyElement } from "./browser";
import { disposeVnode, disposeChildren, topVnodes, topNodes } from "./dispose";
import { instantiateComponent } from "./instantiateComponent";
import { processFormElement } from "./ControlledComponent";
import { createVnode, restoreChildren, fiberizeChildren, createElement } from "./createElement";
import { getContextByTypes } from "./updater";
import { drainQueue } from "./scheduler";
import { captureError } from "./error";
import { Refs, pendingRefs } from "./Refs";
import { diffProps } from "./diffProps";
//[Top API] React.isValidElement
export function isValidElement(vnode) {
return vnode && vnode.vtype;
}
//[Top API] ReactDOM.render
export function render(vnode, container, callback) {
return renderByAnu(vnode, container, callback);
}
//[Top API] ReactDOM.unstable_renderSubtreeIntoContainer
export function unstable_renderSubtreeIntoContainer(lastVnode, nextVnode, container, callback) {
deprecatedWarn("unstable_renderSubtreeIntoContainer");
var parentContext = (lastVnode && lastVnode.context) || {};
return renderByAnu(nextVnode, container, callback, parentContext);
}
//[Top API] ReactDOM.unmountComponentAtNode
export function unmountComponentAtNode(container) {
var lastVnode = container.__component;
if (lastVnode) {
disposeVnode(lastVnode);
emptyElement(container);
container.__component = null;
}
}
//[Top API] ReactDOM.findDOMNode
export function findDOMNode(ref) {
if (ref == null) {
//null instance vnode
return null;
}
var vnode = ref.stateNode;
if (vnode.nodeType) {
return vnode.nodeType === 8 ? null : vnode;
} else {
return findDOMNode(ref.child);
}
}
//[Top API] ReactDOM.createPortal
export function createPortal(vchildren, container) {
var parentVnode = createVnode(container);
var lastChildren = parentVnode.child ? restoreChildren(parentVnode) : [];
var nextChildren = fiberizeChildren(parentVnode);
diffChildren(lastChildren, nextChildren, parentVnode, {}, []);
return null;
}
var AnuWrapper = function(){};
AnuWrapper.prototype.render = function(){
return this.props.child;
};
// ReactDOM.render的内部实现
function renderByAnu(vnode, container, callback, context = {}) {
if (!isValidElement(vnode)) {
throw `ReactDOM.render的第一个参数错误`; // eslint-disable-line
}
if (!(container && container.getElementsByTagName)) {
throw `ReactDOM.render的第二个参数错误`; // eslint-disable-line
}
//__component用来标识这个真实DOM是ReactDOM.render的容器,通过它可以取得上一次的虚拟DOM
// 但是在IE6-8中,文本/注释节点不能通过添加自定义属性来引用虚拟DOM,这时我们额外引进topVnode,
//topNode来寻找它们。
let nodeIndex = topNodes.indexOf(container),
lastVnode,
updateQueue = [];
if (nodeIndex !== -1) {
lastVnode = topVnodes[nodeIndex];
} else {
topNodes.push(container);
nodeIndex = topNodes.length - 1;
}
Refs.currentOwner = null; //防止干扰
var child = vnode;
vnode = createElement(AnuWrapper, {
isTop: true,
child: child
});
topVnodes[nodeIndex] = vnode;
if (lastVnode) {
vnode.return = lastVnode.return;
vnode.child = lastVnode.child;
alignVnode(lastVnode, vnode, context, updateQueue);
} else {
var parent = (vnode.return = createVnode(container));
parent.child = vnode;
genVnodes(vnode, context, updateQueue);
}
container.__component = vnode; //兼容旧的
vnode.return.batchMount();
drainQueue(updateQueue);
var rootNode = vnode.child.stateNode;
if (callback) {
callback.call(rootNode); //坑
}
//组件返回组件实例,而普通虚拟DOM 返回元素节点
return rootNode;
}
function genVnodes(vnode, context, updateQueue) {
let parentNode = vnode.return.stateNode;
let nodes = toArray(parentNode.childNodes || emptyArray);
let lastVnode = null;
for (var i = 0, dom; (dom = nodes[i++]); ) {
if (toLowerCase(dom.nodeName) === vnode.type) {
lastVnode = createVnode(dom);
} else {
parentNode.removeChild(dom);
}
}
if (lastVnode) {
return alignVnode(lastVnode, vnode, context, updateQueue);
} else {
return mountVnode(vnode, context, updateQueue);
}
}
//mountVnode只是转换虚拟DOM为真实DOM,不做插入DOM树操作
function mountVnode(vnode, context, updateQueue) {
options.beforeInsert(vnode);
if (vnode.vtype === 0 || vnode.vtype === 1) {
var dom = createDOMElement(vnode, vnode.return);
vnode.stateNode = dom;
if (vnode.vtype === 1) {
let { _hasRef, _hasProps, type, props } = vnode;
let children = fiberizeChildren(vnode);
mountChildren(vnode, children, context, updateQueue);
vnode.batchMount(); //批量插入 dom节点
if (_hasProps) {
diffProps(dom, emptyObject, props, vnode);
}
if (formElements[type]) {
processFormElement(vnode, dom, props);
}
if (_hasRef) {
pendingRefs.push(vnode, dom);
}
}
} else {
mountComponent(vnode, context, updateQueue);
}
var sibling = vnode.sibling;
if (sibling) {
var lastSibling = sibling._hit;
if (lastSibling) {
// delete sibling._hit;
alignVnode(lastSibling, sibling, context, updateQueue);
} else {
mountVnode(sibling, context, updateQueue);
}
}
return dom;
}
//通过组件虚拟DOM产生组件实例与内部操作实例updater
function mountComponent(vnode, parentContext, updateQueue, parentUpdater) {
let { type, props } = vnode;
let instance = instantiateComponent(type, vnode, props, parentContext); //互相持有引用
let updater = instance.updater;
if (parentUpdater) {
updater.parentUpdater = parentUpdater;
}
updater.parentContext = parentContext;
if (instance.componentWillMount) {
captureError(instance, "componentWillMount", []);
instance.state = updater.mergeStates();
}
updater._hydrating = true;
updater.render(updateQueue);
updateQueue.push(updater);
}
function mountChildren(vnode, children, context, updateQueue) {
if (children[0]) {
mountVnode(vnode.child, context, updateQueue);
}
}
const formElements = {
select: 1,
textarea: 1,
input: 1,
option: 1
};
function updateVnode(lastVnode, nextVnode, context, updateQueue) {
var dom = (nextVnode.stateNode = lastVnode.stateNode);
options.beforeUpdate(nextVnode);
if (lastVnode.vtype === 0) {
if (nextVnode.text !== lastVnode.text) {
dom.nodeValue = nextVnode.text;
}
} else if (lastVnode.vtype === 1) {
nextVnode.childNodes = lastVnode.childNodes;
let { props: lastProps, stateNode: dom, _hasProps, type } = lastVnode;
let { props: nextProps, _hasProps: nextCheckProps } = nextVnode;
let lastChildren = restoreChildren(lastVnode);
if (nextProps[innerHTML]) {
disposeChildren(lastChildren);
} else {
diffChildren(lastChildren, fiberizeChildren(nextVnode), nextVnode, context, updateQueue);
}
if (_hasProps || nextCheckProps) {
diffProps(dom, lastProps, nextProps, nextVnode);
}
if (formElements[type]) {
processFormElement(nextVnode, dom, nextProps);
}
Refs.detachRef(lastVnode, nextVnode, dom);
} else {
dom = receiveComponent(lastVnode, nextVnode, context, updateQueue);
}
return dom;
}
function receiveComponent(lastVnode, nextVnode, parentContext, updateQueue) {
let { type, stateNode } = lastVnode;
let updater = stateNode.updater,
nextContext;
//如果正在更新过程中接受新属性,那么去掉update,加上receive
var willReceive = lastVnode !== nextVnode;
if (!type.contextTypes) {
nextContext = stateNode.context;
} else {
nextContext = getContextByTypes(parentContext, type.contextTypes);
willReceive = true;
}
updater.context = nextContext;
//parentContext在官方中被称之不nextUnmaskedContext, parentVnode称之为nextParentElement
updater.props = nextVnode.props;
updater.parentContext = parentContext;
updater.pendingVnode = nextVnode;
updater.willReceive = willReceive;
if (!updater._dirty) {
//如果在事件中使用了setState
updater._receiving = [lastVnode, nextVnode, nextContext];
updater.addJob("patch");
updateQueue.push(updater);
}
return updater.stateNode;
}
function isSameNode(a, b) {
if (a.type === b.type && a.key === b.key) {
return true;
}
}
function genkey(vnode) {
return vnode.key ? "@" + vnode.key : vnode.type.name || vnode.type;
}
function alignVnode(lastVnode, nextVnode, context, updateQueue) {
if (!lastVnode || lastVnode.nodeType) {
return mountVnode(nextVnode, context, updateQueue);
}
if (isSameNode(lastVnode, nextVnode)) {
//组件虚拟DOM已经在diffChildren生成并插入DOM树
var resolveInDiffChildren = nextVnode._hit && nextVnode.vtype > 1;
if (resolveInDiffChildren) {
delete nextVnode._hit;
} else {
updateVnode(lastVnode, nextVnode, context, updateQueue);
}
var sibling = nextVnode.sibling;
if (sibling) {
alignVnode(sibling._hit, sibling, context, updateQueue);
}
} else {
disposeVnode(lastVnode);
mountVnode(nextVnode, context, updateQueue);
}
return nextVnode.stateNode;
}
function getNearestNode(vnodes, ii, newVnode) {
var distance = Infinity,
hit = null,
vnode,
i = 0;
while ((vnode = vnodes[i])) {
var delta = vnode.index - ii;
if (delta === 0) {
newVnode._hit = vnode;
vnodes.splice(i, 1);
return vnode;
} else {
var d = Math.abs(delta);
if (d < distance) {
distance = d;
hit = vnode;
}
}
i++;
}
newVnode._hit = hit;
return hit;
}
function mergeNodes(children) {
var nodes = [];
for (var i = 0, el; (el = children[i++]); ) {
if (!el._disposed) {
if(el.stateNode && el.stateNode.nodeType){
nodes.push(el.stateNode);
}else{
nodes.push.apply(nodes, el.collectNodes());
}
}
}
return nodes;
}
function diffChildren(lastChildren, nextChildren, parentVnode, parentContext, updateQueue) {
var parentVElement = parentVnode,
nextLength = nextChildren.length,
lastLength = lastChildren.length;
do {
if (parentVElement.vtype === 1) {
break;
}
} while ((parentVElement = parentVElement.return));
var childNodes = parentVElement.childNodes;
if (!lastLength) {
return mountChildren(parentVnode, nextChildren, parentContext, updateQueue);
}
var lastChilds = mergeNodes(lastChildren);
var React15 = false;
if (!childNodes.updateMeta) {
var startIndex = childNodes.indexOf(lastChilds[0]);
var insertPoint = childNodes[startIndex] || null;
childNodes.length = 0; //清空数组,以方便收集节点
childNodes.updateMeta = {
parentVnode,
parentVElement,
insertPoint,
lastChilds
};
}
// parentVnode.childNodes = childNodes;
let fuzzyHits = {},
hit,
nextChild,
i = 0;
lastChildren.forEach(function(lastChild) {
hit = genkey(lastChild);
let hits = fuzzyHits[hit];
if (hits) {
hits.push(lastChild);
} else {
fuzzyHits[hit] = [lastChild];
}
});
//step2: 碰撞检测,并筛选离新节点最新的节点,执行null ref与updateComponent
var priorityQueue = [],
reorderQueue = [];
while (i < nextLength) {
nextChild = nextChildren[i];
hit = genkey(nextChild);
let fLength = fuzzyHits[hit] && fuzzyHits[hit].length,
hitVnode = null;
if (fLength) {
let fnodes = fuzzyHits[hit];
React15 = true;
if (fLength > 1) {
hitVnode = getNearestNode(fnodes, i, nextChild);
} else {
hitVnode = nextChild._hit = fnodes[0];
delete fuzzyHits[hit];
}
if (hitVnode) {
lastChildren[hitVnode.index] = null;
if (hitVnode.index !== nextChild.index) {
reorderQueue.push(nextChild);
}
if (hitVnode.vtype > 1) {
receiveComponent(hitVnode, nextChild, parentContext, updateQueue);//原来updateQueue为priorityQueue
} else {
Refs.detachRef(hitVnode, nextChild);
}
}
}
i++;
}
//step3: 移除没有命中的虚拟DOM,执行它们的钩子与ref
disposeChildren(lastChildren);
drainQueue(updateQueue);//原来updateQueue为priorityQueue
//step4: 更新元素,调整位置或插入新元素
var child = nextChildren[0];
child && alignVnode(child._hit, child, parentContext, updateQueue);
//React的怪异行为,如果没有组件发生更新,那么先执行添加,再执行移除
if (!React15) {
disposeChildren(lastChildren);
}
if (childNodes.updateMeta && childNodes.updateMeta.parentVnode == parentVnode) {
parentVnode.batchUpdate(childNodes.updateMeta, mergeNodes(nextChildren));
}
}
options.diffChildren = diffChildren;
updater.js
import { fiberizeChildren, restoreChildren, createVText } from "./createElement";
import { extend, options, typeNumber, isFn, showQueue } from "../src/util";
import { drainQueue, enqueueUpdater } from "./scheduler";
import { pushError, captureError } from "./error";
import { Refs } from "./Refs";
function alwaysNull() {
return null;
}
let mountOrder = 1;
const support16 = true;
const errorType = {
0: "undefined",
2: "boolean",
3: "number",
4: "string",
7: "array"
};
/**
* 为了防止污染用户的实例,需要将操作组件虚拟DOM与生命周期钩子的逻辑全部抽象到这个类中
*
* @export
* @param {any} instance
* @param {any} vnode
*/
export function Updater(instance, vnode) {
vnode.stateNode = instance;
instance.updater = this;
this.instance = instance;
this.vnode = vnode;
this._pendingCallbacks = [];
this._pendingStates = [];
this._jobs = ["resolve"];
this._mountOrder = mountOrder++;
this._hookName = "componentDidMount";
// update总是保存最新的数据,如state, props, context, parentContext, parentVnode
// this._hydrating = true 表示组件正在根据虚拟DOM合成真实DOM
// this._renderInNextCycle = true 表示组件需要在下一周期重新渲染
// this._forceUpdate = true 表示会无视shouldComponentUpdate的结果
if (instance.__isStateless) {
this.mergeStates = alwaysNull;
}
}
Updater.prototype = {
addJob: function(newJob) {
var jobs = this._jobs;
if (jobs[jobs.length - 1] !== newJob) {
jobs.push(newJob);
}
},
enqueueSetState(state, cb) {
if (isFn(cb)) {
this._pendingCallbacks.push(cb);
}
if (state === true) {
//forceUpdate
this._forceUpdate = true;
} else {
//setState
this._pendingStates.push(state);
}
if (options.async) {
//在事件句柄中执行setState会进行合并
enqueueUpdater(this);
return;
}
if (this._hookName === "componentDidMount") {
//组件挂载期
//componentWillUpdate中的setState/forceUpdate应该被忽略
if (this._hydrating) {
//在render方法中调用setState也会被延迟到下一周期更新.这存在两种情况,
//1. 组件直接调用自己的setState
//2. 子组件调用父组件的setState,
this._renderInNextCycle = true;
}
} else {
//组件更新期
if (this._receiving) {
//componentWillReceiveProps中的setState/forceUpdate应该被忽略
return;
}
if (this._hydrating) {
//在componentDidMount方法里面可能执行多次setState方法,来引发update,但我们只需要一次update
this._renderInNextCycle = true;
// 在componentDidMount里调用自己的setState,延迟到下一周期更新
// 在更新过程中, 子组件在componentWillReceiveProps里调用父组件的setState,延迟到下一周期更新
return;
}
this.addJob("patch");
drainQueue([this]);
}
},
mergeStates() {
let instance = this.instance,
pendings = this._pendingStates,
n = pendings.length,
state = instance.state;
if (n === 0) {
return state;
}
let nextState = extend({}, state); //每次都返回新的state
for (let i = 0; i < n; i++) {
let pending = pendings[i];
if (pending && pending.call) {
pending = pending.call(instance, nextState, this.props);
}
extend(nextState, pending);
}
pendings.length = 0;
return nextState;
},
exec(updateQueue) {
var job = this._jobs.shift();
if (job) {
this[job](updateQueue);
}
},
patch(updateQueue) {
let { instance, context, props, vnode } = this;
if (this._receiving) {
let [lastVnode, nextVnode, nextContext] = this._receiving;
nextVnode.stateNode = instance;
//如果context与props都没有改变,那么就不会触发组件的receive,render,update等一系列钩子
//但还会继续向下比较
captureError(instance, "componentWillReceiveProps", [this.props, nextContext]);
delete this._receiving;
Refs.detachRef(lastVnode, nextVnode);
}
Refs.clearElementRefs();
let state = this.mergeStates();
let shouldUpdate = true;
if (!this._forceUpdate && !captureError(instance, "shouldComponentUpdate", [props, state, context])) {
shouldUpdate = false;
if (this.pendingVnode) {
this.vnode = this.pendingVnode;
delete this.pendingVnode;
}
} else {
var { props: lastProps, context: lastContext, state: lastState } = instance;
captureError(instance, "componentWillUpdate", [props, state, context]);
}
vnode.stateNode = instance;
delete this._forceUpdate;
//既然setState了,无论shouldComponentUpdate结果如何,用户传给的state对象都会作用到组件上
instance.props = props;
instance.state = state;
//vnode.lazyMount();
instance.context = context;
this.addJob("resolve");
if (shouldUpdate) {
this._hydrating = true;
this._hookArgs = [lastProps, lastState, lastContext];
this._hookName = "componentDidUpdate";
this.render(updateQueue);
}
updateQueue.push(this);
},
resolve: function(updateQueue) {
Refs.clearElementRefs();
let instance = this.instance;
let vnode = this.vnode;
var hookName = this._hookName;
delete this._hookName;
//执行componentDidMount/Update钩子
Refs.currentActiveQueue = updateQueue;
this._ending = true;
captureError(instance, hookName, this._hookArgs || []);
this._ending = false;
delete Refs.currentActiveQueue;
delete this._hookArgs;
//执行React Chrome DevTools的钩子
if (hookName === "componentDidMount") {
options.afterMount(instance);
} else {
options.afterUpdate(instance);
}
this._hydrating = false;
//执行组件虚拟DOM的ref回调
if (vnode._hasRef) {
Refs.fireRef(vnode, instance.__isStateless ? null : instance);
}
//如果在componentDidMount/Update钩子里执行了setState,那么再次渲染此组件
if (this._renderInNextCycle) {
delete this._renderInNextCycle;
this.addJob("patch");
updateQueue.push(this);
}
},
render(updateQueue) {
//vnode为组件虚拟DOM,也只能是组件虚拟DOM
let { vnode, pendingVnode, instance, parentContext } = this,
nextChildren,
rendered,lastChildren;
let target = pendingVnode || vnode;
if (this.willReceive === false) {
rendered = vnode.child;
delete this.willReceive;
} else {
let lastOwn = Refs.currentOwner;
Refs.currentOwner = instance;
rendered = captureError(instance, "render", []);
Refs.currentOwner = lastOwn;
}
if (this._hookName !== "componentDidMount") {
lastChildren = restoreChildren( this.vnode );
// console.log("新的",this.vnode, !!this.vnode.child, lastChildren.length);
}else{
// console.log("旧的");
lastChildren = [];
}
var oldProps = target.props;
target.props = { children: rendered };
nextChildren = fiberizeChildren(target);
target.props = oldProps;
if (!nextChildren.length) {
var placeHolder = createVText("#comment");
placeHolder.index = 0;
placeHolder.return = target;
nextChildren.push(placeHolder);
}
let childContext = parentContext,
number = typeNumber(rendered);
if (number === 7) {
if (!support16) {
pushError(instance, "render", new Error("React15 fail to render array"));
}
} else {
if (number < 5) {
var noSupport = !support16 && errorType[number];
if (noSupport) {
pushError(instance, "render", new Error("React15 fail to render " + noSupport));
}
} else {
childContext = getChildContext(instance, parentContext);
}
}
//child在React16总是表示它是父节点的第一个节点
var child = nextChildren[0];
options.diffChildren(lastChildren, nextChildren, target, childContext, updateQueue);
this.rendered = child; //现在还用于devtools中
let u = this;
do {
if (u.pendingVnode) {
u.vnode = u.pendingVnode;
delete u.pendingVnode;
}
} while ((u = u.parentUpdater));
}
};
export function getChildContext(instance, parentContext) {
if (instance.getChildContext) {
let context = instance.getChildContext();
if (context) {
parentContext = Object.assign({}, parentContext, context);
}
}
return parentContext;
}
export function getContextByTypes(curContext, contextTypes) {
let context = {};
if (!contextTypes || !curContext) {
return context;
}
for (let key in contextTypes) {
if (contextTypes.hasOwnProperty(key)) {
context[key] = curContext[key];
}
}
return context;
}
vnode.js
import { typeNumber, toArray, REACT_ELEMENT_TYPE } from "./util";
import { removeElement } from "./browser";
import { Refs } from "./Refs";
export function Vnode(type, vtype, props, key, ref, _hasProps) {
this.type = type;
this.vtype = vtype;
this.uuid = Math.random();
if (vtype) {
this.props = props;
this._owner = Refs.currentOwner;
if (key) {
this.key = key;
}
if (vtype === 1) {
this._hasProps = _hasProps;
this.childNodes = [];
}
let refType = typeNumber(ref);
if (refType === 3 || refType === 4 || refType === 5) {
//number, string, function
this._hasRef = true;
this.ref = ref;
}
}
/*
this.stateNode = null
*/
}
Vnode.prototype = {
getDOMNode() {
return this.stateNode || null;
},
collectNodes(isChild, ret) {
ret = ret || [];
if (isChild && this.vtype < 2) {
ret.push(this.stateNode);
} else {
for (var a = this.child; a; a = a.sibling) {
a.collectNodes(true, ret);
}
}
return ret;
},
batchMount() {
var parentNode = this.stateNode, childNodes = this.collectNodes();
childNodes.forEach(function(dom){
parentNode.appendChild(dom);
});
},
batchUpdate(updateMeta, nextChildren) {
var parentVnode = updateMeta.parentVElement,
parentNode = parentVnode.stateNode,
lastChildren = updateMeta.lastChilds,
insertPoint = updateMeta.insertPoint,
newLength = nextChildren.length,
oldLength = lastChildren.length,
inserted = [];
// console.log(nextChildren, lastChildren, "开始比较");
for (let i = 0; i < newLength; i++) {
let child = nextChildren[i];
let last = lastChildren[i];
if (last === child) {
//如果相同
} else if (last && inserted.indexOf(last) == -1 ) {
parentNode.replaceChild(child, last);//如果这个位置有DOM,并且它不在新的nextChildren之中
} else if (insertPoint) {
parentNode.insertBefore(child, insertPoint.nextSibling);
} else {
parentNode.appendChild(child);
}
insertPoint = child;
inserted.push(child);
}
if (newLength < oldLength) {
for (let i = newLength; i < oldLength; i++) {
removeElement(lastChildren[i]);
}
}
if(parentNode.nodeType === 1){
parentVnode.childNodes = toArray(parentNode.childNodes);
}
},
$$typeof: REACT_ELEMENT_TYPE
};
scheduler.js
import { options, clearArray } from "./util";
import { Refs } from "./Refs";
import { showError } from "./error";
const dirtyComponents = [];
function mountSorter(u1, u2) {
//按文档顺序执行
u1._dirty = false;
return u1._mountOrder - u2._mountOrder;
}
export function flushUpdaters() {
if (dirtyComponents.length) {
var currentQueue = clearArray(dirtyComponents).sort(mountSorter);
currentQueue.forEach(function(el) {
delete el._dirty;
});
drainQueue(currentQueue);
}
}
export function enqueueUpdater(updater) {
if (!updater._dirty) {
updater.addJob("patch");
updater._dirty = true;
dirtyComponents.push(updater);
}
}
export function drainQueue(queue) {
options.beforePatch();
//先执行所有元素虚拟DOMrefs方法(从上到下)
Refs.clearElementRefs();
let needSort = [],
unique = {},
updater;
while ((updater = queue.shift())) {
//queue可能中途加入新元素, 因此不能直接使用queue.forEach(fn)
if (updater._disposed) {
continue;
}
if (!unique[updater._mountOrder]) {
unique[updater._mountOrder] = 1;
needSort.push(updater);
}
updater.exec(queue);
}
//再执行所有setState/forceUpdate回调,根据从下到上的顺序执行
needSort.sort(mountSorter).forEach(function(updater) {
//console.log(updater.name, updater._mountOrder);
clearArray(updater._pendingCallbacks).forEach(function(fn) {
fn.call(updater.instance);
});
});
options.afterPatch();
showError();
}
from anu.
export function arrayRemoveOne(array, el) {
for (var i = array.length; i >= 0; i--) {
if (el === array[i]) {
array.splice(i, 1);
break;
}
}
}
export function arrayRemoveSome(array, toRemoved) {
for (var i = 0, n = toRemoved.length; i < n; i++) {
arrayRemoveOne(array, toRemoved[i]);
}
}
from anu.
Related Issues (20)
- input输入汉字不触发onChange事件 HOT 2
- 项目是否考虑升级为ts HOT 1
- 二次元魔法师希望你在那边过的开心
- 1 HOT 1
- 致敬,大佬走好 HOT 9
- RIP
- RIP HOT 5
- 致敬
- 致敬,默哀 HOT 3
- 一路走好
- 走好 HOT 3
- RIP
- 官方Q群加不进去
- 走好 大佬
- RIP ,走好大佬
- 大佬 走好
- 大佬 走好!
- 还有人维护这个库吗?
- IE8别用这个库,有内存泄漏问题,我因为自己的机器是ie11写完了才发现,特此提醒你们,并且以后也不会有人维护了 HOT 1
- 大佬走好
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
D3
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
-
Recommend Topics
-
javascript
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
-
web
Some thing interesting about web. New door for the world.
-
server
A server is a program made to process requests and deliver data to clients.
-
Machine learning
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from anu.