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);