-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathreactInterview.html
2267 lines (1668 loc) · 147 KB
/
reactInterview.html
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
<!DOCTYPE html>
<!--
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
.' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
佛祖保佑 永无BUG
-->
<html class="theme-next mist use-motion" lang="zh-Hans">
<head>
<meta charset="UTF-8"/>
<link href="//mydearest.cn" rel="dns-prefetch">
<link href="//www.mydearest.cn" rel="dns-prefetch">
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1"/>
<meta name="theme-color" content="#222">
<meta name="description" content="陈宇的博客" />
<meta http-equiv="Cache-Control" content="no-transform" />
<meta http-equiv="Cache-Control" content="no-siteapp" />
<link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic|Roboto Slab:300,300italic,400,400italic,700,700italic|Lobster Two:300,300italic,400,400italic,700,700italic|PT Mono:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
<meta name="keywords" content="react,面试," />
<link rel="alternate" href="/rss2.xml" title="cosyer's Blog" type="application/atom+xml" />
<link rel="shortcut icon" type="image/x-icon" href="/images/favicon.ico" />
<meta property="og:type" content="article">
<meta property="og:title" content="react面试题记录">
<meta property="og:url" content="http://mydearest.cn/reactInterview.html">
<meta property="og:site_name" content="cosyer's Blog">
<meta property="og:image" content="http://cdn.mydearest.cn/blog/images/reactInterview.jpeg">
<meta property="og:image" content="http://cdn.mydearest.cn/blog/images/patch.png">
<meta property="og:image" content="http://cdn.mydearest.cn/blog/images/ssr.png">
<meta property="article:published_time" content="2018-06-07T06:47:09.000Z">
<meta property="article:modified_time" content="2020-11-18T06:11:37.007Z">
<meta property="article:author" content="陈宇(cosyer)">
<meta property="article:tag" content="react">
<meta property="article:tag" content="面试">
<meta name="twitter:card" content="summary">
<meta name="twitter:image" content="http://cdn.mydearest.cn/blog/images/reactInterview.jpeg">
<script type="text/javascript" id="hexo.configurations">
var NexT = window.NexT || {};
var CONFIG = {
root: '/',
scheme: 'Mist',
sidebar: {"position":"left","display":"always","offset":12,"offset_float":12,"b2t":false,"scrollpercent":false,"onmobile":false},
fancybox: true,
tabs: true,
motion: true,
duoshuo: {
userId: '0',
author: '博主'
},
algolia: {
applicationID: '',
apiKey: '',
indexName: '',
hits: {"per_page":10},
labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
}
};
</script>
<link rel="canonical" href="http://mydearest.cn/reactInterview.html"/>
<title>react面试题记录 | cosyer's Blog</title>
<script type="text/javascript">
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "https://hm.baidu.com/hm.js?653a4be35cb6c7b26817038a17c3f0d6";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
</script>
<link href="https://mydearest.cn/css/all-9097fb9016.css" rel="stylesheet" type="text/css">
<style>
// scrollbar滚动条样式优化
::-webkit-scrollbar-corner {
background-color: transparent;
}
::-webkit-scrollbar-button {
width: 0;
height: 0;
display: none;
}
::-webkit-scrollbar-thumb {
width: 7px;
background-color: #b4babf;
border-radius: 7px;
}
::-webkit-scrollbar {
width: 7px;
height: 7px;
}
::-webkit-scrollbar-track {
width: 15px;
}
::-webkit-scrollbar:hover {
background-color: transparent;
}
* {
cursor: url("http://cdn.mydearest.cn/blog/images/miku1.png"),auto!important
}
:active {
cursor: url("http://cdn.mydearest.cn/blog/images/miku2.png"),auto!important
}
a:hover {
cursor: url("http://cdn.mydearest.cn/blog/images/miku2.png"),auto!important
}
</style>
<meta name="generator" content="Hexo 4.2.1"></head>
<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">
<div id="loader">
<div></div>
</div>
<div class="container sidebar-position-left page-post-detail ">
<div class="headband"></div>
<a href="https://github.com/cosyer" target="_blank" rel="noopener" class="github-corner" aria-label="View source on Github"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; left: 0; transform: scale(-1, 1);" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style></a>
<header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
<div class="header-inner"><div class="site-brand-wrapper">
<div class="site-meta ">
<div class="custom-logo-site-title">
<a href="/" class="brand" rel="start">
<span class="logo-line-before"><i></i></span>
<span class="site-title">cosyer's Blog</span>
<span class="logo-line-after"><i></i></span>
</a>
</div>
<h1 class="site-subtitle" itemprop="description">Blog</h1>
</div>
<div class="site-nav-toggle">
<button>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
<span class="btn-bar"></span>
</button>
</div>
</div>
<nav class="site-nav">
<ul id="menu" class="menu">
<li class="menu-item menu-item-home">
<a href="/" class="faa-parent animated-hover" rel="section">
<i class="menu-item-icon fa fa-fw fa-home faa-wrench"></i> <br />
首页
</a>
</li>
<li class="menu-item menu-item-links">
<a href="/links/" class="faa-parent animated-hover" rel="section">
<i class="menu-item-icon fa fa-fw fa-link faa-shake"></i> <br />
友链
</a>
</li>
<li class="menu-item menu-item-guestbook">
<a href="/guestbook/" class="faa-parent animated-hover" rel="section">
<i class="menu-item-icon fa fa-fw fa-comment-o faa-tada"></i> <br />
留言板
</a>
</li>
<li class="menu-item menu-item-archives">
<a href="/archives/" class="faa-parent animated-hover" rel="section">
<i class="menu-item-icon fa fa-fw fa-archive faa-float"></i> <br />
归档
</a>
</li>
<li class="menu-item menu-item-about">
<a href="/about/" class="faa-parent animated-hover" rel="section">
<i class="menu-item-icon fa fa-fw fa-user faa-horizontal"></i> <br />
关于
</a>
</li>
<li class="menu-item menu-item-search">
<a href="javascript:;" class="popup-trigger faa-parent animated-hover">
<i class="menu-item-icon fa fa-search faa-burst fa-fw"></i> <br />
搜索
</a>
</li>
</ul>
<div class="site-search">
<div class="popup search-popup local-search-popup">
<div class="local-search-header clearfix">
<span class="search-icon">
<i class="fa fa-search"></i>
</span>
<span class="popup-btn-close">
<i class="fa fa-times-circle"></i>
</span>
<div class="local-search-input-wrapper">
<input autocomplete="off"
placeholder="搜索..." spellcheck="false"
type="text" id="local-search-input">
</div>
</div>
<div id="local-search-result"></div>
</div>
</div>
</nav>
</div>
</header>
<main id="main" class="main">
<div class="main-inner">
<div class="content-wrap">
<div id="content" class="content">
<div id="posts" class="posts-expand">
<article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
<div class="post-date" data-datetime="2020-11-18T14:11:37+08:00">
<div class="post-time-text">11月</div>
<div class="post-time-count">18</div>
<div class="text-desc">
<div class="date-text">更新于</div>
<div class="post-tiem">11月18</div>
<div class="post-year">2020</div>
</div>
</div>
<div class="post-badge">
<span class="post-category" >
<span itemprop="about" itemscope itemtype="http://schema.org/Thing">
<a href="/categories/React/" itemprop="url" rel="index">
<span itemprop="name">React</span>
</a>
</span>
</span>
</div>
<div class="post-block">
<link itemprop="mainEntityOfPage" href="http://mydearest.cn/reactInterview.html">
<span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
<meta itemprop="name" content="陈宇(cosyer)">
<meta itemprop="description" content="不去做的话永远也做不到。">
<meta itemprop="image" content="/images/avatar.jpg">
</span>
<span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
<meta itemprop="name" content="cosyer's Blog">
</span>
<header class="post-header">
<h2 class="post-title" itemprop="name headline">react面试题记录</h2>
<div class="post-meta">
<span class="post-time">
<span class="post-meta-item-icon">
<i class="fa fa-calendar-o"></i>
</span>
<span class="post-meta-item-text">发表于</span>
<time title="发表于" itemprop="dateCreated datePublished" datetime="2018-06-07T14:47:09+08:00">
2018-06-07
</time>
</span>
<span id="/reactInterview.html" class="leancloud_visitors" data-flag-title="react面试题记录">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-eye"></i>
</span>
<span class="post-meta-item-text">热度 </span>
<span class="leancloud-visitors-count"></span> ℃
</span>
<div class="post-wordcount">
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-pencil-square-o"></i>
</span>
<span class="post-meta-item-text">字数统计:</span>
<span title="字数统计:">
10,197 (字)
</span>
<span class="post-meta-divider">|</span>
<span class="post-meta-item-icon">
<i class="fa fa-clock-o"></i>
</span>
<span class="post-meta-item-text">阅读时长:</span>
<span title="阅读时长:">
39 (分钟)
</span>
</div>
</div>
</header>
<div class="post-body" itemprop="articleBody">
<span itemprop="image" itemscope="" itemtype="http://schema.org/ImageObject"><img itemprop="url image" src="http://cdn.mydearest.cn/blog/images/reactInterview.jpeg " class="full-image" alt="reactInterview" title="React"><meta itemprop="width" content="auto"><meta itemprop="height" content="auto"></span>
<hr>
<a id="more"></a>
<h2 id="React面试问题"><a href="#React面试问题" class="headerlink" title="React面试问题"></a>React面试问题</h2><p>下面是一个常用的关于 React 的面试问题列表:</p>
<h3 id="React-的响应式原理"><a href="#React-的响应式原理" class="headerlink" title="React 的响应式原理"></a>React 的响应式原理</h3><p>React 会创建一个虚拟 DOM(virtual DOM)。当一个组件中的状态改变时,React 首先会通过 “diff” 算法来标记虚拟 DOM 中的改变,第二步是调节(reconciliation),会用<code>diff</code>的结果来更新真实DOM。虚拟
DOM作为一种缓存机制优化了UI渲染减少昂贵的DOM变化的数量。</p>
<ol>
<li>开发者只需关注状态转移(数据),当状态发生变化,React框架会自动根据新的状态重新构建UI。</li>
<li>React框架在接收到用户状态改变通知后,会根据当前渲染树,结合最新的状态改变,通过Diff算法,计算出树中变化的部分,然后只更新变化的部分(DOM操作),从而避免整棵树重构,提高性能。状态变化后
React框架并不会立即去计算并渲染DOM树的变化部分,相反,React会在DOM的基础上建立一个抽象层,即虚拟DOM树,对数据和状态所做的任何改动,都会被自动且高效的同步到虚拟DOM,最后再批量同步到真实DOM
中,而不是每次改变都去操作一下DOM。</li>
</ol>
<p>为什么不能每次改变都直接去操作DOM树?
这是因为在浏览器中每一次DOM操作都有可能引起浏览器的重绘或回流:</p>
<ul>
<li>如果DOM只是外观风格发生变化,如颜色变化,会导致浏览器重绘界面。</li>
<li>如果DOM树的结构发生变化,如尺寸、布局、节点隐藏等导致,浏览器就需要回流(及重新排版布局)。
而浏览器的重绘和回流都是比较昂贵的操作,如果每一次改变都直接对DOM进行操作,这会带来性能问题,而批量操作只会触发一次DOM更新。</li>
</ul>
<h3 id="使用-React-有何优点"><a href="#使用-React-有何优点" class="headerlink" title="使用 React 有何优点"></a>使用 React 有何优点</h3><ul>
<li>只需查看 <code>render</code> 函数就会很容易知道一个组件是如何被渲染的</li>
<li>JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的</li>
<li>支持服务端渲染,这可以改进 SEO 和性能</li>
<li>易于测试</li>
<li>React 只关注 View 层,所以可以和其它任何框架(如Backbone.js, Angular.js)一起使用</li>
</ul>
<h3 id="展示组件-Presentational-component-和容器组件-Container-component-之间有何不同"><a href="#展示组件-Presentational-component-和容器组件-Container-component-之间有何不同" class="headerlink" title="展示组件(Presentational component)和容器组件(Container component)之间有何不同"></a>展示组件(Presentational component)和容器组件(Container component)之间有何不同</h3><p>展示组件关心组件看起来是什么。展示专门通过 props 接受数据和回调,并且几乎不会有自身的状态,但当展示组件拥有自身的状态时,通常也只关心 UI 状态而不是数据的状态。</p>
<p>容器组件则更关心组件是如何运作的。容器组件会为展示组件或者其它容器组件提供数据和行为(behavior),它们会调用 <code>Flux actions</code>,并将其作为回调提供给展示组件。容器组件经常是有状态的,因为它们是(其它组件的)数据源。</p>
<h3 id="类组件-Class-component-和函数式组件-Functional-component-之间有何不同"><a href="#类组件-Class-component-和函数式组件-Functional-component-之间有何不同" class="headerlink" title="类组件(Class component)和函数式组件(Functional component)之间有何不同"></a>类组件(Class component)和函数式组件(Functional component)之间有何不同</h3><ul>
<li>类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 <code>store</code> 并维持状态</li>
<li>当组件仅是接收 <code>props</code>,并将组件自身渲染到页面时,该组件就是一个 ‘无状态组件(stateless component)’,可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件</li>
</ul>
<h3 id="组件的-状态-state-和属性-props-之间有何不同"><a href="#组件的-状态-state-和属性-props-之间有何不同" class="headerlink" title="(组件的)状态(state)和属性(props)之间有何不同"></a>(组件的)状态(state)和属性(props)之间有何不同</h3><p><code>State</code> 是一种数据结构,用于组件挂载时所需数据的默认值。<code>State</code> 可能会随着时间的推移而发生突变,但多数时候是作为用户事件行为的结果。</p>
<p><code>Props</code>(properties 的简写)则是组件的配置。<code>props</code> 由父组件传递给子组件,并且就子组件而言,<code>props</code> 是不可变的(immutable)。组件不能改变自身的 props,但是可以把其子组件的 props 放在一起(统一管理)。Props 也不仅仅是数据–回调函数也可以通过 props 传递。</p>
<h3 id="指出-组件-生命周期方法的不同"><a href="#指出-组件-生命周期方法的不同" class="headerlink" title="指出(组件)生命周期方法的不同"></a>指出(组件)生命周期方法的不同</h3><ul>
<li><code>componentWillMount</code> – 多用于根组件中的应用程序配置</li>
<li><code>componentDidMount</code> – 在这可以完成所有没有 DOM 就不能做的所有配置,并开始获取所有你需要的数据;如果需要设置事件监听,也可以在这完成</li>
<li><code>componentWillReceiveProps</code> – 这个周期函数作用于特定的 prop 改变导致的 state 转换</li>
<li><code>shouldComponentUpdate</code> – 如果你担心组件过度渲染,<code>shouldComponentUpdate</code> 是一个改善性能的地方,因为如果组件接收了新的 <code>prop</code>, 它可以阻止(组件)重新渲染。shouldComponentUpdate 应该返回一个布尔值来决定组件是否要重新渲染</li>
<li><code>componentWillUpdate</code> – 很少使用。它可以用于代替组件的 <code>componentWillReceiveProps</code> 和 <code>shouldComponentUpdate</code>(但不能访问之前的 props)</li>
<li><code>componentDidUpdate</code> – 常用于更新 DOM,响应 prop 或 state 的改变</li>
<li><code>componentWillUnmount</code> – 在这你可以取消网络请求,或者移除所有与组件相关的事件监听器</li>
</ul>
<h3 id="应该在-React-组件的何处发起-Ajax-请求"><a href="#应该在-React-组件的何处发起-Ajax-请求" class="headerlink" title="应该在 React 组件的何处发起 Ajax 请求"></a>应该在 React 组件的何处发起 Ajax 请求</h3><p>在 React 组件中,应该在 <code>componentDidMount</code> 中发起网络请求。这个方法会在组件第一次“挂载”(被添加到 DOM)时执行,在组件的生命周期中仅会执行一次。更重要的是,你不能保证在组件挂载之前 Ajax 请求已经完成,如果是这样,也就意味着你将尝试在一个未挂载的组件上调用 setState,这将不起作用。在 <code>componentDidMount</code> 中发起网络请求将保证这有一个组件可以更新了。</p>
<h3 id="何为受控组件-controlled-component"><a href="#何为受控组件-controlled-component" class="headerlink" title="何为受控组件(controlled component)"></a>何为受控组件(controlled component)</h3><p>在 HTML 中,类似 <code><input></code>, <code><textarea></code> 和 <code><select></code> 这样的表单元素会维护自身的状态,并基于用户的输入来更新。当用户提交表单时,前面提到的元素的值将随表单一起被发送。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值,并且每次调用回调函数时,如 <code>onChange</code> 会更新 state,重新渲染组件。一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为”受控元素”。</p>
<h3 id="在-React-中,refs-的作用是什么"><a href="#在-React-中,refs-的作用是什么" class="headerlink" title="在 React 中,refs 的作用是什么"></a>在 React 中,refs 的作用是什么</h3><p>Refs 可以用于获取一个 DOM 节点或者 React 组件(组件实例)的引用。何时使用 refs 的好的示例有管理焦点/文本选择,触发命令动画,或者和第三方 DOM 库集成。你应该避免使用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所推荐的。</p>
<h3 id="三种ref方式"><a href="#三种ref方式" class="headerlink" title="三种ref方式"></a>三种ref方式</h3><ol>
<li><p>string类型绑定
类似于vue中的ref绑定方式,可以通过this.refs.绑定的ref的名字获取到节点dom,注意的是这种方式已经不被最新版的react推荐使用,有可能会在未来版本中遗弃。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">focus = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.refs.inputRef.focus()</span><br><span class="line">}</span><br><span class="line"><input ref=<span class="string">"inputRef"</span>/></span><br><span class="line"></span><br><span class="line"><span class="comment">// 获取子组件的div</span></span><br><span class="line"><span class="comment">// 父组件</span></span><br><span class="line"><Child myRef={<span class="keyword">this</span>.state.myDiv}/></span><br><span class="line"><span class="comment">// 子组件</span></span><br><span class="line"> <div ref={<span class="keyword">this</span>.props.myRef}>我是子组件<<span class="regexp">/div></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>react.CreateRef()
通过在class中使用React.createRef()方法创建一些变量,可以将这些变量绑定到标签的ref中,该变量的current则指向绑定的标签dom。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">inputRef = React.createRef()</span><br><span class="line">focus = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.inputRef.current.focus()</span><br><span class="line">}</span><br><span class="line"><input ref={<span class="keyword">this</span>.inputRef}/></span><br></pre></td></tr></table></figure>
</li>
<li><p>函数形式
在class中声明函数,在函数中绑定ref使用这种方法可以将子组件暴露给父组件以使得父组件能够调用子组件的方法</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">inputRef = <span class="literal">null</span></span><br><span class="line">focus = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.inputRef.focus()</span><br><span class="line">}</span><br><span class="line"><input ref={(el)=><span class="keyword">this</span>.inputRef=el}/></span><br></pre></td></tr></table></figure>
</li>
<li><p>useRef(实例属性)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">UseRefDemo</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> inputRef = useRef(<span class="literal">null</span> <span class="keyword">as</span> any)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> handleFocusInput = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> inputRef.current.focus()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div></span><br><span class="line"> <input ref={inputRef} /></span><br><span class="line"> <button onClick={handleFocusInput}>click focus<<span class="regexp">/button></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"> )</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>forwardRef(获取组件内的引用)</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 子组件</span></span><br><span class="line"><span class="keyword">const</span> Child = forwardRef(<span class="function">(<span class="params">props, ref</span>)=></span>{</span><br><span class="line"> <span class="keyword">return</span> (</span><br><span class="line"> <div ref={ref}>{props.txt}<<span class="regexp">/div></span></span><br><span class="line"><span class="regexp"> )</span></span><br><span class="line"><span class="regexp">})</span></span><br><span class="line"><span class="regexp"></span></span><br><span class="line"><span class="regexp">/</span><span class="regexp">/ 父组件</span></span><br><span class="line"><span class="regexp"><Child ref={this.state.myDiv} txt="parent props txt"/</span>></span><br></pre></td></tr></table></figure>
</li>
</ol>
<p>注意: react并不推荐过度使用ref,如果能通过state做到的事情,就不应该使用 refs 在你的 app 中“让事情发生”。过度使用ref并不符合数据驱动的思想。</p>
<h3 id="何为高阶组件-higher-order-component"><a href="#何为高阶组件-higher-order-component" class="headerlink" title="何为高阶组件(higher order component)"></a>何为高阶组件(higher order component)</h3><p>高阶组件是一个以组件为参数并返回一个新组件的函数。HOC 运行你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 <code>connect</code> 函数。除了简单分享工具库和简单的组合,HOC最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。
装饰器@decoration
优点:</p>
<ul>
<li>逻辑复用</li>
<li>不影响被包裹组件的逻辑</li>
</ul>
<p>缺点:</p>
<ul>
<li>传递的props和包裹组件的props发生重名会覆盖</li>
<li>组件嵌套导致层级过深</li>
</ul>
<h3 id="渲染属性-render-props"><a href="#渲染属性-render-props" class="headerlink" title="渲染属性(render props)"></a>渲染属性(render props)</h3><p>Render prop 是一个告知组件需要渲染什么内容的函数 prop
优点:</p>
<ul>
<li>逻辑复用</li>
<li>数据共享</li>
</ul>
<p>缺点:</p>
<ul>
<li>嵌套</li>
<li>无法在return语句外访问数据</li>
</ul>
<h3 id="使用箭头函数-arrow-functions-的优点是什么"><a href="#使用箭头函数-arrow-functions-的优点是什么" class="headerlink" title="使用箭头函数(arrow functions)的优点是什么"></a>使用箭头函数(arrow functions)的优点是什么</h3><ul>
<li>作用域安全:在箭头函数之前,每一个新创建的函数都有定义自身的 <code>this</code> 值(在构造函数中是新对象;在严格模式下,函数调用中的 <code>this</code> 是未定义的;如果函数被称为“对象方法”,则为基础对象等),但箭头函数不会,它会使用封闭执行上下文的 <code>this</code> 值。</li>
<li>简单:箭头函数易于阅读和书写</li>
<li>清晰:当一切都是一个箭头函数,任何常规函数都可以立即用于定义作用域。开发者总是可以查找 next-higher 函数语句,以查看 <code>this</code> 的值</li>
</ul>
<h3 id="为什么建议传递给-setState-的参数是一个-callback-而不是一个对象"><a href="#为什么建议传递给-setState-的参数是一个-callback-而不是一个对象" class="headerlink" title="为什么建议传递给 setState 的参数是一个 callback 而不是一个对象"></a>为什么建议传递给 setState 的参数是一个 callback 而不是一个对象</h3><p>因为 <code>this.props</code> 和 <code>this.state</code> 的更新可能是异步的,不能依赖它们的值去计算下一个 state。setState在生命周期里是异步的,第二个参数是组件重新渲染完成后的回调。</p>
<h3 id="除了在构造函数中绑定-this,还有其它方式吗"><a href="#除了在构造函数中绑定-this,还有其它方式吗" class="headerlink" title="除了在构造函数中绑定 this,还有其它方式吗"></a>除了在构造函数中绑定 <code>this</code>,还有其它方式吗</h3><p>在 constructor 里使用 bind。在回调中你可以使用箭头函数,但问题是每次组件渲染时都会创建一个新的回调。</p>
<h3 id="怎么阻止组件的渲染"><a href="#怎么阻止组件的渲染" class="headerlink" title="怎么阻止组件的渲染"></a>怎么阻止组件的渲染</h3><p>在组件的 <code>render</code> 方法中返回 <code>null</code> 并不会影响触发组件的生命周期方法</p>
<h3 id="react-与-vue-数组中-key-的作用是什么"><a href="#react-与-vue-数组中-key-的作用是什么" class="headerlink" title="react 与 vue 数组中 key 的作用是什么"></a>react 与 vue 数组中 key 的作用是什么</h3><p>diff算法需要比对虚拟dom的修改,然后异步的渲染到页面中,当出现大量相同的标签时,vnode会首先判断key和标签名是否一致,如果一致再去判断子节点一致,使用key可以帮助diff算法提升判断的速度,在页面
重新渲染时更快消耗更少。</p>
<h3 id="在构造函数中-调用-super-props-的目的是什么"><a href="#在构造函数中-调用-super-props-的目的是什么" class="headerlink" title="(在构造函数中)调用 super(props) 的目的是什么"></a>(在构造函数中)调用 super(props) 的目的是什么</h3><p>在 <code>super()</code> 被调用之前,子类是不能使用 <code>this</code> 的,在 ES2015 中,子类必须在 <code>constructor</code> 中调用 <code>super()</code>。传递 <code>props</code> 给 <code>super()</code> 的原因则是便于(在子类中)能在 <code>constructor</code> 访问 <code>this.props</code>。</p>
<h3 id="何为-JSX"><a href="#何为-JSX" class="headerlink" title="何为 JSX"></a>何为 JSX</h3><p>JSX 是 JavaScript 语法的一种语法扩展,并拥有 JavaScript 的全部功能。JSX 生产 React “元素”,你可以将任何的 JavaScript 表达式封装在花括号里,然后将其嵌入到 JSX 中。在编译完成之后,JSX 表达式就变成了常规的 JavaScript 对象,这意味着你可以在 <code>if</code> 语句和 <code>for</code> 循环内部使用 JSX,将它赋值给变量,接受它作为参数,并从函数中返回它。</p>
<p>缺点:b&& 强转成boolean类型 否则如果b=0渲染出0</p>
<h3 id="怎么用-React-createElement-重写下面的代码"><a href="#怎么用-React-createElement-重写下面的代码" class="headerlink" title="怎么用 React.createElement 重写下面的代码"></a>怎么用 React.createElement 重写下面的代码</h3><p>Question:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> element = (</span><br><span class="line"> <h1 className=<span class="string">"greeting"</span>></span><br><span class="line"> Hello, world!</span><br><span class="line"> <<span class="regexp">/h1></span></span><br><span class="line"><span class="regexp">);</span></span><br></pre></td></tr></table></figure>
<p>Answer:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> element = React.createElement(</span><br><span class="line"> <span class="string">'h1'</span>,</span><br><span class="line"> {<span class="attr">className</span>: <span class="string">'greeting'</span>},</span><br><span class="line"> <span class="string">'Hello, world!'</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure>
<h3 id="何为-Children"><a href="#何为-Children" class="headerlink" title="何为 Children"></a>何为 <code>Children</code></h3><p>在JSX表达式中,一个开始标签(比如<code><a></code>)和一个关闭标签(比如<code></a></code>)之间的内容会作为一个特殊的属性<code>props.children</code>被自动传递给包含着它的组件。</p>
<p>这个属性有许多可用的方法,包括 <code>React.Children.map</code>,<code>React.Children.forEach</code>, <code>React.Children.count</code>, <code>React.Children.only</code>,<code>React.Children.toArray</code>。</p>
<h3 id="在-React-中,何为-state"><a href="#在-React-中,何为-state" class="headerlink" title="在 React 中,何为 state"></a>在 React 中,何为 state</h3><p>State 和 props 类似,但它是私有的,并且完全由组件自身控制。State 本质上是一个持有数据,并决定组件如何渲染的对象。</p>
<h3 id="你为何排斥-create-react-app"><a href="#你为何排斥-create-react-app" class="headerlink" title="你为何排斥 create-react-app"></a>你为何排斥 create-react-app</h3><p>在你排斥之前,你并不能去配置 webpack 或 babel presets。</p>
<h3 id="何为-redux"><a href="#何为-redux" class="headerlink" title="何为 redux"></a>何为 redux</h3><p>Redux 的基本思想是整个应用的 state 保持在一个单一的 store 中。store 就是一个简单的 javascript 对象,而改变应用 state 的唯一方式是在应用中触发 actions,然后为这些 actions 编写 reducers 来修改 state。整个 state 转化是在 reducers 中完成,并且不应该由任何副作用。</p>
<h3 id="在-Redux-中,何为-store"><a href="#在-Redux-中,何为-store" class="headerlink" title="在 Redux 中,何为 store"></a>在 Redux 中,何为 store</h3><p>Store 是一个 javascript 对象,它保存了整个应用的 state。与此同时,Store 也承担以下职责:</p>
<ul>
<li>允许通过 <code>getState()</code> 访问 state</li>
<li>运行通过 <code>dispatch(action)</code> 改变 state</li>
<li>通过 <code>subscribe(listener)</code> 注册 listeners</li>
<li>通过 <code>subscribe(listener)</code> 返回的函数处理 listeners 的注销</li>
</ul>
<h3 id="何为-action"><a href="#何为-action" class="headerlink" title="何为 action"></a>何为 action</h3><p>Actions 是一个纯 javascript 对象,它们必须有一个 type 属性表明正在执行的 action 的类型。实质上,action 是将数据从应用程序发送到 store 的有效载荷。</p>
<h3 id="何为-reducer"><a href="#何为-reducer" class="headerlink" title="何为 reducer"></a>何为 reducer</h3><p>一个 reducer 是一个纯函数,该函数以先前的 state 和一个 action 作为参数,并返回下一个 state。</p>
<h3 id="Redux-Thunk-的作用是什么"><a href="#Redux-Thunk-的作用是什么" class="headerlink" title="Redux Thunk 的作用是什么"></a>Redux Thunk 的作用是什么</h3><p>Redux thunk 是一个允许你编写返回一个函数而不是一个 action 的 actions creators 的中间件。如果满足某个条件,thunk 则可以用来延迟 action 的派发(dispatch),这可以处理异步 action 的派发(dispatch)。</p>
<h3 id="何为纯函数-pure-function"><a href="#何为纯函数-pure-function" class="headerlink" title="何为纯函数(pure function)"></a>何为纯函数(pure function)</h3><p>一个纯函数是一个不依赖于且不改变其作用域之外的变量状态的函数,这也意味着一个纯函数对于同样的参数总是返回同样的结果。</p>
<ul>
<li>同输入同输出</li>
<li>无副作用(函数内部的操作不会对外部产生影响(如修改全局变量的值、修改 dom 节点等))</li>
</ul>
<h3 id="redux有哪些中间件,作用?"><a href="#redux有哪些中间件,作用?" class="headerlink" title="redux有哪些中间件,作用?"></a>redux有哪些中间件,作用?</h3><p>中间件提供第三方插件的模式,自定义拦截 action -> reducer 的过程。变为 action -> middlewares -> reducer 。这种机制可以让我们改变数据流,实现如异步 action ,action 过滤,日志输出,异常报告等功能。</p>
<p>redux-logger:提供日志输出</p>
<p>redux-thunk:处理异步操作</p>
<p>redux-promise:处理异步操作,actionCreator的返回值是promise</p>
<h3 id="示例项目"><a href="#示例项目" class="headerlink" title="示例项目"></a>示例项目</h3><ul>
<li><a href="https://github.com/Pau1fitz/react-spotify" target="_blank" rel="noopener">React Spotify</a></li>
<li><a href="https://github.com/andrewngu/sound-redux" target="_blank" rel="noopener">React Soundcloud</a></li>
</ul>
<h3 id="虚拟dom-虚拟节点-是用JS对象来模拟真实DOM中的节点"><a href="#虚拟dom-虚拟节点-是用JS对象来模拟真实DOM中的节点" class="headerlink" title="虚拟dom(虚拟节点)是用JS对象来模拟真实DOM中的节点"></a>虚拟dom(虚拟节点)是用JS对象来模拟真实DOM中的节点</h3><p>虚拟dom相当于在js和真实dom中间加了一个缓存,利用dom diff算法避免了没有必要的dom操作,从而提高性能。具体实现步骤如下:用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。插入新组件有了key可以帮助react找到映射。</p>
<ul>
<li><p>真实的元素节点</p>
<figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"wrap"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span> <span class="attr">class</span>=<span class="string">"title"</span>></span>Hello world!<span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure>
</li>
<li><p>vnode</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> tag:<span class="string">'div'</span>,</span><br><span class="line"> attrs:{</span><br><span class="line"> id:<span class="string">'wrap'</span></span><br><span class="line"> },</span><br><span class="line"> children:[</span><br><span class="line"> {</span><br><span class="line"> tag:<span class="string">'p'</span>,</span><br><span class="line"> text:<span class="string">'Hello world!'</span>,</span><br><span class="line"> attrs:{</span><br><span class="line"> class:'title',</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="为什么使用虚拟dom"><a href="#为什么使用虚拟dom" class="headerlink" title="为什么使用虚拟dom"></a>为什么使用虚拟dom</h3><p>起初我们在使用JS/JQuery时,不可避免的会大量操作DOM,而DOM的变化又会引发回流或重绘,从而降低页面渲染性能。那么怎样来减少对DOM的操作呢?此时虚拟DOM
应用而生,所以虚拟DOM出现的主要目的就是<code>为了减少频繁操作DOM而引起回流重绘所引发的性能问题的</code></p>
<h3 id="虚拟dom的作用"><a href="#虚拟dom的作用" class="headerlink" title="虚拟dom的作用"></a>虚拟dom的作用</h3><p>兼容性好。因为Vnode本质是JS对象,所以不管Node还是浏览器环境,都可以操作;
减少了对Dom的操作。页面中的数据和状态变化,都通过Vnode对比,只需要在比对完之后更新DOM,不需要频繁操作,提高了页面性能。</p>
<p>每个setState重新渲染整个子树标记为dirty。 如果要压缩性能,请尽可能调用 setState,并使用shouldComponentUpdate 来防止重新渲染大型子树。把树形结构按照层级分解,只比较同级元素。给列表结构的每个单元添加唯一的key属性,方便比较。pureComponent(浅比较)+immutable 替换成preact</p>
<h3 id="diff算法"><a href="#diff算法" class="headerlink" title="diff算法"></a>diff算法</h3><blockquote>
<p>一开始会根据真实DOM生成虚拟DOM,当虚拟DOM某个节点的数据改变后会生成一个新的Vnode,然后VNode和oldVnode对比,把不同的地方修改在真实DOM上,最后再使得oldVnode的值为Vnode。</p>
</blockquote>
<p><code>diff过程就是调用patch函数,比较新老节点,一边比较一边给真实DOM打补丁(patch);</code></p>
<p><img src="http://cdn.mydearest.cn/blog/images/patch.png" alt="patch"></p>
<p>把树形结构按照层级分解,只比较同级元素。</p>
<p>给列表结构的每个单元添加唯一的key属性,方便比较。</p>
<p>React 只会匹配相同 class 的 component(这里面的class指的是组件的名字)</p>
<p>合并操作,调用 component 的 setState 方法的时候, React 将其标记为 dirty.到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制.</p>
<p>选择性子树渲染。开发人员可以重写shouldComponentUpdate提高diff的性能。</p>
<p>diff的只是html tag,并没有diff数据。</p>
<h3 id="setState的理解"><a href="#setState的理解" class="headerlink" title="setState的理解"></a>setState的理解</h3><ul>
<li>setState 只在<code>合成事件</code>和<code>钩子函数(除了componentDidUpdate)</code>中是“异步”的,在原生事件和 setTimeout 中都是同步的。</li>
<li>setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。</li>
<li><p>setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。</p>
</li>
<li><p>异步与同步: setState并不是单纯的异步或同步,这其实与调用时的环境相关:</p>
<ul>
<li><p>在 合成事件 和 生命周期钩子(除 componentDidUpdate) 中,setState是”异步”的;</p>
<ul>
<li>原因: 因为在setState的实现中,有一个判断: 当更新策略正在事务流的执行中时,该组件更新会被推入dirtyComponents队列中等待执行;否则,开始执行batchedUpdates队列更新;</li>
</ul>
</li>
<li><p>在生命周期钩子调用中,更新策略都处于更新之前,组件仍处于事务流中,而componentDidUpdate是在更新之后,此时组件已经不在事务流中了,因此则会同步执行;
在合成事件中,React 是基于 事务流完成的事件委托机制 实现,也是处于事务流中;</p>
<ul>
<li>问题: 无法在setState后马上从this.state上获取更新后的值。</li>
<li>解决: 如果需要马上同步去获取新值,setState其实是可以传入第二个参数的。setState(updater, callback),在回调中即可获取最新值;</li>
</ul>
</li>
<li><p>在 原生事件 和 setTimeout 中,setState是同步的,可以马上获取更新后的值;</p>
<ul>
<li>原因: 原生事件是浏览器本身的实现,与事务流无关,自然是同步;而setTimeout是放置于定时器线程中延后执行,此时事务流已结束,因此也是同步;</li>
</ul>
</li>
</ul>
</li>
<li><p>批量更新</p>
<ul>
<li>在 合成事件 和 生命周期钩子 中,setState更新队列时,存储的是<code>合并状态(Object.assign)</code>。因此前面设置的 key 值会被后面所覆盖,最终只会执行一次更新;</li>
</ul>
</li>
<li><p>函数式</p>
<ul>
<li>由于 Fiber 及 合并 的问题,官方推荐可以传入 函数 的形式。setState(fn),在fn中返回新的state对象即可,例如this.setState((state, props) => newState);</li>
<li>使用函数式,可以用于避免setState的批量更新的逻辑,传入的函数将会被顺序调用;</li>
</ul>
</li>
<li><p>注意点</p>
<ul>
<li>当组件已被销毁,如果再次调用setState,React 会报错警告,通常有两种解决办法:<ul>
<li>将数据挂载到外部,通过 props 传入,如放到 Redux 或 父级中;</li>
<li>在组件内部维护一个状态量 (isUnmounted),componentWillUnmount中标记为 true,在setState前进行判断;</li>
</ul>
</li>
</ul>
</li>
</ul>
<h3 id="替换的属性"><a href="#替换的属性" class="headerlink" title="替换的属性"></a>替换的属性</h3><ul>
<li>class/className for/htmlFor</li>
</ul>
<h3 id="插入html文本"><a href="#插入html文本" class="headerlink" title="插入html文本"></a>插入html文本</h3><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">dangerouslySetInnerHTML={{<span class="attr">__html</span>: content}}</span><br></pre></td></tr></table></figure>
<h3 id="15版本的生命周期如下:"><a href="#15版本的生命周期如下:" class="headerlink" title="15版本的生命周期如下:"></a>15版本的生命周期如下:</h3><ol>
<li>初始化阶段</li>
</ol>
<ul>
<li>constructor</li>
<li>getDefaultProps</li>
<li>getInitialState</li>
</ul>
<ol start="2">
<li>挂载阶段</li>
</ol>
<ul>
<li>componentWillMount</li>
<li>render</li>
<li>componentDidMount</li>
</ul>
<ol start="3">
<li>更新阶段
props:</li>
</ol>
<ul>
<li>componentWillReceiveProps</li>
<li>shouldComponentUpdate</li>
<li>componentWillUpdate</li>
<li>render</li>
<li>componentDidUpdate
state:</li>
<li>shouldComponentUpdate</li>
<li>componentWillUpdate</li>
<li>render</li>
<li>componentDidUpdate</li>
</ul>
<ol start="4">
<li>卸载阶段</li>
</ol>
<ul>
<li>componentWillUnmount</li>
</ul>
<h3 id="16版本生命周期如下:"><a href="#16版本生命周期如下:" class="headerlink" title="16版本生命周期如下:"></a>16版本生命周期如下:</h3><ol>
<li>初始化阶段</li>
</ol>
<ul>
<li>constructor</li>
<li>getDefaultProps</li>
<li>getInitialState</li>
</ul>
<ol start="2">
<li>挂载阶段<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">组件实例化。</span><br><span class="line">组件的props发生变化。</span><br><span class="line">父组件重新渲染。</span><br><span class="line"><span class="keyword">this</span>.setState()不会触发getDerivedStateFromProps(),但是<span class="keyword">this</span>.forceUpdate()会。</span><br></pre></td></tr></table></figure></li>
</ol>
<ul>
<li>getDerivedStateFromProps:传入nextProps和prevState,根据需要将props映射到state,否则返回null</li>
<li>render</li>
<li>componentDidMount</li>
</ul>
<ol start="3">
<li>更新阶段</li>
</ol>
<ul>
<li>getDerivedStateFromProps</li>
<li>shouldComponentUpdate</li>
<li>render</li>
<li>getSnapshotBeforeUpdate:render之后dom渲染之前会发生,返回一个值作为componentDidUpdate的第三个参数使用</li>
<li>componentDidUpdate</li>
</ul>
<ol start="4">
<li>卸载阶段</li>
</ol>
<ul>
<li>componentWillUnmount</li>
</ul>
<ol start="5">
<li>错误处理</li>
</ol>
<ul>
<li>componentDidCatch</li>
</ul>
<h3 id="事件机制"><a href="#事件机制" class="headerlink" title="事件机制"></a>事件机制</h3><p>react事件并没有绑定到真实的dom节点上,而是通过事件代理,在最外层的document上对事件进行统一分发。</p>
<h3 id="为什么react事件要自己绑定this"><a href="#为什么react事件要自己绑定this" class="headerlink" title="为什么react事件要自己绑定this"></a>为什么react事件要自己绑定this</h3><p>在react中事件处理函数是直接调用的,并没有指定调用的组件,所以不进行手动绑定的情况下直接获取到的this是不准确的,所以我们需要手动将当前组件绑定到this上。</p>
<h3 id="react和原生事件的执行顺序是什么,可以混用吗"><a href="#react和原生事件的执行顺序是什么,可以混用吗" class="headerlink" title="react和原生事件的执行顺序是什么,可以混用吗"></a>react和原生事件的执行顺序是什么,可以混用吗</h3><p>react的所有事件都通过document进行统一分发,当真实dom触发事件后冒泡到document后才会对react事件进行处理</p>
<p>所以原生事件会先执行,然后执行react合成事件,最后执行真正在document上挂载的事件两者最好不要混用,原生事件中如果执行了stopPropagation方法,则会导致其他react事件失效。</p>
<h3 id="虚拟dom比普通dom更快吗"><a href="#虚拟dom比普通dom更快吗" class="headerlink" title="虚拟dom比普通dom更快吗"></a>虚拟dom比普通dom更快吗</h3><p>首次渲染时vdom不具有任何优势甚至要进行更多的计算,消耗更多的内存。</p>
<p>vdom的优势在于react的diff算法和批处理策略,react在页面更新之前,提前计算好了如何进行更新和渲染dom。vdom主要是能在重复渲染时帮助我们计算如何实现更高效的更新,而不是说它比dom操作快。</p>
<h3 id="虚拟dom中的-typeof属性的作用是什么"><a href="#虚拟dom中的-typeof属性的作用是什么" class="headerlink" title="虚拟dom中的$$typeof属性的作用是什么"></a>虚拟dom中的$$typeof属性的作用是什么</h3><p>它被赋值为REACT_ELEMENT_TYPE,是一个symbol类型的变量,这个变量可以防止XSS。react渲染时会把没有$$typeof标识以及规则校验不通过的组件全都过滤掉。当你的环境不支持Symbol时,$$typeof被赋值为0xeac7,为什么采用0xeac7?</p>
<blockquote>
<p>0xeac7看起来有点像React。</p>
</blockquote>
<h3 id="HOC在业务场景中有哪些实际的应用"><a href="#HOC在业务场景中有哪些实际的应用" class="headerlink" title="HOC在业务场景中有哪些实际的应用"></a>HOC在业务场景中有哪些实际的应用</h3><ul>
<li>组合渲染(属性代理)<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 更改 props</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">proxyHoc</span>(<span class="params">Comp</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">const</span> newProps = {</span><br><span class="line"> name: <span class="string">'tayde'</span>,</span><br><span class="line"> age: <span class="number">1</span>,</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">Comp</span> {<span class="attr">...this.props</span>} {<span class="attr">...newProps</span>} /></span></span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
很方便将<code>Input</code>组件转化为受控组件</li>
<li>条件渲染<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 反向继承传递过来的组件</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">withLoading</span>(<span class="params">Comp</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Comp</span> </span>{</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">if</span>(<span class="keyword">this</span>.props.isLoading) {</span><br><span class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">Loading</span> /></span></span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.render()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>操作props</li>
<li>获取refs</li>
<li>操作state
可以直接通过 this.state 获取到被包裹组件的状态,并进行操作。但这样的操作容易使 state 变得难以追踪,不易维护,谨慎使用。</li>
<li>渲染劫持</li>
</ul>
<p>实际应用场景:</p>
<ul>
<li>日志打点<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 性能监控埋点</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">withTiming</span>(<span class="params">Comp</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">Comp</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(props) {</span><br><span class="line"> <span class="keyword">super</span>(props);</span><br><span class="line"> <span class="keyword">this</span>.start = <span class="built_in">Date</span>.now();</span><br><span class="line"> <span class="keyword">this</span>.end = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> componentDidMount() {</span><br><span class="line"> <span class="keyword">super</span>.componentDidMount && <span class="keyword">super</span>.componentDidMount();</span><br><span class="line"> <span class="keyword">this</span>.end = <span class="built_in">Date</span>.now();</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${WrappedComponent.name}</span> 组件渲染时间为 <span class="subst">${<span class="keyword">this</span>.end - <span class="keyword">this</span>.start}</span> ms`</span>);</span><br><span class="line"> }</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">super</span>.render();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>权限控制<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">withAdminAuth</span>(<span class="params">WrappedComponent</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="class"><span class="keyword">class</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(props){</span><br><span class="line"> <span class="keyword">super</span>(props)</span><br><span class="line"> <span class="keyword">this</span>.state = {</span><br><span class="line"> isAdmin: <span class="literal">false</span>,</span><br><span class="line"> }</span><br><span class="line"> } </span><br><span class="line"> <span class="keyword">async</span> componentWillMount() {</span><br><span class="line"> <span class="keyword">const</span> currentRole = <span class="keyword">await</span> getCurrentUserRole();</span><br><span class="line"> <span class="keyword">this</span>.setState({</span><br><span class="line"> isAdmin: currentRole === <span class="string">'Admin'</span>,</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> render() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.state.isAdmin) {</span><br><span class="line"> <span class="keyword">return</span> <span class="xml"><span class="tag"><<span class="name">Comp</span> {<span class="attr">...this.props</span>} /></span></span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> (<span class="xml"><span class="tag"><<span class="name">div</span>></span>您没有权限查看该页面,请联系管理员!<span class="tag"></<span class="name">div</span>></span></span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li>
<li>双向绑定</li>
<li>表单校验</li>
<li>代码复用</li>
</ul>
<h3 id="HOC和mixin的异同点是什么"><a href="#HOC和mixin的异同点是什么" class="headerlink" title="HOC和mixin的异同点是什么"></a>HOC和mixin的异同点是什么</h3><ul>
<li><p>mixin可能会互相依赖,互相耦合,不利于代码维护</p>
</li>
<li><p>不同的mixin中的方法可能会相互冲突</p>
</li>
<li><p>mixin非常多的时候组件是可以感知到的,甚至还要为其做相关处理,这样会给代码造成滚雪球式的复杂性</p>
</li>
<li><p>而HOC的出现则可以解决这些问题</p>
<ul>
<li>hoc是一个没有副作用的纯函数,各个高阶组件不会互相依赖耦合</li>
<li>高阶组件也有可能造成冲突,但我们可以在遵守约定的情况下避免这些情况</li>
<li>高阶组件并不关心数据使用的方式和原因,而被包裹的组件也不关心数据来自何处。高阶组件的增加不会为原组件增加负担</li>
</ul>
</li>
</ul>
<h4 id="hooks有哪些优势-react提供的api-hoc和render-props开发模式"><a href="#hooks有哪些优势-react提供的api-hoc和render-props开发模式" class="headerlink" title="hooks有哪些优势(react提供的api,hoc和render props开发模式)"></a>hooks有哪些优势(react提供的api,hoc和render props开发模式)</h4><ul>
<li><p>组件逻辑越来越复杂(componentDidMount, componentDidUpdate)
尤其是生命周期函数中常常包含一些不相关的逻辑,完全不相关的代码却在同一个方法中组合在一起。如此很容易产生 bug,并且导致逻辑不一致。</p>
</li>
<li><p>组件之间复用状态逻辑很难,避免地狱嵌套
hook和mixin在用法上有一定的相似之处,但是mixin引入的逻辑状态是可以互相覆盖的,而多个hooks之间互不影响,hoc也可能带来一定冲突,比如props覆盖等等,使用hooks则可以避免这些问题。大量使用hoc让我们的代码变得嵌套层级非常深,使用hooks我们可以实现扁平式的状态逻辑复用,而避免了大量的组件嵌套。</p>
</li>
<li><p>让组件变得更加容易理解 class组件this钩子函数
相比函数,编写一个class可能需要更多的知识,hooks让你可以在class之外使用更多的react的新特性</p>
</li>
</ul>
<p>后续中展示组件需要改造成类组件需要有自己的状态管理和生命周期方法将复用逻辑提升到代码顶部。</p>
<h3 id="Fiber"><a href="#Fiber" class="headerlink" title="Fiber"></a>Fiber</h3><blockquote>
<p>Fiber核心是实现了一个基于优先级和requestIdleCallback的循环任务调度算法</p>
</blockquote>
<ul>
<li>reconciliation阶段可以把任务拆分成多个小任务</li>
<li>reconciliation阶段可随时中止或恢复任务</li>
<li>可以根据优先级不同来选择优先执行任务</li>
</ul>
<blockquote>
<p>在任务队列中选出高优先级的fiber node执行,调用requestIdleCallback获取所剩时间,若执行时间超过了deathLine,或者突然插入更高优先级的任务,则执行中断,保存当前结果,修改tag标记一下,设置为pending状态,迅速收尾并再调用一个requestIdleCallback,等主线程释放出来再继续
恢复任务执行时,检查tag是被中断的任务,会接着继续做任务或者重做</p>
</blockquote>
<p>在 v16 之前,reconciliation 简单说就是一个自顶向下递归算法,产出需要对当前DOM进行更新或替换的操作列表,一旦开始,会持续占用主线程,中断操作却不容易实现。当JS长时间执行(如大量计算等),会阻塞
样式计算、绘制等工作,出现页面脱帧现象。所以,v16 进行了一次重写,迎来了代号为Fiber的异步渲染架构。</p>
<p>React 的核心流程可以分为两个部分:</p>
<ul>
<li><p>reconciliation (调度算法,也可称为 render diff阶段):</p>
<ul>
<li>更新 state 与 props;</li>
<li>调用生命周期钩子;</li>
<li>生成 virtual dom;<ul>
<li>这里应该称为 Fiber Tree 更为符合;</li>
</ul>
</li>
<li>通过新旧 vdom 进行 diff 算法,获取 vdom change;</li>
<li>确定是否需要重新渲染</li>
</ul>
</li>
<li><p>commit(操作dom阶段):</p>
<ul>
<li>如需要,则操作 dom 节点更新;</li>
</ul>
</li>
<li><p>问题: 随着应用变得越来越庞大,整个更新渲染的过程开始变得吃力,大量的组件渲染会导致主进程长时间被占用,导致一些动画或高频操作出现卡顿和掉帧的情况。而关键点,便是 同步阻塞。在之前的调度算法中,React 需要实例化每个类组件,生成一颗组件树,使用 同步递归 的方式进行遍历渲染,而这个过程最大的问题就是无法 暂停和恢复。</p>
</li>
</ul>
<ul>
<li><p>解决方案: 解决同步阻塞的方法,通常有两种: 异步与任务分割。而 React Fiber 便是为了实现任务分割而诞生的。</p>
<ul>
<li>在 React V16 将调度算法进行了重构, 将之前的 stack reconciler 重构成新版的 fiber reconciler,变成了具有链表和指针的<code>单链表树遍历算法</code>。通过指针映射,每个单元都记录着遍历当下的上一步与下一步,从而使遍历变得可以被暂停和重启。</li>
<li>这里我理解为是一种<code>任务分割调度算法</code>,主要是将原先同步更新渲染的任务分割成一个个独立的<code>小任务单位</code>,根据不同的优先级,将小任务分散到浏览器的空闲时间执行,充分利用主进程的事件循环机制。</li>
</ul>
</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Fiber</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(instance) {</span><br><span class="line"> <span class="keyword">this</span>.instance = instance</span><br><span class="line"> <span class="comment">// 指向第一个 child 节点</span></span><br><span class="line"> <span class="keyword">this</span>.child = child</span><br><span class="line"> <span class="comment">// 指向父节点</span></span><br><span class="line"> <span class="keyword">this</span>.return = parent</span><br><span class="line"> <span class="comment">// 指向第一个兄弟节点</span></span><br><span class="line"> <span class="keyword">this</span>.sibling = previous</span><br><span class="line"> } </span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<blockquote>
<p>核心思想是 任务拆分和协同,主动把执行权交给主线程,使主线程有时间空挡处理其他高优先级任务。
当遇到进程阻塞的问题时,任务分割、异步调用 和 缓存策略 是三个显著的解决思路。</p>
</blockquote>
<ul>
<li>任务优先级(7种)<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">{ </span><br><span class="line"> NoWork: <span class="number">0</span>, <span class="comment">// No work is pending.</span></span><br><span class="line"> SynchronousPriority: <span class="number">1</span>, <span class="comment">// 文本输入框</span></span><br><span class="line"> TaskPriority: <span class="number">2</span>, <span class="comment">// 当前调度正执行的任务</span></span><br><span class="line"> AnimationPriority: <span class="number">3</span>, <span class="comment">// 动画过渡</span></span><br><span class="line"> HighPriority: <span class="number">4</span>, <span class="comment">// 用户交互反馈</span></span><br><span class="line"> LowPriority: <span class="number">5</span>, <span class="comment">// 数据的更新</span></span><br><span class="line"> OffscreenPriority: <span class="number">6</span>, <span class="comment">// 预估未来需要显示的任务</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
<h3 id="为什么生命周期有了变动"><a href="#为什么生命周期有了变动" class="headerlink" title="为什么生命周期有了变动"></a>为什么生命周期有了变动</h3><p>在 Fiber 中,reconciliation 阶段进行了任务分割,涉及到 暂停 和 重启,因此可能会导致 reconciliation 中的生命周期函数在一次更新渲染循环中被<code>多次调用</code>的情况,产生一些意外错误。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Component</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</span><br><span class="line"> <span class="comment">// 替换 `componentWillReceiveProps` ,</span></span><br><span class="line"> <span class="comment">// 初始化和 update 时被调用</span></span><br><span class="line"> <span class="comment">// 静态函数,无法使用 this</span></span><br><span class="line"> <span class="keyword">static</span> getDerivedStateFromProps(nextProps, prevState) {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 判断是否需要更新组件</span></span><br><span class="line"> <span class="comment">// 可以用于组件性能优化</span></span><br><span class="line"> shouldComponentUpdate(nextProps, nextState) {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 组件被挂载后触发</span></span><br><span class="line"> componentDidMount() {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 替换 componentWillUpdate</span></span><br><span class="line"> <span class="comment">// 可以在更新之前获取最新 dom 数据</span></span><br><span class="line"> getSnapshotBeforeUpdate() {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 组件更新后调用</span></span><br><span class="line"> componentDidUpdate() {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 组件即将销毁</span></span><br><span class="line"> componentWillUnmount() {}</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 组件已销毁</span></span><br><span class="line"> componentDidUnMount() {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 错误边界捕获全局异常</span></span><br><span class="line"> componentDidCatch() {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ul>
<li>在constructor初始化 state;</li>
<li>在componentDidMount中进行事件监听,并在componentWillUnmount中解绑事件;</li>
<li>在componentDidMount中进行数据的请求,而不是在componentWillMount;</li>
<li><p>需要根据 props 更新 state 时,使用getDerivedStateFromProps(nextProps, prevState);</p>
<ul>
<li>旧 props 需要自己存储,以便比较;<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">public <span class="keyword">static</span> getDerivedStateFromProps(nextProps, prevState) {</span><br><span class="line"> <span class="comment">// 当新 props 中的 data 发生变化时,同步更新到 state 上</span></span><br><span class="line"> <span class="keyword">if</span> (nextProps.data !== prevState.data) {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> data: nextProps.data</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
</ul>
</li>
<li><p>可以在componentDidUpdate监听 props 或者 state 的变化,例如:</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">componentDidUpdate(prevProps) {</span><br><span class="line"> <span class="comment">// 当 id 发生变化时,重新获取数据</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.props.id !== prevProps.id) {</span><br><span class="line"> <span class="keyword">this</span>.fetchData(<span class="keyword">this</span>.props.id);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</li>
<li><p>在componentDidUpdate使用setState时,必须加条件,否则将进入死循环;</p>
</li>
<li>getSnapshotBeforeUpdate(prevProps, prevState)可以在更新之前获取最新的渲染数据,它的调用是在 render 之后, update 之前;</li>
<li>shouldComponentUpdate: 默认每次调用setState,一定会最终走到 diff 阶段,但可以通过shouldComponentUpdate的生命钩子返回false来直接阻止后面的逻辑执行,通常是用于做条件渲染,优化渲染的性能。</li>
</ul>
<p>废弃的原因主要是因为 react 在 16 版本重构了调度算法,新的调度可能会导致一些生命周期被反复调用,所以在 16 中就不建议使用了,而改在其他时机中暴露出其
他生命周期钩子用来替代。</p>
<h3 id="SSR"><a href="#SSR" class="headerlink" title="SSR"></a>SSR</h3><p>SSR,俗称 服务端渲染 (Server Side Render),讲人话就是: 直接在服务端层获取数据,渲染出完成的 HTML 文件,直接返回给用户浏览器访问。
前后端分离: 前端与服务端隔离,前端动态获取数据,渲染页面。</p>
<ul>
<li>痛点:<ul>
<li>首屏渲染性能瓶颈:</li>
<li>空白延迟: HTML下载时间 + JS下载/执行时间 + 请求时间 + 渲染时间。在这段时间内,页面处于空白的状态。</li>
<li>SEO 问题: 由于页面初始状态为空,因此爬虫无法获取页面中任何有效数据,因此对搜索引擎不友好。<ul>
<li>虽然一直有在提动态渲染爬虫的技术,不过据我了解,大部分国内搜索引擎仍然是没有实现。</li>
</ul>
</li>
</ul>
</li>
</ul>
<h4 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h4><ul>
<li>Node 服务: 让前后端运行同一套代码成为可能。</li>
<li>Virtual Dom: 让前端代码脱离浏览器运行。</li>
</ul>
<p><img src="http://cdn.mydearest.cn/blog/images/ssr.png" alt="ssr"></p>