diff --git a/2.JSX.md b/2.JSX.md
index 9064b21..6885c59 100644
--- a/2.JSX.md
+++ b/2.JSX.md
@@ -133,4 +133,4 @@ function createTextElement(value) {
---
-在下一篇文章中,[Didact: Instances, reconciliation and virtual DOM](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) |-|_|🌟| [我们介绍了Didact的虚拟DOM和协调算法以支持DOM更新](#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom)
+在下一篇文章中,[Didact: Instances, reconciliation and virtual DOM](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) |-|_|🌟| [我们介绍了Didact的虚拟DOM和对比算法以支持DOM更新](#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom)
diff --git a/3.Virtual.md b/3.Virtual.md
index a96b8d9..e736cc8 100644
--- a/3.Virtual.md
+++ b/3.Virtual.md
@@ -96,11 +96,11 @@ function render(element, parentDom) {
> 请注意,我们在这里引用的实例与[Dan Abramov在React Components,Elements和Instances中使用的实例](https://medium.com/@dan_abramov/react-components-elements-and-instances-90800811f8ca)不同。他引用了`公共实例`,这是React在调用继承自类的构造函数时得到的`React.Component`。我们将在未来的帖子中将`公开实例`添加到`Didact`。
-每个DOM节点都会有一个匹配的实例。协调算法的一个目标是尽可能避免-创建或删除实例。创建和删除实例意味着我们也将-修改DOM树,所以我们重新利用实例的`次数越多`,修改DOM树的`次数越少`。
+每个DOM节点都会有一个匹配的实例。对比算法的一个目标是尽可能避免-创建或删除实例。创建和删除实例意味着我们也将-修改DOM树,所以我们重新利用实例的`次数越多`,修改DOM树的`次数越少`。
### 3.3 重构
-让我们重写我们的`render`函数,保持同样的协调算法,并添加一个`instantiate`函数来`创建`一个给定元素的-实例(及其子元素):
+让我们重写我们的`render`函数,保持同样的对比算法,并添加一个`instantiate`函数来`创建`一个给定元素的-实例(及其子元素):
``` js
// --------------- 运行一次 开始------
@@ -241,7 +241,7 @@ function updateDomProperties(dom, prevProps, nextProps) {
### 3.4 重用DOM节点
-我们说-协调算法-需要尽可能多地重用-DOM节点。让我们为该·reconcile·函数添加一个验证,以检查之前渲染的元素`type`是否与我们当前正在渲染的元素相同。如果`type`相同,我们将重新使用它(更新属性以匹配新的属性):
+我们说-对比算法-需要尽可能多地重用-DOM节点。让我们为该·reconcile·函数添加一个验证,以检查之前渲染的元素`type`是否与我们当前正在渲染的元素相同。如果`type`相同,我们将重新使用它(更新属性以匹配新的属性):
``` js
function reconcile(parentDom, instance, element) {
@@ -267,9 +267,9 @@ function reconcile(parentDom, instance, element) {
}
```
-### 3.5 child-协调
+### 3.5 child-对比
-该`reconcile`功能缺少一个关键步骤,它使`children`不受影响。`child-协调`是`React`的一个关键方面,它需要元素`(key)`中的额外属性来匹配-先前和当前树中的`child`。我们将使用这种算法的简易版本,它只比较-`children-数组`中相同位置的孩子。这种方法的成本是,我们失去了-重用DOM节点的机会,当他们改变渲染之间的子数组的`顺序`时。
+该`reconcile`功能缺少一个关键步骤,它使`children`不受影响。`child-对比`是`React`的一个关键方面,它需要元素`(key)`中的额外属性来匹配-先前和当前树中的`child`。我们将使用这种算法的简易版本,它只比较-`children-数组`中相同位置的孩子。这种方法的成本是,我们失去了-重用DOM节点的机会,当他们改变渲染之间的子数组的`顺序`时。
为了实现这一点,我们将先前的子实例instance.childInstances与子元素进行匹配element.props.children,然后reconcile逐个调用。我们还保留所有返回的实例,reconcile以便我们可以更新childInstances:
@@ -378,7 +378,7 @@ function reconcileChildren(instance, element) {
> [>>> codepen.io](https://codepen.io/pomber/pen/WjLqYW?editors=0010)
-当我们调用`render树`的根时,`-协调-`适用于整棵树。在接下来的文章中,我们将介绍`组件{Component}`,这将使我们能够协调算法适用于只是受影响的子树:
+当我们调用`render树`的根时,`-对比-`适用于整棵树。在接下来的文章中,我们将介绍`组件{Component}`,这将使我们能够对比算法适用于只是受影响的子树:
在GitHub上检查[这 三个 提交](https://github.com/hexacta/didact/commit/6f5fdb7331ed77ba497fa5917d920eafe1f4c8dc),以查看代码如何从前一篇文章中更改。
diff --git a/4.Components-and-State.md b/4.Components-and-State.md
index 72c1fe3..8d3c357 100644
--- a/4.Components-and-State.md
+++ b/4.Components-and-State.md
@@ -134,7 +134,7 @@ function instantiate(element) {
唯一缺少的是处理组件实例对帐,因此我们会在对帐算法中再添加一个案例。
-鉴于`组件实例`只能有一个孩子,我们不需要处理`children-协调`,我们只需更新`props`公共实例,重新呈现孩子并协调它:
+鉴于`组件实例`只能有一个孩子,我们不需要处理`children-对比`,我们只需更新`props`公共实例,重新呈现孩子并对比它:
``` js
// 对比-元素 并 更新 html
@@ -291,7 +291,7 @@ Didact.render(, document.getElementById("root"));
[>>> codepen](https://codepen.io/pomber/pen/RVqBrx?editors=0010)
-使用组件使我们能够创建自己的“JSX标签”,封装组件状态,并仅在受影响的子树上运行协调算法:
+使用组件使我们能够创建自己的“JSX标签”,封装组件状态,并仅在受影响的子树上运行对比算法:
![4-codepen](./imgs/4-codepen.gif)
diff --git a/5.Fibre.readme.md b/5.Fibre.readme.md
index 36b8f85..99b13ae 100644
--- a/5.Fibre.readme.md
+++ b/5.Fibre.readme.md
@@ -1,66 +1,94 @@
## 5. Fibre-递增对比
-> 这个故事是一个部分系列,`手动👋写自己-React`的版本,但因为我们要重写大部分旧代码的,无论如何,我会 tl;dr它为您提供:
+> 这个故事是系列`DIY👋自己-React`的一个部分, 但因为我们要重写大部分旧代码的, 无论如何, 我会 tl;dr它一下的:
----
-
->TL;DR : 到目前为止的系列:我们正在编写一个React克隆来了解React在底层做了什么。我们称之为[`Didact`](./readme.md)。为了简化代码,我们只关注-React-的主要功能。首先我们介绍如何渲染元素并使JSX工作。我们编写了对比算法来重新渲染仅更新之间更改的内容。然后我们添加了-`Component - class` 和 `setState()`。
----
+>TL;DR : 到目前为止的系列: 我们正在编写一个React克隆简化版本,来了解React在底层做了什么. 我们称之为[`Didact`](./readme.md). 为了简化代码, 我们只关注-React-的主要函数. 首先我,们介绍如何渲染元素并使JSX工作. 我们编写了对比算法来重新渲染那些仅在更新期间更改了的内容. 然后我们添加了-`Component - class` 和 `setState()`.
-
-说一说❤️react didact
+#### 说一说 ❤️ react didact
-现在`React-16`已经不复存在,并且有了一个新的内部架构,需要重写-React-的大部分代码。
+现在`React-16`已经不复存在, 并且有了一个新的内部架构, 需要重写-React-的大部分代码.
-这意味着一些期待已久的功能 - 旧的架构很难发展 - 被送走了🐶。
+这意味着一些期待已久的函数 - 旧,的架构很难发展 - 被送走了🐶.
-这也意味着我们在这个系列中编写的大部分代码现在都是毫无价值的。但是思想可以留下😛
+这也意味着我们在这个系列中编写的大部分代码现在都是毫无价值的. 但是思想/逻辑可以留下😛
-在本文中,我们将重写-didact-系列中的大部分代码,以遵循React-16新架构。我们将尝试从React代码库中 模拟 结构,变量和函数名称。我们将跳过我们-公共API-所不需要的一切:
+在本文中, 我们将重写-didact-系列中的大部分代码, 以遵循 React-16 新架构. 我们将尝试从React代码库中 模拟 它的结构, 变量和函数名称. 我们将跳过我们不需要的-公共API-:
- `Didact.createElement()`
- `Didact.render()` (只有DOM渲染)
-- `Didact.Component`(使用setState()但不是context生命周期方法)
+- `Didact.Component`(使用`setState()`但没有`context`或者生命周期方法)
-如果你想跳到前面看代码工作,你可以去
+如果你想跳到前面看代码的运行情况, 你可以去
-[更新的演示-codepen](https://codepen.io/pomber/pen/veVOdd)或访问[->github存储库](https://github.com/hexacta/didact)。
+[更新的演示-codepen](https://codepen.io/pomber/pen/veVOdd)或访问[->github存储库](https://github.com/hexacta/didact).
-现在, 让我解释为什么我们需要重写旧代码。
-
+现在, 让我解释为什么我们需要重写旧代码.
---
+## 目录
+
+
+
+
+
+ - [5.1 为什么选择Fiber](#51-%E4%B8%BA%E4%BB%80%E4%B9%88%E9%80%89%E6%8B%A9fiber)
+ - [fibre 理解提示](#fibre-%E7%90%86%E8%A7%A3%E6%8F%90%E7%A4%BA)
+- [更新 🀄 2018 9.6](#%E6%9B%B4%E6%96%B0--2018-96)
+ - [5.2 调度微任务](#52-%E8%B0%83%E5%BA%A6%E5%BE%AE%E4%BB%BB%E5%8A%A1)
+ - [5.3 Fibre-数据结构](#53-fibre-%E6%95%B0%E6%8D%AE%E7%BB%93%E6%9E%84)
+ - [1. 这 `b`, `p`和`i` 在 `Fibre` 表示 **host components**.](#1-%E8%BF%99-bp%E5%92%8Ci-%E5%9C%A8-fibre-%E8%A1%A8%E7%A4%BA-host-components)
+ - [2. 例子中的 `` 在 `Fibre` 中 表示 **class component**.](#2-%E4%BE%8B%E5%AD%90%E4%B8%AD%E7%9A%84-foo-%E5%9C%A8-fibre-%E4%B8%AD-%E8%A1%A8%E7%A4%BA-class-component)
+ - [3. `div `代表`Fibre`的**host root**. 它与 **host component** 相似, ](#3-div-%E4%BB%A3%E8%A1%A8fibre%E7%9A%84host-root-%E5%AE%83%E4%B8%8E-host-component-%E7%9B%B8%E4%BC%BC)
+ - [4. 另一个重要的属性是`alternate`.](#4-%E5%8F%A6%E4%B8%80%E4%B8%AA%E9%87%8D%E8%A6%81%E7%9A%84%E5%B1%9E%E6%80%A7%E6%98%AFalternate)
+ - [5.4 Didact调用层次结构](#54-didact%E8%B0%83%E7%94%A8%E5%B1%82%E6%AC%A1%E7%BB%93%E6%9E%84)
+ - [5.4.1 旧代码](#541-%E6%97%A7%E4%BB%A3%E7%A0%81)
+ - [5.4.2](#542)
+ - [5.4.3](#543)
+ - [5.4.4](#544)
+ - [5.4.5](#545)
+ - [5.4.6](#546)
+ - [5.4.7](#547)
+ - [5.4.8](#548)
+ - [5.4.9](#549)
+ - [5.4.10](#5410)
+ - [5.5 正在运行的Didact](#55-%E6%AD%A3%E5%9C%A8%E8%BF%90%E8%A1%8C%E7%9A%84didact)
+ - [5.6 下一步是什么?](#56-%E4%B8%8B%E4%B8%80%E6%AD%A5%E6%98%AF%E4%BB%80%E4%B9%88)
+
+
+
+
### 5.1 为什么选择Fiber
-
-> 这不会提供`React-Fiber`的完整画面。如果您想了解更多信息,请查看-[`此资源列表`](https://github.com/koba04/react-fiber-resources)。
+> 这不会提供`React-Fiber`的完整过程. 如果您想了解更多信息, 请查看-[`react-fiber资源列表`](https://github.com/koba04/react-fiber-resources).
-当浏览器的主线程忙于长时间运行时,关键的简短任务必须等待一段不可接受的时间才能完成。
+当浏览器的主线程长时间忙于运行时, 关键的简短任务必须等待一段不可接受的时间,才能完成.
-为了展示这个问题,我做了一个[小演示](https://pomber.github.io/incremental-rendering-demo/react-sync.html)。为了保持行星的旋转,主线程需要每16ms左右一次就要使用。如果主线程被其他东西阻塞,让我们说200毫秒,你会注意到动画丢失帧和行星冻结,直到主线程再次释放。
+为了展示这个问题, 我做了一个[小演示](https://pomber.github.io/incremental-rendering-demo/react-sync.html). 为了保持行星的旋转, 主线程需要在每16ms左右就要运行一次. 如果主线程被其他东西阻塞, 让我们定个200毫秒, 你会注意到动画丢失帧和行星冻结/卡顿, 直到主线程再次释放.
-> 是什么让主线程如此繁忙, 以至于无法为-保持`动画平滑`和`UI响应`的情况下节省一些微秒?
+> 是什么让主线程如此繁忙, 以至于无法将一些 ms, 花费在保持`动画平滑`和`UI响应`上呢?
-记住-[`对比代码`](./readme.md#3-实例-对比和虚拟dom)?一旦开始对比,它不会停止。如果主线程需要做其他任何事情,它将不得不等待。而且,`因为递归调用很大程度上取决于它,所以很难使它可以被使用`。这就是为什么我们要用一个新的数据结构来重写它,这将允许我们用`循环`-替换-`递归调用`。
+记住-[`对比算法代码`-3-实例-对比和虚拟dom](./readme.md#3-实例-对比和虚拟dom)? 一旦开始对比, 它就不会停止. 如果主线程需要做其他任何事情, 它将不得不等待. 而且, **因为很大程度上它取决于递归调用, 所以很难使它停止,再继续**. 这就是为什么我们要用一个新的数据结构来重写它, 这将允许我们用`循环`-替换-`递归调用`.
----
+### fibre 理解提示
-> ## 提示 fibre 理解
+
+
+[如果你是新手,请点击并观察 本🌰中两种不同 `jsbin-fibre`](http://jsbin.com/coyunux/2/edit?js,console)
-[你是新手 请点击 观察 本🌰中两种不同 `jsbin-fibre`](http://jsbin.com/coyunux/2/edit?js,console)
+- 看了上面这个🌰, 你需要明白, `fibre` 带有数据流动 的认知
-- 看了上面这个🌰,你需要明白,`fibre` 带有数据流动 的认知
+- 然后你看 [`此资源列表`]((https://github.com/koba04/react-fiber-resources)) , 这是为了加深-对-react-fibre 的认知, 我们需要其中的[demo-🌰子](https://koba04.github.io/react-fiber-resources/examples/)
-- 然后你看了 [`此资源列表`]((https://github.com/koba04/react-fiber-resources)) 了吗,为了加深-对-react-fibre 的认知, 我们需要其中的[demo-🌰子](https://koba04.github.io/react-fiber-resources/examples/)
+>`说回 react-fibre 这个念想 ` : 带有数据的特性 的 `-fibre-`中 被记录了`-优先级-`属性.
-`说回 react-fibre 这个念想 ` 在 带有数据的特性 的 `-fibre-`中 赋予了`-优先级-`属性记录,可以看到[demo-🌰子](https://koba04.github.io/react-fiber-resources/examples/) 带有共 三个选择或输入项
+可以看到[demo-🌰子](https://koba04.github.io/react-fiber-resources/examples/) 带有共 三个选择或输入项
1. `pleace input text`
@@ -68,72 +96,72 @@
2. `Async mode` 默认
- > 使用了-[实验性`react.unstable_deferredUpdates` 会赋予此元素一个低优先级](https://sourcegraph.com/github.com/koba04/react-fiber-resources/-/blob/examples/components/App.js#L30)
+ > 使用了-[实验性`react.unstable_deferredUpdates` 会赋予此元素一个低的优先级](https://sourcegraph.com/github.com/koba04/react-fiber-resources/-/blob/examples/components/App.js#L30)
3. `sync mode`
- > 会到同步,也就是没有变化
+ > 会到同步, 也就是没有变化
-可以看到 -`Async mode`- 的卡顿, 它这个组件元素被分配的优先级低,而
-,`sync mode` 比 `Async mode` 优先级高, 为优先级高的让道。比如优先级高的动画。
+可以看到 -`Async mode`- 的卡顿, 因为它这个组件元素被分配的优先级低, 而`sync mode`的优先级比 `Async mode` 高, `Async mode`要为优先级高的让道. 比如优先级高的动画.
-> 当然,`react-fibre` 不止做了这件事:P
-
+> 当然, `react-fibre` 不止做了这件事:P
----
+
-> 在这下面的翻译, 并`没有进行校对`, `React` 的 `Fibre` 改造正在进行, 关于 Fibre 的有效与影响
-> 对 `React`的提升仍是`未知之数`, 所以在似乎了解 `Fibre` 的含义后, 我抽身了
+## 更新 🀄 2018 9.6
### 5.2 调度微任务
-
-我们需要将工作-分解为更小的部分,短时间运行这些部分,让主线程执行`更高优先级`的任务,并且如果有-任何待处理的事情-回来完成工作。
+我们需要将工作-分解为更小的部分, 可以短时间运行这些部分, 让主线程执行`更高优先级`的任务, 并且如果有-任何待处理的事情-再回来完成工作.
-我们会在`requestIdleCallback()`功能的帮助下做到这一点。它会在下一次浏览器闲置时调用一个回调`deadline函数`,并包含一个`描述`我们的代码有多少可用时间的参数:
+我们会在[requestIdleCallback()]((https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)函数的帮助下,做到这一点. 它将下一次浏览器空闲时,调用我们的`preformWork`回调函数, 并加入一个`deadline`参数, 用于描述我们的代码可用时间:
``` js
const ENOUGH_TIME = 1; // milliseconds
let workQueue = [];
-let nextUnitOfWork = null;
+let nextUnitOfWork = null; // 全局变量, 那么一次只能走一个回调
-function schedule(task) {
- workQueue.push(task);
- requestIdleCallback(performWork);
+function schedule(task) { // 1. 加
+ workQueue.push(task); // 2. 存好了
+ requestIdleCallback(performWork); // 3. 下一次空闲运行, performWork 函数
}
-function performWork(deadline) {
+function performWork(deadline) { // 空闲机会来了
if (!nextUnitOfWork) {
- nextUnitOfWork = workQueue.shift();
+ nextUnitOfWork = workQueue.shift(); // 4. 拿出来,
}
+// 下一回调 与 看看有没有 足够的时间 再走一趟
while (nextUnitOfWork && deadline.timeRemaining() > ENOUGH_TIME) {
+ // 5. DO something
nextUnitOfWork = performUnitOfWork(nextUnitOfWork);
}
if (nextUnitOfWork || workQueue.length > 0) {
+ // 6. 如果还没有搞定, 那么 再等空闲咯
requestIdleCallback(performWork);
}
}
```
-真正的工作发生在`performUnitOfWork`函数内部。我们需要在那里编写我们的`对比-{reconciliation}`代码。该函数应该运行一部分工作,然后-`返回-{return}`-下一次需要恢复工作的所有信息。
+真正的工作发生在`performUnitOfWork`函数的内部. 我们需要在那里编写我们的`对比-{reconciliation}算法`代码. 该函数应该运行一部分工作, 然后-`返回-{return}`-下一次恢复工作需要的所有信息.
-为了跟踪这些工作,我们将使用`Fibre`。
+为了跟踪这些工作, 我们将使用`Fibre`.
-
+### 5.3 Fibre-数据结构
-### 5.3 Fibre-数据结构
+我们将为每个想要渲染的组件创建一个`Fibre`.
-
+- `nextUnitOfWork`将是对下一个工作`Fibre`的参考.
+- `performUnitOfWork`拿到`Fibre`,并在其上工作, 并返回一个`新的Fibre`用于下一次 - 直到所有工作完成.
-我们将为每个想要渲染的组件创建一个`Fibre`。这`nextUnitOfWork`将是对我们想要工作的下一个`Fibre`的参考。`performUnitOfWork`将在该`Fibre`上工作,并返回一个`新的Fibre`-直到所有工作完成。容许我,我会稍后详细解释这一点。
+容许下, 我会稍后详细解释这一点.
-`Fibre`是怎样的?
+`Fibre`是怎样的 ?
``` js
let fiber = {
@@ -150,73 +178,145 @@ let fiber = {
effects: []
};
```
-这是一个普通的旧JavaScript对象。
+这是一个普通的旧JavaScript对象.
-我们将使用`parent`,`child`和`sibling`-属性-打造的`Fibre`树描述组件的树。
+我们将使用`parent`, `child`和`sibling`-属性-打造的`Fibre`树来描述组件的树.
-这`stateNode`将是对`Component`实例的引用。它可以是-`DOM元素`,也可以是用户定义的`Component-class`的实例。
+这`stateNode`将是对`Component`实例的引用. 它可以是`DOM元素`, 也可以是用户定义的`Component-class`的实例.
-例如:
+例如:
![fibre-1](./imgs/fibre-1.png)
-在这个例子中,我们可以看到我们将支持的三种不同类型的组件:
+在这个例子中, 我们可以看到我们将支持的三种不同类型的组件:
-#### 1. 这 `-b-`,`-p-`和`-i-` 在 `Fibre` 表示 **host components**。
+#### 1. 这 `b`, `p`和`i` 在 `Fibre` 表示 **host components**.
我们将用
``` js
tag: HOST_COMPONENT,
```
-来识别它们。
+来识别它们.
-`fibre.type`将是一个字符串(html元素的标签.
+- `fibre.type`是html元素的标签:`string`.
-`fibre.props`将是元素的-属性-和-事件监听器-。
+- `fibre.props`是元素的-属性-和-事件监听器-.
-#### 2. 例子中 `` 在 `Fibre` 中 表示 **class component**。它的`tag`是`CLASS_COMPONENT`和`type` 参考来自 用户定义 的 `Didact.Component`。
+#### 2. 例子中的 `` 在 `Fibre` 中 表示 **class component**.
-#### 3. `div `代表 **host root** 的`Fibre`。它与 **host component** 相似,因为它具有DOM元素作为`stateNode`,但是它将受到特殊处理。`Fibre.tag`会是**HOST_ROOT**。请注意,`Fibre.stateNode`是传递给的DOM节点`Didact.render()`。
+它的`tag`是`CLASS_COMPONENT`和`type`来自 用户定义的`Didact.Component`.
-另一个重要的属性是`alternate`。我们需要它,因为大多数时候我们会有两棵`Fibre`树。
+#### 3. `div `代表`Fibre`的**host root**. 它与 **host component** 相似,
-- 一棵树将对应于我们已经呈现给-`html-DOM`-的东西,我们将它称为当前树或旧树。
+因为它有DOM元素可以作为`stateNode`, 但作为树的根, 它会得到特殊处理. `Fibre.tag`会是**HOST_ROOT**. 请注意, `Fibre.stateNode`是传递给`Didact.render()`的DOM节点.
-- 另一棵是我们在创建新更新(调用`setState()`或 `Didact.render()` 时构建的树,我们将此树称为正在进行中的树
+#### 4. 另一个重要的属性是`alternate`.
-正在进行的工作树不会与旧树共享任何`Fibre`。一旦我们完成建设-`正在进行树`-工作并取得所需的 DOM变化,`正在进行树`成为旧树。
+我们需要它, 因为大多数时候我们会有两棵`Fibre`树.
-因此,我们使用`alternate`链接 正在工作的`Fibre`树 与 旧树 中相应的`Fibre`。`Fibre`和它的`alternate` 分享 相同`tag`,`type`,`stateNode`。有时 - 当我们渲染新的东西时 - `Fibre`不会有`alternate`。
+- 一棵树将对应于我们已经呈现给-`html-DOM`-的东西, 我们将它称为当前树或旧树.
-最后,我们有`effects`清单和`effectTag`。当我们发现在工作`正在进行树`的`Fibre`,需要改变的DOM,我们将设置`effectTag`到`PLACEMENT`,`UPDATE`或`DELETION`。为了更容易将所有- DOM变化 一起提交,我们保留了列出的所有`Fibre`(来自`Fibre`子树)的`effectTag`列表`effects`。
+- 另一棵是我们在创建新更新(调用`setState()`或 `Didact.render()` 时构建的树, 我们将此树称为正在进行中的树, 简称为`工作树`
-> 这可能是一次太多的信息,如果你没有跟上,不要担心,我们很快就会看到`Fibre`树在运行。
+工作树不会与旧树共享任何`Fibre`. 一旦我们完成-`工作树`-的工作建设,并取得所需的 DOM变化, `工作树`会成为旧树.
-
+因此, 我们使用`alternate`链接 正在进行中的`Fiber`与 相应旧树的`Fiber`. `Fibre`和它的`alternate`分享相同`tag`, `type`与`stateNode`. 有时,当我们渲染新的东西时,`Fibre`不会有`alternate`.
----
+最后, 我们有`effects`列表和`effectTag`. 当我们发现`工作树`的`Fibre`有需要改变的DOM, 我们将设置`effectTag`为`PLACEMENT`, `UPDATE`或`DELETION`. 为了更容易将所有DOM变化一起提交, 我们保留了所有`Fibre`(来自`Fibre`子树)的`effectTag`项到列表`effects`.
+
+> 这可能是一次太多的信息, 如果你没有跟上, 不要担心, 我们很快就会看到`Fibre`树的运行.
### 5.4 Didact调用层次结构
-要了解我们要编写的代码的流程,请查看此图表:
+要了解我们要编写的代码的流程, 请查看此图表:
![5-pic](./imgs/5-pic.png)
-我们将从`render()`和开始,`setState()`并按照结束于的流程进行`commitAllWork()`。
-
----
+我们将从`render()`和`setState()`开始, 并遵循以`commitAllWork()`结尾的流程。
#### 5.4.1 旧代码
+我告诉过你, 我们将重写大部分代码, 但我们首先回顾一下我们不会h重写的代码.
+
+- 在[`元素创建和JSX`](./2.JSX.md)中, 我们编写了用于编译JSX的函数代码`createElement()`. 我们不需要改变它, 我们将继续使用相同的元素. 如果你不知道元素中, 关于`type`, `props` 和 `children`这些, 请查看旧帖子.
+
+- 在[实例, 对比算法和虚拟DOM](./3.Virtual.md)中, 我们编写了用于更新节点DOM属性的`updateDomProperties()`函数. 我还扩展了用于创建DOM元素的函数`createDomElement()`. 你可以在这个[dom-utils.js 的 gist](https://gist.github.com/pomber/c63bd22dbfa6c4af86ba2cae0a863064)中看到这两个函数.
+
-我告诉过你,我们将重写大部分代码,但我们首先回顾一下我们不重写的代码。
+ gist 内容在这里 👌
-在[`createElement和JSX`](./)中,我们编写了用于编译JSX的函数的代码`createElement()`。我们不需要改变它,我们将继续使用相同的元素。如果你不知道的元素,`type,props和children`,请查看旧帖子。
+``` js
+const isEvent = name => name.startsWith("on");
+const isAttribute = name =>
+ !isEvent(name) && name != "children" && name != "style";
+const isNew = (prev, next) => key => prev[key] !== next[key];
+const isGone = (prev, next) => key => !(key in next);
+
+function updateDomProperties(dom, prevProps, nextProps) {
+ // Remove event listeners
+ Object.keys(prevProps)
+ .filter(isEvent)
+ .filter(key => !(key in nextProps) || isNew(prevProps, nextProps)(key))
+ .forEach(name => {
+ const eventType = name.toLowerCase().substring(2);
+ dom.removeEventListener(eventType, prevProps[name]);
+ });
+
+ // Remove attributes
+ Object.keys(prevProps)
+ .filter(isAttribute)
+ .filter(isGone(prevProps, nextProps))
+ .forEach(name => {
+ dom[name] = null;
+ });
+
+ // Set attributes
+ Object.keys(nextProps)
+ .filter(isAttribute)
+ .filter(isNew(prevProps, nextProps))
+ .forEach(name => {
+ dom[name] = nextProps[name];
+ });
+
+ // Set style
+ prevProps.style = prevProps.style || {};
+ nextProps.style = nextProps.style || {};
+ Object.keys(nextProps.style)
+ .filter(isNew(prevProps.style, nextProps.style))
+ .forEach(key => {
+ dom.style[key] = nextProps.style[key];
+ });
+ Object.keys(prevProps.style)
+ .filter(isGone(prevProps.style, nextProps.style))
+ .forEach(key => {
+ dom.style[key] = "";
+ });
+
+ // Add event listeners
+ Object.keys(nextProps)
+ .filter(isEvent)
+ .filter(isNew(prevProps, nextProps))
+ .forEach(name => {
+ const eventType = name.toLowerCase().substring(2);
+ dom.addEventListener(eventType, nextProps[name]);
+ });
+}
+
+function createDomElement(fiber) {
+ const isTextElement = fiber.type === TEXT_ELEMENT;
+ const dom = isTextElement
+ ? document.createTextNode("")
+ : document.createElement(fiber.type);
+ updateDomProperties(dom, [], fiber.props);
+ return dom;
+}
+```
+
-在[实例中](./),我们编写了`updateDomProperties()`用于更新节点DOM属性的[调节和虚拟DOM](./)。我还提取了用于创建DOM元素的代码`createDomElement()`。你可以在这个[dom-utils.js的gist](https://gist.github.com/pomber/c63bd22dbfa6c4af86ba2cae0a863064)中看到这两个函数。
+
-在[组件和状态](./)中,我们编写了Component-基类。让我们改变它以便`setState()`调用`scheduleUpdate()`,并`createInstance()`保存对实例中`Fibre`的引用:
+- 在[组件和状态](./4.Components-and-State.md)中, 我们编写了Component-基类. 让我们改变它,以便`setState()`可以调用`scheduleUpdate()`,和`createInstance()`来保存实例中`Fibre`的引用:
``` js
class Component {
@@ -237,20 +337,19 @@ function createInstance(fiber) {
}
```
----
-从这段代码开始,让我们从头开始重写其余部分。
-
+从这段代码开始, 让我们从头开始重写其余部分
+
-#### 5.4.2
+
+#### 5.4.2 `render` 和 `scheduleUpdate`
![5-pic1](./imgs/5-pic1.png)
-
-除了`Component` class 和 `createElement()` 之外,我们还会有两个公共函数:`render()` 和 `setState()`,我们看到这`setState()`只是调用`scheduleUpdate()`。
+除了`Component` class 和 `createElement()` 之外, 我们还会有两个公共函数: `render()` 和 `setState()`, 我们看到这`setState()`只是调用`scheduleUpdate()`.
-`render()`和`scheduleUpdate()`是相似的,他们会收到一个新的更新并对其进行排队:
+`render()`和`scheduleUpdate()`是相似的, 他们会收到一个新的更新并对其进行排队:
``` js
// Fiber tags
@@ -264,15 +363,15 @@ let nextUnitOfWork = null;
let pendingCommit = null;
function render(elements, containerDom) {
- updateQueue.push({
+ updateQueue.push({ // 用作一个队列, 先进先出
from: HOST_ROOT,
dom: containerDom,
newProps: { children: elements }
});
- requestIdleCallback(performWork);
+ requestIdleCallback(performWork); // 下一个浏览器空闲时
}
-function scheduleUpdate(instance, partialState) {
+function scheduleUpdate(instance, partialState) { // 提供给 setState 使用
updateQueue.push({
from: CLASS_COMPONENT,
instance: instance,
@@ -282,19 +381,15 @@ function scheduleUpdate(instance, partialState) {
}
```
-我们将使用该`updateQueue`数组来跟踪待处理的更新。每次打运行`render()`或`scheduleUpdate()`推送一个新的更新到`updateQueue`。每个更新中的更新信息都不同,我们将看到我们以后如何使用它`resetNextUnitOfWork()`。
+我们将使用该`updateQueue`数组来跟踪待处理的更新. 每次运行`render()`或`scheduleUpdate()`会推送一个新的更新到`updateQueue`. 每个更新中的更新信息都不同, 等会你会看到,我们在之后的`resetNextUnitOfWork()`函数中使用它.
-将更新推送到队列后,我们触发延迟呼叫`performWork()`。
-
+将更新推送到队列后, 我们触发延迟回调`performWork()`函数
----
-
-#### 5.4.3
+#### 5.4.3 `performWork` 和 `workLoop`
![5-pic2](./imgs/5-pic2.png)
-
``` js
const ENOUGH_TIME = 1; // milliseconds
@@ -321,37 +416,34 @@ function workLoop(deadline) {
}
```
-这是我们使用`performUnitOfWork()`之前看到的模式的地方。
+> 正如我们在 [5.2 调度微任务](#52-%E8%B0%83%E5%BA%A6%E5%BE%AE%E4%BB%BB%E5%8A%A1) 中 了解到的循环工作流程, 只是具体分支起了对应的名称
-`requestIdleCallback()`以`截止日期-{deadline}`为参数调用目标函数。`performWork()`将`deadline`传给它`workLoop()`。然后`workLoop()`返回,`performWork()`检查是否还有待审批工作。如果有的话,它会为它自己安排一个新的`requestIdleCallback(performWork)`。
+是之前我们使用`performUnitOfWork()`时,看到的模式.
-`workLoop()`是关注时间的功能。如果`deadline`太近,它会停止工作循环并保持`nextUnitOfWork`更新状态,以便下次恢复。
+`requestIdleCallback()`提供`截止日期-{deadline}`作为目标函数的参数调用. `performWork()`将`deadline`传给它的`workLoop()`. 然后`workLoop()`运行返回, `performWork()`检查是否还有待审批工作. 如果有的话, 它会为它自己安排一个新的`requestIdleCallback(performWork)`.
-我们使用`ENOUGH_TIME`(1ms常数,与React相同)来检查是否`deadline.timeRemaining()`足以运行另一个工作单元。如果`performUnitOfWork()`超过这一点,我们将超过最后期限。`deadline`只是来自浏览器的建议,所以将其超过几毫秒并不是那么糟糕。
-`performUnitOfWork()`将为其正在进行的更新构建`正在进行工作树`,并找出我们需要对-DOM-应用哪些更改。**这将逐步完成,每次一段`Fibre`数据**。
+`workLoop()`是关注时间的函数. 如果`deadline`时间太少, 它会停止工作循环,并保持下一次`nextUnitOfWork`更新需要的`Fibre`状态, 以便下次恢复.
-当`performUnitOfWork()`完成当前更新的所有工作时,它将`返回null`并将待处理的更改留在DOM中`pendingCommit`。最后,`commitAllWork()`将采取`effects`从`pendingCommit`, 还会`变更DOM`。
+>我们使用`ENOUGH_TIME`(1ms常数, 与React相同)来检查`deadline.timeRemaining()`,是否有足够运行另一个工作单元的时间. 如果`performUnitOfWork()`超过这一时间, 我们将留待下次继续. `deadline`只是来自浏览器的建议, 所以将其超过几毫秒并不是那么糟糕.
-请注意,`commitAllWork()`在循环之外调用。
+`performUnitOfWork()`将用正在进行的更新,和找出我们需要对-DOM-应用哪些更改来构建`工作树`. **这将逐步完成, 每次一段`Fibre`数据**.
-完成的工作`performUnitOfWork()`不会改变DOM,因此可以将其分开。
+当`performUnitOfWork()`完成当前更新的所有工作时, 它将`返回null`和将在DOM中待处理的更改留给`pendingCommit`. 最后, `commitAllWork()`获取来自`pendingCommit`的`effects`, 和变更DOM.
-另一方面,`commitAllWork()`将改变DOM,它应该一次完成,以避免不一致的UI。
+请注意, `commitAllWork()`在循环之外调用. `performUnitOfWork()`完成的工作并不会改变DOM, 因此可以将其分开.
----
-
-> 我们还没有看到第一个`nextUnitOfWork`来自哪里。
-
+另一方面, `commitAllWork()`将改变DOM, 它应该一次完成, 以避免不一致的UI.
----
+> 我们还没有看到第一个`nextUnitOfWork`来自哪里
-#### 5.4.4
+#### 5.4.4 `resetNextUnitOfWork`
![5-pic3](./imgs/5-pic3.png)
-
-`resetNextUnitOfWork()`更新一次并将其转换为第一个`nextUnitOfWork`:
+> `resetNextUnitOfWork()` 运行在上一小节的`workLoop`函数中
+
+`resetNextUnitOfWork()`获取一次更新,并将其转换为第一个`nextUnitOfWork`:
``` js
function resetNextUnitOfWork() {
@@ -372,7 +464,7 @@ function resetNextUnitOfWork() {
nextUnitOfWork = {
tag: HOST_ROOT,
- stateNode: update.dom || root.stateNode,
+ stateNode: update.dom || root.stateNode, // 两种情况
props: update.newProps || root.props,
alternate: root
};
@@ -387,40 +479,32 @@ function getRoot(fiber) {
}
```
-`resetNextUnitOfWork()` 首先从队列中提取第一个更新。
+- `resetNextUnitOfWork()` 首先从队列中提取第一个更新.
-如果更新有,`partialState`我们将它存储在属于组件实例的`Fibre`上,以便稍后在调用组件时使用它`render()`。
+- 如果有更新, `partialState`我们将它存储在属于组件实例的`Fibre`上, 以便稍后在调用组件`render()`时使用它.
-然后我们找到老`Fibre`树的根。如果更新来自于第一次`render()`被调用时,我们会不会有一个根`Fibre`所以`root == null`。如果它来自后续调用`render()`,我们可以`_rootContainerFiber`在DOM节点的属性上找到根。如果更新来自a setState(),我们需要从实例`Fibre`上去,直到找到没有`Fibre`-**parent**。
+- 然后我们找到旧`Fibre`树的根. 如果更新是第一次调用`render()`时, 我们不会有一个根`Fibre`,所以`root == null`. 如果它来自后续的`render()`调用, 我们可以在DOM节点的`_rootContainerFiber`属性上找到根. 如果更新来自一次`setState()`, 我们需要从实例`Fibre`往上找, 直到找到一个`Fibre`是没有-`parent`的.
-然后我们分配`nextUnitOfWork`一个新的`Fibre`。这种`Fibre`是一个新的工作进行中的树的根。
+- 然后我们让`nextUnitOfWork`重新获得了一个新的`Fibre`.**这`Fibre`是一个新的工作树的根**.
-如果我们没有一个旧的根,那么`stateNode`这个**DOM节点**就在`render()`调用中作为参数接收。这`props`将是`newProps`从更新:一个`children`属性的对象具有元素 - 的另一个参数`render()`。该`alternate == null`。
+如果我们没有一个 _旧_ 的根, 那么这个**DOM节点**`stateNode`就会在`render()`调用中,作为参数被接收. 这`props`将是来自更新的`newProps`: 对象中的一个`children`属性具有元素,`children`是`render()`的另一个参数. 该`alternate == null`.
-如果我们有一个旧的根,那么`stateNode`将是前一个根的**DOM节点**。该`props`会又`newProps`如果不是null,否则我们复制`props`从老根。这alternate将是老根。
+如果我们有一个 _旧_ 的根, 那么`stateNode`将是前一个根的**DOM节点**. 该`props`如果不是null,又会是`newProps`, 否则我们将从 _旧_ 根复制`props`. 那这`alternate`将是 _旧_ 根.
----
-
-我们现在拥有`正在进行中的树`的根,让我们开始构建剩余的树。
-
----
-
-
+我们现在拥有`正在进行中的树`的根, 让我们开始构建剩余的树.
----
-#### 5.4.5
+#### 5.4.5 `performUnitOfWork`
![5-pic4](./imgs/5-pic4.png)
-
``` js
function performUnitOfWork(wipFiber) {
beginWork(wipFiber);
- if (wipFiber.child) {
+ if (wipFiber.child) { // 工作没有完成, 返回下一次更新的状态
return wipFiber.child;
}
@@ -429,34 +513,30 @@ function performUnitOfWork(wipFiber) {
while (uow) {
completeWork(uow);
if (uow.sibling) {
- // Sibling needs to beginWork
+ // Sibling 返回, 再次变为 wipFiber, 被 beginWork 调用
return uow.sibling;
}
uow = uow.parent;
}
}
```
-`performUnitOfWork()` 走在进行中的工作树。
-我们称之为`beginWork()` - 创造一种`Fibre`的新生儿 - 然后让第一个孩子回到原来的状态`nextUnitOfWork`。
+`performUnitOfWork()` 会运行 进行中的工作树.
-如果没有任何的孩子,我们呼吁`completeWork()`并返回sibling的`nextUnitOfWork`。
+- 我们称之为`beginWork()` - 创造一个新的`Fibre`孩子 - 然后让第一个孩子返回,成为`nextUnitOfWork`.
-如果没有sibling,我们会去找父母打电话,`completeWork()`直到我们找到sibling(我们将成为`nextUnitOfWork`)或直到我们到达根部。
+- 如果没有任何的孩子, 我们接着`completeWork()`,并返回`sibling`作为`nextUnitOfWork`.
-`performUnitOfWork()`多次呼叫会沿着树木向下,造成每根`Fibre`的第一个孩子的孩子,直到找到没有孩子的`Fibre`。然后它就像兄弟姐妹一样向右移动。而且它也跟叔叔一样。(为了更加生动的描述,尝试在`Fibre`调试器上渲染一些组件)
+- 如果没有`sibling`, 我们会继续往`parent`中找, `completeWork()`直到我们找到`sibling`(将成为`nextUnitOfWork`)或到达根部.
----
-
+`performUnitOfWork()`多次调用,会沿着 工作树 向下, 创建每根`Fibre`的第一个`child`的`children`, 若找到没有孩子的`Fibre`. 就向'右'和向'上'找, 就好像有兄弟姐妹,父母,整个家族图似的. (为了更加生动, 可以尝试在[fiber-调试器](http://fiber-debugger.surge.sh/)上渲染一些组件)
----
-#### 5.4.6
+#### 5.4.6 `beginWork`
![5-pic4](./imgs/5-pic5.png)
-
``` js
function beginWork(wipFiber) {
@@ -478,10 +558,10 @@ function updateHostComponent(wipFiber) {
function updateClassComponent(wipFiber) {
let instance = wipFiber.stateNode;
if (instance == null) {
- // Call class constructor
+ // 调用类初始化
instance = wipFiber.stateNode = createInstance(wipFiber);
} else if (wipFiber.props == instance.props && !wipFiber.partialState) {
- // No need to render, clone children from last time
+ // 不需要更新,最后 复制 孩子
cloneChildFibers(wipFiber);
return;
}
@@ -495,31 +575,27 @@ function updateClassComponent(wipFiber) {
}
```
-`beginWork()` 做两件事:
+`beginWork()` 做两件事:
-创造`stateNode`如果我们没有一个
-获取组件子项并将它们传递给 `reconcileChildrenArray()`
-因为两者都取决于我们处理的组件的类型,所以我们将它分成两部分:`updateHostComponent()`和`updateClassComponent()`。
+- 创造`stateNode`,如果我们没有
+- 获取组件子项并将它们传递给 `reconcileChildrenArray()`
-`updateHostComponent()`处理主机组件以及根组件。如果需要的话,它会创建一个新的DOM节点(只有一个节点,没有子节点,并且不会将其附加到DOM)。然后它`reconcileChildrenArray()`使用`Fibre`中的子元素进行调用`props`。
+因为两者都取决于我们处理的组件的类型, 所以我们将它分成两部分: `updateHostComponent()`和`updateClassComponent()`.
-`updateClassComponent()`处理类组件实例。如果需要的话,它会创建一个调用组件构造函数的新实例。它更新实例`props`,`state`因此它可以调用该render()函数来获取新的子项。
+- `updateHostComponent()`处理 _主机组件_ 以及 _根组件_. 如果需要的话, 它会创建一个新的DOM节点(只有一个节点, 没有子节点, 并且不会将其附加到DOM). **然后它通过调用`reconcileChildrenArray()`函数, 使用来自`Fibre`中`props`属性的子元素**.
-`updateClassComponent()`也验证是否有意义调用`render()`。这是一个简单的版本`shouldComponentUpdate()`。如果看起来我们不需要重新渲染,那么我们只是将当前的子树克隆到正在进行的工作树中,而不进行任何调整。
+- `updateClassComponent()`处理类组件实例. 如果需要的话, 它会创建一个新实例,通过调用`createInstance`函数. 它更新实例的`props`, `state`,因此它的`render()`函数可以获取新的孩子.
----
+- `updateClassComponent()`也验证调用`render()`是否有意义. 这是一个简单版本的`shouldComponentUpdate()`. 如果看起来我们不需要重新渲染, 那么我们只是将当前的子树克隆到正在进行的工作树中, 而不进行任何调整.
-现在,我们已经`newChildElements`准备好为正在进行中的`Fibre`创建子`Fibre`。
-
----
+我们现在有了`newChildElements`,准备好为 _工作中的`Fibre`_ 创建子`Fibre`
-#### 5.4.7
+#### 5.4.7 `reconcileChildrenArray`
![5-pic4](./imgs/5-pic6.png)
-
``` js
// Effect tags
@@ -587,38 +663,28 @@ function reconcileChildrenArray(wipFiber, newChildElements) {
}
```
-这是本库的核心,`正在进行的工作树`在不断增长,我们决定在提交阶段对-DOM-做什么更改。
+这是本库的核心, `正在进行的工作树`在不断增长, 我们决定在提交阶段对 _DOM_ 做什么更改.
+- 开始之前, 我们确保`newChildElements`是一个数组. (与之前的对比算法不同, 这个算法总是与子数组一起工作, 这意味着我们现在可以在组件的`render()`函数上返回数组)
-开始之前,我们确保`newChildElements`是一个数组。(与之前的对比算法不同,这个算法总是与子数组一起工作,这意味着我们现在可以在组件的`render()`函数上返回数组)
+- 然后我们开始比较旧`Fibre`树的孩子和新元素(我们将`Fibre`与元素进行比较). 来自 _旧_ `Fibre`树的孩子们正是`wipFiber.alternate`的孩子们. 新元素是我们从`wipFiber.props.children`调用或`wipFiber.stateNode.render()`调用来的.
-然后我们开始比较旧`Fibre`树的孩子和新元素(我们将`Fibre`与元素进行比较)。来自老`Fibre`树的孩子们都是孩子们的孩子`wipFiber.alternate`。新元素是我们`wipFiber.props.children`从调用或从调用中获得的元素`wipFiber.stateNode.render()`。
+我们的对比算法通过 第一个旧`Fibre`(匹配`wipFiber.alternate.child`)与第一子元素(elements[0]), 第二个旧`Fibre`(`wipFiber.alternate.child.sibling`)的第二子元素(elements[1])等. 对于每对 `oldFiber` - `element`:
-我们-算法通过 第一`Fibre`(匹配`wipFiber.alternate.child`)与第一子元素(elements[0]),第二旧`Fibre`(`wipFiber.alternate.child.sibling`)的第二子元素(elements[1])等。而对于每 对 `oldFiber` - `element` :
+ - 如果`oldFiber`和`element`同样的`type`, 好消息, 这意味着我们可以保留旧的`stateNode`. 我们创建一个基于旧的`Fibre`的新`Fibre`. 我们添加`UPDATE` `effectTag`. 我们将新`Fibre`添加到`正在进行的工作树`中.
-- 如果`oldFiber`和`element`同样的`type`好消息,这意味着我们可以保留旧的`stateNode`。我们创建一个基于旧的`Fibre`的新`Fibre`。
+ - 如果我们`element`与`oldFiber`有一个不同`type`和我们没有`oldFiber`(因为我们有更多的新孩子), 我们用`element`的信息创建一个新的`Fibre`. 请注意, 这种新`Fibre`没有`alternate`, 也不会有`stateNode`(我们将在`beginWork()`创建`stateNode`). 该`Fibre`的`effectTag`是`PLACEMENT`.
-我们添加`UPDATE effectTag`。我们将新`Fibre`添加到`正在进行的工作树`中。
+ - 如果`oldFiber`和`element`有不同的`type`和有`oldFiber`,也没有任何的`element`(因为我们有更多 _旧_ 孩子), 我们将标记`oldFiber`为`DELETION`. 鉴于这种`Fibre`不是正在进行工作的树的一部分, 我们需要将它添加到`wipFiber.effects`列表中, 以便我们不会丢失它的踪迹.
-- 如果我们`element`与我们有一个不同`type`的`oldFiber`或者我们没有`oldFiber`(因为我们有比新的孩子更多的新孩子),我们创建一个新的`Fibre`与我们的信息`element`。请注意,这种新`Fibre`没有`alternate`,也不会有`stateNode`(`stateNode`我们将创建`beginWork()`)。该`Fibre`的`effectTag`是`PLACEMENT`。
+> 与 React 不同的是, 我们没有使用 key 来进行比对, 所以我们不知道一个孩子是否从之前的位置移动过来.
-- 如果`oldFiber`和`element`有不同的`type`或没有任何`element`这`oldFiber`(因为我们有更多的老孩子比新来的孩子),我们将标记`oldFiber`为`DELETION`。鉴于这种`Fibre`不是正在进行工作的树的一部分,我们需要将它添加到`wipFiber.effects`列表中,以便我们不会丢失它的踪迹。
-> 与React不同的是,我们没有使用 key 来进行比对,所以我们不知道一个孩子是否从之前的位置移动过来。
-
----
-
-
----
-
-#### 5.4.8
+#### 5.4.8 `cloneChildFibers`
![5-pic4](./imgs/5-pic7.png)
-
-
-
-`updateClassComponent()` 有一个特殊情况,我们采取快捷方式,将旧的`Fibre`子树克隆到正在进行中的工作树,而不是进行调整。
+`updateClassComponent()` 有一个特殊情况, 我们采取了快捷方式, 将旧的`Fibre`子树克隆到正在进行中的工作树, 而不是进行调整.
``` js
function cloneChildFibers(parentFiber) {
@@ -650,21 +716,14 @@ function cloneChildFibers(parentFiber) {
}
```
-`cloneChildFibers()`克隆每个`wipFiber.alternate`孩子并将其附加到`正在进行中的树`中。我们不需要添加任何内容,`effectTag`因为我们确信没有任何变化。
-
-
+`cloneChildFibers()`克隆每个`wipFiber.alternate`孩子并将其附加到`正在进行中的树`中. 我们不需要添加任何内容, 因为我们确信`effectTag`没有任何变化.
-
-
----
-
-#### 5.4.9
+#### 5.4.9 `completeWork`
![5-pic4](./imgs/5-pic8.png)
-
-在`performUnitOfWork()`a wipFiber没有新的孩子或者我们已经完成了所有孩子的工作时,我们打电话给他`completeWork()`。
+在`performUnitOfWork()`,当`wipFiber`没有新的孩子或者我们已经完成了所有孩子的工作时, 我们运行`completeWork()`.
``` js
function completeWork(fiber) {
@@ -683,23 +742,19 @@ function completeWork(fiber) {
}
```
-`completeWork()`首先更新与类组件实例相关的`Fibre`引用。(说实话,这并不是真的需要在这里,但它必须在某个地方)
+- `completeWork()`首先更新与`CLASS_COMPONENT`实例相关的`Fibre`引用. (说实话, 这并不是真的需要在这里, 但它必须在某个地方)
-然后它建立一个列表`effects`。该列表将包含来自正在进行中的子树的所有`Fibre.effectTag`(它也包含来自旧子树的`Fibre`-`DELETION effectTag`)。这个想法是在根`effects`表中累积所有的`Fibre.effectTag`。
+- 然后它建立一个`effects`列表. 该列表将包含来自 正在进行中的子树 的所有`Fibre.effectTag`(它也包含来自旧子树的`Fibre`-`DELETION effectTag`). 这个想法是在根`effects`列表中集合所有的`Fibre.effectTag`.
-最后,如果`Fibre`没有**parent**,我们就是正在进行工作的树的根。所以我们完成了这次更新的所有工作并收集了所有的效果。我们分配根,`pendingCommit`以便`workLoop()`可以调用`commitAllWork()`。
-
+- 最后, 如果`Fibre`没有**parent**, 我们拿到了 正在进行工作的树的根. 所以我们完成了这次更新的所有工作,并收集了所有的`effects`. 我们分配根到`pendingCommit`,以便`workLoop()`中`commitAllWork()`可以调用.
----
-#### 5.4.10
+#### 5.4.10 `commitAllWork`
![5-pic4](./imgs/5-pic9.png)
-
-
-我们需要做的最后一件事是:改变DOM。
+我们需要做的最后一件事是: 改变DOM.
``` js
function commitAllWork(fiber) {
@@ -707,7 +762,7 @@ function commitAllWork(fiber) {
commitWork(f);
});
fiber.stateNode._rootContainerFiber = fiber;
- nextUnitOfWork = null;
+ nextUnitOfWork = null; // Reset
pendingCommit = null;
}
@@ -723,7 +778,7 @@ function commitWork(fiber) {
const domParent = domParentFiber.stateNode;
if (fiber.effectTag == PLACEMENT && fiber.tag == HOST_COMPONENT) {
- domParent.appendChild(fiber.stateNode);
+ domParent.appendChild(fiber.stateNode); // add
} else if (fiber.effectTag == UPDATE) {
updateDomProperties(fiber.stateNode, fiber.alternate.props, fiber.props);
} else if (fiber.effectTag == DELETION) {
@@ -750,23 +805,20 @@ function commitDeletion(fiber, domParent) {
}
```
-`commitAllWork()`首先迭代每个根上`effects`调用的所有根`commitWork()`。`commitWork()`检查effectTag每根`Fibre`:
+`commitAllWork()`首先迭代`effects`中每个项调用`commitWork()`. `commitWork()`检查每根`Fibre`的`effectTag`:
-- 如果是,PLACEMENT我们查找父DOM节点,然后简单地追加`Fibre.stateNode`。
+- 如果是`PLACEMENT`, 我们查找父DOM节点, 然后简单地追加`Fibre.stateNode`.
-- 如果是这样的话,`UPDATE`我们会把`stateNode`旧道具和新道具放在一起,然后`updateDomProperties()`决定要更新什么。
+- 如果是`UPDATE`, 我们会把`stateNode`旧道具和新道具放在一起, 然后`updateDomProperties()`决定要更新什么.
-- 如果它是`a DELETION`并且`Fibre`是主机组件,那很简单,我们只是打电话`removeChild()`。但是如果`Fibre`是类组件,在调用之前,`removeChild()`我们需要从`Fibre`子树中找到需要删除的所有主机组件。
+- 如果它是`DELETION`并且`Fibre`是主机组件, 那很简单, 我们只是`removeChild()`. 但是如果`Fibre`是类组件, 在调用`removeChild()`之前, 我们需要从`Fibre`子树中,找到需要删除的所有主机组件.
-一旦我们完成了所有的效果,我们可以重置`nextUnitOfWork`和`pendingCommit`。正在进行的工作树不再是正在进行中的工作树,并成为旧树,因此我们将其根指定给`_rootContainerFiber`。之后,我们完成当前的更新,我们准备开始下一个🚀。
-
-
----
+一旦我们完成了所有的效果, 我们可以重置`nextUnitOfWork`和`pendingCommit`. 正在进行的工作树 不再是 正在进行中的工作树, 并成为旧树, 因此我们将其根指定给`_rootContainerFiber`. 之后, 我们完成当前的更新, 我们准备开始下一个🚀
### 5.5 正在运行的Didact
-如果你想把所有的东西放在一起,只公开API,你可以这样做:
+如果你想把所有的东西放在一起, 只公开API, 你可以这样做:
``` js
function importDidact() {
@@ -796,12 +848,12 @@ Didact.render(
);
```
-或者你可以使用的[演示更新版本](https://codepen.io/pomber/pen/veVOdd)。所有这些代码也可以在[Didact的仓库](https://github.com/hexacta/didact)和[npm - didact](https://unpkg.com/didact)中获得。
+或者你可以查看的[codepen.IO](https://codepen.io/pomber/pen/veVOdd). 所有这些代码也可以在[Didact的仓库](https://github.com/hexacta/didact)和[npm - didact](https://unpkg.com/didact)中获得.
----
### 5.6 下一步是什么?
-Didact缺少很多React的功能,但我特别有兴趣根据优先级安排更新:
+
+Didact缺少很多React的函数, 但我特别有兴趣根据优先级安排更新:
``` js
module.exports = {
@@ -816,8 +868,8 @@ module.exports = {
[react-源](https://github.com/facebook/react/blob/5f93ee6f6ce068228b01516c021c9054b627bf11/src/renderers/shared/fiber/ReactPriorityLevel.js)
-所以啊,下一篇文章可能会这样。
+所以啊, 下一篇文章可能会这样.
-就这样!如果你喜欢它,不要忘记拍手👏,在[Twitter上关注我](https://twitter.com/pomber),发表评论和所有这些内容。
+就这样!如果你喜欢它, 不要忘记拍手👏, 在[Twitter上关注我](https://twitter.com/pomber), 发表评论和所有这些内容.
-谢谢阅读。!!
\ No newline at end of file
+谢谢阅读. !!
\ No newline at end of file
diff --git a/fibre.en.md b/fibre.en.md
new file mode 100644
index 0000000..2a63777
--- /dev/null
+++ b/fibre.en.md
@@ -0,0 +1,317 @@
+* * *
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*9VubAng-r08AkzxMzRN-5A.jpeg?q=20)
+
+![](https://cdn-images-1.medium.com/max/2000/1*9VubAng-r08AkzxMzRN-5A.jpeg)
+
+![](https://cdn-images-1.medium.com/max/2000/1*9VubAng-r08AkzxMzRN-5A.jpeg)
+
+![img source](https://upload.wikimedia.org/wikipedia/commons/thumb/d/d4/Canon_PowerShot_A520_Disassembled.jpg/800px-Canon_PowerShot_A520_Disassembled.jpg)
+
+Didact Fiber: Incremental reconciliation
+========================================
+
+This story is part of a [series where we write our own version of React](https://engineering.hexacta.com/didact-learning-how-react-works-by-building-it-from-scratch-51007984e5c5), but since we are going to rewrite most of the old code anyway, I’ll _tl;dr_ it for you:
+
+> TL;DR of the series so far: We are writing a React clone to understand what React does under the hood. We call it Didact. To keep our code simple we focus only on React main features. First we covered how to render elements and make JSX work. We wrote the reconciliation algorithm to re-render only the stuff that changed between updates. And then we added class components and `setState()`.
+
+Now React 16 is out, and with it a new internal architecture that required a rewrite of most of React’s code.
+
+This means that some long-awaited features — that were hard to develop with the old architecture — were shipped.
+
+It also means that most of the code we have written on this series is now worthless 😛.
+
+In this post we are going to rewrite most of the code from the didact series to follow React 16 new architecture. We’ll try to mirror the structure, variables and function names from the React codebase. We’ll skip everything we don’t need for our public API:
+
+* `Didact.createElement()`
+* `Didact.render()` (only DOM rendering)
+* `Didact.Component` (with `setState()` but not `context` or life cycle methods)
+
+If you want to jump ahead and see the code working you can go to the [updated demo](https://codepen.io/pomber/pen/veVOdd) or visit the [github repository](https://github.com/hexacta/didact).
+
+Let me explain why we need to rewrite the old code.
+
+#### Why Fiber
+
+> This won’t offer the full picture of React Fiber. If you want to know more about it, please check this [list of resources](https://github.com/koba04/react-fiber-resources).
+
+When the browser’s main thread is kept busy running something for a long time, critical brief tasks have to wait an unacceptable amount of time to get done.
+
+To showcase this problem I made a [little demo](https://pomber.github.io/incremental-rendering-demo/react-sync.html). In order to keep the planets spinning, the main thread needs to be available for an instant every 16ms or so. If the main thread is blocked doing other stuff for, let’s say 200 ms, you’ll notice the animation missing frames and the planets frozen until the main thread is free again.
+
+What makes the main thread so busy that can’t spare some microseconds on keeping the animation smooth and the UI responsive?
+
+Remember the [reconciliation code](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0)? Once it starts reconciling it doesn’t stop. If the main thread needs to do anything else it will have to wait. And, **because it depends a lot on recursive calls, it’s hard to make it pausable.** That’s why we are going to rewrite it using a new data structure that will allow us to replace the recursive calls with loops.
+
+![](https://i.embed.ly/1/display/resize?url=https%3A%2F%2Fpbs.twimg.com%2Fmedia%2FDMRGGSCX0AQ5IOZ.jpg%3Alarge&key=a19fcc184b9711e1b4764040d3dc5c07&width=40)
+
+The truth is that I wrote this article so more people could get the joke
+
+#### Scheduling micro-tasks
+
+We’ll need to split up the work into smaller pieces, run those pieces for a short period of time, let the main thread do higher priority stuff, and come back to finish the work if there’s anything pending.
+
+We’ll do this we the help of `[requestIdleCallback()](https://developer.mozilla.org/en-US/docs/Web/API/Window/requestIdleCallback)` function. It queues a callback to be called the next time the browser is idle and includes a `deadline` parameter describing how much time available we have for our code:
+
+
+The real work happens inside the `performUnitOfWork` function. We need to write our reconciliation code there. The function should run a piece of the work and then returns all the information it needs to resume the work the next time.
+
+To track those pieces of work we will use fibers.
+
+#### The fiber data structure
+
+We will create a fiber for each component we want to render. The `nextUnitOfWork` will be a reference to the next fiber we want to work on. `performUnitOfWork` will work on that fiber and return a new one until all the work is finished. Bear with me, I’ll explain this in detail later.
+
+How does a fiber look like?
+
+
+It’s a plain old JavaScript object.
+
+We’ll use the `parent`, `child`, and `sibling` properties to build a tree of fibers that describes the tree of components.
+
+The `stateNode` will be the reference to the component instance. It could be either a DOM element or an instance of a user defined class component.
+
+For example:
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*tc8Jcye70jRI79dmI4PUcw.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*tc8Jcye70jRI79dmI4PUcw.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*tc8Jcye70jRI79dmI4PUcw.png)
+
+In the example we can see the three different kind of components we will support:
+
+* The fibers for `b`, `p`, and `i` represent **host components**. We will identify them with the `tag` `HOST_COMPONENT`. The `type` for these fibers will be a string (the tag of the html element). The `props` will be the attributes and event listeners of the element.
+* The `Foo` fiber represents a **class component**. Its `tag` will be `CLASS_COMPONENT` and the `type` a reference to the user defined class extending `Didact.Component`.
+* The fiber for `div` represents the **host root**. It’s similar to a host component because it has a DOM element as the `stateNode` but being the root of the tree it will receive special treatment. The `tag` for this fiber will be `HOST_ROOT`. Note that the `stateNode` of this fiber is the DOM node passed to `Didact.render()`.
+
+Another important property is `alternate`. We need it because most of the time we will have two fiber trees. **One tree will correspond to the things we’ve already rendered to the DOM, we’ll call it the _current_ tree or the _old_ tree. The other is the tree we build when we work on a new update (a call to** `**setState()**` **or** `**Didact.render()**`**), we’ll call this tree the _work-in-progress_ tree.**
+
+The work-in-progress tree won’t share any fiber with the old tree. Once we finished building the work-in-progress tree and made the needed DOM mutations, the work-in-progress tree becomes the _old_ tree.
+
+So we use `alternate` to link the work-in-progress fibers with their corresponding fibers from the old tree. A fiber and its `alternate` share the same `tag`, `type` and `stateNode`. Sometimes — when we are rendering new stuff — fibers won’t have an `alternate`.
+
+Finally, we have the `effects` list and `effectTag`. When we find a fiber in the work-in-progress tree that requires a change to the DOM we will set the `effectTag` to `PLACEMENT`, `UPDATE` or `DELETION`. To make it easier to commit all the DOM mutations together we keep a list of all the fibers (from the fiber sub-tree) that have an `effectTag` listed in `effects`.
+
+That was probably too much information at once, don’t worry if you didn’t follow, we’ll see the fiber trees in action very soon.
+
+#### Didact call hierarchy
+
+To get an idea of the flow of the code we are going to write take a look at this diagram:
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*dZtg4-sSt_8FlmFWArAxkA.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*dZtg4-sSt_8FlmFWArAxkA.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*dZtg4-sSt_8FlmFWArAxkA.png)
+
+We’ll start from `render()` and `setState()` and follow the flow ending at `commitAllWork()`.
+
+#### Old code
+
+I told you that we are going to rewrite most of the code, but let’s first review the code that we are not rewriting.
+
+In [Element creation and JSX](https://engineering.hexacta.com/didact-element-creation-and-jsx-d05171c55c56) we wrote the [code for](https://gist.github.com/pomber/2bf987785b1dea8c48baff04e453b07f) `[createElement()](https://gist.github.com/pomber/2bf987785b1dea8c48baff04e453b07f)`, the function used by transpiled JSX. We don’t need to change it, we’ll keep using the same elements. If you don’t know about elements, `type`, `props` and `children`, please review the old post.
+
+In [Instances, reconciliation and virtual DOM](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) we wrote `updateDomProperties()` for updating the DOM properties of a node. I also extracted the code for creating DOM elements to `createDomElement()`. You can see both functions in this [dom-utils.js gist](https://gist.github.com/pomber/c63bd22dbfa6c4af86ba2cae0a863064).
+
+In [Components and state](https://engineering.hexacta.com/didact-components-and-state-53ab4c900e37) we wrote the `Component` base class. Let’s change it so `setState()` calls `scheduleUpdate()`, and `createInstance()` saves a reference to the fiber on the instance:
+
+
+Starting with this code and nothing else let’s rewrite the rest from scratch.
+
+* * *
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*axF-r9RCJPFYiCbyHActAA.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*axF-r9RCJPFYiCbyHActAA.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*axF-r9RCJPFYiCbyHActAA.png)
+
+Besides `Component` class and `createElement()`, we’ll have two public functions: `render()` and `setState()`, and we saw that `setState()` just calls `scheduleUpdate()`.
+
+`render()` and `scheduleUpdate()` are similar, they receive a new update and queue it:
+
+
+We’ll use the `updateQueue` array to keep track of the pending updates. Every call to `render()` or `scheduleUpdate()` pushes a new update to the `updateQueue`. The update information in each of the updates is different and we’ll see how we use it later in `resetNextUnitOfWork()`.
+
+After pushing the update to the queue, we trigger a deferred call to `performWork()`.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*uDJSf8E4fU87701Fb4bmdQ.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*uDJSf8E4fU87701Fb4bmdQ.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*uDJSf8E4fU87701Fb4bmdQ.png)
+
+
+Here’s where we use the `performUnitOfWork()` pattern that we saw earlier.
+
+`requestIdleCallback()` calls the target function with a deadline as an argument. `performWork()` takes that deadline and pass it to `workLoop()`. After `workLoop()` returns, `performWork()` checks if there’s pending work. If there is, it schedules a new deferred call to itself.
+
+`workLoop()` is the function that keeps an eye on the time. If the deadline is too close, it stops the work loop and leaves `nextUnitOfWork` updated so it can be resumed the next time.
+
+> We use `ENOUGH_TIME` (a 1ms constant, same as [React’s](https://github.com/facebook/react/blob/b52a5624e95f77166ffa520476d68231640692f9/packages/react-reconciler/src/ReactFiberScheduler.js#L154)) to check if `deadline.timeRemaining()` is enough to run another unit of work or not. If `performUnitOfWork()` takes more than that, we will overrun the deadline. The deadline is just a suggestion from the browser, so overrunning it for a few milliseconds is not that bad.
+
+`performUnitOfWork()` will build the work-in-progress tree for the update it’s working on and also find out what changes we need to apply to the DOM. **This will be done incrementally, one fiber at a time.**
+
+When `performUnitOfWork()` finishes all the work for the current update, it returns null and leaves the pending changes to the DOM in `pendingCommit`. Finally, `commitAllWork()` will take the `effects` from `pendingCommit` and mutate the DOM.
+
+Note that `commitAllWork()` is called outside of the loop. The work done on `performUnitOfWork()` won’t mutate the DOM so it’s OK to split it. On the other hand, `commitAllWork()` will mutate the DOM, it should be done all at once to avoid an inconsistent UI.
+
+We still haven’t seen where the first `nextUnitOfWork` comes from.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*G2NLlU1jgdIfJu_s7CU0oA.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*G2NLlU1jgdIfJu_s7CU0oA.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*G2NLlU1jgdIfJu_s7CU0oA.png)
+
+\*should be resetNextUnitOfWork()
+
+The function that takes an update and convert it to the first `nextUnitOfWork` is `resetNextUnitOfWork()`:
+
+
+`resetNextUnitOfWork()` starts by pulling the first update from the queue.
+
+If the update has a `partialState` we store it on the fiber that belongs to the instance of the component, so we can use it later when we call component’s `render()`.
+
+Then we find the root of the _old_ fiber tree. If the updates comes from the first time `render()` was called, we won’t have a root fiber so `root` will be `null`. If it comes from a subsequent call to `render()`, we can find the root on the `_rootContainerFiber` property of the DOM node. And if the update comes from a `setState()`, we need to go up from the instance fiber until we find a fiber without `parent`.
+
+Then we assign to `nextUnitOfWork` a new fiber. **This fiber is the root of a new work-in-progress tree**.
+
+If we don’t have an _old_ root, the `stateNode` will be the DOM node received as parameter in the `render()` call. The `props` will be the `newProps` from the update: an object with a `children` property that has the elements — the other parameter of `render()`. The `alternate` will be `null`.
+
+If we do have an _old_ root, the `stateNode` will be the DOM node from the previous root. The `props` will be again `newProps` if not `null`, or else we copy the `props` from the _old_ root. The `alternate` will be the _old_ root.
+
+We now have the root of the work-in-progress tree, let’s start building the rest of it.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*T9vp_ugVrWc3cEPdskNb3w.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*T9vp_ugVrWc3cEPdskNb3w.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*T9vp_ugVrWc3cEPdskNb3w.png)
+
+
+`performUnitOfWork()` walks the work-in-progress tree.
+
+We call `beginWork()` — to create the new children of a fiber — and then return the first child so it becomes the `nextUnitOfWork`.
+
+If there isn’t any child, we call `completeWork()` and return the `sibling` as the `nextUnitOfWork`.
+
+If there isn’t any `sibling`, we go up to the parents calling `completeWork()` until we find a `sibling` (that we’ll become the `nextUnitOfWork`) or until we reach the root.
+
+Calling `performUnitOfWork()` multiple times will go down the tree creating the children of the first child of each fiber until it finds a fiber without children. Then it moves right doing the same with the siblings. And the it moves up doing the same with the uncles. (For a more vivid description try render some components on [fiber-debugger](http://fiber-debugger.surge.sh/))
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*YzayoR7QCM3b77tOiEO23w.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*YzayoR7QCM3b77tOiEO23w.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*YzayoR7QCM3b77tOiEO23w.png)
+
+
+`beginWork()` does two things:
+
+* create the `stateNode` if we don’t have one
+* get the component children and pass them to `reconcileChildrenArray()`
+
+Because both depend on the type of component we are dealing with, we split it in two: `updateHostComponent()` and `updateClassComponent()`.
+
+`updateHostComponent()` handles host components and also the root component. It creates a new DOM node if it needs to (only one node, without children and without appending it to the DOM). **Then it calls** `**reconcileChildrenArray()**` **using the child elements from the fiber** `**props**`**.**
+
+`updateClassComponent()` deals with class component instances. It creates a new instance calling the component constructor if it needs to. **It updates the instance’s** `**props**` **and** `**state**` **so it can call the** `**render()**` **function to get the new children.**
+
+`updateClassComponent()` also validates if it makes sense to call `render()`. This is a simple version of `shouldComponentUpdate()`. If it looks like we don’t need to re-render, we just clone the current sub-tree to the work-in-progress tree without any reconciliation.
+
+Now that we have the `newChildElements`, we are ready to create the child fibers for the work-in-progress fiber.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*PBfEr0xOp6AxpyaCkNPtcQ.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*PBfEr0xOp6AxpyaCkNPtcQ.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*PBfEr0xOp6AxpyaCkNPtcQ.png)
+
+This is the heart of the library, where the work-in-progress tree grows and where we decide what changes we will do to the DOM on the commit phase.
+
+
+Before starting we make sure `newChildElements` is an array. (Unlike the previous reconciliation algorithm, this one works always with arrays of children, this means we can now return arrays on component’s `render()` function)
+
+Then we start comparing the children from the old fiber tree with the new elements (we compare fibers to elements). The children from the old fiber tree are the children of `wipFiber.alternate`. The new elements are the ones we got from the `wipFiber.props.children` or from calling `wipFiber.stateNode.render()`.
+
+Our reconciliation algorithm works by matching the first old fiber (`wipFiber.alternate.child`) with the first child element (`elements[0]`), the second old fiber (`wipFiber.alternate.child.sibling`) to the second child element (`elements[1]`) and so on. For each `oldFiber`\-`element` pair:
+
+* If the `oldFiber` and the `element` have the same `type`, good news, it mean we can keep the old `stateNode`. We create a new fiber based on the old one. We add the `UPDATE` `effectTag`. And we append the new fiber to the work-in-progress tree.
+* If we have an `element` with a different `type` to the `oldFiber` or we don’t have an `oldFiber` (because we have more new children than old children), we create a new fiber with the information we have in the `element`. Note that this new fiber won’t have an `alternate` and won’t have a `stateNode` ( the `stateNode` we’ll be created in `beginWork()`). The `effectTag` for this fiber is `PLACEMENT`.
+* If the `oldFiber` and the `element` have a different `type` or there isn’t any `element` for this `oldFiber` (because we have more old children than new children) we tag the `oldFiber` for `DELETION`. Given that this fiber is not part of the work-in-progress tree, we need to add it now to the `wipFiber.effects` list so we don’t lose track of it.
+
+> Unlike React, we are not using keys to do the reconciliation, so we won’t know if a child moved from it’s previous position.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*EKR5c8JGXamBM5G5xzCk1A.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*EKR5c8JGXamBM5G5xzCk1A.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*EKR5c8JGXamBM5G5xzCk1A.png)
+
+`updateClassComponent()` has a special case where we take a shortcut and clone the old fiber sub-tree to the work-in-progress tree instead of doing the reconciliation.
+
+
+`cloneChildFibers()` clones each of the `wipFiber.alternate` children and appends them to the work-in-progress tree. We don’t need to add any `effectTag` because we are sure that nothing changed.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*6YautmtfMnxyvTUcmVHJtQ.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*6YautmtfMnxyvTUcmVHJtQ.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*6YautmtfMnxyvTUcmVHJtQ.png)
+
+In `performUnitOfWork()`, when a `wipFiber` doesn’t have new children or when we already completed the work of all the children, we call `completeWork()`.
+
+
+`completeWork()` first updates the reference to the fiber related to the instance of a class component. (To be honest, this doesn’t really need to be here, but it has to be somewhere)
+
+Then it build a list of `effects`. This list will contain all the fibers from the work-in-progress sub-tree that have any `effectTag` (it also contains the fibers from the old sub-tree with the `DELETION` `effectTag`). The idea is to accumulate in the root `effects` list all the fibers that have an `effectTag`.
+
+Finally, if the fiber doesn’t have a `parent`, we are at the root of the work-in-progress tree. So we have completed all the work for this update and collected all the effects. We assign the root to `pendingCommit` so `workLoop()` can call `commitAllWork()`.
+
+![](https://cdn-images-1.medium.com/freeze/max/60/1*izGLw0xSkSAQstIQAf193A.png?q=20)
+
+![](https://cdn-images-1.medium.com/max/1600/1*izGLw0xSkSAQstIQAf193A.png)
+
+![](https://cdn-images-1.medium.com/max/1600/1*izGLw0xSkSAQstIQAf193A.png)
+
+There’s one last thing we need to do: mutate the DOM.
+
+
+`commitAllWork()` first iterates all the root `effects` calling `commitWork()` on each one. `commitWork()` checks the `effectTag` of each fiber:
+
+* If it is a `PLACEMENT` we look for the parent DOM node and then simply append the fiber’s `stateNode`.
+* If it is an `UPDATE` we pass the `stateNode` together with the old props and the new props and let `updateDomProperties()` decide what to update.
+* If it is a `DELETION` and the fiber is a host component, it’s easy, we just call `removeChild()`. But if the fiber is a class component, before calling `removeChild()` we need to find all the host components from the fiber sub-tree that need to be removed.
+
+Once we finished with all the effects, we can reset `nextUnitOfWork` and `pendingCommit`. The work-in-progress tree stops being the work-in-progress tree and becomes the old tree, so we assign its root to `_rootContainerFiber`. After that, we are done with the current update and we are ready to start the next one 🚀.
+
+* * *
+
+#### Running Didact
+
+If you want to put all together and expose only the public API you can do something like this:
+
+
+Or you can play with the [updated version of the demo](https://codepen.io/pomber/pen/veVOdd) from the previous posts. All this code is also available at [Didact’s repository](https://github.com/hexacta/didact) and [npm](https://unpkg.com/didact).
+
+#### What’s next?
+
+Didact is missing a lot of React’s features, but I’m particularly interested in scheduling the updates according to priorities:
+
+
+[React source](https://github.com/facebook/react/blob/5f93ee6f6ce068228b01516c021c9054b627bf11/src/renderers/shared/fiber/ReactPriorityLevel.js)
+
+So the next post may go that way.
+
+That’s all! If you like it, don’t forget to clap 👏, [follow me on twitter](https://twitter.com/pomber), leave a comment, and all that stuff.
+
+Thanks for reading.
+
+* * *
+
+> I build things at [Hexacta](http://www.hexacta.com/?hil).
+
+> If you had any idea or project we can help with, [write us](http://www.hexacta.com/contact/?hil).
\ No newline at end of file
diff --git a/readme.md b/readme.md
index 1a8757e..b6a7b80 100644
--- a/readme.md
+++ b/readme.md
@@ -11,7 +11,7 @@
---
-### 更新 🀄
+### 更新 √
@@ -53,7 +53,7 @@ Didact的目标是, 通过提供相同API的更简单实现, 以及如何构建
| [介绍](https://engineering.hexacta.com/didact-learning-how-react-works-by-building-it-from-scratch-51007984e5c5) | | | [中文](#介绍) |
| [渲染DOM元素](https://engineering.hexacta.com/didact-rendering-dom-elements-91c9aa08323b) | [codepen](https://codepen.io/pomber/pen/eWbwBq?editors=0010) | [diff](https://github.com/hexacta/didact/commit/fc4d360d91a1e68f0442d39dbce5b9cca5a08f24) | [中文](#1-%E6%B8%B2%E6%9F%93dom%E5%85%83%E7%B4%A0) |
| [元素创建和JSX](https://engineering.hexacta.com/didact-element-creation-and-jsx-d05171c55c56) | [codepen](https://codepen.io/pomber/pen/xdmoWE?editors=0010) | [diff](https://github.com/hexacta/didact/commit/15010f8e7b8b54841d1e2dd9eacf7b3c06b1a24b) | [中文](#2-%E5%85%83%E7%B4%A0%E5%88%9B%E5%BB%BA%E5%92%8Cjsx) |
-| [虚拟DOM和协调](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) | [codepen](https://codepen.io/pomber/pen/WjLqYW?editors=0010) | [diff](https://github.com/hexacta/didact/commit/8eb7ffd6f5e210526fb4c274c4f60d609fe2f810) [diff](https://github.com/hexacta/didact/commit/6f5fdb7331ed77ba497fa5917d920eafe1f4c8dc) [diff](https://github.com/hexacta/didact/commit/35619a039d48171a6e6c53bd433ed049f2d718cb) | [中文](#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom) |
+| [虚拟DOM和对比](https://engineering.hexacta.com/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) | [codepen](https://codepen.io/pomber/pen/WjLqYW?editors=0010) | [diff](https://github.com/hexacta/didact/commit/8eb7ffd6f5e210526fb4c274c4f60d609fe2f810) [diff](https://github.com/hexacta/didact/commit/6f5fdb7331ed77ba497fa5917d920eafe1f4c8dc) [diff](https://github.com/hexacta/didact/commit/35619a039d48171a6e6c53bd433ed049f2d718cb) | [中文](#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom) |
| [组件和状态](https://engineering.hexacta.com/didact-components-and-state-53ab4c900e37) | [codepen](https://codepen.io/pomber/pen/RVqBrx) | [diff](https://github.com/hexacta/didact/commit/2e290ff5c486b8a3f361abcbc6e36e2c21db30b8) | [中文](#4-%E7%BB%84%E4%BB%B6%E5%92%8C%E7%8A%B6%E6%80%81) |
| [Fibre-递增对比](https://engineering.hexacta.com/didact-fiber-incremental-reconciliation-b2fe028dcaec) | [codepen](https://codepen.io/pomber/pen/veVOdd) | [diff](https://github.com/hexacta/didact/commit/6174a2289e69895acd8fc85abdc3aaff1ded9011) [diff](https://github.com/hexacta/didact/commit/accafb81e116a0569f8b7d70e5b233e14af999ad) | [中文](#5-fibre-%E9%80%92%E5%A2%9E%E5%AF%B9%E6%AF%94) |
@@ -148,7 +148,6 @@ Didact.render(
[递增渲染,演示](https://pomber.github.io/incremental-rendering-demo/didact.html)
-
## 执照
MIT©[Hexacta](https://www.hexacta.com)
\ No newline at end of file