-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathatom.xml
3038 lines (2329 loc) · 254 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><![CDATA[NO END FOR LEARNING]]></title>
<link href="http://benweizhu.github.io/atom.xml" rel="self"/>
<link href="http://benweizhu.github.io/"/>
<updated>2018-05-09T00:21:36+08:00</updated>
<id>http://benweizhu.github.io/</id>
<author>
<name><![CDATA[ZHU Benwei]]></name>
</author>
<generator uri="http://octopress.org/">Octopress</generator>
<entry>
<title type="html"><![CDATA[React的思考(十二)- 组织state和reducer(4)]]></title>
<link href="http://benweizhu.github.io/blog/2018/05/08/deep-thinking-in-react-12/"/>
<updated>2018-05-08T23:45:26+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/05/08/deep-thinking-in-react-12</id>
<content type="html"><![CDATA[<h2>不同的页面也可能有相同领域,不同状态的数据,也算是一个Domain吧?</h2>
<h2>没有BFF的情况下</h2>
<p>基于Redux的应用程序中最常见的state结构是一个简单的JavaScript对象,它最外层的每个key中拥有特定域的数据。</p>
<p>然而,BFF可以是来自不同Domain的数据库合并后返回到前端的。</p>
<h2>暴露的服务和接口</h2>
<p>思考后端的操作逻辑,微服务的场景,按照业务的Restful API,Redux的这种设计模式导致这种CRUD和Service的冲突</p>
<h2>Normalize State</h2>
<h2>BFF层的数据结构</h2>
<h2>一个action要操作多个reducer中的state</h2>
<h2>redux的state和页面的关系</h2>
<h2>开发过程中state的演进过程</h2>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(十一)- 组织state和reducer(3)]]></title>
<link href="http://benweizhu.github.io/blog/2018/05/04/deep-thinking-in-react-11/"/>
<updated>2018-05-04T23:09:38+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/05/04/deep-thinking-in-react-11</id>
<content type="html"><![CDATA[<p>以React为首的“数据驱动”的开发方式,让我们从过去的DOM操作,慢慢转化成对应用状态和数据的管理,这些状态和数据在应用的生命周期中被持久化,被应用管理和使用,怎么样有效的在SPA中管理这些数据变的特别重要。</p>
<h2>前后端不同的数据模型</h2>
<p>在后端开发中,我们设计数据库或者对象模型,通常会根据领域模型来建立不同的数据表和对象,以反映我们对客观现实的抽象,这种抽象在MVC的世界里通常由Model表示。</p>
<p>而在前端开发中,我们一般会从UI的角度出发,去设计前端展示所需要的视图对象模型,我们也称之为View Model。</p>
<p>而往往在大多数情况下,后端的数据模型Model和前端的数据模型View Model是不对等的。</p>
<h2>来自后方的数据</h2>
<p>当一个应用的生命周期启动,应用会调用后台的API获取后端数据,然后以某种方式转换成前端所需数据模型,最后展示在应用的页面上。</p>
<p>作为一个后端的开发人员,通常以什么样的方式暴露后端的资源给别的应用使用呢?</p>
<p>如果需要API的通用度比较高,一般最简单直接的方法是开放领域资源的CRUD操作(你可以是RESTful API也可以是别的方式)。</p>
<figure class='code'><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
</pre></td><td class='code'><pre><code class=''><span class='line'>https://api.example.com/v1/zoos
</span><span class='line'>https://api.example.com/v1/animals
</span><span class='line'>https://api.example.com/v1/employees
</span><span class='line'>
</span><span class='line'>GET /zoos:列出所有动物园
</span><span class='line'>POST /zoos:新建一个动物园
</span><span class='line'>GET /zoos/ID:获取某个指定动物园的信息
</span><span class='line'>PUT /zoos/ID:更新某个指定动物园的信息(提供该动物园的全部信息)</span></code></pre></td></tr></table></div></figure>
<p>我们目前就暂且以这种方式为例,目测这种方式暴露的API比较常见,后面我们再讨论其他的。</p>
<h2>combineReducer下的拆分</h2>
<p>让我们再回到Redux中,基于Redux的应用程序中,比较常见的state结构是一个简单的JavaScript对象,它最外层的每个key中拥有特定域的数据,这其实是Redux官方文档上的一句话,我在其他的一些博客上也看到了采用基于业务领域的方式组织state结构。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="s2">"zoos"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">"animals"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">},</span>
</span><span class='line'> <span class="s2">"employees"</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>在combineReducer的帮助下,构建上面这个结构,其实就是拆分成多个reducer,拆分之后的reducer独立负责管理该特定切片state的更新。好处是,它提供了一种非常直接的代码逻辑拆分管理的方式,职责独立,物理位置独立(文件独立)。</p>
<h2>响应同一个操作</h2>
<p>有了这样一层结构,我们该如何操作它呢?</p>
<p>现在我们假设有这样一个操作,它既需要更新zoos下面的数据,又需要更新animals下面的数据。</p>
<p>如果放在后端代码中,你会怎么做?</p>
<p>我的思路会是写一个函数A,在这个函数里面调用zoos和animals的service或者repository的方法完成更新操作,那么,真正使用的时候,只需要调用这个函数A即可,思路很明朗直接。</p>
<p>在combineReducer和redux结合情况下,我们就需要转换一下思路了,不同业务领域下的数据被放置到了不同的reducer,而你能做的只是发送action。</p>
<p>combineReducer神奇的地方就是,被发送的action会被所有的reducer接收到。(有点像发布订阅模式)</p>
<p>这样一个过去直觉上同步有序的操作过程,在redux中,被分发到多个拆分之后的reducer中,每个reducer都去响应这个action,在需要的情况下独立的更新他们自己的切片state,最后组合成新的state。</p>
<h2>计算衍生(derived)数据</h2>
<p>后端的业务数据被存储在了redux的store中,然而它是以后端model的形式保存在那。我们的前端页面需要的数据模型,一般和后端model不完全一样,也许是多个后端model组合在一起才得到可以使用的view model,这种情况很常见。</p>
<p>这个时候,我们就需要从后端数据模型中计算衍生数据,得到我们最终需要的View Model。</p>
<p>一般的做法是在mapStateToProps中进行,mapStateToProps中的state是根节点上的state,所以可以拿到所有的领域数据,此时我们就能根据它衍生出我们需要的视图模型。</p>
<p>而在这里一般都会推荐使用reselect库来做,好处是:</p>
<p>第一,能够借机将这个计算衍生数据的逻辑拆分到另一个模块中 <br/>
第二,它能帮你记住之前的计算过的数据,避免二次计算,同时避免无意义的重新渲染(关于什么时候重新渲染,在前面已经介绍过了)。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">import</span> <span class="p">{</span> <span class="nx">createSelector</span> <span class="p">}</span> <span class="nx">from</span> <span class="s1">'reselect'</span>
</span><span class='line'>
</span><span class='line'><span class="kr">const</span> <span class="nx">getVisibilityFilter</span> <span class="o">=</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="o">=></span> <span class="nx">state</span><span class="p">.</span><span class="nx">visibilityFilter</span>
</span><span class='line'><span class="kr">const</span> <span class="nx">getTodos</span> <span class="o">=</span> <span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="o">=></span> <span class="nx">state</span><span class="p">.</span><span class="nx">todos</span>
</span><span class='line'>
</span><span class='line'><span class="kr">export</span> <span class="kr">const</span> <span class="nx">getVisibleTodos</span> <span class="o">=</span> <span class="nx">createSelector</span><span class="p">(</span>
</span><span class='line'> <span class="p">[</span> <span class="nx">getVisibilityFilter</span><span class="p">,</span> <span class="nx">getTodos</span> <span class="p">],</span>
</span><span class='line'> <span class="p">(</span><span class="nx">visibilityFilter</span><span class="p">,</span> <span class="nx">todos</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="p">(</span><span class="nx">visibilityFilter</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_ALL'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_COMPLETED'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="nx">t</span><span class="p">.</span><span class="nx">completed</span><span class="p">)</span>
</span><span class='line'> <span class="k">case</span> <span class="s1">'SHOW_ACTIVE'</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">todos</span><span class="p">.</span><span class="nx">filter</span><span class="p">(</span><span class="nx">t</span> <span class="o">=></span> <span class="o">!</span><span class="nx">t</span><span class="p">.</span><span class="nx">completed</span><span class="p">)</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<h2>总结</h2>
<p>这么分析下来,每个部分的职责都很清晰,开发的模式也比较明确,然而理想和设计在业务不复杂的时候都很美好,现实往往比它们骨感很多,这一篇文章只是给了一个简单的例子,没有分析复杂的场景。</p>
<p>在下一篇,我们可以在此基础上分析开发中的复杂场景,一起思考下怎么样管理是合适的。同时,我们也来思考另一种新的前端架构BFF下的Redux应该怎么管理,当后端API不再是资源的CRUD,而是面向业务的API操作时,Redux又应该怎么管理state。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(十)- 组织state和reducer(2)]]></title>
<link href="http://benweizhu.github.io/blog/2018/05/03/deep-thinking-in-react-10/"/>
<updated>2018-05-03T23:05:04+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/05/03/deep-thinking-in-react-10</id>
<content type="html"><![CDATA[<h2>共享的数据</h2>
<p>前面提到应用的数据类型有三种:领域数据,应用状态,UI状态。</p>
<p>其中,UI状态看上去更多是存放在React组件里面。</p>
<p>那是不是领域数据和应用状态都应该归Redux管理呢?不一定,为什么呢?我是从这角度思考的:</p>
<p>Redux本身存储的是一个全局数据,即被共享的数据。那么,我们就会问,不会被共享的数据应该存在哪呢?</p>
<h4>存储在Redux里面</h4>
<p>首先,你当然仍然可以存储在Redux里面,那么当前情况会是,数据被存放在Redux state树形结构的某一片区域中,被某一个路由下的某一个组件所使用。你一定会问,这样做有什么好处?</p>
<p>1.单一的数据源,你很放心的将数据存在那里,打开Redux Debug Tool就能看到(心里长叹一口气,数据还在还在)</p>
<p>2.将同类型(比如:领域数据)的数据统一类聚在一起,方便你统一管理,让你拥有绝对的上帝视角</p>
<p>3.某一天,设计师说,在一个离原组件很遥远的位置加一个按钮,当点击时,要改变这个数据,此时你可以很轻松的发送一个action并写一个reducer的case操作它,就完成了(数据操作和组件的关系是低耦合的)</p>
<p>等等其他我没有想到的好处</p>
<h4>存储在Component里面</h4>
<p>如果我不存放在Redux中,应该放哪里?可以是组件里面。那么同样的问题,放在组件里面有什么好处?</p>
<p>1.Redux中存放的都是被共享的数据,相比存放所有的数据,redux的state结构会小一些</p>
<p>2.组件的数据和状态是自管理的,无论你把我放在哪,我都能坚强的活着</p>
<p>3.某一天,我被页面的多个位置用到,你不需要在Redux里面配多个数据空间和action以供我的分身使用,我自带装备,我为自己“带盐”</p>
<p>4.我还能配合高阶组件(用高阶函数做数据加载部分),让你的代码看上更加装逼</p>
<p>等等其他我没有想到的好处</p>
<p>如果博客里面可以发表情,此时我特别想发一个拍脑袋的表情,请问,看完这一段,我胡乱分析的结果,是不是感觉有些不知所措?我到底应该放在哪呢?</p>
<h2>官文FAQ的经验法则</h2>
<p>正如官方文档里面FAQ的结果</p>
<blockquote><p>问:必须将所有state都维护在Redux中吗? 可以用React的setState()方法吗?</p>
<p>答:没有 “标准”。作为一名开发者,应该决定使用何种 state 来组装你的应用,每个 state 的生存范围是什么。在两者之间做好平衡,然后就去做吧。</p></blockquote>
<p>所以说,看情况(It depends)永远是正确的答案。</p>
<p>当然官方文档也给了一些将怎样的数据放入Redux的经验法则:</p>
<p>1.应用的其他部分是否关心这个数据? <br/>
2.是否需要根据需要在原始数据的基础上创建衍生数据? <br/>
3.相同的数据是否被用作驱动多个组件? <br/>
4.能否将状态恢复到特定时间点(在时光旅行调试的时候)? <br/>
5.是否要缓存数据(比如:数据存在的情况下直接去使用它而不是重复去请求他)?</p>
<p>我个人感觉这些经验法则是有些道理的,和我们前面讲解和分析的套路差不多,可以作为参考。</p>
<h2>总结</h2>
<p>首先,我个人认为一定不是将所有的数据都放在Redux里面,但是至于什么样的数据该放在哪里,那就需要看是什么样的使用场景?你需要询问自己几个问题(如上面说问),分析它的好处和坏处,是否满足你的需求,然后做出判断。</p>
<p>我们下一节,来看redux state的组织结构(shape),以及action,reducer如何配合state的更新。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(九)- 组织state和reducer(1)]]></title>
<link href="http://benweizhu.github.io/blog/2018/05/01/deep-thinking-in-react-9/"/>
<updated>2018-05-01T22:47:38+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/05/01/deep-thinking-in-react-9</id>
<content type="html"><![CDATA[<h2>前提条件</h2>
<p>在进行深入探讨之前,我先确保大家的理解是一致的,因为这部分是客观的,而之后的内容是相对主观和有争议的。</p>
<p>1.当组件的某个操作dispatch了一个action,所有的reducer都会接收到:<a href="https://stackoverflow.com/questions/33590579/all-reducers-will-be-invoked-when-an-action-is-dispatched" title="All reducers will be invoked when an action is dispatched?">All reducers will be invoked when an action is dispatched?</a>,<a href="https://cn.redux.js.org/docs/recipes/reducers/UsingCombineReducers.html" title="combineReducers用法中也有讲到">combineReducers用法中也有讲到</a></p>
<p>2.当combineReducers发现有任意一个reducer返回了新的state,会通知所有和redux关联(connect)的组件准备更新,请检查自己是否要更新</p>
<p>3.每一个通过connect构建的组件,其mapStateToProps中的state,是combineReducers合并的state,也就是每个组件都能拿到所有reducer中的state(曾经有遇到过有人误以为是跟它action相关的reducer的state)</p>
<h2>Redux的上帝视角</h2>
<p>Redux的三大设计原则之一,单一数据源,定义了整个应用的state被储存在一棵object tree中,并且这个object tree只存在于唯一一个store中。这样一个顶层的状态树,会拥有一个全局的视角,掌握着整个应用的状态。</p>
<p>单一数据源的设计原则在许多程序设计的领域都是准确的,但仅仅用一个JavaScript对象来存储整个应用的状态,总会让人感觉某一天这个对象一定会特别臃肿,上帝实在太忙,要关注的东西太多。</p>
<p>很自然的,我们就会去思考,物尽其用,到底什么样的数据应该放在Redux中,什么样的数据应该放在别处来管理。</p>
<h2>应用拥有的数据和状态</h2>
<p>大多数应用会处理多种类型的数据和状态,通常可以分为以下三类:领域数据(Domain data),应用状态(App state),UI状态(UI state)</p>
<p>领域数据也就是业务相关数据,一般和你的后台业务系统数据相关联,是领域数据的数据来源,但他们不一定直接对等。</p>
<p>应用状态和UI状态有时候不容易分清,应用状态是描述应用无限循环的生命周期中的某一种存在(中间)状态,而这样一个应用状态可能会导致一个或者多个UI的状态变化。比如:用户登录,是一个应用状态,它可能导致导航栏的UI状态改变。</p>
<p>UI的状态,自然是描述UI的改变,但不一定是由应用状态的变化导致。比如:页面上tab的切换。</p>
<h2>应用数据和状态的存储位置和影响范围</h2>
<p>根据上面的应用数据和状态的分类,好像让我们对这样一个问题有些头绪。不过在你下任何判断之前,我们在从另外一个维度继续思考一下。</p>
<p>从前面我们就了解到,Redux存储的数据,在任何一个与之关联的组件中都能拿到,也就是说,Redux存储的是一个全局的数据。</p>
<p>反之,React本身也有一个state,而它所关注的只是组件本身以及它的子组件,兄弟和父组件它都不关心。</p>
<p>除了Redux和React,就没有别的位置保存数据了?当然不是,比如:cookie,local storage。这些也是保存数据的关键位置,毕竟当页面刷新后,Redux和React中的state就丢失了,还需要从后台重新加载。</p>
<h2>React组件的state存什么?</h2>
<p>现在还不是时候讨论Redux里面存什么,我们反向推理,用排除法,先看看React里面应该存在什么。</p>
<p>React中的state存在于组件当中,那我们就需要思考组件的特性,它能独立,也许还能自治,一般都高可重用,这是它的部分典型特性。基于它这些特性,也就决定了它的state也必须满足这些要求,组件的state是服务于组件本身的,这些state能够在不收外部干预下就自我管理,这些state当组件被用在任何位置时,都能适应。一个典型的例子:UI状态。</p>
<p>当然,上面是我对React组件state理解的一个抽象描述,能够一定程度下知道我的思考。还有什么原则,能够帮助决策什么样的state可以放在组件中,Dan Abramov在他的Twitter发了这样一张图片也具有不错的指导意义:</p>
<p><img src="https://camo.githubusercontent.com/5e85994aa142e7699548e2f5a1e74583229ebd10/68747470733a2f2f7062732e7477696d672e636f6d2f6d656469612f436d654273477a57384151705f61762e6a7067" width="500" alt="" /></p>
<p>1.如果这个数据可以从props中计算得到,那么就不应该放在state中 <br/>
2.如果这个数据在render方法中不被使用,那么就不应该放在state中</p>
<h2>总结</h2>
<p>今天先写到这里,后续还会继续讨论redux的state和reducer的设计思考。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(八)- Redux的Middleware(下)异步的世界]]></title>
<link href="http://benweizhu.github.io/blog/2018/04/27/deep-thinking-in-react-8/"/>
<updated>2018-04-27T13:38:01+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/04/27/deep-thinking-in-react-8</id>
<content type="html"><![CDATA[<h2>有序而独立的同步世界</h2>
<p>没有异步的情况下,Redux配合React很容易理解的(Action->Reducer->CombineReducers->React-Redux->Component),简单回顾下:</p>
<p>1.在组件里面dispatch(发出)一个action对象(带上类型和数据) <br/>
2.action对象被传递到reducer的入口,reducer根据类型给到不同的switch分支,然后根据带入的数据操作state,返回新的state <br/>
3.redux发现有新的state,配合React-Redux,通知所有component,告诉组件请注意你自己要不要更新,然后各自判断各自更新</p>
<p>一个被传递到组件里的disptach</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">const</span> <span class="nx">mapDispatchToProps</span> <span class="o">=</span> <span class="nx">dispatch</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">onAddTodo</span><span class="o">:</span> <span class="nx">text</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">addTodo</span><span class="p">(</span><span class="nx">text</span><span class="p">))</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'><span class="kr">const</span> <span class="nx">addTodo</span> <span class="o">=</span> <span class="p">(</span><span class="nx">text</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="nx">type</span><span class="o">:</span> <span class="s1">'ADD_TODO'</span><span class="p">,</span>
</span><span class='line'> <span class="nx">text</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">//在组件里面点击时触发</span>
</span><span class='line'><span class="nx">handleClickAdd</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">onAddTodo</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">todoText</span><span class="p">)</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>action被传递到reducer的入口</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kd">function</span> <span class="nx">todoApp</span><span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">initialState</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">case</span> <span class="nx">ADD_TODO</span><span class="o">:</span><span class="c1">//命中这个switch case</span>
</span><span class='line'> <span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span> <span class="nx">state</span><span class="p">,</span> <span class="p">{</span><span class="c1">//返回新的state</span>
</span><span class='line'> <span class="nx">todos</span><span class="o">:</span> <span class="p">[</span>
</span><span class='line'> <span class="p">...</span><span class="nx">state</span><span class="p">.</span><span class="nx">todos</span><span class="p">,</span>
</span><span class='line'> <span class="p">{</span>
</span><span class='line'> <span class="nx">text</span><span class="o">:</span> <span class="nx">action</span><span class="p">.</span><span class="nx">text</span><span class="p">,</span>
</span><span class='line'> <span class="nx">completed</span><span class="o">:</span> <span class="kc">false</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">]</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">state</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>组件被通知请查看你是否需要更新,connect发现todos变了,所以要更新这个connect嵌入的组件</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">const</span> <span class="nx">mapStateToProps</span> <span class="o">=</span> <span class="nx">state</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">todos</span><span class="o">:</span> <span class="nx">getVisibleTodos</span><span class="p">(</span><span class="nx">state</span><span class="p">.</span><span class="nx">todos</span><span class="p">,</span> <span class="nx">state</span><span class="p">.</span><span class="nx">visibilityFilter</span><span class="p">)</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<h2>没有Redux的异步世界</h2>
<p>异步世界其实没什么可怕的(又不是异世界),看下面一个React里面用fetch实现的数据异步加载:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
<span class='line-number'>29</span>
<span class='line-number'>30</span>
<span class='line-number'>31</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">class</span> <span class="nx">ExampleComponent</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">constructor</span><span class="p">(</span><span class="nx">props</span><span class="p">){</span>
</span><span class='line'> <span class="kr">super</span><span class="p">(</span><span class="nx">props</span><span class="p">);</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">state</span> <span class="o">=</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">''</span><span class="p">,</span> <span class="nx">loading</span><span class="o">:</span> <span class="kc">false</span><span class="p">,</span> <span class="nx">errorMessage</span><span class="o">:</span> <span class="s1">''</span> <span class="p">};</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">_getRandomName</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">getRandomName</span><span class="p">.</span><span class="nx">bind</span><span class="p">(</span><span class="k">this</span><span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">render</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="p">{</span> <span class="nx">name</span><span class="p">,</span> <span class="nx">errorMessage</span> <span class="p">}</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">;</span>
</span><span class='line'> <span class="k">return</span><span class="p">(</span>
</span><span class='line'> <span class="o"><</span><span class="nx">div</span><span class="o">></span>
</span><span class='line'> <span class="o"><</span><span class="nx">h1</span><span class="o">></span><span class="p">{</span><span class="nx">name</span><span class="p">}</span><span class="o"><</span><span class="err">/h1></span>
</span><span class='line'> <span class="o"><</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">_getRandomName</span><span class="p">}</span><span class="o">></span><span class="nx">PRESS</span> <span class="nx">ME</span><span class="o">!<</span><span class="err">/button></span>
</span><span class='line'> <span class="p">{</span> <span class="nx">errorMessage</span> <span class="o">&&</span> <span class="o"><</span><span class="nx">div</span><span class="o">></span><span class="p">{</span><span class="nx">errorMessage</span><span class="p">}</span><span class="o"><</span><span class="err">/div> }</span>
</span><span class='line'> <span class="o"><</span><span class="err">/div></span>
</span><span class='line'> <span class="p">);</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'>
</span><span class='line'> <span class="nx">getRandomName</span><span class="p">()</span> <span class="p">{</span> <span class="c1">// 重点是这么一段代码</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">loading</span><span class="o">:</span> <span class="kc">true</span><span class="p">})</span>
</span><span class='line'> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'https://randomuser.me/api/'</span><span class="p">)</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">results</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span> <span class="nx">name</span><span class="o">:</span> <span class="err">`</span><span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">first</span><span class="p">}</span> <span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">last</span><span class="p">}</span><span class="err">`</span><span class="p">,</span> <span class="nx">loading</span><span class="o">:</span> <span class="kc">false</span> <span class="p">})</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">reason</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">errorMessage</span><span class="o">:</span><span class="s1">'get name failed'</span><span class="p">,</span> <span class="nx">loading</span><span class="o">:</span> <span class="kc">false</span><span class="p">})</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>过程很简单,我们需要考虑三种页面状态:请求开始和进行中,请求成功,请求失败,然后分别设置组件的state。</p>
<h2>如果没有Redux异步中间件</h2>
<p>按照“没有Redux的异步世界”的思想,在Redux里面,我们仍然可以依葫芦画瓢的进行异步的Redux的操作。</p>
<p>首先,一个异步请求都需要dispatch至少三种action,对应至少三个不同的状态:</p>
<p>1.通知reducer请求开始 <br/>
2.通知reducer请求成功 <br/>
3.通知reducer请求失败</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="s1">'FETCH_POSTS_REQUEST'</span> <span class="p">}</span>
</span><span class='line'><span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="s1">'FETCH_POSTS_FAILURE'</span><span class="p">,</span> <span class="nx">error</span><span class="o">:</span> <span class="s1">'Oops'</span> <span class="p">}</span>
</span><span class='line'><span class="p">{</span> <span class="nx">type</span><span class="o">:</span> <span class="s1">'FETCH_POSTS_SUCCESS'</span><span class="p">,</span> <span class="nx">response</span><span class="o">:</span> <span class="p">{</span> <span class="nx">name</span><span class="o">:</span> <span class="s1">'Redux'</span><span class="p">}</span> <span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>如果没有Redux异步中间件,那么你的做法和没有Redux时是类似的,你需要在mapDispatchToProps那传入三个disptach,将异步的fetch逻辑放在组件里面实现,Redux本身仍然是处理同步的state操作。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">class</span> <span class="nx">ExampleComponent</span> <span class="kr">extends</span> <span class="nx">React</span><span class="p">.</span><span class="nx">Component</span> <span class="p">{</span>
</span><span class='line'> <span class="p">...</span>
</span><span class='line'> <span class="nx">getRandomName</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">fetchRequest</span><span class="p">(</span><span class="kc">true</span><span class="p">)</span>
</span><span class='line'> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'https://randomuser.me/api/'</span><span class="p">)</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">results</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">fetchSuccess</span><span class="p">(</span><span class="err">`</span><span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">first</span><span class="p">}</span> <span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">last</span><span class="p">}</span><span class="err">`</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">reason</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">fetchFailure</span><span class="p">(</span><span class="s1">'get name failed'</span><span class="p">,</span> <span class="kc">false</span><span class="p">)</span>
</span><span class='line'> <span class="p">});</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<h2>使用Redux-Thunk的不同点在哪?</h2>
<p>因为Redux官网推荐,我们就以Redux-Thunk为例。</p>
<p>Redux-Thunk刚刚引入的时候,往往容易让使用者有些感到混乱,一个原因是函数式编程的嵌套写法,第二个是和Redux之前dispatch函数做的事情不一样了。</p>
<p>其实,没有Redux-Thunk我们已经可以处理异步请求,只不过异步逻辑不在Redux里面,而是在组件里面,如果我们加入Redux-Thunk会有什么不同呢?</p>
<p>我在<a href="http://benweizhu.github.io/blog/2018/04/25/deep-thinking-in-react-7/" title="上面篇文章">上面篇文章</a>讲过,中间件的作用是在dispatch的附近做一些额外的操作,让Redux拥有不同的能力,Redux-Thunk中间件的能力,可以让action creater,不用返回一个action对象,而是一个函数,这个action创建的函数就成为一个thunk。</p>
<p>(关于Thunk函数的含义:编译器的”传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做Thunk函数。)</p>
<p>这个函数并不需要保持纯净,它还可以带有副作用,包括执行异步API请求。这个函数还可以dispatch action。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
<span class='line-number'>17</span>
<span class='line-number'>18</span>
<span class='line-number'>19</span>
<span class='line-number'>20</span>
<span class='line-number'>21</span>
<span class='line-number'>22</span>
<span class='line-number'>23</span>
<span class='line-number'>24</span>
<span class='line-number'>25</span>
<span class='line-number'>26</span>
<span class='line-number'>27</span>
<span class='line-number'>28</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="c1">//actions.js</span>
</span><span class='line'><span class="kr">export</span> <span class="kd">function</span> <span class="nx">fetchRandmonName</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="kd">function</span> <span class="p">(</span><span class="nx">dispatch</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">fetchRequest</span><span class="p">(</span><span class="kc">true</span><span class="p">))</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">fetch</span><span class="p">(</span><span class="s1">'https://randomuser.me/api/'</span><span class="p">)</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">response</span> <span class="o">=></span> <span class="nx">response</span><span class="p">.</span><span class="nx">json</span><span class="p">())</span>
</span><span class='line'> <span class="p">.</span><span class="nx">then</span><span class="p">(</span><span class="nx">data</span> <span class="o">=></span><span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="nx">person</span> <span class="o">=</span> <span class="nx">data</span><span class="p">.</span><span class="nx">results</span><span class="p">[</span><span class="mi">0</span><span class="p">];</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">fetchSuccess</span><span class="p">(</span><span class="err">`</span><span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">first</span><span class="p">}</span> <span class="nx">$</span><span class="p">{</span><span class="nx">person</span><span class="p">.</span><span class="nx">name</span><span class="p">.</span><span class="nx">last</span><span class="p">}</span><span class="err">`</span><span class="p">,</span> <span class="kc">false</span><span class="p">))</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">.</span><span class="k">catch</span><span class="p">(</span><span class="nx">reason</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">fetchFailure</span><span class="p">(</span><span class="s1">'get name failed'</span><span class="p">,</span> <span class="kc">false</span><span class="p">))</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kr">const</span> <span class="nx">mapDispatchToProps</span> <span class="o">=</span> <span class="nx">dispatch</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">fetchRandmonName</span><span class="o">:</span> <span class="p">()</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">fetchRandmonName</span><span class="p">())</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="c1">//ExampleComponent.js</span>
</span><span class='line'><span class="nx">getRandomName</span><span class="p">()</span> <span class="p">{</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">props</span><span class="p">.</span><span class="nx">fetchRandmonName</span><span class="p">();</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>你看其实代码差不多,只不过,因为Redux-Thunk,<strong>你可以将异步的处理逻辑,从组件里面拿出来,将它放在一个和Redux其他代码更加的内聚的位置</strong>,也许是action的存放位置actions.js,而组件里面只需要dispatch一个thunk。</p>
<h2>总结</h2>
<p>关于Redux的异步世界,暂时更新到这里,Redux里面处理异步的中间件有好多,我就不一个个分析了,你肯定很关心这个,<a href="http://react-china.org/t/redux/8761" title="《Redux异步方案选型》">《Redux异步方案选型》</a></p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(七)- Redux的Middleware(上)- 中间件的概念]]></title>
<link href="http://benweizhu.github.io/blog/2018/04/25/deep-thinking-in-react-7/"/>
<updated>2018-04-25T21:14:18+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/04/25/deep-thinking-in-react-7</id>
<content type="html"><![CDATA[<p>Redux的Middleware(中间件)是Redux中相对比较神秘的部分。</p>
<h2>怎么理解middleware这个概念呢?</h2>
<p>middle这个词很重要,它是指,这个件(ware),被放在(穿插)于某一个已存在操作的过程当中(middle)。</p>
<p>我这么平淡直白无水准的解释,你应该能get到吧,如果你熟悉Java Web开发,你可能第一时间会想到Java Servlet Filters,当然也许你比较年轻,你对Spring熟悉,你可能会立刻想到AOP(面向切面编程),它们可以完成日志,审计,authentication, authorization等。</p>
<p>那么,再往后面走,如果你使用过Express或者Koa等服务端框架,在这类框架中,middleware是指可以被嵌入在框架“接收请求到产生响应过程之中”的代码。例如,Express或者Koa的middleware可以完成添加CORS headers、记录日志、内容压缩等工作。</p>
<h2>Redux的中间件是做什么呢?</h2>
<p>回过头来,看Redux的中间件,同样,它肯定是指穿插在Redux的某一个过程当中,那么问题来了,这个ware可以在哪里穿插,或者哪里有穿插操作的需要呢?我们来逐一分析,以下内容,参考阮一峰的文章:</p>
<p>(1)Reducer:纯函数,只承担计算State的功能,不合适承担其他功能,也承担不了,因为理论上,纯函数不能进行读写操作。</p>
<p>(2)View:与State一一对应,可以看作State的视觉层,也不合适承担其他功能。</p>
<p>(3)Action:存放数据的对象,即消息的载体,只能被别人操作,自己不能进行任何操作。</p>
<p>其实,和其他服务端框架类似,它被嵌入到Redux“接收请求到产生响应过程之中”,位于action被发起之后,到达reducer之前,也就是store.dispatch()附近。</p>
<h2>举个例子:如何在dispatch的时候log state和action</h2>
<p>我在看官方文档的时候,看到一个非常有趣的概念,猴子补丁(monkey patching),大概的意思是指在运行时动态修改模块、类或函数,通常是添加功能或修正缺陷。</p>
<p>通过这种方式,可以在代码的运行过程中,给store.dispatch打补丁,增加额外的功能,比如log state和action,代码如下:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kd">let</span> <span class="nx">next</span> <span class="o">=</span> <span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">;</span>
</span><span class='line'><span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span> <span class="o">=</span> <span class="kd">function</span> <span class="nx">dispatchAndLog</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'dispatching'</span><span class="p">,</span> <span class="nx">action</span><span class="p">);</span>
</span><span class='line'> <span class="nx">next</span><span class="p">(</span><span class="nx">action</span><span class="p">);</span>
</span><span class='line'> <span class="nx">console</span><span class="p">.</span><span class="nx">log</span><span class="p">(</span><span class="s1">'next state'</span><span class="p">,</span> <span class="nx">store</span><span class="p">.</span><span class="nx">getState</span><span class="p">());</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>通过上面这样一段代码,我们就在Redux原来的store.dispatch附近添加了我们自己代码,当我们再次调用store.dispatch,它就会打印log了,不过monkey patching本质上是一种hack,“将任意的方法替换成你想要的”。</p>
<p>如果,我们想给dispatch加另一个补丁,那就在它的前面,或者后面,在加上一段类似的代码呗。</p>
<h2>Redux applyMiddlewares()</h2>
<p>真实情况下,我们肯定不用这样写代码,那Redux提供了applyMiddlewares的方式,所以就不需要我们像上面那样写,那它是怎么做的呢?</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">const</span> <span class="nx">store</span> <span class="o">=</span> <span class="nx">createStore</span><span class="p">(</span>
</span><span class='line'> <span class="nx">reducer</span><span class="p">,</span>
</span><span class='line'> <span class="nx">applyMiddleware</span><span class="p">(</span><span class="nx">thunk</span><span class="p">,</span> <span class="nx">promise</span><span class="p">,</span> <span class="nx">logger</span><span class="p">)</span>
</span><span class='line'><span class="p">);</span>
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
<span class='line-number'>11</span>
<span class='line-number'>12</span>
<span class='line-number'>13</span>
<span class='line-number'>14</span>
<span class='line-number'>15</span>
<span class='line-number'>16</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kr">export</span> <span class="k">default</span> <span class="kd">function</span> <span class="nx">applyMiddleware</span><span class="p">(...</span><span class="nx">middlewares</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">(</span><span class="nx">createStore</span><span class="p">)</span> <span class="o">=></span> <span class="p">(</span><span class="nx">reducer</span><span class="p">,</span> <span class="nx">preloadedState</span><span class="p">,</span> <span class="nx">enhancer</span><span class="p">)</span> <span class="o">=></span> <span class="p">{</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">store</span> <span class="o">=</span> <span class="nx">createStore</span><span class="p">(</span><span class="nx">reducer</span><span class="p">,</span> <span class="nx">preloadedState</span><span class="p">,</span> <span class="nx">enhancer</span><span class="p">);</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">dispatch</span> <span class="o">=</span> <span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">;</span>
</span><span class='line'> <span class="kd">var</span> <span class="nx">chain</span> <span class="o">=</span> <span class="p">[];</span>
</span><span class='line'>
</span><span class='line'> <span class="kd">var</span> <span class="nx">middlewareAPI</span> <span class="o">=</span> <span class="p">{</span> <span class="c1">//注意这里:getState和dispatch都传入了middleware</span>
</span><span class='line'> <span class="nx">getState</span><span class="o">:</span> <span class="nx">store</span><span class="p">.</span><span class="nx">getState</span><span class="p">,</span>
</span><span class='line'> <span class="nx">dispatch</span><span class="o">:</span> <span class="p">(</span><span class="nx">action</span><span class="p">)</span> <span class="o">=></span> <span class="nx">dispatch</span><span class="p">(</span><span class="nx">action</span><span class="p">)</span>
</span><span class='line'> <span class="p">};</span>
</span><span class='line'> <span class="nx">chain</span> <span class="o">=</span> <span class="nx">middlewares</span><span class="p">.</span><span class="nx">map</span><span class="p">(</span><span class="nx">middleware</span> <span class="o">=></span> <span class="nx">middleware</span><span class="p">(</span><span class="nx">middlewareAPI</span><span class="p">));</span><span class="c1">//注意这里:这些middleware都是函数</span>
</span><span class='line'> <span class="nx">dispatch</span> <span class="o">=</span> <span class="nx">compose</span><span class="p">(...</span><span class="nx">chain</span><span class="p">)(</span><span class="nx">store</span><span class="p">.</span><span class="nx">dispatch</span><span class="p">);</span>
</span><span class='line'>
</span><span class='line'> <span class="k">return</span> <span class="p">{...</span><span class="nx">store</span><span class="p">,</span> <span class="nx">dispatch</span><span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>所有中间件被放进了一个数组chain,通过compose,将多个中间件合并,从右到左执行。中间件可以拿到getState和dispatch这两个方法。</p>
<p>注:compose: 将多个函数合并成一个函数,从右到左执行。例如:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="nx">R</span><span class="p">.</span><span class="nx">compose</span><span class="p">(</span><span class="nb">Math</span><span class="p">.</span><span class="nx">abs</span><span class="p">,</span> <span class="nx">R</span><span class="p">.</span><span class="nx">add</span><span class="p">(</span><span class="mi">1</span><span class="p">),</span> <span class="nx">R</span><span class="p">.</span><span class="nx">multiply</span><span class="p">(</span><span class="mi">2</span><span class="p">))(</span><span class="o">-</span><span class="mi">4</span><span class="p">)</span> <span class="c1">// 7</span>
</span></code></pre></td></tr></table></div></figure>
<p>-4 * 2 + 1 再求绝对值</p>
<h2>总结</h2>
<p>仔细看下来之后,中间件就没有那么神秘了,下一篇文章,我们来介绍下Redux的异步中间件,比如:Redux-Thunk,看懂这一个,其他的都差不多。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(六)- 不可变性]]></title>
<link href="http://benweizhu.github.io/blog/2018/04/23/deep-thinking-in-react-6/"/>
<updated>2018-04-23T22:29:51+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/04/23/deep-thinking-in-react-6</id>
<content type="html"><![CDATA[<h2>为什么immutable在React中那么重要?</h2>
<p>我尝试用简(kuo)单(hao)易(li)懂(mian)的词解释</p>
<p>1.可以给你的React应用带来性能提升(不用深度对比) <br/>
2.简单的编程和调试体验,少出bug(不会操作共享对象) <br/>
3.数据更容易追踪,推导(保留previewState可以对比)</p>
<p>Talk is shit, show me your money.</p>
<p>光说还是差了点,虽然很通俗了,但是还是看下面一段代码,我用一个React初学者,特别容易犯的一个错误来说明这三个问题,请看题板:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="nx">updateState</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="p">{</span><span class="nx">value</span><span class="p">}</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">;</span>
</span><span class='line'> <span class="kd">let</span> <span class="nx">user</span> <span class="o">=</span> <span class="k">this</span><span class="p">.</span><span class="nx">state</span><span class="p">.</span><span class="nx">user</span><span class="p">;</span>
</span><span class='line'> <span class="nx">user</span><span class="p">.</span><span class="nx">name</span> <span class="o">=</span> <span class="nx">value</span><span class="p">;</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">user</span><span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="nx">updateState</span><span class="p">(</span><span class="nx">event</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="kr">const</span> <span class="p">{</span><span class="nx">value</span><span class="p">}</span> <span class="o">=</span> <span class="nx">event</span><span class="p">.</span><span class="nx">target</span><span class="p">;</span>
</span><span class='line'> <span class="kd">let</span> <span class="nx">user</span> <span class="o">=</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</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">user</span><span class="p">,</span> <span class="p">{</span><span class="nx">name</span><span class="o">:</span> <span class="nx">value</span><span class="p">});</span>
</span><span class='line'> <span class="k">this</span><span class="p">.</span><span class="nx">setState</span><span class="p">({</span><span class="nx">user</span><span class="p">});</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>请问,第一种写法有什么问题?你有20秒的时间思考并作答,对不起,由于我没法遮挡住答案,请自觉闭眼思考。</p>
<p>答案:就是上面说的三点,举个例子:shouldComponentUpdate(nextProps, nextState),需要对比nextState和this.state的区别来决定是否渲染,但此时this.state已经不是以前的state了状态了,特别是PureComponent的shallow compare,直接导致组件不重新渲染,会出bug</p>
<h2>为什么immutable在Redux中那么重要?</h2>
<p>因为不用就会出bug,因为你就会问“为什么UI不更新”,因为Redux和React-Redux都使用了浅比较。</p>
<p>具体行为体现在两处,一个在按照Domain区分的combineReducers那,一个在使用数据被connect包裹的组件那:</p>
<p><strong>1.Redux的combineReducers方法浅比较它调用的reducer的引用是否发生变化</strong></p>
<p>比如:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="nx">combineReducers</span><span class="p">({</span> <span class="nx">todos</span><span class="o">:</span> <span class="nx">myTodosReducer</span><span class="p">,</span> <span class="nx">counter</span><span class="o">:</span> <span class="nx">myCounterReducer</span> <span class="p">})</span>
</span></code></pre></td></tr></table></div></figure>
<p>combineReducers会遍历所有这些键值对,判断每一个reducer执行返回结果后的引用是否发生变化(所以是浅比较),如果其中一个变化了,就会设置一个标志位hasChanged为true,当遍历结束,如果hasChanged为true,则返回新的conbineReducers合并的总的state,否则返回旧的那个state(这个新旧的state在React-Redux中会用到)。</p>
<p>这就是为什么在reducer当中要使用Object.assign({},state,{..}}来返回一个新的state,如果你直接操作state,并返回它,combineReducers会认为它没有改变。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kd">function</span> <span class="nx">myTodosReducer</span><span class="p">(</span><span class="nx">state</span> <span class="o">=</span> <span class="nx">initialState</span><span class="p">,</span> <span class="nx">action</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">switch</span> <span class="p">(</span><span class="nx">action</span><span class="p">.</span><span class="nx">type</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">case</span> <span class="nx">SET_VISIBILITY_FILTER</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nb">Object</span><span class="p">.</span><span class="nx">assign</span><span class="p">({},</span> <span class="nx">state</span><span class="p">,</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">visibilityFilter</span><span class="o">:</span> <span class="nx">action</span><span class="p">.</span><span class="nx">filter</span>
</span><span class='line'> <span class="p">})</span>
</span><span class='line'> <span class="k">default</span><span class="o">:</span>
</span><span class='line'> <span class="k">return</span> <span class="nx">state</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p><strong>2.React-Redux的state和mapStateToProps</strong></p>
<p>React-Redux的connect方法生成的组件,会假设包装的组件是一个“纯”(pure)组件,即给定相同的props和state,这个组件会返回相同的结果。做出这样的假设后,React-Redux就只需检查根state对象或mapStateToProps的返回值是否改变。如果没变,包装的组件就无需重新渲染。</p>
<p>我们回忆一下,上面说道,combineReducers会决定是否返回新的根state,而每次调用React-Redux提供的connect函数时,它之前储存的根state对象的引用,会与当前传递给store的根state对象之间进行浅比较。如果相等,说明根state对象没有变化,也就无需重新渲染组件,甚至无需调用mapStateToProps。</p>
<p>如果不相等,则connect会调用mapStateToProps来,并查看最后传给组件的props是否被更新。同样,这里也是一个浅比较,它要对比的是这个对象的第一层引用是否变化。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kd">function</span> <span class="nx">mapStateToProps</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">todos</span><span class="o">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">todos</span><span class="p">,</span> <span class="c1">// prop value</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kr">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">)(</span><span class="nx">TodoApp</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>即,这个对象的todos是否变化。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="p">{</span>
</span><span class='line'> <span class="nx">todos</span><span class="o">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">todos</span><span class="p">,</span> <span class="c1">// prop value</span>
</span><span class='line'><span class="p">}</span>
</span></code></pre></td></tr></table></div></figure>
<p>比如,如果在todos的reducer返回的state没有变化,那么这里的todos也就是没有变化,因此组件就不需要渲染。</p>
<p>mapStateToProps因为它特殊的作用,很容易出现一种反模式,我们需要注意,就是像下面这样写:</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
<span class='line-number'>8</span>
<span class='line-number'>9</span>
<span class='line-number'>10</span>
</pre></td><td class='code'><pre><code class='JavaScript'><span class='line'><span class="kd">function</span> <span class="nx">mapStateToProps</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span> <span class="p">{</span>
</span><span class='line'> <span class="k">return</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">todos</span><span class="o">:</span> <span class="p">{</span>
</span><span class='line'> <span class="nx">all</span><span class="o">:</span> <span class="nx">state</span><span class="p">.</span><span class="nx">todos</span><span class="p">,</span>
</span><span class='line'> <span class="nx">visibleTodos</span><span class="o">:</span> <span class="nx">getVisibleTodos</span><span class="p">(</span><span class="nx">state</span><span class="p">)</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'> <span class="p">}</span>
</span><span class='line'><span class="p">}</span>
</span><span class='line'>
</span><span class='line'><span class="kr">export</span> <span class="k">default</span> <span class="nx">connect</span><span class="p">(</span><span class="nx">mapStateToProps</span><span class="p">)(</span><span class="nx">TodoApp</span><span class="p">)</span>
</span></code></pre></td></tr></table></div></figure>
<p>todos对象永远拿到的都是新的对象{},而不是直接由reducer里面返回的对象,所以组件一定会重新渲染,无论state是否变化。</p>
<p><strong>React-Redux这样设计是有道理的</strong></p>
<p>因为Redux本身的CombineReducers只会决定最根节点的state有没有变化,也就是这个{ todos: myTodosReducer, counter: myCounterReducer }里面的存不存在变化,然后决定返回新的或者旧的,而每一个connect的组件拿到这个新的根state,首先判断state有没有变化,然后判断这个变化和它想要的数据(一个或者多个reducer)有没有关,如果没有关系,当然就不用重新渲染。</p>
<h2>总结</h2>
<p>React和Redux之所以要求使用Immutable,原因的初衷是性能,避免深度对比。对于我们使用React和Redux的开发人员,除了关注性能,更是因为需要遵循React和Redux的编程规范来写出正确的代码。</p>
]]></content>
</entry>
<entry>
<title type="html"><![CDATA[React的思考(五)- Reconciliation]]></title>
<link href="http://benweizhu.github.io/blog/2018/04/22/deep-thinking-in-react-5/"/>
<updated>2018-04-22T12:33:42+08:00</updated>
<id>http://benweizhu.github.io/blog/2018/04/22/deep-thinking-in-react-5</id>
<content type="html"><![CDATA[<h3>Reconciliation的含义本身</h3>
<p>Reconciliation有的人翻译成“协调算法”,有的人翻译成“一致性对比”,在没有官方答案之前,我认为直译可能会比较准确,它的作用是React用来区分一棵节点树和另一棵节点树的算法,以确定哪些部分需要更改。</p>
<p>Reconciliation是通常被理解为“虚拟DOM”背后的算法。</p>
<p>简单描述就是:当您渲染React应用时,会生成描述该应用的节点树并将其保存在内存中。然后将该节点树刷新到渲染环境 - 例如,在浏览器应用程序的情况下,它会转换为一组DOM操作。当应用程序更新(比如,通过setState)时,会生成一棵新树。对比得到新树与前一棵树的区别,以计算需要更新渲染应用的操作。</p>
<h3>React 16+的Reconciler - React Fiber</h3>
<p>React团队在2016年7月公开发布React Fiber,React新的核心算法,React Fiber的目标是提高其对动画,布局和手势等领域的适用性。它的特征是增量渲染:能够将渲染工作分割成块并将其分散到多个帧中。</p>
<p>按照官方文档的说法,Fiber Reconciler的主要目标:</p>
<ul>
<li>能够将可中断的任务拆分成块。</li>
<li>能够对进程中的工作划分优先级、重新设定基址(Rebase)、恢复。</li>
<li>能够在父子之间来回反复,借此为React的Layout提供支持。</li>
<li>能够通过render()返回多个元素。</li>
<li>为错误边界提供了更好的支持。</li>
</ul>
<p>这个项目持续的2年之久,蕴含着过去多年来Facebook不断改进的工作成果。该架构可向后兼容,彻底重写了React的协调(Reconciliation)算法。他们还专门做了一个网站叫:<a href="http://isfiberreadyyet.com/">http://isfiberreadyyet.com/</a></p>
<p><img src="http://benweizhu.github.io/images/isfiberready.jpg" width="500" title="isfiberreadyyet" alt="Alt text" /> <br/>
27 September 2017</p>
<p>我们都知道DOM只是React可以渲染的渲染环境之一,另外一个就是React Native。(这就是为什么“虚拟DOM”有点用词不当)。</p>
<p>它可以支持如此多环境的原因是因为React的设计使是Reconciliation和渲染是分开的阶段。Reconciler执行计算树的哪些部分已经改变; 渲染器然后使用该信息实际更新呈现的应用。这种分离意味着React DOM和React Native可以在共享由React核心提供的相同Reconciler的同时使用它们自己的渲染器。</p>
<h3>React 15以及以前的Reconciler - Stack Reconciler</h3>
<p>Fiber在React 16中首次登场,发布时间是2017年9月26号,那么意味着,在这之前,有另外一套Reconciliation的算法。React现在把它命名为Stack Reconciler。它存在于React 15及更早版本的实现中。</p>
<p><strong>Stack Reconciler犯了一个单线程或者存在UI主线程环境下的“禁忌” - 用同步的方式来处理整个组件树。</strong></p>
<p>Virtual DOM diff会一次性处理整个组件树,重点在于,Stack Reconciler始终会一次性地同步处理整个组件树。因为整个过程都是在内存中完成,所以当组件树比较小的时候的,不会感觉到问题,但是,当组件树比较庞大的时候,就会出现卡顿(掉帧)的情况。</p>
<p><img src="http://benweizhu.github.io/images/Stack.jpg" width="500" title="Stack Reconciler" alt="Alt text" /> <br/>
单线程进入到栈中,要从栈从退出来,才能响应其他用户操作</p>
<p><img src="http://benweizhu.github.io/images/Fiber.jpg" width="500" title="Fiber Reconciler" alt="Alt text" /> <br/>
按照时间片段的方式执行</p>
<p>参考:<a href="https://www.youtube.com/watch?v=ZCuYPiUIONs" title="Lin Clark - A Cartoon Intro to Fiber - React Conf 2017">Lin Clark - A Cartoon Intro to Fiber - React Conf 2017</a></p>
<h3>会不会有影响(参考官方文档)</h3>
<p>首先,不变的地方是,diff节点或者说判断两个节点是否相同的方式没有变:</p>
<p>1.不同的元素类型</p>
<p>每当根元素具有不同类型时,React就会销毁旧的树并从头开始构建新树。从a到img ,或者从Article到Comment,从Button到div – 这些都将导致全部重新构建。</p>
<figure class='code'><figcaption><span></span></figcaption><div class="highlight"><table><tr><td class="gutter"><pre class="line-numbers"><span class='line-number'>1</span>
<span class='line-number'>2</span>
<span class='line-number'>3</span>
<span class='line-number'>4</span>
<span class='line-number'>5</span>
<span class='line-number'>6</span>
<span class='line-number'>7</span>
</pre></td><td class='code'><pre><code class='HTML'><span class='line'><span class="nt"><div></span>
</span><span class='line'> <span class="nt"><Counter</span> <span class="nt">/></span>
</span><span class='line'><span class="nt"></div></span>
</span><span class='line'>
</span><span class='line'><span class="nt"><span></span>
</span><span class='line'> <span class="nt"><Counter</span> <span class="nt">/></span>
</span><span class='line'><span class="nt"></span></span>
</span></code></pre></td></tr></table></div></figure>