PageSourceSearch

https://njleonzhang.github.io/2021/03/16/deep-into-react-2.html

html njleonzhang.github.io collected 2026-10-03 09:41:12 UTC 93,840 bytes, 1,217 lines download raw bytes

1<!DOCTYPE html>
2<html>
3<head>
4    <meta charset="UTF-8">
5    <meta name="viewport" content="width=device-width, initial-scale=1.0">
6    <title>Deep into React 2 - setState 更新 - Blog of Leon Zhang</title>
7    <meta name="author"  content="Leon Zhang">
8    <meta name="description" content="Deep into React 2 - setState 更新">
9    <meta name="keywords"  content="frontend, react">
10    <!-- Open Graph -->
11    <meta property="og:title" content="Deep into React 2 - setState 更新 - Blog of Leon Zhang">
12    <meta property="og:type" content="website">
13    <meta property="og:url" content="https://www.njleonzhang.com/2021/03/16/deep-into-react-2.html">
14    <meta property="og:description" content="Leon Zhang, font end engineer. record and share something here">
15    <meta property="og:site_name" content="Blog of Leon Zhang">
16    <link rel="stylesheet" href="//cdn.staticfile.org/normalize/6.0.0/normalize.min.css">
17    <link rel="stylesheet" href="//at.alicdn.com/t/font_roc50gemkxpw4s4i.css">
18    <link rel="stylesheet" href="/assets/css/github-markdown.css">
19    <link rel="stylesheet" href="/assets/css/prism.css">
20    <link rel="stylesheet" href="/assets/css/share.min.css">
21    <link rel="stylesheet" href="/assets/css/app.min.css">
22    <link rel="stylesheet" href="//cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css">
23    
23<script src="/assets/js/jquery-3.2.1.min.js"></script>
23
24    <style>
25        .sizable-iframe {
26            width: 100%;
27        }
28
29        .sizable-iframe iframe {
30            margin-left: auto;
31            margin-right: auto;
32            display: block;
33        }
34
35        .sizable-iframe .controller {
36            display: flex;
37            align-items: center;
38            justify-content: space-between;
39            margin-top: 10px;
40        }
41
42        .sizable-iframe + .sizable-iframe {
43            margin-top: 20px;
44        }
45
46        .g-banner h1::selection, .g-banner a::selection {
47            background-color: #ADA2E5;
48        }
49    </style>
50	
51	<!--
52Author: Ray-Eldath
53refer to:
54 - http://docs.mathjax.org/en/latest/options/index.html
55-->
56
57	
57<script type="text/javascript" async src="https://cdn.bootcss.com/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML"></script>
57
58	
59    
59<script type="text/x-mathjax-config">
60      MathJax.Hub.Config({
61		jax: ["input/TeX", "output/HTML-CSS"],
62		tex2jax: {
63			inlineMath: [ ["$", "$"] ],
64			displayMath: [ ["$$", "$$"] ],
65			skipTags: ['script', 'noscript', 'style', 'textarea', 'pre', 'code']
66		},
67		"HTML-CSS": { preferredFont: "TeX", availableFonts: ["STIX","TeX"] }
68      });
69    </script>
69
70
71	
72    <!--
73Author: Ray-Eldath
74-->
75<style>
76    .markdown-body .anchor{
77        float: left;
78        margin-top: -8px;
79        margin-left: -20px;
80        padding-right: 4px;
81        line-height: 1;
82        opacity: 0;
83    }
84
85    .markdown-body .anchor .anchor-icon{
86        font-size: 15px
87    }
88
89    .markdown-body h1,
90    .markdown-body h2,
91    .markdown-body h3{
92        cursor: pointer;
93    }
94
95    .markdown-body h1:hover,
96    .markdown-body h2:hover,
97    .markdown-body h3:hover{
98        text-decoration: underline;
99    }
100
101    /* html{
102        scroll-behavior: smooth;
103    } */
104</style>
105<script>
106    $(document).ready(function() {
107        let nodes = document.querySelector(".markdown-body").querySelectorAll("h1,h2,h3")
108        for(let node of nodes) {
109            var anchor = document.createElement("a")
110            var anchorIcon = document.createElement("i")
111            anchorIcon.setAttribute("class", "fa fa-anchor fa-lg anchor-icon")
112            anchorIcon.setAttribute("aria-hidden", true)
113            anchor.setAttribute("class", "anchor")
114
115            let $node  = $(node)
116
117            node.onmouseover = function() {
118                let $icon = $node.find('a')
119                $icon.css({
120                    opacity: '0.4'
121                })
122            }
123
124            node.onmouseout = function() {
125                let $icon = $node.find('a')
126                $icon.css({
127                    opacity: '0'
128                })
129            }
130
131            node.onclick = function() {
132                location.hash = node.getAttribute("id")
133            }
134
135            anchor.appendChild(anchorIcon)
136            node.appendChild(anchor)
137        }
138
139        let $postContent = $('.post-content')
140        let height =  $postContent.height()
141
142        if (height <= 15000) {
143            $('html').css('scroll-behavior', 'smooth')
144        }
145    })
146</script>
146
147
148</head>
149
150
151<body>
152  <!--[if lt IE 10]>
153<div class="alert-danger" role="alert">你的浏览器实在太太太旧了,放学别走,升级完浏览器再说!<a target="_blank" class="alert-link" href="http://browsehappy.com">立即升级</a></div>
154<![endif]-->
155  <input id="nm-switch" type="hidden" value="false"> <header class="g-header">
156    <div class="g-logo">
157      <a href="/"></a>
158    </div>
159    <i id="menu-toggle" class="iconfont icon-menu"></i>
160    <nav class="g-nav">
161        <ul>
162            
163            <li><a href="/">home</a></li>
164            
165            <li><a href="/tags.html">tags</a></li>
166            
167        </ul>
168    </nav>
169</header>
170
171
172  <header class="g-banner post-header post-pattern-circuitBoard bgcolor-default " data-theme="default">
173    <div class="post-wrapper">
174      <div class="post-tags">
175        
176          
177            <a href="https://www.njleonzhang.com/tags#frontend" class="post-tag">frontend</a>
178          
179            <a href="https://www.njleonzhang.com/tags#react" class="post-tag">react</a>
180          
181        
182      </div>
183      <h1>Deep into React 2 - setState 更新</h1>
184      <div class="post-meta">
185        <span class="post-meta-item"><i class="iconfont icon-author"></i><a href="https://www.njleonzhang.com" target="_blank" rel="author">Leon Zhang</a></></span>
186        <time class="post-meta-item" datetime="21-03-16"><i class="iconfont icon-date"></i>16 Mar 2021</time>
187      </div>
188    </div>
189    
190    <div class="filter"></div>
191      <div class="post-cover" style="background: url('https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/71603321-aa3b-954f-bae2-64965bd91fc9.png') center no-repeat; background-size: cover;"></div>
192    
193  </header>
194
195  <div class="post-content visible">
196    
197
198    <div class='post-footer-item ad' id="codefund"><!-- fallback content --></div>
199
200    <article class="markdown-body language-js">
201      <p>上次我们分析了下面这个例子首次 mount 的过程</p>
202
203<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">class</span> <span class="nx">App</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
204  <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
205    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">App componentDidMount</span><span class="dl">'</span><span class="p">);</span>
206  <span class="p">}</span>
207  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
208    <span class="k">return</span> <span class="p">(</span>
209      <span class="o">&lt;</span><span class="nx">div</span> <span class="nx">className</span><span class="o">=</span><span class="dl">"</span><span class="s2">wrap</span><span class="dl">"</span><span class="o">&gt;</span>
210        <span class="o">&lt;</span><span class="nx">Box</span> <span class="o">/&gt;</span>
211        <span class="o">&lt;</span><span class="nx">span</span><span class="o">&gt;</span><span class="nx">list组件</span><span class="o">&lt;</span><span class="sr">/span</span><span class="err">&gt;
212</span>      <span class="o">&lt;</span><span class="sr">/div</span><span class="err">&gt;
213</span>    <span class="p">);</span>
214  <span class="p">}</span>
215<span class="p">}</span>
216<span class="kd">class</span> <span class="nx">Box</span> <span class="kd">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
217  <span class="nx">state</span> <span class="o">=</span> <span class="p">{</span>
218    <span class="na">count</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
219  <span class="p">};</span>
220  <span class="nx">handleClick</span> <span class="o">=</span> <span class="p">()</span> <span class="o">=&gt;</span> <span class="p">{</span>
221    <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">(</span><span class="nx">state</span> <span class="o">=&gt;</span> <span class="p">{</span>
222      <span class="k">return</span> <span class="p">{</span>
223        <span class="na">count</span><span class="p">:</span> <span class="o">++</span><span class="nx">state</span><span class="p">.</span><span class="nx">count</span><span class="p">,</span>
224      <span class="p">};</span>
225    <span class="p">});</span>
226  <span class="p">};</span>
227  <span class="nx">componentDidMount</span><span class="p">()</span> <span class="p">{</span>
228    <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="dl">'</span><span class="s1">Box componentDidMount</span><span class="dl">'</span><span class="p">);</span>
229  <span class="p">}</span>
230  <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
231    <span class="k">return</span> <span class="p">(</span>
232      <span class="o">&lt;</span><span class="nx">button</span> <span class="nx">onClick</span><span class="o">=</span><span class="p">{</span><span class="k">this</span><span class="p">.</span><span class="nx">handleClick</span><span class="p">}</span><span class="o">&gt;</span><span class="nx">点击次数</span><span class="p">({</span><span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">count</span><span class="p">})</span><span class="o">&lt;</span><span class="sr">/button</span><span class="err">&gt;
233</span>    <span class="p">);</span>
234  <span class="p">}</span>
235<span class="p">}</span>
236<span class="k">export</span> <span class="k">default</span> <span class="nx">App</span><span class="p">;</span>
237</code></pre></div></div>
238
239<p>本次我们来分析 button 按钮被点击后,页面的渲染过程。</p>
240
241<p>首次 mount 后的 fiber 树状态如下:</p>
242
243<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/efbf460e-59c4-6fc2-b0ed-52ce3b6064c9.png" alt="" /></p>
244
245<h2 id="setstate">setState</h2>
246<p>我们跳过事件分发机制,从 setState 执行说起</p>
247
248<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">Component</span><span class="p">.</span><span class="nx">prototype</span><span class="p">.</span><span class="nx">setState</span> <span class="o">=</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">partialState</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
249  <span class="k">this</span><span class="p">.</span><span class="nx">updater</span><span class="p">.</span><span class="nx">enqueueSetState</span><span class="p">(</span><span class="k">this</span><span class="p">,</span> <span class="nx">partialState</span><span class="p">,</span> <span class="nx">callback</span><span class="p">,</span> <span class="dl">'</span><span class="s1">setState</span><span class="dl">'</span><span class="p">)</span>
250<span class="p">}</span>
251
252<span class="kd">const</span> <span class="nx">classComponentUpdater</span> <span class="o">=</span> <span class="p">{</span>
253  <span class="nx">isMounted</span><span class="p">,</span>
254  <span class="nx">enqueueSetState</span><span class="p">(</span><span class="nx">inst</span><span class="p">,</span> <span class="nx">payload</span><span class="p">,</span> <span class="nx">callback</span><span class="p">)</span> <span class="p">{</span>
255    <span class="c1">// 1. 获取class实例对应的Fiber节点</span>
256    <span class="kd">const</span> <span class="nx">fiber</span> <span class="o">=</span> <span class="nx">getInstance</span><span class="p">(</span><span class="nx">inst</span><span class="p">)</span>
257    <span class="c1">// 2. 创建update对象</span>
258    <span class="err">…</span>
259    <span class="kd">const</span> <span class="nx">update</span> <span class="o">=</span> <span class="nx">createUpdate</span><span class="p">(</span><span class="nx">currentTime</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">,</span> <span class="nx">suspenseConfig</span><span class="p">)</span>
260    <span class="cm">/**
261    payload 就是setState的这个回调函数
262    state =&gt; {
263      return {
264        count:++state.count,
265      };
266    }
267    */</span>
268    <span class="nx">update</span><span class="p">.</span><span class="nx">payload</span> <span class="o">=</span> <span class="nx">payload</span>
269    <span class="k">if</span> <span class="p">(</span><span class="nx">callback</span> <span class="o">!==</span> <span class="kc">undefined</span> <span class="o">&amp;&amp;</span> <span class="nx">callback</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
270      <span class="nx">update</span><span class="p">.</span><span class="nx">callback</span> <span class="o">=</span> <span class="nx">callback</span>
271    <span class="p">}</span>
272    <span class="c1">// 3. 将update对象添加到当前Fiber节点的updateQueue队列当中</span>
273    <span class="nx">enqueueUpdate</span><span class="p">(</span><span class="nx">fiber</span><span class="p">,</span> <span class="nx">update</span><span class="p">)</span>
274    <span class="c1">// 4. 在当前Fiber节点上进行调度更新</span>
275    <span class="nx">scheduleUpdateOnFiber</span><span class="p">(</span><span class="nx">fiber</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">)</span>
276  <span class="p">}</span>
277<span class="p">}</span>
278</code></pre></div></div>
279
280<p><code class="language-js highlighter-rouge"><span class="nx">setState</span></code> 在 Box 组件对应 fiber 的 updateQueue 上, 然后触发更新。</p>
281
282<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/5e858ed6-0f42-694d-eae1-4e049944fa2b.png" alt="" /></p>
283
284<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/ffaff1a3-266d-bdf1-4852-f02bb045461b.png" alt="" /></p>
285
286<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="nx">type</span> <span class="nx">ExecutionContext</span> <span class="o">=</span> <span class="nx">number</span><span class="p">;</span>
287
288<span class="kd">const</span> <span class="nx">NoContext</span> <span class="o">=</span> <span class="cm">/*                    */</span> <span class="mb">0b000000</span><span class="p">;</span>
289<span class="c1">// 批量更新 batchedUpdates, 对应非批量更新,即直接更新</span>
290<span class="kd">const</span> <span class="nx">BatchedContext</span> <span class="o">=</span> <span class="cm">/*               */</span> <span class="mb">0b000001</span><span class="p">;</span>
291<span class="c1">// 批量事件更新 batchedEventUpdates</span>
292<span class="kd">const</span> <span class="nx">EventContext</span> <span class="o">=</span> <span class="cm">/*                 */</span> <span class="mb">0b000010</span><span class="p">;</span>
293<span class="c1">// 事件 dispatch 的时候</span>
294<span class="kd">const</span> <span class="nx">DiscreteEventContext</span> <span class="o">=</span> <span class="cm">/*         */</span> <span class="mb">0b000100</span><span class="p">;</span>
295<span class="kd">const</span> <span class="nx">LegacyUnbatchedContext</span> <span class="o">=</span> <span class="cm">/*       */</span> <span class="mb">0b001000</span><span class="p">;</span>
296<span class="c1">// 比如 renderRootSync,也就是建立 fiber 树的过程</span>
297<span class="kd">const</span> <span class="nx">RenderContext</span> <span class="o">=</span> <span class="cm">/*                */</span> <span class="mb">0b010000</span><span class="p">;</span>
298<span class="c1">// 渲染后 commit 的时候</span>
299<span class="kd">const</span> <span class="nx">CommitContext</span> <span class="o">=</span> <span class="cm">/*                */</span> <span class="mb">0b100000</span><span class="p">;</span>
300</code></pre></div></div>
301
302<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">scheduleUpdateOnFiber</span><span class="p">(</span>
303  <span class="nx">fiber</span><span class="p">:</span> <span class="nx">Fiber</span><span class="p">,</span>
304  <span class="nx">expirationTime</span><span class="p">:</span> <span class="nx">ExpirationTime</span><span class="p">,</span>
305<span class="p">)</span> <span class="p">{</span>
306  <span class="kd">const</span> <span class="nx">root</span> <span class="o">=</span> <span class="nx">markUpdateTimeFromFiberToRoot</span><span class="p">(</span><span class="nx">fiber</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">);</span>
307
308  <span class="c1">// leagcy下, expirationTime = Sync</span>
309  <span class="k">if</span> <span class="p">(</span><span class="nx">expirationTime</span> <span class="o">===</span> <span class="nx">Sync</span><span class="p">)</span> <span class="p">{</span>
310    <span class="k">if</span> <span class="p">(</span>
311      <span class="p">(</span><span class="nx">executionContext</span> <span class="o">&amp;</span> <span class="nx">LegacyUnbatchedContext</span><span class="p">)</span> <span class="o">!==</span> <span class="nx">NoContext</span> <span class="o">&amp;&amp;</span>
312      <span class="p">(</span><span class="nx">executionContext</span> <span class="o">&amp;</span> <span class="p">(</span><span class="nx">RenderContext</span> <span class="o">|</span> <span class="nx">CommitContext</span><span class="p">))</span> <span class="o">===</span> <span class="nx">NoContext</span>
313    <span class="p">)</span> <span class="p">{</span>
314      <span class="c1">// ... 第一次render进入</span>
315    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
316      <span class="c1">// 事件分发的时候在 discreteUpdates 函数里, 会给 executionContext 赋值</span>
317      <span class="c1">// executionContext |= DiscreteEventContext;</span>
318      <span class="c1">// 更新时进入</span>
319      <span class="nx">ensureRootIsScheduled</span><span class="p">(</span><span class="nx">root</span><span class="p">);</span>
320      <span class="p">...</span>
321    <span class="p">}</span>
322  <span class="p">}</span>
323<span class="p">}</span>
324</code></pre></div></div>
325
326<h2 id="markupdatetimefromfibertoroot">markUpdateTimeFromFiberToRoot</h2>
327<p>从当前 fiber 开始一直访问到根节点,修改所有节点的 childExpirationTime.</p>
328
329<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">const</span> <span class="nx">MAX_SIGNED_31_BIT_INT</span> <span class="o">=</span> <span class="mi">1073741823</span><span class="p">;</span>
330<span class="k">export</span> <span class="kd">const</span> <span class="nx">Sync</span> <span class="o">=</span> <span class="nx">MAX_SIGNED_31_BIT_INT</span><span class="p">;</span> <span class="c1">// 最大的符号整形数</span>
331
332<span class="kd">function</span> <span class="nx">markUpdateTimeFromFiberToRoot</span><span class="p">(</span><span class="nx">fiber</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">)</span> <span class="p">{</span>
333  <span class="c1">// Update the source fiber's expiration time</span>
334  <span class="k">if</span> <span class="p">(</span><span class="nx">fiber</span><span class="p">.</span><span class="nx">expirationTime</span> <span class="o">&lt;</span> <span class="nx">expirationTime</span><span class="p">)</span> <span class="p">{</span>
335    <span class="nx">fiber</span><span class="p">.</span><span class="nx">expirationTime</span> <span class="o">=</span> <span class="nx">expirationTime</span><span class="p">;</span>
336  <span class="p">}</span>
337  <span class="kd">let</span> <span class="nx">alternate</span> <span class="o">=</span> <span class="nx">fiber</span><span class="p">.</span><span class="nx">alternate</span><span class="p">;</span>
338  <span class="k">if</span> <span class="p">(</span><span class="nx">alternate</span> <span class="o">!==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">alternate</span><span class="p">.</span><span class="nx">expirationTime</span> <span class="o">&lt;</span> <span class="nx">expirationTime</span><span class="p">)</span> <span class="p">{</span>
339    <span class="nx">alternate</span><span class="p">.</span><span class="nx">expirationTime</span> <span class="o">=</span> <span class="nx">expirationTime</span><span class="p">;</span>
340  <span class="p">}</span>
341
342  <span class="c1">// Walk the parent path to the root and update the child expiration time.</span>
343  <span class="kd">let</span> <span class="nx">node</span> <span class="o">=</span> <span class="nx">fiber</span><span class="p">.</span><span class="k">return</span><span class="p">;</span>
344  <span class="kd">let</span> <span class="nx">root</span> <span class="o">=</span> <span class="kc">null</span><span class="p">;</span>
345  <span class="k">if</span> <span class="p">(</span><span class="nx">node</span> <span class="o">===</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">fiber</span><span class="p">.</span><span class="nx">tag</span> <span class="o">===</span> <span class="nx">HostRoot</span><span class="p">)</span> <span class="p">{</span>
346    <span class="nx">root</span> <span class="o">=</span> <span class="nx">fiber</span><span class="p">.</span><span class="nx">
346stateNode</span><span class="p">;</span>
347  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
348    <span class="k">while</span> <span class="p">(</span><span class="nx">node</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
349      <span class="nx">alternate</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">alternate</span><span class="p">;</span>
350      <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">&lt;</span> <span class="nx">expirationTime</span><span class="p">)</span> <span class="p">{</span>
351        <span class="nx">node</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">=</span> <span class="nx">expirationTime</span><span class="p">;</span>
352        <span class="k">if</span> <span class="p">(</span>
353          <span class="nx">alternate</span> <span class="o">!==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span>
354          <span class="nx">alternate</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">&lt;</span> <span class="nx">expirationTime</span>
355        <span class="p">)</span> <span class="p">{</span>
356          <span class="nx">alternate</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">=</span> <span class="nx">expirationTime</span><span class="p">;</span>
357        <span class="p">}</span>
358      <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span>
359        <span class="nx">alternate</span> <span class="o">!==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span>
360        <span class="nx">alternate</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">&lt;</span> <span class="nx">expirationTime</span>
361      <span class="p">)</span> <span class="p">{</span>
362        <span class="nx">alternate</span><span class="p">.</span><span class="nx">childExpirationTime</span> <span class="o">=</span> <span class="nx">expirationTime</span><span class="p">;</span>
363      <span class="p">}</span>
364      <span class="k">if</span> <span class="p">(</span><span class="nx">node</span><span class="p">.</span><span class="k">return</span> <span class="o">===</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">node</span><span class="p">.</span><span class="nx">tag</span> <span class="o">===</span> <span class="nx">HostRoot</span><span class="p">)</span> <span class="p">{</span>
365        <span class="nx">root</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="nx">stateNode</span><span class="p">;</span>
366        <span class="k">break</span><span class="p">;</span>
367      <span class="p">}</span>
368      <span class="nx">node</span> <span class="o">=</span> <span class="nx">node</span><span class="p">.</span><span class="k">return</span><span class="p">;</span>
369    <span class="p">}</span>
370  <span class="p">}</span>
371<span class="p">}</span>
372</code></pre></div></div>
373<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/9cb97956-fa8f-de3d-b7df-9cc48468aeab.png" alt="" /></p>
374
375<blockquote>
376  <p>经过这个过程,可以保证后续的 render 阶段,如果发现孩子的过期时间小于当前时间,意思是这个孩子没有更新,则停止调度</p>
377</blockquote>
378
379<h2 id="ensurerootisscheduled">ensureRootIsScheduled</h2>
380<p>ensureRootIsScheduled 涉及调度机制,我们简单的把它理解为最终触发了 performSyncWorkOnRoot,进入 render 阶段。</p>
381
382<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/663bde0a-5eb4-5308-1b69-8a737674073a.png" alt="" /></p>
383
384<h2 id="preparefreshstack">prepareFreshStack</h2>
385<p>render 阶段一开始,先准备环境。新建了一些指针,最重要的是把 workInProgress 指到了 fiberRoot.current.alternate
386<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/d707a41d-509f-57b5-5122-aefcb869292d.png" alt="" /></p>
387
388<h2 id="进入-workloop">进入 workLoop</h2>
389<p>进入 render 阶段,回溯 fiber 树,交替执行 beginWork 和 completeWork。</p>
390
391<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">let</span> <span class="nx">workInProgress</span> <span class="o">=</span> <span class="kc">null</span>
392
393<span class="kd">function</span> <span class="nx">workLoopSync</span><span class="p">()</span> <span class="p">{</span>
394  <span class="k">while</span> <span class="p">(</span><span class="nx">workInProgress</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
395    <span class="nx">performUnitOfWork</span><span class="p">(</span><span class="nx">workInProgress</span><span class="p">);</span>
396  <span class="p">}</span>
397<span class="p">}</span>
398
399<span class="c1">// ... 函数中省略了与首次render无关代码, 先关心主流程</span>
400<span class="kd">function</span> <span class="nx">performUnitOfWork</span><span class="p">(</span><span class="nx">unitOfWork</span><span class="p">:</span> <span class="nx">Fiber</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
401  <span class="c1">// 第一次render时, unitOfWork=HostRootFiber, alternate已经初始化</span>
402  <span class="kd">const</span> <span class="nx">current</span> <span class="o">=</span> <span class="nx">unitOfWork</span><span class="p">.</span><span class="nx">alternate</span><span class="p">;</span>
403  <span class="kd">let</span> <span class="nx">next</span><span class="p">;</span>
404  <span class="c1">// 1. 创建Fiber节点</span>
405  <span class="nx">next</span> <span class="o">=</span> <span class="nx">beginWork</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">unitOfWork</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">);</span>
406  <span class="nx">unitOfWork</span><span class="p">.</span><span class="nx">memoizedProps</span> <span class="o">=</span> <span class="nx">unitOfWork</span><span class="p">.</span><span class="nx">pendingProps</span><span class="p">;</span>
407  <span class="k">if</span> <span class="p">(</span><span class="nx">next</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
408    <span class="c1">//2. 处理beginWork中的Fiber节点</span>
409    <span class="nx">completeUnitOfWork</span><span class="p">(</span><span class="nx">unitOfWork</span><span class="p">);</span>
410  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
411    <span class="nx">workInProgress</span> <span class="o">=</span> <span class="nx">next</span><span class="p">;</span>
412  <span class="p">}</span>
413  <span class="c1">
413// ...</span>
414<span class="p">}</span>
415</code></pre></div></div>
416
417<h2 id="beginwork">beginWork</h2>
418
419<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">beginWork</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">)</span> <span class="p">{</span>
420  <span class="kd">const</span> <span class="nx">updateExpirationTime</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">expirationTime</span><span class="p">;</span>
421
422  <span class="k">if</span> <span class="p">(</span><span class="nx">current</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
423    <span class="kd">const</span> <span class="nx">oldProps</span> <span class="o">=</span> <span class="nx">current</span><span class="p">.</span><span class="nx">memoizedProps</span><span class="p">;</span>
424    <span class="kd">const</span> <span class="nx">newProps</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">pendingProps</span><span class="p">;</span>
425
426    <span class="k">if</span> <span class="p">(</span><span class="nx">oldProps</span> <span class="o">!==</span> <span class="nx">newProps</span> <span class="o">||</span> <span class="nx">hasLegacyContextChanged</span><span class="p">())</span> <span class="p">{</span>
427      <span class="c1">// props 或 context 改变了</span>
428      <span class="nx">didReceiveUpdate</span> <span class="o">=</span> <span class="kc">true</span><span class="p">;</span>
429    <span class="p">}</span> <span class="k">else</span> <span class="k">if</span> <span class="p">(</span><span class="nx">updateExpirationTime</span> <span class="o">&lt;</span> <span class="nx">renderExpirationTime</span><span class="p">)</span> <span class="p">{</span>
430      <span class="c1">// 此 fiber 没有变化,NoWork, 这个分支的主要逻辑是克隆孩子并返回</span>
431      <span class="nx">didReceiveUpdate</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
432      <span class="c1">// ...</span>
433      <span class="k">return</span> <span class="nx">bailoutOnAlreadyFinishedWork</span><span class="p">(</span>
434        <span class="nx">current</span><span class="p">,</span>
435        <span class="nx">workInProgress</span><span class="p">,</span>
436        <span class="nx">renderExpirationTime</span><span class="p">,</span>
437      <span class="p">);</span>
438    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
439      <span class="c1">// setState</span>
440      <span class="nx">didReceiveUpdate</span> <span class="o">=</span> <span class="kc">false</span><span class="p">;</span>
441    <span class="p">}</span>
442  <span class="p">}</span>
443
444  <span class="c1">// Before entering the begin phase, clear pending update priority.</span>
445  <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">expirationTime</span> <span class="o">=</span> <span class="nx">NoWork</span><span class="p">;</span>
446
447  <span class="k">switch</span> <span class="p">(</span><span class="nx">workInProgress</span><span class="p">.</span><span class="nx">tag</span><span class="p">)</span> <span class="p">{</span>
448    <span class="k">case</span> <span class="nx">XXXComponent</span><span class="p">:</span> <span class="p">{</span>
449      <span class="c1">// ...</span>
450      <span class="k">return</span> <span class="nx">updateXXXComponent</span><span class="p">(...)</span>
451      <span class="c1">// ...</span>
452    <span class="p">}</span>
453  <span class="p">}</span>
454<span class="p">}</span>
455</code></pre></div></div>
456
457<p>函数开始有 3 个分支:</p>
458<ol>
459  <li>props 或者 context 改变,didReceiveUpdate 设置成 true,调 updateXXX 函数</li>
460  <li>无 props 变化也无 state 变化,则执行bailoutOnAlreadyFinishedWork</li>
461  <li>如果是 state 变化,则 didReceiveUpdate 为 false,调 updateXXX 函数</li>
462</ol>
463
464<h1 id="分析例子">分析例子</h1>
465<p>下面我们来分析文章开头说的例子,首先是 <code class="language-js highlighter-rouge"><span class="nx">RootFiber</span></code> 的 <code class="language-js highlighter-rouge"><span class="nx">beginWork</span></code>。</p>
466
467<h2 id="beginwork-for-rootfiber">beginWork for RootFiber</h2>
468
469<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/29037114-30b2-ead5-68b8-00674a16c815.png" alt="" /></p>
470
471<p>进入第 2 个分支,调用 bailoutOnAlreadyFinishedWork 对孩子进行克隆。</p>
472<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">bailoutOnAlreadyFinishedWork</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">,)</span> <span class="p">{</span>
473  <span class="p">...</span>
474  <span class="kd">const</span> <span class="nx">childExpirationTime</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">childExpirationTime</span><span class="p">;</span>
475  <span class="k">if</span> <span class="p">(</span><span class="nx">childExpirationTime</span> <span class="o">&lt;</span> <span class="nx">renderExpirationTime</span><span class="p">)</span> <span class="p">{</span>
476    <span class="c1">// 此 fiber 的孩子也没有更新</span>
477    <span class="k">return</span> <span class="kc">null</span><span class="p">;</span>
478  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
479    <span class="c1">// 孩子有更新,对孩子进行一层克隆</span>
480    <span class="nx">cloneChildFibers</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">);</span>
481    <span class="k">return</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">child</span><span class="p">;</span>
482  <span class="p">}</span>
483<span class="p">}</span>
484</code></pre></div></div>
485<p>注意 <code class="language-js highlighter-rouge"><span class="nx">childExpirationTime</span> <span class="o">&lt;</span> <span class="nx">renderExpirationTime</span></code> 这个分支,会跳过对此分支的更新,这就是 <code class="language-js highlighter-rouge"><span class="nx">markUpdateTimeFromFiberToRoot</span></code> 的作用,优化了不需要更新的分支。</p>
486
487<p>此次 beginWork 后,fiber 树的示意:
488<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/26084b59-00df-ef53-30ae-e5373aa59399.png" alt="" /></p>
489
490<h2 id="beginwork-for-app-and-div">beginWork for App and div</h2>
491<p>过程和 RootFiber 类似,此 2 次 beginWork 后,fiber 树的示意:
492<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/c583e639-d057-aba6-e00f-1ac90e8f2be3.png" alt="" /></p>
493
494<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/52978704-1ba4-a7c4-f182-dd9132d673b1.png" alt="" /></p>
495
496<h2 id="beginwork-for-box">beginWork for Box</h2>
497<p>显然这次会进入 beginWork 的第 3 个分支。</p>
498
499<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/df3b68a7-b71b-59e5-1697-776451807af8.png" alt="" /></p>
500
501<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">beginWork</span><span class="p">()</span> <span class="p">{</span>
502  <span class="c1">// ...</span>
503  <span class="k">case</span> <span class="nx">ClassComponent</span><span class="p">:</span> <span class="p">{</span>
504     <span class="c1">// ...</span>
505    <span class="k">return</span> <span class="nx">updateClassComponent</span><span class="p">(</span>
506      <span class="nx">current</span><span class="p">,</span>
507      <span class="nx">workInProgress</span><span class="p">,</span>
508      <span class="nx">Component</span><span class="p">,</span>
509      <span class="nx">resolvedProps</span><span class="p">,</span>
510      <span class="nx">renderExpirationTime</span><span class="p">,</span>
511    <span class="p">);</span>
512  <span class="p">}</span>
513<span class="p">}</span>
514</code></pre></div></div>
515
516<p><code class="language-js highlighter-rouge"><span class="nx">updateClassComponent</span></code> 主要就是 2 个步骤:<code class="language-js highlighter-rouge"><span class="nx">updateClassInstance</span></code> 和 <code class="language-js highlighter-rouge"><span class="nx">finishClassComponent</span></code>。</p>
517
518<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">updateClassComponent</span><span class="p">(...)</span> <span class="p">{</span>
519  <span class="kd">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">stateNode</span><span class="p">;</span>
520  <span class="kd">let</span> <span class="nx">shouldUpdate</span><span class="p">;</span>
521
522  <span class="nx">shouldUpdate</span> <span class="o">=</span> <span class="nx">updateClassInstance</span><span class="p">(</span>
523    <span class="nx">current</span><span class="p">,</span>
524    <span class="nx">workInProgress</span><span class="p">,</span>
525    <span class="nx">Component</span><span class="p">,</span>
526    <span class="nx">nextProps</span><span class="p">,</span>
527    <span class="nx">renderExpirationTime</span><span class="p">,</span>
528  <span class="p">);</span>
529
530  <span class="kd">const</span> <span class="nx">nextUnitOfWork</span> <span class="o">=</span> <span class="nx">finishClassComponent</span><span class="p">(</span>
531    <span class="nx">current</span><span class="p">,</span>
532    <span class="nx">workInProgress</span><span class="p">,</span>
533    <span class="nx">Component</span><span class="p">,</span>
534    <span class="nx">shouldUpdate</span><span class="p">,</span>
535    <span class="nx">hasContext</span><span class="p">,</span>
536    <span class="nx">renderExpirationTime</span><span class="p">,</span>
537  <span class="p">);</span>
538  <span class="k">return</span> <span class="nx">nextUnitOfWork</span><span class="p">;</span>
539<span class="p">}</span>
540</code></pre></div></div>
541
542<h3 id="updateclassinstance">updateClassInstance</h3>
543<p><code class="language-js highlighter-rouge"><span class="nx">updateClassInstance</span></code> 主要做以下工作:</p>
544<ul>
545  <li>处理 <code class="language-js highlighter-rouge"><span class="nx">updateQueue</span></code></li>
546  <li>调用 <code class="language-js highlighter-rouge"><span class="nx">getDerivedStateFromProps</span></code></li>
547  <li>调用 <code class="language-js highlighter-rouge"><span class="nx">souldComponentUpdate</span></code></li>
548</ul>
549
550<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// Invokes the update life-cycles and returns false if it shouldn't rerender.</span>
551<span class="kd">function</span> <span class="nx">updateClassInstance</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">ctor</span><span class="p">,</span> <span class="nx">newProps</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">,)</span> <span class="p">{</span>
552  <span class="kd">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">stateNode</span><span class="p">;</span>
553  <span class="nx">cloneUpdateQueue</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">);</span>
554  <span class="p">...</span>
555  <span class="kd">const</span> <span class="nx">getDerivedStateFromProps</span> <span class="o">=</span> <span class="nx">ctor</span><span class="p">.</span><span class="nx">getDerivedStateFromProps</span><span class="p">;</span>
556
557  <span class="kd">const</span> <span class="nx">oldState</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">memoizedState</span><span class="p">;</span>
558  <span class="kd">let</span> <span class="nx">newState</span> <span class="o">=</span> <span class="p">(</span><span class="nx">instance</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">oldState</span><span class="p">);</span>
559  <span class="c1">
559// 处理 updateQueue</span>
560  <span class="nx">processUpdateQueue</span><span class="p">(</span><span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">newProps</span><span class="p">,</span> <span class="nx">instance</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">);</span>
561  <span class="nx">newState</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">memoizedState</span><span class="p">;</span>
562
563  <span class="c1">// …</span>
564
565  <span class="c1">// 调用生命周期 getDerivedStateFromProps</span>
566  <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">getDerivedStateFromProps</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">function</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
567    <span class="nx">applyDerivedStateFromProps</span><span class="p">(</span><span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">ctor</span><span class="p">,</span> <span class="nx">getDerivedStateFromProps</span><span class="p">,</span> <span class="nx">newProps</span><span class="p">);</span>
568    <span class="nx">newState</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">memoizedState</span><span class="p">;</span>
569  <span class="p">}</span>
570
571  <span class="c1">// 调用 shouldComponentUpdate</span>
572  <span class="kd">const</span> <span class="nx">shouldUpdate</span> <span class="o">=</span> <span class="nx">checkHasForceUpdateAfterProcessing</span><span class="p">()</span>
573      <span class="o">||</span> <span class="nx">checkShouldComponentUpdate</span><span class="p">(</span><span class="err">…</span><span class="p">);</span>
574
575  <span class="k">if</span> <span class="p">(</span><span class="nx">shouldUpdate</span><span class="p">)</span> <span class="p">{</span>
576    <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">instance</span><span class="p">.</span><span class="nx">componentDidUpdate</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">function</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
577      <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">|=</span> <span class="nx">Update</span><span class="p">;</span>
578    <span class="p">}</span>
579    <span class="k">if</span> <span class="p">(</span><span class="k">typeof</span> <span class="nx">instance</span><span class="p">.</span><span class="nx">getSnapshotBeforeUpdate</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">function</span><span class="dl">'</span><span class="p">)</span> <span class="p">{</span>
580      <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">|=</span> <span class="nx">Snapshot</span><span class="p">;</span>
581    <span class="p">}</span>
582  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
583    <span class="p">...</span>
584  <span class="p">}</span>
585
586  <span class="c1">// Update the existing instance's state, props, and context pointers even</span>
587  <span class="c1">// if shouldComponentUpdate returns false.</span>
588  <span class="nx">instance</span><span class="p">.</span><span class="nx">props</span> <span class="o">=</span> <span class="nx">newProps</span><span class="p">;</span>
589  <span class="nx">instance</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">newState</span><span class="p">;</span>
590  <span class="nx">instance</span><span class="p">.</span><span class="nx">context</span> <span class="o">=</span> <span class="nx">nextContext</span><span class="p">;</span>
591
592  <span class="k">return</span> <span class="nx">shouldUpdate</span><span class="p">;</span>
593<span class="p">}</span>
594</code></pre></div></div>
595
596<p><code class="language-js highlighter-rouge"><span class="nx">processUpdateQueue</span></code> 主要就是遍历 <code class="language-js highlighter-rouge"><span class="nx">updateQueue</span></code> 依次对 state 进行更新,从而得到最新的 state。</p>
597
598<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">processUpdateQueue</span><span class="o">&lt;</span><span class="nx">State</span><span class="o">&gt;</span><span class="p">(</span><span class="err">…</span><span class="p">):</span> <span class="k">void</span> <span class="p">{</span>
599  <span class="c1">// This is always non-null on a ClassComponent or HostRoot</span>
600  <span class="kd">const</span> <span class="na">queue</span><span class="p">:</span> <span class="nx">UpdateQueue</span><span class="o">&lt;</span><span class="nx">State</span><span class="o">&gt;</span> <span class="o">=</span> <span class="p">(</span><span class="nx">workInProgress</span><span class="p">.</span><span class="na">updateQueue</span><span class="p">:</span> <span class="nx">any</span><span class="p">);</span>
601
602  <span class="kd">let</span> <span class="nx">firstBaseUpdate</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">firstBaseUpdate</span><span class="p">;</span>
603
604  <span class="c1">// These values may change as we process the queue.</span>
605  <span class="k">if</span> <span class="p">(</span><span class="nx">firstBaseUpdate</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
606    <span class="c1">// Iterate through the list of updates to compute the result.</span>
607    <span class="kd">let</span> <span class="nx">newState</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">baseState</span><span class="p">;</span>
608
609    <span class="c1">
609// ...</span>
610
611    <span class="kd">let</span> <span class="nx">update</span> <span class="o">=</span> <span class="nx">firstBaseUpdate</span><span class="p">;</span>
612    <span class="c1">// 遍历 queue 对没一个 update 做处理</span>
613    <span class="k">do</span> <span class="p">{</span>
614      <span class="c1">// Process this update.</span>
615      <span class="nx">newState</span> <span class="o">=</span> <span class="nx">getStateFromUpdate</span><span class="p">(</span>
616        <span class="nx">workInProgress</span><span class="p">,</span>
617        <span class="nx">queue</span><span class="p">,</span>
618        <span class="nx">update</span><span class="p">,</span>
619        <span class="nx">newState</span><span class="p">,</span>
620        <span class="nx">props</span><span class="p">,</span>
621        <span class="nx">instance</span><span class="p">,</span>
622      <span class="p">);</span>
623
624      <span class="kd">const</span> <span class="nx">callback</span> <span class="o">=</span> <span class="nx">update</span><span class="p">.</span><span class="nx">callback</span><span class="p">;</span>
625      <span class="k">if</span> <span class="p">(</span><span class="nx">callback</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
626        <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">|=</span> <span class="nx">Callback</span><span class="p">;</span>
627        <span class="kd">const</span> <span class="nx">effects</span> <span class="o">=</span> <span class="nx">queue</span><span class="p">.</span><span class="nx">effects</span><span class="p">;</span>
628        <span class="k">if</span> <span class="p">(</span><span class="nx">effects</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
629          <span class="nx">queue</span><span class="p">.</span><span class="nx">effects</span> <span class="o">=</span> <span class="p">[</span><span class="nx">update</span><span class="p">];</span>
630        <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
631          <span class="nx">effects</span><span class="p">.</span><span class="nx">push</span><span class="p">(</span><span class="nx">update</span><span class="p">);</span>
632        <span class="p">}</span>
633      <span class="p">}</span>
634
635      <span class="nx">update</span> <span class="o">=</span> <span class="nx">update</span><span class="p">.</span><span class="nx">next</span><span class="p">;</span>
636
637      <span class="c1">// ...</span>
638
639    <span class="p">}</span> <span class="k">while</span> <span class="p">(</span><span class="kc">true</span><span class="p">);</span>
640
641    <span class="k">if</span> <span class="p">(</span><span class="nx">newLastBaseUpdate</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
642      <span class="nx">newBaseState</span> <span class="o">=</span> <span class="nx">newState</span><span class="p">;</span>
643    <span class="p">}</span>
644
645    <span class="nx">queue</span><span class="p">.</span><span class="nx">baseState</span> <span class="o">=</span> <span class="p">((</span><span class="na">newBaseState</span><span class="p">:</span> <span class="nx">any</span><span class="p">):</span> <span class="nx">State</span><span class="p">);</span>
646
647    <span class="c1">// ...</span>
648
649    <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">memoizedState</span> <span class="o">=</span> <span class="nx">newState</span><span class="p">;</span>
650  <span class="p">}</span>
651<span class="p">}</span>
652</code></pre></div></div>
653
654<p>经历以上的 <code class="language-js highlighter-rouge"><span class="nx">updateClassInstance</span></code> 过程后,我们就得到了该组件的最新 state,现在需要重新执行组件的 render 函数来获得新的虚拟 dom、执行 diff 算法、然后给 fiber 打上更新标记。这正是 <code class="language-js highlighter-rouge"><span class="nx">finishClassComponent</span></code> 要做的事。</p>
655
656<h3 id="finishclasscomponent">finishClassComponent</h3>
657
658<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">finishClassComponent</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span> <span class="err">…</span><span class="p">)</span> <span class="p">{</span>
659  <span class="c1">
659// ...</span>
660  <span class="kd">const</span> <span class="nx">instance</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">stateNode</span><span class="p">;</span>
661
662  <span class="c1">// Rerender</span>
663  <span class="nx">ReactCurrentOwner</span><span class="p">.</span><span class="nx">current</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">;</span>
664  <span class="kd">let</span> <span class="nx">nextChildren</span><span class="p">;</span>
665  <span class="c1">// ...</span>
666  <span class="c1">// 调用 render</span>
667  <span class="cm">/**
668    render() {
669     return (
670      &lt;button onClick={this.handleClick}&gt;点击次数({this.state.count})&lt;/button&gt;
671     );
672   }
673   */</span>
674  <span class="nx">nextChildren</span> <span class="o">=</span> <span class="nx">instance</span><span class="p">.</span><span class="nx">render</span><span class="p">();</span>
675  <span class="c1">// ...</span>
676  <span class="c1">// React DevTools reads this flag.</span>
677  <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">|=</span> <span class="nx">PerformedWork</span><span class="p">;</span>
678
679  <span class="c1">// 调用 render, 协调孩子, 即所谓的 diff 算法</span>
680  <span class="nx">reconcileChildren</span><span class="p">(</span>
681    <span class="nx">current</span><span class="p">,</span>
682    <span class="nx">workInProgress</span><span class="p">,</span>
683    <span class="nx">nextChildren</span><span class="p">,</span>
684    <span class="nx">renderExpirationTime</span><span class="p">,</span>
685  <span class="p">);</span>
686  <span class="c1">// ...</span>
687  <span class="k">return</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">child</span><span class="p">;</span>
688<span class="p">}</span>
689</code></pre></div></div>
690
691<p>nextChildren 显然是 render 渲染出来的虚拟 dom,他是一个对象:</p>
692
693<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/34506eb4-44a4-11bc-a6c7-be7f6957dd18.png" alt="" /></p>
694
695<p>下面即将进入 diff 算法,即用代表页面当前的内存结构和 代表页面将变成的内存结构 对对比,并找出差异。</p>
696<ul>
697  <li>现在:current fiber</li>
698  <li>未来:render 的这个返回,即虚拟 dom 对象
699我们现在看当前这个简单 singleElement 场景。</li>
700</ul>
701
702<blockquote>
703  <p>可以看出 react diff 算法是 fiber 链表和 vdom (对象、数组) 的对比,这和 vue 就很不相同,vue 的 diff 算法就是 vdom 和 vdom 之间的对比。那么,想一想哪一个更高效呢?</p>
704</blockquote>
705
706<h3 id="reconcilechildren---singleelement">reconcileChildren - singleElement</h3>
707
708<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">export</span> <span class="kd">function</span> <span class="nx">reconcileChildren</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span>
709  <span class="nx">nextChildren</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">)</span> <span class="p">{</span>
710  <span class="c1">// 此时 current 肯定不是空, 为空是首次渲染该组件的场景</span>
711  <span class="k">if</span> <span class="p">(</span><span class="nx">current</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
712    <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">child</span> <span class="o">=</span> <span class="nx">mountChildFibers</span><span class="p">(</span>
713      <span class="nx">workInProgress</span><span class="p">,</span>
714      <span class="kc">null</span><span class="p">,</span>
715      <span class="nx">nextChildren</span><span class="p">,</span>
716      <span class="nx">renderExpirationTime</span><span class="p">,</span>
717    <span class="p">);</span>
718  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
719    <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">child</span> <span class="o">=</span> <span class="nx">reconcileChildFibers</span><span class="p">(</span>
720      <span class="nx">workInProgress</span><span class="p">,</span>
721      <span class="nx">current</span><span class="p">.</span><span class="nx">child</span><span class="p">,</span> <span class="c1">// 你能说出这个变量是上面内存示意图里的哪一个么?</span>
722      <span class="nx">nextChildren</span><span class="p">,</span>
723      <span class="nx">renderExpirationTime</span><span class="p">,</span>
724    <span class="p">);</span>
725  <span class="p">}</span>
726<span class="p">}</span>
727</code></pre></div></div>
728
729<p><code class="language-js highlighter-rouge"><span class="nx">nextChildren</span></code> 是一个对象,而非数组,这意味着我们的目标是渲染一个单元素,所以这次会走进 <code class="language-js highlighter-rouge"><span class="nx">reconcileSingleElement</span></code> 的单元素协调逻辑。</p>
730
731<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">reconcileChildFibers</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">currentFirstChild</span><span class="p">,</span> <span class="nx">newChild</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">,)</span> <span class="p">{</span>
732  <span class="c1">// ...</span>
733  <span class="c1">// 现在 newChild 是对象类型</span>
734  <span class="kd">const</span> <span class="nx">isObject</span> <span class="o">=</span> <span class="k">typeof</span> <span class="nx">newChild</span> <span class="o">===</span> <span class="dl">'</span><span class="s1">object</span><span class="dl">'</span> <span class="o">&amp;&amp;</span> <span class="nx">newChild</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">;</span>
735
736  <span class="k">if</span> <span class="p">(</span><span class="nx">isObject</span><span class="p">)</span> <span class="p">{</span>
737    <span class="k">switch</span> <span class="p">(</span><span class="nx">newChild</span><span class="p">.</span><span class="nx">$$typeof</span><span class="p">)</span> <span class="p">{</span>
738      <span class="k">case</span> <span class="nx">REACT_ELEMENT_TYPE</span><span class="p">:</span>
739        <span class="k">return</span> <span class="nx">placeSingleChild</span><span class="p">(</span>
740          <span class="nx">reconcileSingleElement</span><span class="p">(</span>
741            <span class="nx">returnFiber</span><span class="p">,</span>
742            <span class="nx">currentFirstChild</span><span class="p">,</span>
743            <span class="nx">newChild</span><span class="p">,</span>
744            <span class="nx">expirationTime</span><span class="p">,</span>
745          <span class="p">),</span>
746        <span class="p">);</span>
747    <span class="p">}</span>
748  <span class="p">}</span>
749  <span class="c1">// ...</span>
750<span class="p">}</span>
751</code></pre></div></div>
752
753<p>想一想,给你一个单链表,而你的目标是渲染一个元素,用最小的代价创建出新的 fiber。</p>
754
755<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/7eea1903-a0c2-bc43-cb69-9838dd26e590.png" alt="" /></p>
756
757<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">reconcileSingleElement</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">currentFirstChild</span><span class="p">,</span> <span class="nx">element</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">)</span> <span class="p">{</span>
758  <span class="kd">const</span> <span class="nx">key</span> <span class="o">=</span> <span class="nx">element</span><span class="p">.</span><span class="nx">key</span><span class="p">;</span>
759  <span class="kd">let</span> <span class="nx">child</span> <span class="o">=</span> <span class="nx">currentFirstChild</span><span class="p">;</span>
760
761  <span class="k">while</span> <span class="p">(</span><span class="nx">child</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
762    <span class="c1">// 如果 key 和 child.key 都是 null,那么只有第一个元素会被遍历到</span>
763    <span class="k">if</span> <span class="p">(</span><span class="nx">child</span><span class="p">.</span><span class="nx">key</span> <span class="o">===</span> <span class="nx">key</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// key 是目标的, 进入这个分支说明找到了 key 相同的。</span>
764      <span class="k">switch</span> <span class="p">(</span><span class="nx">child</span><span class="p">.</span><span class="nx">tag</span><span class="p">)</span> <span class="p">{</span>
765        <span class="c1">// ...</span>
766        <span class="nl">default</span><span class="p">:</span> <span class="p">{</span>
767          <span class="k">if</span> <span class="p">(</span><span class="nx">child</span><span class="p">.</span><span class="nx">elementType</span> <span class="o">===</span> <span class="nx">element</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span> <span class="c1">// 如果 type 也相同</span>
768            <span class="nx">deleteRemainingChildren</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">child</span><span class="p">.</span><span class="nx">sibling</span><span class="p">);</span>
769            <span class="kd">const</span> <span class="nx">existing</span> <span class="o">=</span> <span class="nx">useFiber</span><span class="p">(</span><span class="nx">child</span><span class="p">,</span> <span class="nx">element</span><span class="p">.</span><span class="nx">props</span><span class="p">);</span>
770            <span class="nx">existing</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="nx">coerceRef</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">child</span><span class="p">,</span> <span class="nx">element</span><span class="p">);</span>
771            <span class="nx">existing</span><span class="p">.</span><span class="k">return</span> <span class="o">=</span> <span class="nx">returnFiber</span><span class="p">;</span>
772            <span class="k">return</span> <span class="nx">existing</span><span class="p">;</span>
773          <span class="p">}</span>
774          <span class="k">break</span><span class="p">;</span>
775        <span class="p">}</span>
776      <span class="p">}</span>
777      <span class="c1">// 找到 key 相同的,但是 type 不一样,那么全删了</span>
778      <span class="nx">deleteRemainingChildren</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">child</span><span class="p">);</span>
779      <span class="k">break</span><span class="p">;</span>
780    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
781      <span class="c1">// 如果不是,删了</span>
782      <span class="nx">deleteChild</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">child</span><span class="p">);</span>
783    <span class="p">}</span>
784    <span class="nx">child</span> <span class="o">=</span> <span class="nx">child</span><span class="p">.</span><span class="nx">sibling</span><span class="p">;</span>
785  <span class="p">}</span>
786
787  <span class="c1">// ...</span>
788  <span class="c1">// 如果没找到,创建一个,并返回</span>
789  <span class="kd">const</span> <span class="nx">created</span> <span class="o">=</span> <span class="nx">createFiberFromElement</span><span class="p">(</span>
790    <span class="nx">element</span><span class="p">,</span>
791    <span class="nx">returnFiber</span><span class="p">.</span><span class="nx">mode</span><span class="p">,</span>
792    <span class="nx">expirationTime</span><span class="p">,</span>
793  <span class="p">);</span>
794  <span class="nx">created</span><span class="p">.</span><span class="nx">ref</span> <span class="o">=</span> <span class="nx">coerceRef</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">currentFirstChild</span><span class="p">,</span> <span class="nx">element</span><span class="p">);</span>
795  <span class="nx">created</span><span class="p">.</span><span class="k">return</span> <span class="o">=</span> <span class="nx">returnFiber</span><span class="p">;</span>
796  <span class="k">return</span> <span class="nx">created</span><span class="p">;</span>
797<span class="p">}</span>
798</code></pre></div></div>
799
800<p>遍历 fiber 链,尝试找到 key 相同的。如果找到了,还要看 type 是否相同</p>
801<ul>
802  <li>如果 type 也相同,那么即复用此 fiber, 删除剩余的</li>
803  <li>如果 type 不相同,那么则删除所有的 fiber</li>
804</ul>
805
806<p>我们来看些例子:
807<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/e1ea6fdb-b337-04cb-1da9-eb4e05a5ba00.png" alt="" /></p>
808
809<p>回到我们的例子里,fiber 链只有 1 个元素,目标也是一个元素,key 都是 undefined,  type 都是 button,那自然就复用了。 所以这一步后
809,fiber 树是这样的:</p>
810
811<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/c9094ff0-283f-5526-b876-e97cf53e6960.png" alt="" /></p>
812
813<p>下面继续对 button fiber 做 beginWork</p>
814
815<h2 id="beginwork-for-button-fiber">beginWork for button fiber</h2>
816<p>button 是 HostFiber 的类型,所以 beginWork 会走进 updateHostComponent 的逻辑。接入还是走进了 reconcileChildren 的 diff 逻辑。</p>
817<blockquote>
818  <p>react 里的 diff 算法,叫 reconcile,所以也叫协调算法。</p>
819</blockquote>
820
821<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">updateHostComponent</span><span class="p">(</span><span class="nx">current</span><span class="p">,</span> <span class="nx">workInProgress</span><span class="p">,</span> <span class="nx">renderExpirationTime</span><span class="p">)</span> <span class="p">{</span>
822  <span class="c1">// ...</span>
823  <span class="kd">const</span> <span class="nx">nextProps</span> <span class="o">=</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">pendingProps</span><span class="p">;</span>
824
825  <span class="kd">let</span> <span class="nx">nextChildren</span> <span class="o">=</span> <span class="nx">nextProps</span><span class="p">.</span><span class="nx">children</span><span class="p">;</span>
826
827  <span class="nx">reconcileChildren</span><span class="p">(</span>
828    <span class="nx">current</span><span class="p">,</span>
829    <span class="nx">workInProgress</span><span class="p">,</span>
830    <span class="nx">nextChildren</span><span class="p">,</span>
831    <span class="nx">renderExpirationTime</span><span class="p">,</span>
832  <span class="p">);</span>
833  <span class="k">return</span> <span class="nx">workInProgress</span><span class="p">.</span><span class="nx">child</span><span class="p">;</span>
834<span class="p">}</span>
835</code></pre></div></div>
836
837<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="kd">function</span> <span class="nx">reconcileChildFibers</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">currentFirstChild</span><span class="p">,</span> <span class="nx">newChild</span><span class="p">,</span> <span class="nx">expirationTime</span><span class="p">,)</span> <span class="p">{</span>
838  <span class="c1">// ...</span>
839  <span class="c1">// 现在 newChild 这次是数组了</span>
840  <span class="k">if</span> <span class="p">(</span><span class="nx">isArray</span><span class="p">(</span><span class="nx">newChild</span><span class="p">))</span> <span class="p">{</span>
841    <span class="k">return</span> <span class="nx">reconcileChildrenArray</span><span class="p">(</span>
842      <span class="nx">returnFiber</span><span class="p">,</span>
843      <span class="nx">currentFirstChild</span><span class="p">,</span>
844      <span class="nx">newChild</span><span class="p">,</span>
845      <span class="nx">expirationTime</span><span class="p">,</span>
846    <span class="p">);</span>
847  <span class="p">}</span>
848  <span class="c1">// ...</span>
849<span class="p">}</span>
850</code></pre></div></div>
851
852<p><code class="language-js highlighter-rouge"><span class="nx">reconcileChildrenArray</span></code> 就是 react diff 的核心部分了,即设计链表和数组之间的 diff。</p>
853
854<h3 id="reconcilechildren---array---reconcilechildren">reconcileChildren - Array - reconcileChildren</h3>
855
856<p>我们面临的问题:</p>
857
858<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/700fdc21-c4d1-0260-ed1f-4c78df3e93b4.png" alt="" /></p>
859
860<blockquote>
861  <p>此例的场景十分简单,实际情况会有插入,移动,甚至反转等复杂场景。</p>
862</blockquote>
863
864<p>此算法主要由 3 个循环组成:</p>
865<ul>
866  <li>第 1 个循环,遍历 newChildren 和 fiber 链,对 key 相同的 fiber 做 updateSlot 操作。</li>
867</ul>
868
869<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">for</span> <span class="p">(;</span> <span class="nx">oldFiber</span> <span class="o">!==</span> <span class="kc">null</span> <span class="o">&amp;&amp;</span> <span class="nx">newIdx</span> <span class="o">&lt;</span> <span class="nx">newChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">newIdx</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
870  <span class="nx">nextOldFiber</span> <span class="o">=</span> <span class="nx">oldFiber</span><span class="p">.</span><span class="nx">sibling</span><span class="p">;</span>
871
872  <span class="c1">// 如果 Key match 则对 old fiber 做更新,否则返回 null</span>
873  <span class="kd">const</span> <span class="nx">newFiber</span> <span class="o">=</span> <span class="nx">updateSlot</span><span class="p">(</span>
874    <span class="nx">returnFiber</span><span class="p">,</span>
875    <span class="nx">oldFiber</span><span class="p">,</span>
876    <span class="nx">newChildren</span><span class="p">[</span><span class="nx">newIdx</span><span class="p">],</span>
877    <span class="nx">expirationTime</span><span class="p">,</span>
878  <span class="p">);</span>
879
880  <span class="c1">// 一旦出现 key 不 match 的 bad case,则跳出循环。</span>
881  <span class="k">if</span> <span class="p">(</span><span class="nx">newFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
882    <span class="k">break</span><span class="p">;</span>
883  <span class="p">}</span>
884
885  <span class="k">if</span> <span class="p">(</span><span class="nx">oldFiber</span> <span class="o">&amp;&amp;</span> <span class="nx">newFiber</span><span class="p">.</span><span class="nx">alternate</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
886    <span class="c1">// We matched the slot, but we didn't reuse the existing fiber, so we</span>
887    <span class="c1">// need to delete the existing child.</span>
888    <span class="c1">// key 相同但是 type 不同的场景,即 updateSlot 里的 createFiber 分支</span>
889    <span class="nx">deleteChild</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">oldFiber</span><span class="p">);</span>
890  <span class="p">}</span>
891
892  <span class="nx">lastPlacedIndex</span> <span class="o">=</span> <span class="nx">placeChild</span><span class="p">(</span><span class="nx">newFiber</span><span class="p">,</span> <span class="nx">lastPlacedIndex</span><span class="p">,</span> <span class="nx">newIdx</span><span class="p">);</span>
893  <span class="c1">// 如果找到了,则将拼在 resultingFirstChild 这个链上。</span>
894  <span class="k">if</span> <span class="p">(</span><span class="nx">previousNewFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
895    <span class="nx">resultingFirstChild</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
896  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
897    <span class="nx">previousNewFiber</span><span class="p">.</span><span class="nx">sibling</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
898  <span class="p">}</span>
899  <span class="nx">previousNewFiber</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
900  <span class="nx">oldFiber</span> <span class="o">=</span> <span class="nx">nextOldFiber</span><span class="p">;</span>
901<span class="p">}</span>
902</code></pre></div></div>
903
904<ul>
905  <li>
906    <p>updateSlot 的逻辑:
907<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/a7184286-20c5-cbfc-47f8-cf80d28ae76c.png" alt="" /></p>
908  </li>
909  <li>
910    <p>placeChild 的逻辑</p>
911  </li>
912</ul>
913
914<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code> <span class="c1">// 只有在原位置找到元素的时候才会更新 index</span>
915 <span class="kd">function</span> <span class="nx">placeChild</span><span class="p">(</span>
916    <span class="nx">newFiber</span><span class="p">,</span>
917    <span class="nx">lastPlacedIndex</span><span class="p">,</span>
918    <span class="nx">newIndex</span><span class="p">,</span>
919  <span class="p">)</span> <span class="p">{</span>
920    <span class="nx">newFiber</span><span class="p">.</span><span class="nx">index</span> <span class="o">=</span> <span class="nx">newIndex</span><span class="p">;</span>
921
922    <span class="kd">const</span> <span class="nx">current</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">.</span><span class="nx">alternate</span><span class="p">;</span>
923    <span class="k">if</span> <span class="p">(</span><span class="nx">current</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
924      <span class="kd">const</span> <span class="nx">oldIndex</span> <span class="o">=</span> <span class="nx">current</span><span class="p">.</span><span class="nx">index</span><span class="p">;</span>
925      <span class="k">if</span> <span class="p">(</span><span class="nx">oldIndex</span> <span class="o">&lt;</span> <span class="nx">lastPlacedIndex</span><span class="p">)</span> <span class="p">{</span>
926        <span class="c1">// This is a move.</span>
927        <span class="nx">newFiber</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">=</span> <span class="nx">Placement</span><span class="p">;</span>
928        <span class="k">return</span> <span class="nx">lastPlacedIndex</span><span class="p">;</span>
929      <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
930        <span class="c1">// This item can stay in place.</span>
931        <span class="k">return</span> <span class="nx">oldIndex</span><span class="p">;</span>
932      <span class="p">}</span>
933    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
934      <span class="c1">// This is an insertion.</span>
935      <span class="nx">newFiber</span><span class="p">.</span><span class="nx">effectTag</span> <span class="o">=</span> <span class="nx">Placement</span><span class="p">;</span>
936      <span class="k">return</span> <span class="nx">lastPlacedIndex</span><span class="p">;</span>
937    <span class="p">}</span>
938  <span class="p">}</span>
939</code></pre></div></div>
940<p>我们来看个例子:
941<img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/5eee5c9a-763f-5000-69e9-50d7099251e4.png" alt="" /></p>
942
943<p>第 1 个循环后,如果新的孩子列表已经遍历完了,那么清空老的 fiber 链表。</p>
944<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">newIdx</span> <span class="o">===</span> <span class="nx">newChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">)</span> <span class="p">{</span>
945  <span class="c1">// We've reached the end of the new children. We can delete the rest.</span>
946  <span class="nx">deleteRemainingChildren</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">oldFiber</span><span class="p">);</span>
947  <span class="k">return</span> <span class="nx">resultingFirstChild</span><span class="p">;</span>
948<span class="p">}</span>
949</code></pre></div></div>
950
951<ul>
952  <li>如果新孩子列表 newChildren 没有遍历完,老孩子链表 oldFiber 走完了,那么进入第 2 个循环,即遍历剩余的新孩子,依次创建即可。
953    <div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="k">if</span> <span class="p">(</span><span class="nx">oldFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
954<span class="c1">// If we don't have any more existing children we can choose a fast path</span>
955<span class="c1">// since the rest will all be insertions.</span>
956<span class="k">for</span> <span class="p">(;</span> <span class="nx">newIdx</span> <span class="o">&lt;</span> <span class="nx">newChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">newIdx</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
957  <span class="kd">const</span> <span class="nx">newFiber</span> <span class="o">=</span> <span class="nx">createChild</span><span class="p">(</span>
958    <span class="nx">returnFiber</span><span class="p">,</span>
959    <span class="nx">newChildren</span><span class="p">[</span><span class="nx">newIdx</span><span class="p">],</span>
960    <span class="nx">expirationTime</span><span class="p">,</span>
961  <span class="p">);</span>
962  <span class="k">if</span> <span class="p">(</span><span class="nx">newFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
963    <span class="k">continue</span><span class="p">;</span>
964  <span class="p">}</span>
965  <span class="nx">lastPlacedIndex</span> <span class="o">=</span> <span class="nx">placeChild</span><span class="p">(</span><span class="nx">newFiber</span><span class="p">,</span> <span class="nx">lastPlacedIndex</span><span class="p">,</span> <span class="nx">newIdx</span><span class="p">);</span>
966  <span class="k">if</span> <span class="p">(</span><span class="nx">previousNewFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
967    <span class="nx">resultingFirstChild</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
968  <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
969    <span class="nx">previousNewFiber</span><span class="p">.</span><span class="nx">sibling</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
970  <span class="p">}</span>
971  <span class="nx">previousNewFiber</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
972<span class="p">}</span>
973<span class="k">return</span> <span class="nx">resultingFirstChild</span><span class="p">;</span>
974<span class="p">}</span>
975</code></pre></div>    </div>
976    <p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/2d016931-0347-4c6e-8a66-ad462662b9c3.png" alt="" /></p>
977  </li>
978  <li>如果新孩子列表 newChildren 没有遍历完,老孩子 oldFiber 也没完,那么进入第三个循环。
979    <ul>
980      <li>把老孩子放在一个 map 里用 fiber 的 key 或者 index 作为 map 的 key, fiber 对象作为 value</li>
981      <li>继续遍历 newChildren ,在 map 里找 fiber,找到了就复用,找不到就新建。</li>
982      <li>
982最后如果还有剩余的老孩子,则全部标记为删除。</li>
983    </ul>
984  </li>
985</ul>
986
987<div class="language-js highlighter-rouge"><div class="highlight"><pre class="highlight"><code><span class="c1">// 构建老 fiber 的 map</span>
988<span class="kd">const</span> <span class="nx">existingChildren</span> <span class="o">=</span> <span class="nx">mapRemainingChildren</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">oldFiber</span><span class="p">);</span>
989
990<span class="c1">// 扫描剩余的新孩子,在 map 里查找</span>
991<span class="k">for</span> <span class="p">(;</span> <span class="nx">newIdx</span> <span class="o">&lt;</span> <span class="nx">newChildren</span><span class="p">.</span><span class="nx">length</span><span class="p">;</span> <span class="nx">newIdx</span><span class="o">++</span><span class="p">)</span> <span class="p">{</span>
992  <span class="kd">const</span> <span class="nx">newFiber</span> <span class="o">=</span> <span class="nx">updateFromMap</span><span class="p">(</span>
993    <span class="nx">existingChildren</span><span class="p">,</span>
994    <span class="nx">returnFiber</span><span class="p">,</span>
995    <span class="nx">newIdx</span><span class="p">,</span>
996    <span class="nx">newChildren</span><span class="p">[</span><span class="nx">newIdx</span><span class="p">],</span>
997    <span class="nx">expirationTime</span><span class="p">,</span>
998  <span class="p">);</span>
999
1000  <span class="k">if</span> <span class="p">(</span><span class="nx">newFiber</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
1001    <span class="k">if</span> <span class="p">(</span><span class="nx">shouldTrackSideEffects</span><span class="p">)</span> <span class="p">{</span>
1002      <span class="k">if</span> <span class="p">(</span><span class="nx">newFiber</span><span class="p">.</span><span class="nx">alternate</span> <span class="o">!==</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
1003        <span class="c1">// 我们复用了这个 fiber,不会销毁它,所以从 map 中删除。</span>
1004        <span class="nx">existingChildren</span><span class="p">.</span><span class="k">delete</span><span class="p">(</span>
1005          <span class="nx">newFiber</span><span class="p">.</span><span class="nx">key</span> <span class="o">===</span> <span class="kc">null</span> <span class="p">?</span> <span class="nx">newIdx</span> <span class="p">:</span> <span class="nx">newFiber</span><span class="p">.</span><span class="nx">key</span><span class="p">,</span>
1006        <span class="p">);</span>
1007      <span class="p">}</span>
1008    <span class="p">}</span>
1009    <span class="nx">lastPlacedIndex</span> <span class="o">=</span> <span class="nx">placeChild</span><span class="p">(</span><span class="nx">newFiber</span><span class="p">,</span> <span class="nx">lastPlacedIndex</span><span class="p">,</span> <span class="nx">newIdx</span><span class="p">);</span>
1010    <span class="k">if</span> <span class="p">(</span><span class="nx">previousNewFiber</span> <span class="o">===</span> <span class="kc">null</span><span class="p">)</span> <span class="p">{</span>
1011      <span class="nx">resultingFirstChild</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
1012    <span class="p">}</span> <span class="k">else</span> <span class="p">{</span>
1013      <span class="nx">previousNewFiber</span><span class="p">.</span><span class="nx">sibling</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
1014    <span class="p">}</span>
1015    <span class="nx">previousNewFiber</span> <span class="o">=</span> <span class="nx">newFiber</span><span class="p">;</span>
1016  <span class="p">}</span>
1017<span class="p">}</span>
1018
1019<span class="k">if</span> <span class="p">(</span><span class="nx">shouldTrackSideEffects</span><span class="p">)</span> <span class="p">{</span>
1020  <span class="c1">// 还有剩余的老 fiber,全标记为删除,后续会销毁。</span>
1021  <span class="nx">existingChildren</span><span class="p">.</span><span class="nx">forEach</span><span class="p">(</span><span class="nx">child</span> <span class="o">=&gt;</span> <span class="nx">deleteChild</span><span class="p">(</span><span class="nx">returnFiber</span><span class="p">,</span> <span class="nx">child</span><span class="p">));</span>
1022<span class="p">}</span>
1023</code></pre></div></div>
1024
1025<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/2aff7a76-83a2-3fb4-ea66-db0146b079c5.png" alt="" /></p>
1026
1027<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/3aa77192-ab91-a159-aa32-e792dda7f670.png" alt="" /></p>
1028
1029<p>可见对于交换的场景,react 的效率并不是很高,尤其当列表比较长的时候,移动的元素会原来越来越多。vue 的双指针优化,在这种场景下更加优秀。</p>
1030
1031<p>最后我们看一下这个算法作者的注释:</p>
1032<blockquote>
1033  <p>This algorithm can’t optimize by searching from both ends since we don’t have backpointers on fibers. I’m trying to see how far we can get with that model. If it ends up not being worth the tradeoffs, we can add it later.
1034Even with a two ended optimization, we’d want to optimize for the case where there are few changes and brute force the comparison instead of going for the Map. It’d like to explore hitting that path first in forward-only mode and only go for the Map once we notice that we need lots of look ahead. This doesn’t handle reversal as well as two ended search but that’s unusual. Besides, for the two ended optimization to
1035work on Iterables, we’d need to copy the whole set. In this first iteration, we’ll just live with hitting the bad case (adding everything to a Map) in for every insert/move.</p>
1036</blockquote>
1037
1038<p>我大概翻译了一下:</p>
1039
1040<blockquote>
1041  <p>因为 fiber 结构不是双向链表,所以我们无法对此算法进行双向搜索优化(Vue 的方案)。我正在调查双向搜索优化能达到的高度,如果有值得的话,我们可以以后再优化。
1042<del>双端优化可以让我们优化以下这样的场景:只有一点点变化的时候,不进行 Map 查找了,直接进行暴力对比。</del>本算法采用了仅向后查找的模式,一旦发现需要回头找的时候,则进入 Map 查找的方案。本算法和双指针优化一样没有处理列表反转的场景,但还好它并不常见。<del>除了这些以外,如果采用双向搜索优化,在处理 Iterables 的时候,我们还需要将整个 Iterable 复制一份。</del>这是算法的第一版,我们遇到 bad case(插入/移动)就会转向 Map 查找的方案。</p>
1043</blockquote>
1044
1045<blockquote>
1046  <p>划掉的地方是我硬翻的,我太理解他的意思。希望有高手给解释解释。</p>
1047</blockquote>
1048
1049<p>anyway,回到例子,经过 reconcileChildren, button 子元素的 fiber 会被建立好。</p>
1050
1051<p><img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/75bbfd53-c8eb-83b2-34ce-77a8c0b4cc62.png" alt="" /></p>
1052
1053<p>再往后,就和新建的场景类似啦。completeWork 建立 effectList,在进入commit 阶段,根据 effectList 对 dom 进行更新。</p>
1054
1055<h1 id="参考文档">参考文档</h1>
1056<p>https://github.com/7kms/react-illustration-series</p>
1057
1058    </article>
1059
1060    
1061    <div class="social-share-wrapper">
1062      <div class="social-share"></div>
1063    </div>
1064    
1065  </div>
1066
1067  <section class="author-detail">
1068    <section class="post-footer-item author-card">
1069      <div class="avatar">
1070        <img src="https://www.njleonzhang.com/assets/img/profile.jpeg" alt="">
1071      </div>
1072      <div class="author-name" rel="author">Leon Zhang</div>
1073      <div class="bio">
1074        <p>FED @ a startup<br/> Former engineer @citrix <br/> Former engineer @trendmicro</p>
1075      </div>
1076      
1077      <ul class="sns-links">
1078        
1079        <li>
1080          <a href="//weibo.com/u/1199856281" target="_blank">
1081                    <i class="iconfont icon-weibo"></i>
1082                </a>
1083        </li>
1084        
1085        <li>
1086          <a href="//github.com/njleonzhang" target="_blank">
1087                    <i class="iconfont icon-github"></i>
1088                </a>
1089        </li>
1090        
1091      </ul>
1092      
1093    </section>
1094    <section class="post-footer-item read-next">
1095      
1096
1097      
1098      <div class="read-next-item">
1099        <a href="/2020/08/24/compose.html" class="read-next-link"></a>
1100          <section>
1101            <span>函数式编程 compose 函数理解</span>
1102            <p>compose 在前端被广泛使用,主要用于实现各种插件系统,如 ko
1102a 和 redux 的 middleware。...</p>
1103          </section>
1104          
1105          <div class="filter"></div>
1106          <img src="https://raw.githubusercontent.com/njleonzhang/image-bed/master/assets/7dc9f602-201e-480d-f829-79b8e62b6d9c.png" alt="">
1107          
1108      </div>
1109      
1110    </section>
1111    <section class="post-footer-item comment">
1112      <div id="disqus_thread"></div>
1113    </section>
1114
1115    <style>
1116      @media screen and (max-width: 768px) {
1117        .ad {
1118          width: 100%;
1119        }
1120      }
1121    </style>
1122  </section>
1123
1124  <footer class="g-footer">
1125  <section>Blog of Leon Zhang ©
1126  
1127  
1128    2018
1129    -
1130  
1131  2023
1132  </section>
1133  <section>Powered by <a href="//jekyllrb.com">Jekyll</a> | <a href="https://github.com/kaeyleo/jekyll-theme-H2O">Theme H2O</a></section>
1134</footer>
1135
1136
1137  
1137<script src="/assets/js/social-share.min.js"></script>
1137
1138  
1138<script>
1139    socialShare('.social-share', {
1140      sites: [
1141        
1142          'wechat'
1143          ,
1144          
1145        
1146          'weibo'
1147          
1148        
1149      ],
1150      wechatQrcodeTitle: "分享到微信朋友圈",
1151      wechatQrcodeHelper: '<p>扫码后点击右上角</p><p>将本文分享至朋友圈</p>'
1152    });
1153  </script>
1153
1154
1155  
1156  
1156<script>
1157    /**
1158     *  RECOMMENDED CONFIGURATION VARIABLES: EDIT AND UNCOMMENT THE SECTION BELOW TO INSERT DYNAMIC VALUES FROM YOUR PLATFORM OR CMS.
1159     *  LEARN WHY DEFINING THESE VARIABLES IS IMPORTANT: https://disqus.com/admin/universalcode/#configuration-variables*/
1160    /*
1161    var disqus_config = function () {
1162    this.page.url = PAGE_URL;  // Replace PAGE_URL with your page's canonical URL variable
1163    this.page.identifier = PAGE_IDENTIFIER; // Replace PAGE_IDENTIFIER with your page's unique identifier variable
1164    };
1165    */
1166    (function() { // DON'T EDIT BELOW THIS LINE
1167      var d = document,
1168        s = d.createElement('script');
1169      s.src = 'https://leonblog-1.disqus.com/embed.js';
1170      s.setAttribute('data-timestamp', +new Date());
1171
1172      (d.head || d.body).appendChild(s);
1173    })();
1174  </script>
1174
1175  
1176
1177  
1177<script src="/assets/js/prism.js"></script>
1177
1178  
1178<script src="/assets/js/index.min.js"></script>
1178
1179
1180  
1181    
1181<script src="https://codefund.app/properties/68/funder.js" async="async"></script>
1181
1182  
1183
1184  
1184<script>
1185    // sizable-iframe 的 jquery 插件
1186    $('.sizable-iframe').map((index, element) => {
1187      var $item = $(element)
1188      var link = $item.data('link');
1189      var width = $item.data('width') || 640;
1190      var height = $item.data('height') || 360;
1191
1192      $item.prepend('<iframe src="' + link +
1193        '"></iframe><div class="controller"><input /><input /><button>改变iframe大小</button></div>');
1194
1195      var $iframe = $item.find('iframe')
1196      var inputs = $item.find('input');
1197      var $button = $item.find('button');
1198      var $widthInput = inputs.eq(0);
1199      var $hightInput = inputs.eq(1);
1200
1201      $iframe
1202        .css('width', width)
1203        .css('height', height);
1204
1205      $widthInput.val(width);
1206      $hightInput.val(height);
1207
1208      $button.on('click', _ => {
1209        $iframe
1210          .css('width', $widthInput.val())
1211          .css('height', $hightInput.val())
1212      });
1213    })
1214  </script>
1214
1215</body>
1216
1217</html>

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.