create-vnode
Version:
一个简易的虚拟dom生成器和渲染器,包含diff算法
113 lines (103 loc) • 2.98 kB
JavaScript
import {
isString,
isObject,
objForEach,
aryForEach,
setAttr,
toArray
} from "../util/index.js";
import { REPLACE, REORDER, PROPS, TEXT, NOKEY } from "../util/common.js";
function patch(node, patches) {
const walker = { index: 0 };
dfsWalk(node, walker, patches);
}
// 深度遍历更新
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);
}
// 更新类型
function 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) {
// 使用纯文本
node.textContent = item.content;
} else {
// 仅仅对CDATA片段,注释comment,Processing Instruction节点或text节点有效
node.nodeValue = item.content;
}
break;
default:
throw new Error("Unknown patch type " + item.type);
}
});
}
// 修改属性
function setProps(node, props) {
objForEach(props, key => {
if (props[key] === void NOKEY) {
node.removeAttribute(key);
} else {
setAttr(node, key, props[key]);
}
});
}
// 列表排序渲染
function reorderChildren(node, moves) {
const staticNodeList = toArray(node.childNodes);
const maps = {};
aryForEach(staticNodeList, node => {
// Element
if (node.nodeType === 1) {
const key = node.getAttribute("key");
key && (maps[key] = node);
}
});
aryForEach(moves, move => {
const index = move.index;
// 0:删除 1:替换
if (move.type === 0) {
// 找到对应节点删除
staticNodeList[index] === node.childNodes[index] &&
node.removeChild(node.childNodes[index]);
staticNodeList.splice(index, 1);
} else if (move.type === 1) {
let insertNode;
if (maps[move.item.key]) {
// 删除并返回节点
insertNode = node.removeChild(maps[move.item.key]);
// 获取删除节点位置
staticNodeList.splice(Array.prototype.indexOf.call(node.childNodes, maps[move.item.key]), 1);
} else {
// 创建节点
insertNode = isObject(move.item)
? move.item.render()
: document.createTextNode(move.item);
}
// 同步staticNodeList信息
staticNodeList.splice(index, 0, insertNode);
// 操作Dom
node.insertBefore(insertNode, node.childNodes[index] || null);
}
});
}
export default patch;