子节点更新和ReactTextComponent
无论是原生组件还是复合组件,在更新时,都有可能需要更新其子节点。当然,对于复合组件来说,归根结底也是原生组件的子节点更新,因此,当原生组件的属性发生更新时,就会执行_updateDOMChildren。
在_updateDOMChildren时,会将props.children进行预处理。预处理的过程中,还会将文本内容转换为ReactTextComponent,这是一个特殊的抽象组件子类,专门负责显示文字信息,ReactTextComponent的挂载过程非常简单
mountComponent: function(rootID) {
ReactComponent.Mixin.mountComponent.call(this, rootID);
return (
'<span id="' + rootID + '">' +
escapeTextForBrowser(this.props.text) +
'</span>'
);
},
ReactTextComponent会将文本内容进行escape处理,内部是通过正则表达式进行的字符替换,其实现细节可以在src/utils/escapeTextForBrowser中查看。
预处理后的children信息存储到一个对象中,记为nextChildren,当前的子节点信息则存储在_renderedChildren中,然后调用ReactMultiChildren中的updateMultiChild方法进行更新。
在执行updateMultiChild时,会进行两轮判断:
- 出现在nextChildren且同时出现在_renderedComponent中,说明该子节点是需要保留的节点,但有可能需要根据情况调整位置
- 只出现在_renderedComponent但没有出现在nextChildren中,说明该节点可以直接卸载。
- 只出现在了nextChildren中,但没有出现在_renderedComponent中,说明该节点是新插入的。
所有的DOM操作都会先被添加到不同类型的队列之中,这三个队列分别是:移动队列、卸载队列和插入队列。
为了防止_renderedChildren中有需要被卸载但没有被处理的元素,在遍历了nextChlidren之后,还会在遍历一遍_renderedChildren。如果某个元素只出现在了_renderedChildren中而没有出现在nextChildren中,就说明需要卸载该节点。