本节是 stack reconciler程序的实现说明的集合。
本文有必定的技术含量,要对React公共API以及它如何分为核心,渲染器和协调(和解,reconciler)程序有很深的理解。若是你对React代码库不是很熟悉,请首先阅读代码库概述。html
它还假设你了解React组件的实例和元素之间的差别。node
stack reconciler
用于15版本和早期. 它的代码在 src/renderers/shared/stack/reconciler.react
Paul O’Shannessy谈到了从头开始构建react,这在很大程度上启发了这个文档。git
本文档和他的演讲都是对实际代码库的简化,所以你能够经过熟悉它们来得到更好的理解。github
reconciler(协调,调解)自己不存在公共的API。像React DOM和React Native这样的渲染器使用它根据用户编写的React组件有效地更新用户界面。算法
让咱们考虑第一次挂载组件:数组
ReactDOM.render(<App />, rootEl);
React DOM会将<App />
传递给调节器(reconciler)。请记住,<App />
是一个React元素,即对要呈现的内容的描述。你能够将其视为普通对象(笔者:不了解的能够查看这篇文章):缓存
console.log(<App />); // { type: App, props: {} }
调解器会检查这个App
是类仍是函数(对于这个得实现能够查看如何知道是函数仍是类这篇文章)。架构
若是App
是一个函数,则调解器将调用App(props)
来获取渲染元素。app
若是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) ); } // 这个函数接受一个React元素 (例如 <App />) // 而且返回一个已经挂载了树的DOM或原生节点 function mount(element) { var type = element.type; var props = element.props; // 咱们将肯定渲染元素的类型 // 函数就直接调用 // 类就实例化后调用render(). var renderedElement; if (isClass(type)) { // 类组件 var publicInstance = new type(props); // 设置props publicInstance.props = props; // 必要时调用生命周期方法 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
)和他的props.App
)能够是函数或者类,可是他们都会渲染这些元素。<App />
)的状况下建立DOM或Native树。若是咱们没有在屏幕上呈现某些内容,则此过程将毫无用处。
除了用户定义的(“复合”)组件以外,React元素还能够表示特定于平台的(“计算机”)组件。例如,Button
可能会从其render方法返回<div />
。
若是element的type
属性是一个字符串,咱们认为正在处理一个计算机元素:
console.log(<div />); // { type: 'div', props: {} }
没有与计算机元素关联的用户定义代码。
当协调程序(调解器)遇到这些计算机元素时,它会让渲染器(renderer)负责mounting它。例如,React DOM将建立一个DOM节点。
若是计算机元素具备子节点,则协调器以与上述相同的算法递归地mounts它们。子节点是不是计算机元素(<div><hr /></div>
)或用户合成的组件(<div><Button /></div>
),都没有关系,都会去让渲染器去负责mounting它。
由子组件生成的DOM节点将附加到父DOM节点,而且将递归地组装完整的DOM结构。
注意: 调解器自己与DOM无关。mounting(安装)的确切结果(有时在源代码中称为“mount image”)取决于渲染器,能够是DOM节点(React DOM),字符串(React DOM Server)或表示原生视图(React Native)。
若是咱们要扩展代码来处理计算机元素,它将以下所示:
function isClass(type) { // 继承自 React.Component 类有一个标签 isReactComponent 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); // 设置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); } // 这个函数仅仅处理计算机元素 // 例如它处理<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); // 这段代码不该该在协调器中。 // 不一样的渲染器可能对节点进行不一样的初始化。 // 例如,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); // 下面这个也是一个特定于平台的 // 它会根据不一样的渲染器来处理,这里只是一个假设他是一个dom渲染器 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 />) return mountHost(element); } } var rootEl = document.getElementById('root'); var node = mount(<App />); rootEl.appendChild(node);
这是有效的,但仍远未达到协调者的实际运行方式。关键的缺失部分是对更新的支持。
react的关键特色是你能够从新渲染全部东西,它不会从新建立DOM或重置状态。
ReactDOM.render(<App />, rootEl); // 应该重用现有的DOM: ReactDOM.render(<App />, rootEl);
可是,咱们上面的实现只知道如何挂载初始树。它没法对其执行更新,由于它不存储全部必需的信息,例如全部publicInstance
s,或哪些DOM节点
对应于哪些组件。
堆栈协调器代码库经过使mount
函数成为一个类上面的方法来解决这个问题。可是这种方法存在一些缺点,咱们在正在进行的协调重写任务中正朝着相反的方向去发展(笔者:目前fiber已经出来了)。不过 这就是它如今的运做方式。
咱们将建立两个类:DOMComponent
和CompositeComponent
,而不是单独的mountHost
和mountComposite
函数。
两个类都有一个接受元素的构造函数,以及一个返回已安装节点的mount()
方法。咱们将用实例化类的工厂替换顶级mount()
函数:
function instantiateComponent(element) { var type = element.type; if (typeof type === 'function') { // 用户定义的组件 return new CompositeComponent(element); } else if (typeof type === 'string') { // 特定于平台的组件,如计算机组件(<div />) 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)) { // Component class publicInstance = new type(props); // Set the props publicInstance.props = props; // Call the lifecycle if necessary 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; // 根据元素实例化子内部实例 // 他将是DOMComponent,例如<div />, <p /> // 或者是CompositeComponent,例如<App />,<Button /> var renderedComponent = instantiateComponent(renderedElement); this.renderedComponent = renderedComponent; // Mount the rendered output return renderedComponent.mount(); } }
这与咱们以前的mountComposite()
实现没什么不一样,但如今咱们能够存储一些信息,例如this.currentElement
,this.renderedComponent
和this.publicInstance
,在更新期间使用。
请注意,CompositeComponent
的实例与用户提供的element.type
的实例不一样。CompositeComponent
是咱们的协调程序的实现细节,永远不会向用户公开。用户定义的类是咱们从element.type
读取的,CompositeComponent
会建立这个类的实例。
为避免混淆,咱们将CompositeComponent
和DOMComponent
的实例叫作“内部实例”。 它们存在,所以咱们能够将一些长期存在的数据与它们相关联。只有渲染器和调解器知道它们存在。
相反,咱们将用户定义类的实例称为“公共实例(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]); } }); // 建立并保存包含的子元素 // 这些子元素,每一个均可以是DOMComponent或CompositeComponent // 这些匹配是依赖于元素类型的返回值(string或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)); // DOM节点做为mount的节点返回 return node; } }
与上面的相比,mountHost()
重构以后的主要区别是如今将this.node
和this.renderedChildren
与内部DOM组件实例相关联。咱们会用他来用于在后面作非破坏性的更新。
所以,每一个内部实例(复合或主机)如今都指向其子级内部实例。为了帮助可视化,若是函数<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>
。可是,内部实例树包含复合和主机内部实例。
复合内部实例须要存储:
DOMComponent
或CompositeComponent
。计算机内部实例须要存储:
DOMComponent
或CompositeComponent
。若是你正在努力想象如何在更复杂的应用程序中构建内部实例树,React DevTools能够给你一个近似的结果,由于它突显灰色的计算机实例,以及带紫色的复合实例:
为了完成这个重构,咱们将引入一个将完整树安装到容器节点的函数,就像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节点,那么咱们就能够实现卸载。对于复合组件,卸载会调用生命周期方法并进行递归。
class CompositeComponent { // ... unmount() { // 必要的时候调用生命周期方法 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() { // 卸载全部的子级 var renderedChildren = this.renderedChildren; renderedChildren.forEach(child => child.unmount()); } }
实际上,卸载DOM组件也会删除事件侦听器并清除一些缓存,但咱们将跳过这些细节。
咱们如今能够添加一个名为unmountTree(containerNode)
的新顶级函数,它相似于ReactDOM.unmountComponentAtNode()
:
function unmountTree(containerNode) { // 从DOM节点读取内部实例 // (目前这个不会正常工做, 咱们将须要改变mountTree()方法去存储) var node = containerNode.firstChild; var rootComponent = node._internalInstance; // 清除容器而且卸载树 rootComponent.unmount(); containerNode.innerHTML = ''; }
为了让他工做,咱们须要从DOM节点读取内部根实例。咱们将修改mountTree()
以将_internalInstance
属性添加到DOM根节点。咱们还将让mountTree()
去销毁任何现有树,以即可以屡次调用它:
function mountTree(element, containerNode) { // 销毁存在的树 if (containerNode.firstChild) { unmountTree(containerNode); } // 建立顶层的内部实例 var rootComponent = instantiateComponent(element); // 挂载顶层的组件到容器 var node = rootComponent.mount(); containerNode.appendChild(node); // 保存内部实例的引用 node._internalInstance = rootComponent; // 返回他提供的公共实例 var publicInstance = rootComponent.getPublicInstance(); return publicInstance; }
如今,重复运行unmountTree()
或运行mountTree()
,删除旧树并在组件上运行componentWillUnmount()
生命周期方法。
在上一节中,咱们实现了卸载。可是,若是每一个prop更改致使卸载并安装整个树,则React就会显得不是很好用了。协调程序的目标是尽量重用现有实例来保留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提供的描述保持同步。
这是常常被描述为“虚拟DOM区别”的部分,尽管真正发生的是咱们递归地遍历内部树并让每一个内部实例接收更新。
当复合组件接收新元素时,咱们运行componentWillUpdate()
生命周期方法。
而后咱们使用新的props从新渲染组件,并获取下一个渲染元素:
class CompositeComponent { // ... receive(nextElement) { var prevProps = this.currentElement.props; var publicInstance = this.publicInstance; var prevRenderedComponent = this.renderedComponent; var prevRenderedElement = prevRenderedComponent.currentElement; // 更新自有的元素 this.currentElement = nextElement; var type = nextElement.type; var nextProps = nextElement.props; // 弄清楚下一个render()的输出是什么 var nextRenderedElement; if (isClass(type)) { // 类组件 // 必要的时候调用生命周期 if (publicInstance.componentWillUpdate) { publicInstance.componentWillUpdate(nextProps); } // 更新props publicInstance.props = nextProps; // Re-render nextRenderedElement = publicInstance.render(); } else if (typeof type === 'function') { // 函数式组件 nextRenderedElement = type(nextProps); } // ...
接下来,咱们能够查看渲染元素的type
。若是自上次渲染后type
未更改,则下面的组件也能够在以前的基础上更新。
例如,若是第一次返回<Button color =“red"/>
,第二次返回<Button color =“blue"/>
,咱们能够告诉相应的内部实例receive()
下一个元素:
// ... // 若是渲染的元素类型没有改变, // 重用现有的组件实例 if (prevRenderedElement.type === nextRenderedElement.type) { prevRenderedComponent.receive(nextRenderedElement); return; } // ...
可是,若是下一个渲染元素的类型与先前渲染的元素不一样,咱们没法更新内部实例。<button />
不可能变成<input />
。
相反,咱们必须卸载现有的内部实例并挂载与呈现的元素类型相对应的新实例。例如,当先前呈现<button />
的组件呈现<input />
时,会发生这种状况:
// ... // 若是咱们到达了这一点,那么咱们就须要卸载以前挂载的组件 // 挂载新的一个,而且交换他们的节点 // 找到旧的节点,由于咱们须要去替换他 var prevNode = prevRenderedComponent.getHostNode(); // 卸载旧的子级而且挂载新的子级 prevRenderedComponent.unmount(); var nextRenderedComponent = instantiateComponent(nextRenderedElement); var nextNode = nextRenderedComponent.mount(); // 替换对子级的引用 this.renderedComponent = nextRenderedComponent; // 新的节点替换旧的 // 记住:下面的代码是特定于平台的,理想状况下是在CompositeComponent以外的 prevNode.parentNode.replaceChild(nextNode, prevNode); } }
总而言之,当复合组件接收到新元素时,它能够将更新委托给其呈现的内部实例,或者卸载它并在其位置安装新的实例。
在另外一个条件下,组件将从新安装而不是接收元素,即元素的key
已更改。咱们不讨论本文档中的key
处理,由于它为本来就很复杂的教程增长了更多的复杂性。
请注意,咱们须要将一个名为getHostNode()
的方法添加到内部实例协定中,以即可以在更新期间找到特定于平台的节点并替换它。它的实现对于两个类都很简单:
class CompositeComponent { // ... getHostNode() { // 请求渲染的组件提供他 // 这将向下递归复合组件 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; // 移除旧的属性 Object.keys(prevProps).forEach(propName => { if (propName !== 'children' && !nextProps.hasOwnProperty(propName)) { node.removeAttribute(propName); } }); // 设置接下来的属性 Object.keys(nextProps).forEach(propName => { if (propName !== 'children') { node.setAttribute(propName, nextProps[propName]); } }); // ...
而后,计算机组件须要更新他们的子组件。与复合组件不一样,它们可能包含多个子组件。
在这个简化的示例中,咱们使用内部实例数组并对其进行迭代,根据接收的类型是否与以前的类型匹配来更新或替换内部实例。除了插入和删除以外,真正的协调程序还会使用元素的键跟踪移动,但咱们将省略此逻辑。
咱们在列表中收集子级的DOM操做,以便批量执行它们:
// ... // 这个是React elements数组 var prevChildren = prevProps.children || []; if (!Array.isArray(prevChildren)) { prevChildren = [prevChildren]; } var nextChildren = nextProps.children || []; if (!Array.isArray(nextChildren)) { nextChildren = [nextChildren]; } // 这是内部实例的数组: var prevRenderedChildren = this.renderedChildren; var nextRenderedChildren = []; // 当咱们迭代子级的时候,咱们将会添加操做到数组 var operationQueue = []; //注意:下面的部分很是简单! //它的存在只是为了说明整个流程,而不是细节。 for (var i = 0; i < nextChildren.length; i++) { // 尝试获取此子级的现有内部实例 var prevChild = prevRenderedChildren[i]; // 若是这个索引下不存在内部实例,那就把子级被追加到后面。 // 建立一个新的内部实例,挂载他并使用他的节点 if (!prevChild) { var nextChild = instantiateComponent(nextChildren[i]); var node = nextChild.mount(); // 记录咱们须要追加的节点 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(); // 记录咱们须要交换的节点 operationQueue.push({type: 'REPLACE', prevNode, nextNode}); nextRenderedChildren.push(nextChild); continue; } // 若是咱们能够更新一个存在的内部实例 // 只须要让他接收下一个元素而且处理他本身的更新 prevChild.receive(nextChildren[i]); nextRenderedChildren.push(prevChild); } // 最后卸载不存在的元素的子级 for (var j = nextChildren.length; j < prevChildren.length; j++) { var prevChild = prevRenderedChildren[j]; var node = prevChild.getHostNode(); prevChild.unmount(); // 记录咱们须要移除的节点 operationQueue.push({type: 'REMOVE', node}); } // 将渲染的子级列表指到更新的版本里 this.renderedChildren = nextRenderedChildren; // ...
做为最后一步,咱们执行DOM操做。一样,真正的协调代码更复杂,由于它也处理移动:
// ... // Process the operation queue. 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; } } } }
这就是更新计算机组件(DOMComponent)
如今CompositeComponent
和DOMComponent
都实现了receive(nextElement)
方法,咱们能够更改顶级mountTree()
函数,以便在元素类型与上次相同时使用它:
function mountTree(element, containerNode) { // 检查存在的树 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); // Reuses the existing DOM: mountTree(<App />, rootEl);
这些是React内部工做原理的基础知识。
与真实代码库相比,本文档获得了简化。咱们没有解决几个重要方面:
null
,而且协调程序能够处理数组中的“空”并呈现输出。key
,并使用它来肯定哪一个内部实例对应于数组中的哪一个元素。实际React实现中的大部分复杂性与此相关。null
得到的“空槽”。React DOM
告诉协调程序使用ReactDOMComponent
做为计算机内部实例实现。ReactMultiChild
的mixin
中,它由React DOM
和React Native
中的计算机内部实例类实现使用。setState()
的支持。事件处理程序内的多个更新将被批处理为单个更新。componentDidMount()
和componentDidUpdate()
)将被收集到“回调队列”中并在单个批处理中执行。React DOM
提供的事务类在任何更新后恢复输入选择。mountTree()
和unmountTree()
之类的代码。他负责安装和卸载顶层的组件。ReactNativeMount
是React Native的模拟。ReactDOMComponent
等同于本教程中的DOMComponent
。它实现了React DOM渲染器的计算机组件类。ReactNativeBaseComponent
是对React Native的模拟。ReactCompositeComponent
是等同于本教程中的CompositeComponent
。他处理用户自定义的组件并维护状态。instantiateReactComponent
用于选择要为元素构造的内部实例类。它等同于本教程中的instantiateComponent()
。ReactReconciler
里是mountComponent()
,receiveComponent()
, unmountComponent()
方法。它调用内部实例上的底层实现,但也包括一些由全部内部实例实现共享的代码。ReactChildReconciler
实现独立于渲染器处理子级的插入,删除和移动的操做队列。mount()
,receive()
和unmount()
在React代码库中实际上称为mountComponent()
,receiveComponent()
和unmountComponent()
,但它们接收元素。_currentElement
。它们被认为是整个代码库中的只读公共字段。堆栈协调器(stack reconciler)具备固有的局限性,例如同步而且没法中断工做或将其拆分为块。新的 Fiber reconciler正在进行中(笔:固然,你们都知道,目前已经完成了),他们有彻底不一样的架构。在将来,咱们打算用它替换堆栈协调程序,但目前它远非功能校验。
阅读下一节,了解咱们用于React开发的指导原则。
原文: Implementation Notes原译文: react的实现记录