React的简单实现
尝试写一个属于自己的 React 框架。uplateElement的难点
by ZhiLin GeGe
HTML
<script src="https://getfirebug.com/firebug-lite-debug.js"></script>
<div id="root"></div>
Babel + JSX
/** @jsx h */
function h(type,props,...children){
return {type,props,children};
}
function createElement(node){
//如果不是 HTML 的节点的形式
if(!node.type){
return document.createTextNode(node);
}
//如果是 HTML 的节点的形式
const $el = document.createElement(node.type);
node.children.map(createElement)
.forEach($el.appendChild.bind($el));
return $el;
}
//diff 算法
function changed(nodeNew,nodeOld){
return typeof(nodeNew) !== typeof(nodeOld) || //一个是对象,一个直接是字符串
typeof(nodeNew) === "string" && nodeNew !== nodeOld ||
nodeOld.type !== nodeNew.type; //其实应该还有很多需要比较,比如props,children,等,我觉得都可以用JSON.stringify来做判断
}
/* 难点! 尤其是index的使用 和 真实的子节点的获取$parent.childNodes[index] */
//更新算法, 进行比较,如果相等则不更新,否则更新;
function updateElement($parent, newNode, oldNode, index = 0) {
if (!oldNode) {
$parent.appendChild(createElement(newNode));
} else if (!newNode) {
$parent.removeChild(oldNode);
} else if (changed(newNode, oldNode)) {
$parent.replaceChild(createElement(newNode),$parent.childNodes[index]);
} else if (newNode.type){ //如果自己没变,就递归子元素
const length = Math.max(newNode.children.length,oldNode.children.length);
for (let i=0; i<length; i++){
updateElement($parent.childNodes[index],newNode.children[i],oldNode.children[i],i);
}
}
}
const list = (
<ul class="list">
<li>苹果</li>
<li>2</li>
</ul>
);
const $root = document.getElementById('root');
$root.appendChild(createElement(list));
let listNew = (
<ul class="list">
<li>苹果</li>
<li>0</li>
</ul>
);
let passedTime = 3;
setInterval(()=>{
listNew = (
<ul class="list">
<li>苹果</li>
<li>{++passedTime}</li>
</ul>
);
updateElement($root,listNew,list);
},1000);