今天休息 写写博客 带来react diff
- 实现一个createElement类123456789101112131415161718192021222324252627282930313233343536373839404142434445class CreateElement {constructor(tagName, props, children) {this.tagName = tagName;this.props = isObject(props) ? props : {};this.children =children ||(!isNotEmptyObj(this.props) &&((isString(props) && [props]) || (isArray(props) && props))) ||[];this.key = props ? props.key : void NOKEY;// Calculate the number of nodeslet count = 0;aryForEach(this.children, (item, index) => {if (item instanceof Element) {count += item.count;} else {this.children[index] = "" + item;}count++;});this.count = count;}render() {// Construction based on tagNameconst dom = document.createElement(this.tagName);// Setting up propsobjForEach(this.props, propName =>dom.setAttribute(propName, this.props[propName]));// Rendering childrenaryForEach(this.children, child => {const childDom =child instanceof Element? child.render() // If the child node is also a virtual DOM, the DOM node is constructed recursively: document.createTextNode(child); // If the string, only the text node is constructeddom.appendChild(childDom);});return dom;}}
上面就实现了一个React.createElement的方法
进入dom diff的主要内容
123456789// 获取到新老树之间的patchesfunction diff(oTree, nTree) {// Node Locationlet index = 0;// Difference recordconst patches = {};dfsWalk(oTree, nTree, index, patches);return patches;}来看看dfsWalk都做了些什么
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172function dfsWalk(oNode, nNode, index, patches) {const currentPatch = [];// First renderif (nNode === null) return;// 节点是文字if (isString(oNode) && isString(nNode)) {oNode !== nNode &¤tPatch.push({type: TEXT,content: nNode});// Same label and same key} else if (oNode.tagName === nNode.tagName && oNode.key === nNode.key) {// At least one party is worth itif (isNotEmptyObj(oNode.props) || isNotEmptyObj(nNode.props)) {// Computation of props resultsconst propsPatches = diffProps(oNode, nNode);// If there are differences, reorderpropsPatches &¤tPatch.push({type: PROPS,props: propsPatches});}// 如果新node props不为空if (isNotEmptyObj(nNode.props) || !nNode.props.hasOwnProperty("ignore")) {(oNode.children.length || nNode.children.length) &&diffChildren(oNode.children,nNode.children,index,patches,currentPatch);}} else {// If they don't meet the above requirements, they should be replaced directly.currentPatch.push({ type: REPLACE, node: nNode });}currentPatch.length && (patches[index] = currentPatch);}function diffChildren(oChildren, nChildren, index, patches, currentPatch) {// Relatively Simplified Moving Pathconst diffs = listDiff(oChildren, nChildren, "key");// Retention elementnChildren = diffs.children;// Record sort displacementdiffs.moves.length &¤tPatch.push({ type: REORDER, moves: diffs.moves });// depth-first traversallet leftNode = null;let currentNodeIndex = index;aryForEach(oChildren, (_item, _index) => {const nChild = nChildren[_index];currentNodeIndex =leftNode && leftNode.count? currentNodeIndex + leftNode.count + 1: currentNodeIndex + 1;// 如果不相等 继续dfswalk 会出现断层的情况 patches {1, 5, 7}_item !== nChild && dfsWalk(_item, nChild, currentNodeIndex, patches);leftNode = _item;});}
其实这个就是收集patches的函数
patches收集到了 就要准备开始更新视图
1234567891011121314151617181920const patches = diff(tree, newTree);// 关键的代码来了function patch(node, patches) {const walker = { index: 0 };dfsWalk(node, walker, patches);}// 递归 给每一个node 进行patch操作function dfsWalk(node, walker, patches) {const currentPatches = patches[walker.index];node.childNodes &&aryForEach(node.childNodes, item => {walker.index++;dfsWalk(item, walker, patches);});currentPatches && applyPatches(node, currentPatches);}来看看 applyPatches是如何工作的
123456789101112131415161718192021222324252627282930// Update typefunction applyPatches(node, currentPatches) {aryForEach(currentPatches, item => {switch (item.type) {case REPLACE:const nNode = isString(item.node)? document.createTextNode(item.node): item.node.render();node.parentNode.replaceChild(nNode, node);break;case REORDER:reorderChildren(node, item.moves);break;case PROPS:setProps(node, item.props);break;case TEXT:if (node.textContent) {// Use plain textnode.textContent = item.content;} else {// Only valid for CDATA fragments, comment, Processing Instruction nodes, or text nodesnode.nodeValue = item.content;}break;default:throw new Error("Unknown patch type " + item.type);}});}
这就是react diff真正的流程 目前发现的最好一篇文章