若是咱们在同一个容器中使用两次ReactDOM.render()
会发生什么?app
ReactDOM.render( <button className="blue" />, document.getElementById('container') ); // ... later ... // 应该替换掉 button 宿主实例吗? // 仍是在已有的 button 上更新属性? ReactDOM.render( <button className="red" />, document.getElementById('container') );
再次说明,React的工做是使宿主树和提供的React元素的树一致。肯定宿主树怎么样来响应新的信息的这个过程被称为协调。
协调有两种方法。React的简单的版本是抛弃已经存在的树,从新创建新的树:dom
let domContainer = document.getElementById('container'); // 清除树 domContainer.innerHTML = ''; // 建立新的宿主树 let domNode = document.createElement('button'); domNode.className = 'red'; domContainer.appendChild(domNode);
可是在DOM中,这是低效的,而且会丢失一些重要的信息像聚焦状态,选中状态,滚动状态等等。因此咱们但愿React像下面同样工做:code
let domNode = domContainer.firstChild; // Update existing host instance domNode.className = 'red';
换句话说,React须要决定那时候须要更新一个已经存在的宿主实例来响应新的React元素,那时候须要新建一个宿主实例。
这就提出了关于分别的问题,React的元素可能一直在变化,那理论上那时候引用同一个宿主实例呢?
在咱们的例子上是很简单的。咱们已经建立了一个<button>
做为第一个(也是惟一一个)子元素,而且咱们但愿在同一个地方再次渲染一个<button>
。咱们已经有一个<button>
的宿主实例,咱们就不须要再建立新的,再次使用它就行了。rem
这个已经与React的思想很是接近了。
若是元素的种类在树的同一个地方以前一次的渲染和接下来的渲染是相同的,React会再次使用已经存在的宿主实例。
下面是React带有备注的大体实现过程:get
// let domNode = document.createElement('button'); // domNode.className = 'blue'; // domContainer.appendChild(domNode); ReactDOM.render( <button className="blue" />, document.getElementById('container') ); // 能够再次使用吗? Yes! (button → button) // domNode.className = 'red'; ReactDOM.render( <button className="red" />, document.getElementById('container') ); // 能够再次使用吗? No! (button → p) // domContainer.removeChild(domNode); // domNode = document.createElement('p'); // domNode.textContent = 'Hello'; // domContainer.appendChild(domNode); ReactDOM.render( <p>Hello</p>, document.getElementById('container') ); // 能够再次使用吗? Yes! (p → p) // domNode.textContent = 'Goodbye'; ReactDOM.render( <p>Goodbye</p>, document.getElementById('container') );
这套规则对子树也一样适用。例如,当咱们在更新有两个<button>
子组件时的<dialog>
,React首先决定是否重用<dialog>
,
而后再对每个子组件进行相同的过程。class