废话不多说,为了记录大前端的发展,研读React的内在,写下记录:
传统 diff 算法
计算一棵树形结构转换成另一棵树形结构的最少操作,是一个复杂且值得研究的问题。传统 diff 算法通过循环递归对节点进行依次对比,效率低下,算法复杂度达到 O(n^3),其中 n 是树中节点的总数。O(n^3) 到底有多可怕,这意味着如果要展示1000个节点,就要依次执行上十亿次的比较。这种指数型的性能消耗对于前端渲染场景来说代价太高了!现今的 CPU 每秒钟能执行大约30亿条指令,即便是最高效的实现,也不可能在一秒内计算出差异情况。
因此,如果 React 只是单纯的引入 diff 算法而没有任何的优化改进,那么其效率是远远无法满足前端渲染所要求的性能。
通过下面的 demo 可以清晰的描述传统 diff 算法的实现过程。
var result = [];
var diff = function (beforeDom, afterDom) {
// 获取较大节点树的长度
var count = Math.max(beforeDom.children.length, afterDom.children.length);
//循环遍历
for (var i=0; i<count; i++) {
var beforeTag = beforeDom.children[i];
var afterTag = afterDom.children[i];
// 添加 afterTag 节点
if (beforeTag === undefined) {
//beforeTag 不存在
//{type: type, elment: elment}
result.push({type: 'add', element: afterTag});
//删除 beforeTag 节点
} else if (afterTag === undefined) {
result.push({type: 'remove', element: beforeTag});
//节点名改变时,删除 beforeTag 节点,添加 afterTag 节点
} else if (beforeTag.tagName !== afterTag.tagName) {
result.push({type: 'remove', element: beforeTag});
result.push({type: 'add', element: afterTag});
// 节点不变而内容改变时,改变节点
} else if (beforeTag.innerHTML !== afterTag.innerHTML) {
//如果之前DomTag的子元素个数为0
if (beforeTag.children.length === 0) {
result.push({type: 'changed', beforeElement: beforeTag, afterElement: afterTag, html: afterTag.innerHTML});
} else {
// 递归比较
diff(beforeTag, afterTag)
}
}
}
return result;
}