关于源码解读的系列文章,能够关注个人github的这个仓库, 如今才刚刚写,后续有空就写点。争取把react源码剖析透学习透。有不正确的地方但愿你们帮忙指正。你们互相学习,共同进步。html
本篇文章是官方文档的翻译,英文原文请访问官网node
这个章节是stack reconciler的一些实现说明.react
它的技术性很强并假定你能彻底理解React的公开API,以及它是如何划分为核心、渲染器和协调器的。若是你对React代码不是很熟悉,请先阅读代码概览。ios
它还假定你可以理解React组件、实例和元素的区别。git
Stack reconciler 被用在React 15 以及更早的版本中, 它在源代码中的位置是src/renderers/shared/stack/reconciler.github
Paul O'Shannessy给出了一个关于从零开始构建React的讨论,在很大程度上对本文档给予了启发。算法
本文档与上边的视频都是对实际代码库的简化,所以你能够经过熟悉二者来更好地理解。数组
协调器自己没有公共 API. 可是诸如React DOM 和React Native的渲染器使用它依据用户所编写的React组件来有效地更新用户界面.浏览器
让咱们考虑首次装载组件的情形:缓存
ReactDOM.render(<App />, rootEl);
React DOM会将 <App />
传递给协调器。请记住, <App />
是一个React元素,也就是说是对哪些要渲染的东西的说明。你能够把它当作一个普通的对象:
console.log(<App />); // { type: App, props: {} }
协调器(reconciler)会检查 App
是类仍是函数。若是 App
是函数,协调器会调用App(props)
来获取所渲染的元素。若是App
是类,协调器则会使用new App(props)
建立一个App
实例,调用 componentWillMount()
生命周期方法,进而调用 render()
方法来获取所渲染的元素。不管如何,协调器都会学习App元素的“渲染行为”。
此过程是递归的。App
可能渲染为<Greeting />
,而<Greeting />
可能渲染为 <Button />
,如此类推。由于协调器会依次学习他们各自将如何渲染,因此协调器会递归地“向下钻取”全部用户定义组件。
你能够经过以下伪代码来理解该过程:
function isClass(type) { // React.Component的子类都会含有这一标志 return ( Boolean(type.prototype) && Boolean(type.prototype.isReactComponent) ); } // This function takes a React element (e.g. <App />) // and returns a DOM or Native node representing the mounted tree. // 此函数读取一个React元素(例如<App />) // 并返回一个表达所装载树的DOM或内部节点。 function mount(element) { var type = element.type; var props = element.props; // 咱们以此判断所渲染元素: // 是以函数型运行该类型 // 仍是建立新实例并调用render()。 var renderedElement; if (isClass(type)) { // Component class var publicInstance = new type(props); // Set the props publicInstance.props = props; // Call the lifecycle if necessary if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } // 调用render()以获取所渲染元素 renderedElement = publicInstance.render(); } else { // 组件函数 renderedElement = type(props); } // 该过程是递归实现,缘由在于组件可能返回一个其它组件类型的元素。 return mount(renderedElement); // 注意:该实现不完整,且将无穷递归! 它只处理<App />或<Button />等元素。尚不处理<div />或<p />等元素。 } var rootEl = document.getElementById('root'); var node = mount(<App />); rootEl.appendChild(node);
注意:这的确是一段伪代码。它与真实的实现不一样。它会致使栈溢出,由于咱们尚未讨论什么时候中止递归。
让咱们回顾一下上面示例中的几个关键概念:
App
)与属性的普通对象。App
)能够为类或者函数,但它们都会被“渲染为”元素。该过程将没有任何意义,若是最终没有渲染内容到屏幕上。
除了用户定义的(“复合”)组件外, React元素还可能表示特定于平台的(“主机”)组件。例如,Button
可能会从其渲染方法中返回 <div />
。
若是元素的type属性是一个字符串,即表示咱们正在处理一个主机元素(host element):
console.log(<div />); // { type: 'div', props: {} }
主机元素(host elements)不存在关联的用户定义代码。
当协调器遇到主机元素(host element)时,它会让渲染器(renderer)装载它(mounting)。例如,React DOM将会建立一个DOM节点。
若是主机元素(host element)有子级,协调器(reconciler)则会用上述相同算法递归地将它们装载。而无论子级是主机元素(如<div><hr /></div>
)仍是混合元素(如<div><Button /></div>
)或是二者兼有。
由子级组件生成的DOM节点将被追加到DOM父节点,同时整的DOM结构会被递归装配。
注意:协调器自己(reconciler)并不与DOM捆绑。装载(mounting)的具体结果(有时在源代码中称为“装载映像”)取决于渲染器(renderer),可能为 DOM节点(React DOM)、字符串(React DOM服务器)或表示本机视图的数值(React Native)。
咱们来扩展一下代码,以处理主机元素(host elements):
function isClass(type) { // React.Component 子类含有这一标志 return ( Boolean(type.prototype) && Boolean(type.prototype.isReactComponent) ); } // 该函数仅处理含复合类型的元素。 例如,它处理<App />和<Button />,但不处理<div />。 function mountComposite(element) { var type = element.type; var props = element.props; var renderedElement; if (isClass(type)) { // 组件类 var publicInstance = new type(props); // 设置属性 publicInstance.props = props; // 若必要,则调用生命周期函数 if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } renderedElement = publicInstance.render(); } else if (typeof type === 'function') { // 组件函数 renderedElement = type(props); } // 该过程是递归,一旦该元素为主机(如<div />}而非复合(如<App />)时,则逐渐结束 return mount(renderedElement); } // 该函数仅处理含主机类型的元素(handles elements with a host type)。 例如,它处理<div />和<p />但不处理<App />。 function mountHost(element) { var type = element.type; var props = element.props; var children = props.children || []; if (!Array.isArray(children)) { children = [children]; } children = children.filter(Boolean); // 该代码块不可出如今协调器(reconciler)中。 // 不一样渲染器(renderers)可能会以不一样方式初始化节点。 // 例如,React Native会生成iOS或Android视图。 var node = document.createElement(type); Object.keys(props).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, props[propName]); } }); // 装载子节点 children.forEach(childElement => { // 子节点有多是主机元素(如<div />)或复合元素(如<Button />). // 因此咱们应该递归的装载 var childNode = mount(childElement); // 此行代码还是特定于渲染器的。不一样的渲染器则会使用不一样的方法 node.appendChild(childNode); }); // 返回DOM节点做为装载结果 // 此处即为递归结束. return node; } function mount(element) { var type = element.type; if (typeof type === 'function') { // 用户定义的组件 return mountComposite(element); } else if (typeof type === 'string') { // 平台相关的组件,好比说浏览器中的div,ios和安卓中的视图 return mountHost(element); } } var rootEl = document.getElementById('root'); var node = mount(<App />); rootEl.appendChild(node);
该代码可以工做但仍与协调器(reconciler)的真正实现相差甚远。其所缺乏的关键部分是对更新的支持。
React 的关键特征是您能够从新渲染全部内容, 它不会从新建立 DOM 或重置状态:
ReactDOM.render(<App />, rootEl); // 应该从新使用现存的 DOM: ReactDOM.render(<App />, rootEl);
可是, 上面的实现只知道如何装载初始树。它没法对其执行更新, 由于它没有存储全部必需的信息, 例如全部 publicInstance
,
或者哪一个 DOM 节点 对应于哪些组件。
堆栈协调(stack reconciler)的基本代码是经过使 mount () 函数成为一个方法并将其放在类上来解决这一问题。
这种方式有一些缺陷,可是目前代码中仍然使用的是这种方式。不过目前咱们也正在重写协调器(reconciler)
咱们将建立两个类: DOMComponent 和 CompositeComponent , 而不是单独的 mountHost 和 mountComposite 函数。
两个类都有一个接受 element 的构造函数, 以及一个能返回已装入节点的 mount () 方法。咱们将用一个能实例化正确类的工厂函数替换掉以前
例子里的mount函数:
function instantiateComponent(element) { var type = element.type; if (typeof type === 'function') { // 用户自定义组件 return new CompositeComponent(element); } else if (typeof type === 'string') { // 特定于平台的组件 return new DOMComponent(element); } }
首先, 让咱们考虑如何实现 CompositeComponent
:
class CompositeComponent { constructor(element) { this.currentElement = element; this.renderedComponent = null; this.publicInstance = null; } getPublicInstance() { // 针对复合组合, 返回类的实例. return this.publicInstance; } mount() { var element = this.currentElement; var type = element.type; var props = element.props; var publicInstance; var renderedElement; if (isClass(type)) { // 组件类 publicInstance = new type(props); // 设置属性 publicInstance.props = props; // 若是有必要,调用生命周期 if (publicInstance.componentWillMount) { publicInstance.componentWillMount(); } renderedElement = publicInstance.render(); } else if (typeof type === 'function') { // Component function publicInstance = null; renderedElement = type(props); } // Save the public instance this.publicInstance = publicInstance; // 经过element实例化内部的child实例,这个实例有多是DOMComponent,好比<div /> or <p /> // 也多是CompositeComponent 好比说<App /> or <Button /> var renderedComponent = instantiateComponent(renderedElement); this.renderedComponent = renderedComponent; // 增长渲染输出 return renderedComponent.mount(); } }
这与咱们之前的 mountComposite()
实现没有太大的不一样, 但如今咱们能够保存一些信息,
好比this.currentElement
、this.renderedComponent
和 this.publicInstance
,这些保存的信息会在更新期间被使用。
请注意, CompositeComponent
的实例与用户提供的 element.type
的实例不是一回事。CompositeComponent
是咱们的协调器(reconciler)的一个实现细节, 从不向用户公开。
用户自定义类是咱们从 element.type
读取的,而且经过 CompositeComponent
建立它的一个实例。
为避免混乱,咱们将CompositeComponent
和DOMComponent
的实例称为“内部实例”。
因为它们的存在, 咱们能够将一些长寿数据(ong-lived)与它们关联起来。只有渲染器(renderer)和协调器(reconciler)知道它们的存在。
另外一方面, 咱们将用户定义的类的实例称为 "公共实例"(public instance)。公共实例是您在 render()
和自定义组件的其余方法中看到的 this
mountHost()
函数被重构为 DOMComponent
类上的 mount()
方法, 也看起来很熟悉:
class DOMComponent { constructor(element) { this.currentElement = element; this.renderedChildren = []; this.node = null; } getPublicInstance() { // For DOM components, only expose the DOM node. return this.node; } mount() { var element = this.currentElement; var type = element.type; var props = element.props; var children = props.children || []; if (!Array.isArray(children)) { children = [children]; } // Create and save the node var node = document.createElement(type); this.node = node; // Set the attributes Object.keys(props).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, props[propName]); } }); // Create and save the contained children. // Each of them can be a DOMComponent or a CompositeComponent, // depending on whether the element type is a string or a function. var renderedChildren = children.map(instantiateComponent); this.renderedChildren = renderedChildren; // Collect DOM nodes they return on mount var childNodes = renderedChildren.map(child => child.mount()); childNodes.forEach(childNode => node.appendChild(childNode)); // Return the DOM node as mount result return node; } }
从 mountHost () 重构后的主要区别在于, 咱们如今将 this.node
和 this.renderedChildren
与内部 DOM 组件实例相关联。
咱们还将使用它们在未来应用非破坏性更新。
所以, 每一个内部实例 (复合实例或主机实例)(composite or host) 如今都指向内部的子实例。为帮助可视化, 若是功能 <App>
组件呈现 <Button>
类组件, 而且 <Button>
类呈现<div>
, 则内部实例树将以下所显示:
[object CompositeComponent] { currentElement: <App />, publicInstance: null, renderedComponent: [object CompositeComponent] { currentElement: <Button />, publicInstance: [object Button], renderedComponent: [object DOMComponent] { currentElement: <div />, node: [object HTMLDivElement], renderedChildren: [] } } }
在 DOM 中, 您只会看到<div>
。可是, 内部实例树同时包含复合和主机内部实例(composite and host internal instances)。
内部的复合实例须要存储下面的信息:
DOMComponent
或一个CompositeComponent
。内部的主机实例须要存储下面的信息:
DOMComponent
or a CompositeComponent
。(All the child internal instances. Each of them can be either a DOMComponent
or a CompositeComponent
).若是你很难想象一个内部的实例树是如何在更复杂的应用中构建的, React DevTools)能够给出一个很是接近的近似,由于它突出显示了带有灰色的主机实例,以及用紫色表示的组合实例:
<img src="../images/implementation-notes-tree.png" width="500" style="max-width: 100%" alt="React DevTools tree" />
为了完成这个重构,咱们将引入一个函数,它将一个完整的树挂载到一个容器节点,就像ReactDOM.render()
。它返回一个公共实例,也相似于 ReactDOM.render()
:
function mountTree(element, containerNode) { // 建立顶级内部实例 var rootComponent = instantiateComponent(element); // 将顶级组件装载到容器中 var node = rootComponent.mount(); containerNode.appendChild(node); // 返回它所提供的公共实例 var publicInstance = rootComponent.getPublicInstance(); return publicInstance; } var rootEl = document.getElementById('root'); mountTree(<App />, rootEl);
如今,咱们有了保存有它们的子节点和DOM节点的内部实例,咱们能够实现卸载。对于一个复合组件(composite component),卸载将调用一个生命周期钩子而后递归进行。
class CompositeComponent { // ... unmount() { // Call the lifecycle hook if necessary var publicInstance = this.publicInstance; if (publicInstance) { if (publicInstance.componentWillUnmount) { publicInstance.componentWillUnmount(); } } // Unmount the single rendered component var renderedComponent = this.renderedComponent; renderedComponent.unmount(); } }
对于DOMComponent
,卸载操做让每一个孩子进行卸载:
class DOMComponent { // ... unmount() { // Unmount all the children var renderedChildren = this.renderedChildren; renderedChildren.forEach(child => child.unmount()); } }
在实践中,卸载DOM组件也会删除事件侦听器并清除一些缓存,为了便于理解,咱们暂时跳过这些细节。
如今咱们能够添加一个顶级函数,叫做unmountTree(containerNode)
,它与ReactDOM.unmountComponentAtNode()
相似:
function unmountTree(containerNode) { // Read the internal instance from a DOM node: // (This doesn't work yet, we will need to change mountTree() to store it.) var node = containerNode.firstChild; var rootComponent = node._internalInstance; // Unmount the tree and clear the container rootComponent.unmount(); containerNode.innerHTML = ''; }
为了使其工做,咱们须要从一个DOM节点读取一个内部根实例。咱们将修改 mountTree()
以将 _internalInstance
属性添加到DOM 根节点。
咱们也将教mountTree()
去销毁任何现存树,以便未来它能够被屡次调用:
function mountTree(element, containerNode) { // Destroy any existing tree if (containerNode.firstChild) { unmountTree(containerNode); } // Create the top-level internal instance var rootComponent = instantiateComponent(element); // Mount the top-level component into the container var node = rootComponent.mount(); containerNode.appendChild(node); // Save a reference to the internal instance node._internalInstance = rootComponent; // Return the public instance it provides var publicInstance = rootComponent.getPublicInstance(); return publicInstance; }
如今,能够反复运行unmountTree()
或者 mountTree()
,清除旧树而且在组件上运行 componentWillUnmount()
生命周期钩子。
在上一节中,咱们实现了卸载。然而,若是每一个组件的prop的变更都要卸载并挂载整个树,这是不可接受的。幸亏咱们设计了协调器。
协调器(reconciler)的目标是重用已存在的实例,以便保留DOM和状态:
var rootEl = document.getElementById('root'); mountTree(<App />, rootEl); // 应该重用现有的DOM: mountTree(<App />, rootEl);
咱们将用一种方法扩展咱们的内部实例。
除了 mount()
和 unmount()
。DOMComponent
和 CompositeComponent
将实现一个新的方法,它叫做 receive(nextElement)
:
class CompositeComponent { // ... receive(nextElement) { // ... } } class DOMComponent { // ... receive(nextElement) { // ... } }
它的工做是作任何须要的工做,以使组件(及其任何子节点) 可以根据 nextElement
提供的信息保持信息为最新状态。
这是常常被描述为"virtual DOM diffing"的部分,尽管真正发生的是咱们递归地遍历内部树,并让每一个内部实例接收到更新指令。
当一个复合组件接收到一个新元素(element)时,咱们运行componentWillUpdate()生命周期钩子。
而后,咱们使用新的props从新render组件,并得到下一个render的元素(rendered element):
class CompositeComponent { // ... receive(nextElement) { var prevProps = this.currentElement.props; var publicInstance = this.publicInstance; var prevRenderedComponent = this.renderedComponent; var prevRenderedElement = prevRenderedComponent.currentElement; // Update *own* element this.currentElement = nextElement; var type = nextElement.type; var nextProps = nextElement.props; // Figure out what the next render() output is var nextRenderedElement; if (isClass(type)) { // Component class // Call the lifecycle if necessary if (publicInstance.componentWillUpdate) { publicInstance.componentWillUpdate(nextProps); } // Update the props publicInstance.props = nextProps; // Re-render nextRenderedElement = publicInstance.render(); } else if (typeof type === 'function') { // Component function nextRenderedElement = type(nextProps); } // ...
下一步,咱们能够看一下渲染元素的type。若是自从上次渲染,type 没有被改变,组件接下来能够被适当更新。
例如,若是它第一次返回 <Button color="red" />
,而且第二次返回 <Button color="blue" />
,咱们能够告诉内部实例去 receive()
下一个元素:
// ... // 若是被渲染元素类型没有被改变, // 重用现有的组件实例. if (prevRenderedElement.type === nextRenderedElement.type) { prevRenderedComponent.receive(nextRenderedElement); return; } // ...
可是,若是下一个被渲染元素和前一个相比有一个不一样的type
,咱们不能更新内部实例。由于一个 <button> 不“能变”为一个 <input>.
相反,咱们必须卸载现有的内部实例并挂载对应于渲染的元素类型的新实例。
例如,这就是当一个以前被渲染的元素<button />
以后又被渲染成一个 <input />
的过程:
// ... // If we reached this point, we need to unmount the previously // mounted component, mount the new one, and swap their nodes. // Find the old node because it will need to be replaced var prevNode = prevRenderedComponent.getHostNode(); // Unmount the old child and mount a new child prevRenderedComponent.unmount(); var nextRenderedComponent = instantiateComponent(nextRenderedElement); var nextNode = nextRenderedComponent.mount(); // Replace the reference to the child this.renderedComponent = nextRenderedComponent; // Replace the old node with the new one // Note: this is renderer-specific code and // ideally should live outside of CompositeComponent: prevNode.parentNode.replaceChild(nextNode, prevNode); } }
总而言之,当一个复合组件(composite component)接收到一个新元素时,它可能会将更新委托给其渲染的内部实例((rendered internal instance),
或者卸载它,并在其位置上挂一个新元素。
另外一种状况下,组件将从新挂载而不是接收一个元素,而且这发生在元素的key
变化时。本文档中,咱们不讨论key 处理,由于它将使本来复杂的教程更加复杂。
注意,咱们须要添加一个叫做getHostNode()
的新方法到内部实例(internal instance),以即可以定位特定于平台的节点并在更新期间替换它。
它的实现对两个类都很简单:
class CompositeComponent { // ... getHostNode() { // 请求渲染的组件提供它(Ask the rendered component to provide it). // 这将递归地向下钻取任何组合(This will recursively drill down any composites). return this.renderedComponent.getHostNode(); } } class DOMComponent { // ... getHostNode() { return this.node; } }
主机组件实现(例如DOMComponent), 是以不一样方式更新.当它们接收到一个元素时,它们须要更新底层特定于平台的视图。在 React DOM 中,这意味着更新 DOM 属性:
class DOMComponent { // ... receive(nextElement) { var node = this.node; var prevElement = this.currentElement; var prevProps = prevElement.props; var nextProps = nextElement.props; this.currentElement = nextElement; // Remove old attributes. Object.keys(prevProps).forEach(propName => { if (propName !== 'children' && !nextProps.hasOwnProperty(propName)) { node.removeAttribute(propName); } }); // Set next attributes. Object.keys(nextProps).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, nextProps[propName]); } }); // ...
接下来,主机组件须要更新它们的子元素。与复合组件不一样的是,它们可能包含多个子元素。
在这个简化的例子中,咱们使用一个内部实例的数组并对其进行迭代,是更新或替换内部实例,这取决于接收到的type是否与以前的type匹配。
真正的调解器(reconciler)同时在账户中获取元素的key而且追踪变更,除了插入与删除,可是咱们如今先忽略这一逻辑。
咱们在列表中收集DOM操做,这样咱们就能够批量地执行它们。
// ... // // 这些是React元素(element)数组: var prevChildren = prevProps.children || []; if (!Array.isArray(prevChildren)) { prevChildren = [prevChildren]; } var nextChildren = nextProps.children || []; if (!Array.isArray(nextChildren)) { nextChildren = [nextChildren]; } // 这些是内部实例(internal instances)数组: var prevRenderedChildren = this.renderedChildren; var nextRenderedChildren = []; // 当咱们遍历children时,咱们将向数组中添加操做。 var operationQueue = []; // 注意:如下章节大大减化! // 它不处理reorders,空children,或者keys。 // 它只是用来解释整个流程,而不是具体的细节。 for (var i = 0; i < nextChildren.length; i++) { // 尝试为这个子级获取现存内部实例。 var prevChild = prevRenderedChildren[i]; // 若是在这个索引下没有内部实例,那说明是一个child被添加了末尾。 // 这时应该去建立一个内部实例,挂载它,并使用它的节点。 if (!prevChild) { var nextChild = instantiateComponent(nextChildren[i]); var node = nextChild.mount(); // 记录一下咱们未来须要append一个节点(node) operationQueue.push({type: 'ADD', node}); nextRenderedChildren.push(nextChild); continue; } // 若是它的元素类型匹配,咱们只须要更新该实例便可 // 例如, <Button size="small" /> 能够更新为 // <Button size="large" /> 可是不能被更新为 <App />. var canUpdate = prevChildren[i].type === nextChildren[i].type; // 若是咱们不能更新现有的实例,咱们就必须卸载它。而后装一个新的替代它。 if (!canUpdate) { var prevNode = prevChild.getHostNode(); prevChild.unmount(); var nextChild = instantiateComponent(nextChildren[i]); var nextNode = nextChild.mount(); // 记录一下咱们未来须要替换这些nodes operationQueue.push({type: 'REPLACE', prevNode, nextNode}); nextRenderedChildren.push(nextChild); continue; } // 若是咱们能够更新现存的内部实例(internal instance), // 咱们仅仅把下一个元素传入其receive便可,让其receive函数处理它的更新便可 prevChild.receive(nextChildren[i]); nextRenderedChildren.push(prevChild); } // 最后,卸载(unmount)哪些不存在的children for (var j = nextChildren.length; j < prevChildren.length; j++) { var prevChild = prevRenderedChildren[j]; var node = prevChild.getHostNode(); prevChild.unmount(); // 记录一下咱们未来须要remove这些node operationQueue.push({type: 'REMOVE', node}); } // Point the list of rendered children to the updated version. this.renderedChildren = nextRenderedChildren; // ...
做为最后一步,咱们执行DOM操做。仍是那句话,真正的协调器(reconciler)代码更复杂,由于它还能处理移动:
// ... // 处理队列里的operation。 while (operationQueue.length > 0) { var operation = operationQueue.shift(); switch (operation.type) { case 'ADD': this.node.appendChild(operation.node); break; case 'REPLACE': this.node.replaceChild(operation.nextNode, operation.prevNode); break; case 'REMOVE': this.node.removeChild(operation.node); break; } } } }
这是用来更新主机组件(host components)的。
如今 CompositeComponent 与 DOMComponent 都实现了 receive(nextElement) 方法,
咱们如今能够改变顶级 mountTree() 函数了,当元素(element)的type
相同时,咱们可使用receive了。
function mountTree(element, containerNode) { // Check for an existing tree if (containerNode.firstChild) { var prevNode = containerNode.firstChild; var prevRootComponent = prevNode._internalInstance; var prevElement = prevRootComponent.currentElement; // 若是能够,使用现存根组件 if (prevElement.type === element.type) { prevRootComponent.receive(element); return; } // 不然,卸载现存树 unmountTree(containerNode); } // ... }
如今调用 mountTree()两次,一样的类型不会先卸载再装载了:
var rootEl = document.getElementById('root'); mountTree(<App />, rootEl); // 复用现存 DOM: mountTree(<App />, rootEl);
These are the basics of how React works internally.
与真正的代码库相比,这个文档被简化了。有一些重要的方面咱们没有提到:
将主机内部类传递给协调器(reconciler)。例如,React DOM 告诉协调器使用 ReactDOMComponent
做为主机内部实现实例。
ReactMultiChild
的mixin中,它被主机内部实例类实如今 React DOM和 React Native时都使用。为了warning而嵌套的当前DOM(the current DOM nesting for the warnings)以及任何“全局”到特定的更新都是有用的。
事务还能够确保在更新后“清除全部内容”。例如,由 React DOM提供的事务类在任何更新以后恢复input的选中与否。
ReactMount
中能够查看此教程中相似 mountTree()
和 unmountTree()
的代码.它负责装载(mounting)和卸载(unmounting)顶级组件。ReactNativeMount
is its React Native analog.
在教程中与DOMComponent等同. 它实现了 React DOM渲染器(renderer)的主机组件类(host component class。ReactNativeBaseComponent
is its React Native analog.
在教程中与 CompositeComponent
等同. 它处理调用用户定义的组件并维护它们的状态。
instantiateReactComponent
instantiateComponent()
等同。ReactReconciler
是一个具备 mountComponent()
, receiveComponent()
, 和 unmountComponent()
方法的封装.
它调用内部实例的底层实现,但也包含了全部内部实例实现共享的代码。
根据元素的 key
,实现了mounting、updating和unmounting的逻辑.
独立于渲染器的操做队列,实现了处理child的插入、删除和移动
mount()
, receive()
, and unmount()
被称做 mountComponent()
,receiveComponent()
, and unmountComponent()
可是他们却接收elements
_currentElement
. 在整个代码库中,它们被认为是只读的公共字段。堆栈协调器具备固有的局限性, 如同步和没法中断工做或分割成区块。
咱们正在实现一个新的协调器Fiber reconciler,
你能够在这里看它的具体思路
未来咱们会用fiber协调器代替stack协调器(译者注:其实如今react16已经发布,在react16中fiber算法已经取代了stack算法)
阅读next section以了解有关协调器的当前实现的详细信息。