-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathindex.html
863 lines (636 loc) · 134 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
<!DOCTYPE html>
<html>
<head><meta name="generator" content="Hexo 3.8.0">
<meta charset="utf-8">
<title>Rin's blog</title>
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta property="og:type" content="website">
<meta property="og:title" content="Rin's blog">
<meta property="og:url" content="http://yoursite.com/index.html">
<meta property="og:site_name" content="Rin's blog">
<meta property="og:locale" content="default">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="Rin's blog">
<link rel="alternate" href="/atom.xml" title="Rin's blog" type="application/atom+xml">
<link rel="icon" href="/favicon.png">
<link href="//fonts.googleapis.com/css?family=Source+Code+Pro" rel="stylesheet" type="text/css">
<link rel="stylesheet" href="/css/style.css">
</head>
</html>
<body>
<div id="container">
<div id="wrap">
<header id="header">
<div id="banner"></div>
<div id="header-outer" class="outer">
<div id="header-title" class="inner">
<h1 id="logo-wrap">
<a href="/" id="logo">Rin's blog</a>
</h1>
</div>
<div id="header-inner" class="inner">
<nav id="main-nav">
<a id="main-nav-toggle" class="nav-icon"></a>
<a class="main-nav-link" href="/">Home</a>
<a class="main-nav-link" href="/archives">Archives</a>
</nav>
<nav id="sub-nav">
<a id="nav-rss-link" class="nav-icon" href="/atom.xml" title="RSS Feed"></a>
<a id="nav-search-btn" class="nav-icon" title="Search"></a>
</nav>
<div id="search-form-wrap">
<form action="//google.com/search" method="get" accept-charset="UTF-8" class="search-form"><input type="search" name="q" class="search-form-input" placeholder="Search"><button type="submit" class="search-form-submit"></button><input type="hidden" name="sitesearch" value="http://yoursite.com"></form>
</div>
</div>
</div>
</header>
<div class="outer">
<section id="main">
<article id="post-js继承方式总结" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2020/03/01/js继承方式总结/" class="article-date">
<time datetime="2020-03-01T12:20:34.000Z" itemprop="datePublished">2020-03-01</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/03/01/js继承方式总结/">js继承方式总结</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>在看js继承的方式和原理之前,首先需要把js中的原型和原型链搞明白。先来看一下原型和原型链是什么。</p>
<h2 id="原型和原型链"><a href="#原型和原型链" class="headerlink" title="原型和原型链"></a>原型和原型链</h2><ul>
<li><p>原型: 是一个指针, 指向一个对象, 该对象的作用是包含可以由特定类型的所有实例共享的属性和方法. 默认所有原型对象会自动获得一个constructor属性, 这个属性包含一个指向prototype属性所在函数的指针.</p>
</li>
<li><p>实例: 实例包含一个指向原型对象的内部指针<strong>proto</strong></p>
</li>
<li><p>原型链: 原型对象等于另一个类型的实例, 则该原型对象将包含一个指向另一个原型的指针<strong>proto</strong>, 相应地另一个原型中也包含一个指向另一个构造函数的指针,若另一个原型又是另一个类型的实例,如此递进形成原型链.</p>
</li>
</ul>
<p>在js当中,访问对象的某个属性时,首先搜索实例中有无该属性,如果没有,那么在原型对象中查找.</p>
<ul>
<li>new的时候做了什么?</li>
</ul>
<p><code>var person = new Person()</code> 的时候实际做了以下事情:</p>
<figure class="highlight javascript"><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="keyword">var</span> person = {};</span><br><span class="line">person.__proto__ = Person.prototype;</span><br><span class="line">Person.call(person);</span><br></pre></td></tr></table></figure>
<h2 id="继承方式"><a href="#继承方式" class="headerlink" title="继承方式"></a>继承方式</h2><ol>
<li>借助原型链实现继承</li>
</ol>
<p>基于原型链的概念, 可以想到将父类的实例作为子类的原型能够实现继承:</p>
<figure class="highlight javascript"><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">Parent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.parentProperty = <span class="string">'parent'</span>;</span><br><span class="line"> <span class="keyword">this</span>.parentObj = {</span><br><span class="line"> addr: <span class="string">'beijing'</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.childProperty = <span class="string">'child'</span>;</span><br><span class="line">}</span><br><span class="line">Child.prototype = <span class="keyword">new</span> Parent();</span><br><span class="line"><span class="keyword">var</span> child = <span class="keyword">new</span> Child();</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(child <span class="keyword">instanceof</span> Parent); <span class="comment">// true</span></span><br><span class="line"><span class="built_in">console</span>.log(child.parentProperty); <span class="comment">// parent</span></span><br></pre></td></tr></table></figure>
<p>原型链继承的问题是当原型链中包含引用类型的值时,该值会被所有实例共享:</p>
<figure class="highlight javascript"><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">var</span> child1 = <span class="keyword">new</span> Child();</span><br><span class="line">child1.parentObj.addr = <span class="string">'shanghai'</span>;</span><br><span class="line"><span class="built_in">console</span>.log(child.parentObj.addr); <span class="comment">// 'shanghai'</span></span><br><span class="line"><span class="built_in">console</span>.log(child1.parentObj.addr); <span class="comment">// 'shanghai'</span></span><br></pre></td></tr></table></figure>
<p>从上面代码看到, 修改了一个实例中的parentObj的属性的值, 另一个实例中的值也跟着被修改了.</p>
<p>原型链继承的另一个问题是创建子类实例时无法向父类构造函数中传参数。</p>
<ol start="2">
<li>通过构造函数实现继承</li>
</ol>
<figure class="highlight javascript"><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="function"><span class="keyword">function</span> <span class="title">Parent</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.parentName = name;</span><br><span class="line"> <span class="keyword">this</span>.ids = [<span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params">parentName</span>) </span>{</span><br><span class="line"> Parent.call(<span class="keyword">this</span>, parentName); <span class="comment">// 父类执行的属性挂载到子类上</span></span><br><span class="line"> <span class="keyword">this</span>.type = <span class="string">'child'</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> child1 = <span class="keyword">new</span> Child(<span class="string">'tom'</span>);</span><br><span class="line"><span class="keyword">var</span> child2 = <span class="keyword">new</span> Child(<span class="string">'Jason'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(child1.parentName);</span><br><span class="line"><span class="built_in">console</span>.log(child2.parentName);</span><br><span class="line"></span><br><span class="line">child1.ids.push(<span class="number">3</span>);</span><br><span class="line"><span class="built_in">console</span>.log(child1.ids); <span class="comment">// ["papa", "mama", "grandpa"]</span></span><br><span class="line"><span class="built_in">console</span>.log(child2.ids); <span class="comment">// ["papa", "mama"]</span></span><br></pre></td></tr></table></figure>
<p>通过构造函数实现继承的方式解决了原型链继承中引用类型的值被所有子类实例共享的问题以及无法向父构造函数传参的问题。</p>
<p>但是构造函数继承也存在它自身的问题,如下代码:</p>
<figure class="highlight javascript"><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">Parent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.parentName = <span class="string">'parent'</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Parent.prototype.getParentName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.parentName;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params"></span>) </span>{</span><br><span class="line"> Parent.call(<span class="keyword">this</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> child1= <span class="keyword">new</span> Child();</span><br><span class="line"><span class="built_in">console</span>.log(child.getParentName()); <span class="comment">// child.getParentName is not a function</span></span><br></pre></td></tr></table></figure>
<p>如上,当父类的prototype中还有函数的话,子类无法拿到该函数。</p>
<h2 id="组合继承"><a href="#组合继承" class="headerlink" title="组合继承"></a>组合继承</h2><p>组合继承将原型链继承和借用构造函数组合在了一起,这样可以解决上述两种继承方式所存在的问题:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.ids = [<span class="number">1</span>, <span class="number">2</span>];</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Parent.prototype.getParentName = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.name;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params">name, age</span>) </span>{</span><br><span class="line"> Parent.call(<span class="keyword">this</span>, name);</span><br><span class="line"> <span class="keyword">this</span>.age= age;</span><br><span class="line">}</span><br><span class="line">Child.prototype = <span class="keyword">new</span> Parent();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> child1 = <span class="keyword">new</span> Child(<span class="string">'tom'</span>, <span class="number">1</span>);</span><br><span class="line"><span class="keyword">var</span> child2 = <span class="keyword">new</span> Child(<span class="string">'jack'</span>, <span class="number">2</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决父原型中引用类型值被子类实例共享的问题</span></span><br><span class="line"><span class="built_in">console</span>.log(child1.ids); <span class="comment">// [1,2]</span></span><br><span class="line">child1.ids.push(<span class="number">3</span>);</span><br><span class="line"><span class="built_in">console</span>.log(child1.ids); <span class="comment">// [1,2,3]</span></span><br><span class="line"><span class="built_in">console</span>.log(child2.ids); <span class="comment">// [1,2]</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 解决子类实例拿不到父类prototype中方法的问题</span></span><br><span class="line"><span class="built_in">console</span>.log(child1.getParentName()); <span class="comment">// tom</span></span><br></pre></td></tr></table></figure>
<p>组合继承存在的问题是调用了两次父类构造函数,分别在 <code>Parent.call(this)</code> 和 <code>Child.prototype = new Parent();</code>中, 造成了资源的浪费。</p>
<h2 id="组合继承的优化1"><a href="#组合继承的优化1" class="headerlink" title="组合继承的优化1"></a>组合继承的优化1</h2><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> Parent.call(<span class="keyword">this</span>, name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">Child.prototype = Parent.prototype;</span><br></pre></td></tr></table></figure>
<h2 id="组合继承的优化2"><a href="#组合继承的优化2" class="headerlink" title="组合继承的优化2"></a>组合继承的优化2</h2><figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Parent</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Child</span>(<span class="params"></span>) </span>{</span><br><span class="line"> Parent.call(<span class="keyword">this</span>);</span><br><span class="line">}</span><br><span class="line">Child.prototype = <span class="built_in">Object</span>.create(Parent.prototype);</span><br><span class="line">Child.prototype.constructor = Child;</span><br></pre></td></tr></table></figure>
<p><code>Object.create</code> 等价于:</p>
<figure class="highlight javascript"><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="function"><span class="keyword">function</span> <span class="title">create</span>(<span class="params">o</span>)</span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">F</span>(<span class="params"></span>)</span>{}</span><br><span class="line"> F.prototype = o;</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> F();</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>它可以用于从原型对象生成新的对象实例,可以代替new命令</p>
<p>将这种继承方式抽象为一个函数:</p>
<figure class="highlight javascript"><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="function"><span class="keyword">function</span> <span class="title">extend</span>(<span class="params">subClass, superClass</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> prototype = <span class="built_in">Object</span>.create(superClass.prototype);</span><br><span class="line"> prototype.constructor = subClass; </span><br><span class="line"> subClass.prototype = prototype;</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2020/03/01/js继承方式总结/" data-id="ck7an8uuj0003bkuogwwb6qt9" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-前端性能优化总结" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2020/02/27/前端性能优化总结/" class="article-date">
<time datetime="2020-02-27T00:51:51.000Z" itemprop="datePublished">2020-02-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/02/27/前端性能优化总结/">前端性能优化总结'</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>前端性能优化也是一个老生常谈的问题了,前端性能优化的方法有很多种,之前在项目中一直都是遇到问题了再对症下药,一直也没对前端优化的方案进行总结,这篇文章就是对自己所了解的优化知识做一个总结和巩固。</p>
<h2 id="加载优化"><a href="#加载优化" class="headerlink" title="加载优化"></a>加载优化</h2><ol>
<li><p>请求包优化<br>对代码进行压缩,减少加载时间:<br>1)html压缩: 将注释、空格和新行等从代码中删除,减少HTML大小,加快网站页面加载时间<br>2)css压缩:将注释、空格和新行等从代码中删除<br>3)图片压缩: 可参考后面图片相关优化<br>4)使用gzip压缩传输内容</p>
</li>
<li><p>合理减少请求Header字段和cookie大小以减小http请求的大小</p>
</li>
<li><p>MTU策略<br>MTU即最大传输单元(Maximum Transmission Unit), TCP的MTU一般认为是1500B,那么可以合理控制请求内容在一个MTU内。</p>
</li>
</ol>
<h2 id="缓存"><a href="#缓存" class="headerlink" title="缓存"></a>缓存</h2><p>缓存技术相当于用空间换取时间,它可以非常有效地提升性能。对于前端来说,主要接触到的是浏览器的缓存,当浏览器请求一个网站的时候,会加载html、css、js、图片等各种资源,其中有些资源是不会经常变化的,对于这些资源浏览器会将其保存在本地缓存中,下次再访问这些资源时,直接中缓存中取出资源,比向服务器发起请求更快,并且可以减少服务器的负载。</p>
<p>HTTP报文中与缓存相关的字段主要有:</p>
<ul>
<li><p>Cache-Control: 在Cache-Contro中可设置max-age(最大缓存时间)、public(缓存能够被用户共享)、private、no-cache(资源不进行缓存,但在缓存前要向服务器确认资源是否被更改)、no-store(绝对禁止缓存)</p>
</li>
<li><p>Last-Modified: 资源的最后修改时间,在响应头中返回。浏览器收到带Last-Modified的响应头后,下次发送请求就会带上If-Modified-Since或者If-Unmodified-Since,服务器收到这个request的If-Modified-Since后,通过读取它的值对比资源存在的地方的Last-Modified,服务器就告诉浏览器是否可以使用缓存。</p>
</li>
<li><p>Etag: 对于Last-Modified来说可能会出现同一秒中的修改或者服务器上文件修改的时间不一致这样的问题,Etag是一个相对更为严格的验证,它是根据文件的内容生成Etag(数据签名,最常用做法是对资源内容进行哈希计算),收到带Etag这个头,下次浏览器发送请求就会带上If-Match或者If-Non-Match。 If-Match表示只有当请求的资源Etag匹配的情况下服务器才返回资源, If-None-Match则反之。 服务器收到这个请求的If-Match或者If-Non-Match后,通过读取它的值对比资源存在的地方的Etag,服务器就告诉浏览器是否可以使用缓存。</p>
</li>
<li><p>If-Match/If-None-Match: 参考Etag</p>
</li>
<li><p>If-Modified-Since: 只有当资源在指定的时间之后进行过修改的情况下,服务器才会返回请求的资源</p>
</li>
<li><p>If-Unmodified-Since:只有当资源在指定的时间之后没有进行过修改的情况下,服务器才会返回请求的资源</p>
</li>
<li><p>Expires: 过期时间</p>
</li>
</ul>
<p>对于前端缓存,可以使用Service Worker来设置缓存策略。如图片等不太变化的资源可以使用缓存优先的策略,js\css如果有hash的话可以使用stalewhileRevalidate的缓存策略。</p>
<h2 id="图片相关优化"><a href="#图片相关优化" class="headerlink" title="图片相关优化"></a>图片相关优化</h2><p>在前端一个页面可能会同时展示大量的图片,图片越多请求的次数越多,造成的页面延迟可能就越高。图片优化是前端性能优化中重要的一项工作,针对图片优化有以下几种方案:</p>
<ol>
<li><p>减小图片体积<br>利用图片压缩工具压缩图片的体积,如使用tinypng,pngquant等。需要注意的是jpg图片不能多次压缩。</p>
</li>
<li><p>图片懒加载<br>图片的懒加载一种是图片列表滚动加载,当图片滚动到其即将要显示的位置时才加载。方法是监听滚动条判断图片是否即将可见,当图片即将可见时再发起http请求获取图片。</p>
</li>
<li><p>使用字体图库代替图片<br>如使用font-awesome等字体图库代替图标,或者可以用css制作一些简单的图标代替图片图标。</p>
</li>
<li><p>使用雪碧图<br>将多张图片合成一张大图,加载图片时通过坐标定位的方式从大图中取到需要的图片。这样原本需要请求多张图片现在只需要请求一次即可。</p>
</li>
<li><p>使用svg/canvas绘制图片</p>
</li>
</ol>
<h2 id="DOM相关优化"><a href="#DOM相关优化" class="headerlink" title="DOM相关优化"></a>DOM相关优化</h2><p>在js中对DOM进行操作可能会非常的耗费性能,例如修改DOM元素的颜色造成repaint,修改元素大小、位置或添加可见的DOM元素等导致reflow,以及对DOM的访问、事件处理等都会影响网页的性能。与DOM相关的优化主要有以下几种:</p>
<ol>
<li><p>减少repaint和reflow<br>1)合并样式改变,减少repaint和reflow,如使用cssText或者使用className来对多次样式修改进行合并,以减少浏览器的重新渲染<br>2)在需要对DOM进行一系列操作的时候,可以使DOM先脱离文档流,给其添加完相应的改变之后,再添加到文档流中。例如,先隐藏需要进行操作的愿随,使用DocumentFragment存储这些元素,在 DocumentFragment中对DOM进行修改后,再替换原本的元素。</p>
</li>
<li><p>事件委托<br>事件委托是指将子元素上的事件绑定在父元素上,利用事件冒泡机制,在父元素绑定的监听函数中进行事件的处理。在有很多元素都需要进行事件监听的情况下,给每个元素都分别绑定监听函数的话,对内存比较浪费。利用事件委托的话,可以减少内存的浪费,并且在对监听函数进行解绑的时候,处理起来比较方便。</p>
</li>
<li><p>DOM的修改<br>需要对同一个DOM进行修改时,可以合并统一属性的修改,减少浏览器重新计算导致页面的重新渲染</p>
</li>
<li><p>DOM的访问<br>需要多次使用同一个DOM元素或其属性时,可以使用一个变量保存它。如遍历一个DOM list时,将其length属性保存在循环外面。此外,使用querySelector比getElementById和getElementsByTagName查找元素会快一些。</p>
</li>
</ol>
<h2 id="运行性能"><a href="#运行性能" class="headerlink" title="运行性能"></a>运行性能</h2><ol>
<li><p>防抖&节流<br>在输入、窗口resize、滚动等会发生高频触发事件的场景中,如果每次事件触发都执行一次事件,则高频地执行事件可能会导致页面卡顿、浏览器崩溃等情况出现。使用防抖/节流可以降低事件函数的执行频率或者将事件函数的多次调用压缩成一次,提高运行性能。<br>具体参考之前的博文: <a href="https://aliceyuling.github.io/2019/06/13/节流和防抖函数/" target="_blank" rel="noopener">https://aliceyuling.github.io/2019/06/13/节流和防抖函数/</a></p>
</li>
<li><p>动画性能<br>一般设备屏幕的刷新频率为 60次/秒,每帧运行时间约为1s/60=16.66ms, 因此动画代码的运行时间需要控制在10ms以内才能看起来流畅。</p>
</li>
</ol>
<p>## 使用CDN</p>
<p> 将内容放在各地的边缘服务器,用户请求时从最近的服务器获取内容,提高用户访问网站的速度。如jQuery等一类的库文件,可以使用CDN进行加载。</p>
<h2 id="协议性能"><a href="#协议性能" class="headerlink" title="协议性能"></a>协议性能</h2><p>HTTP1.x存在连接无法复用、队头阻塞、协议头开销大的问题,而http2.0连接能够复用以及header压缩,能够极大地提高传输性能。</p>
<p>客户端也可以使用支持压缩的Accept-Encoding请求头: <code>Accept-Encoding: gzip, deflate</code>, 服务器接收到这个请求头,会使用客户端列出的一种方式来压缩响应。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2020/02/27/前端性能优化总结/" data-id="ck7an8uux000cbkuolntb9qds" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-浏览器知识总结" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2020/02/15/浏览器知识总结/" class="article-date">
<time datetime="2020-02-15T11:26:42.000Z" itemprop="datePublished">2020-02-15</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2020/02/15/浏览器知识总结/">浏览器知识总结</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<h1 id="1-浏览器简介"><a href="#1-浏览器简介" class="headerlink" title="1 浏览器简介"></a>1 浏览器简介</h1><p>浏览器也就是用来显示在万维网或局域网等内的文字、图像及其他信息的软件,它还可以让用户与这些文件进行交互操作</p>
<h2 id="1-1-浏览器结构"><a href="#1-1-浏览器结构" class="headerlink" title="1.1 浏览器结构"></a>1.1 浏览器结构</h2><p>1) 用户界面: 主要包括工具栏、地址栏、前进/后退按钮、书签菜单、可视化页面加载进度、智能下载处理、首选项、打印等。<br>2)浏览器内核:通常所谓的浏览器内核也就是浏览器所采用的渲染引擎,渲染引擎决定了浏览器如何显示网页的内容以及页面的格式信息<br>内核分类:<br>通用浏览器: Trident(IE)、Gecko(Mozilla Firefox)、Webkit(Safari)、Blink(Chrome28及往后版本、Opera 15及往后版本和Yandex浏览器)<br>专用浏览器: Andriod WebView、iOS WebView、WeChat WebView</p>
<h2 id="1-2-浏览器渲染页面过程"><a href="#1-2-浏览器渲染页面过程" class="headerlink" title="1.2 浏览器渲染页面过程"></a>1.2 浏览器渲染页面过程</h2><p> 1)Html经过HTMLDocumentParser后形成DOM树(Document Object Model),DOM 中每个元素具有属性, innerText等,DOM是为了让浏览器理解html方便渲染,同时也是给Javascript暴露API接口的一个基本单元</p>
<p> 2)解析css: 通过选择器,根据DOM的结构生成style tree</p>
<p> 3) Layout/reflow: 浏览器计算所有元素的位置、尺寸<br> 4)绘制/repaint:绘制元素像素信息<br> 5)浏览器将各层的信息发送给GPU,GPU将各层合成,显示在屏幕上。</p>
<p>与前端相关的关键渲染路径:<br>让网页尽快加载出来<br>减少浏览器reflow、repaint</p>
<h1 id="2-浏览器本地存储"><a href="#2-浏览器本地存储" class="headerlink" title="2. 浏览器本地存储"></a>2. 浏览器本地存储</h1><h2 id="2-1-Cookie"><a href="#2-1-Cookie" class="headerlink" title="2.1 Cookie"></a>2.1 Cookie</h2><p>Cookie是一小段的文本信息(key-value格式)。客户端向服务器发起请求,如果服务器需要记录该用户状态,就使用response向客户端浏览器颁发一个Cookie。</p>
<p>Cookie机制</p>
<p>Cookie各项属性值:<br> Expires: 过期时间<br> Domain: 生成该 Cookie 的域名<br> Path: 该 Cookie 是在当前的哪个路径下生成的,如 path=/wp-admin/<br> Secure: 如果设置了这个属性,那么只会在 SSH 连接时才会回传该 Cookie</p>
<p>Cookie应用场景:带参数,一般不用于存数据</p>
<h2 id="2-2-WebStorage"><a href="#2-2-WebStorage" class="headerlink" title="2.2 WebStorage"></a>2.2 WebStorage</h2><ul>
<li><p>1.sessionStorage:将数据保存在session对象中。所谓session,是指用户在浏览某个网站时,从进入网站到浏览器关闭所经过的这段时间,也就是用户浏览这个网站所花费的时间。session对象可以用来保存在这段时间内所要求保存的任何数据。</p>
</li>
<li><p>2.localStorage:将数据保存在客户端本地的硬件设备(通常指硬盘,也可以是其他硬件设备)中,即使浏览器被关闭了,该数据仍然存在,下次打开浏览器访问网站时仍然可以继续使用。</p>
</li>
</ul>
<h2 id="2-3-IndexedDB-amp-WebSQL"><a href="#2-3-IndexedDB-amp-WebSQL" class="headerlink" title="2.3 IndexedDB & WebSQL"></a>2.3 IndexedDB & WebSQL</h2><p>WebSQL: 已废弃<br>IndexedDB: 可用于一次性把很多数据从后端取到存在前端,后面的计算不用每次请求后端,直接从前端取数据进行计算的场景</p>
<h1 id="3-Web-Worker-amp-Service-Worker"><a href="#3-Web-Worker-amp-Service-Worker" class="headerlink" title="3 Web Worker & Service Worker"></a>3 Web Worker & Service Worker</h1><h2 id="3-1-Web-Worker"><a href="#3-1-Web-Worker" class="headerlink" title="3.1 Web Worker"></a>3.1 Web Worker</h2><p>Web Worker是运行在后台的 JavaScript,独立于其他脚本,不会影响页面的性能。设计目的是为了拉一条独立的线程出来,以便处理一些比较大的计算量等</p>
<h2 id="3-2-Service-Worker"><a href="#3-2-Service-Worker" class="headerlink" title="3.2 Service Worker"></a>3.2 Service Worker</h2><p>Service Worker 可以做和Web Worker一样的事情,但是Service Worker是为缓存而设计的。<br>参考:淘宝网的service worker,其使用了google官方的工具workbox定义其缓存策略:</p>
<h1 id="4-浏览器网络"><a href="#4-浏览器网络" class="headerlink" title="4 浏览器网络"></a>4 浏览器网络</h1><h2 id="4-1-http协议"><a href="#4-1-http协议" class="headerlink" title="4.1 http协议"></a>4.1 http协议</h2><p>http协议是基于TCP/IP的通讯协议,http请求与响应的结构如下:<br>http请求:</p>
<ul>
<li>schema://域名:端口/路径?查询#xx</li>
<li>方法</li>
<li>协议</li>
<li>消息体</li>
<li>头信息</li>
</ul>
<ul>
<li>缓存控制<ul>
<li>cookie</li>
<li>User Agent</li>
<li>来源</li>
<li>其他<br>http响应:</li>
</ul>
</li>
</ul>
<ul>
<li>状态码</li>
<li>消息体</li>
<li>头信息</li>
<li>缓存控制</li>
<li>set-cookie</li>
<li>其他</li>
</ul>
<p>Http2.0相较于Http1.1的区别:<br>HTTP1.1:</p>
<ul>
<li>一次请求-响应,建立一个连接,用完关闭;每一个请求都要建立一个连接</li>
</ul>
<p>HTTP2.0: </p>
<pre><code>- 一次请求多次响应:多个请求可同时在一个连接上并行执行。某个请求任务耗时严重,不会影响到其它连接的正常执行,由此优化了性能
</code></pre><ul>
<li>服务端推送:把客户端所需要的资源伴随着index.html一起发送到客户端,省去了客户端重复请求的步骤。正因为没有发起请求,建立连接等操作,所以静态资源通过服务端推送的方式可以极大地提升速度。</li>
<li>合并多次请求的头信息</li>
</ul>
<h2 id="4-2-浏览器网络安全相关"><a href="#4-2-浏览器网络安全相关" class="headerlink" title="4.2 浏览器网络安全相关"></a>4.2 浏览器网络安全相关</h2><ol>
<li><p>运营商劫持<br>分为 1)DNS劫持:通过攻击域名解析服务器(DNS),或伪造域名解析服务器(DNS)的方法,把目标网站域名解析到错误的地址从而实现用户无法访问目标网站的目的。主要预防方法是使用固定的DNS地址<br>2)HTTP劫持:在运营商的路由器节点上,设置协议检测,一旦发现是HTTP请求,而且是html类型请求,则拦截处理。后续做法往往分为2种,1种是类似DNS劫持返回302让用户浏览器跳转到另外的地址,还有1种是在服务器返回的HTML数据中插入js或dom节点(广告)。</p>
</li>
<li><p>MiM中间人攻击<br>MiM即Man in the Middle,中间人攻击。攻击者在请求和响应传输途中,拦截并篡改内容。</p>
</li>
</ol>
<h1 id="5-跨域问题"><a href="#5-跨域问题" class="headerlink" title="5 跨域问题"></a>5 跨域问题</h1><h2 id="5-1-同源策略"><a href="#5-1-同源策略" class="headerlink" title="5.1 同源策略"></a>5.1 同源策略</h2><p>同源,指的是协议,域名,端口相同。浏览器处于安全方面的考虑,只允许本域名下的接口交互,不同源的客户端脚本,在没有明确授权的情况下,不能读写对方的资源。</p>
<h2 id="5-2-跨域方案"><a href="#5-2-跨域方案" class="headerlink" title="5.2 跨域方案"></a>5.2 跨域方案</h2><ol>
<li><p>JSONP</p>
<p>利用script标签可以跨域的能力,web客户端通过与调用脚本一模一样的方式,来调用跨域服务器上动态生成的js格式文件,客户端在对JSON文件调用成功之后,也就获得了自己所需的数据。为了便于客户端使用数据,逐渐形成了一种非正式传输协议,称作JSONP,该协议的一个要点就是允许用户传递一个callback参数给服务端,然后服务端返回数据时会将这个callback参数作为函数名来包裹住JSON数据,这样客户端就可以随意定制自己的函数来自动处理返回数据了。<br>JSONP存在的问题:</p>
<ul>
<li>只支持GET</li>
<li>风险代码注入</li>
</ul>
</li>
<li>CORS</li>
</ol>
<p>使用额外的请求头Access-Allow-Origin-Control来告诉浏览器让运行在一个origin上的Web应用被准许访问来自不同源服务器上的指定的资源。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2020/02/15/浏览器知识总结/" data-id="ck7an8uv3000gbkuo0hzxsavm" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-Canvas绘制图形" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/09/27/Canvas绘制图形/" class="article-date">
<time datetime="2019-09-27T06:57:37.000Z" itemprop="datePublished">2019-09-27</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/09/27/Canvas绘制图形/">Canvas绘制图形</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>最近几天研究了一下使用 Canvas 在图片上划分出一块区域的功能,具体需求是在图片上圈出一块区域并重新生成图片。实现如下;</p>
<p>html 部分,因为要实现橡皮的功能,所以这里使用了两层 canvas,底层 canvas 绘制原图片,上层 canvas 用于画笔绘制和擦除:</p>
<figure class="highlight javascript"><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"><div id=<span class="string">"container"</span> style=<span class="string">"position: relative"</span>></span><br><span class="line"> <canvas id=<span class="string">"oriCanvas"</span> width=<span class="string">"1000"</span> height=<span class="string">"1000"</span> style=<span class="string">"position: relative; left:0; top:0;"</span>></canvas></span><br><span class="line"> <canvas id=<span class="string">"maskCanvas"</span> width=<span class="string">"1000"</span> height=<span class="string">"1000"</span> style=<span class="string">"position: absolute;left:0;top:0;z-index:99;"</span>></canvas></span><br><span class="line"> <div <span class="class"><span class="keyword">class</span></span>=<span class="string">"toolbar"</span>></span><br><span class="line"> <button id=<span class="string">"eraser"</span>>擦除<<span class="regexp">/button></span></span><br><span class="line"><span class="regexp"> <button id="pen">绘制</</span>button></span><br><span class="line"> <button id=<span class="string">"gen"</span>>生成<<span class="regexp">/button></span></span><br><span class="line"><span class="regexp"> </</span>div></span><br><span class="line"><<span class="regexp">/div></span></span><br></pre></td></tr></table></figure>
<p>首先, 进行原图像的绘制, 为了避免在图片未加载完成时绘制图片无效,在 Image 的 onload 事件中进行图片的绘制:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> oriCanvas = <span class="built_in">document</span>.getElementById(<span class="string">'oriCanvas'</span>);</span><br><span class="line"><span class="keyword">var</span> maskCanvas = <span class="built_in">document</span>.getElementById(<span class="string">'maskCanvas'</span>);</span><br><span class="line"><span class="keyword">var</span> oriCtx = oriCanvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line"><span class="keyword">var</span> maskCtx = maskCanvas.getContext(<span class="string">'2d'</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">loadImage</span>(<span class="params"></span>) </span>{</span><br><span class="line"> img = <span class="keyword">new</span> Image();</span><br><span class="line"> img.onload = <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> isImgLoaded = <span class="literal">true</span>;</span><br><span class="line"> oriCanvas.width = <span class="keyword">this</span>.width;</span><br><span class="line"> oriCanvas.height = <span class="keyword">this</span>.height;</span><br><span class="line"> maskCanvas.width = <span class="keyword">this</span>.width;</span><br><span class="line"> maskCanvas.height = <span class="keyword">this</span>.height;</span><br><span class="line"> drawImage();</span><br><span class="line"> };</span><br><span class="line"> img.src = <span class="string">"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1569233132184&di=a1a45444230d083a872aff5db08ec216&imgtype=0&src=http%3A%2F%2Fphotocdn.sohu.com%2F20090224%2FImg262436027.jpg"</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">drawImage</span>(<span class="params"></span>) </span>{</span><br><span class="line"> oriCtx.drawImage(img, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在按下鼠标时,记下当前鼠标所在位置,并标记当前是正在绘制中/擦除中:</p>
<figure class="highlight javascript"><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">maskCanvas.onmousedown = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (isPenSelected) {</span><br><span class="line"> isPainting = <span class="literal">true</span>;</span><br><span class="line"> startX = e.pageX - maskCanvas.offsetLeft;</span><br><span class="line"> startY = e.pageY - maskCanvas.offsetTop;</span><br><span class="line"> prevX = startX;</span><br><span class="line"> prevY = startY;</span><br><span class="line"> maskCtx.beginPath();</span><br><span class="line"> maskCtx.moveTo(startX, startY);</span><br><span class="line"> maskCtx.lineWidth = <span class="number">1</span>;</span><br><span class="line"> maskCtx.strokeStyle = <span class="string">'#fff'</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> isClearing = <span class="literal">true</span>;</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure>
<p>鼠标移动时,计算出当前坐标,并根据当前的操作进行绘制/擦除。绘制时将当前鼠标所在点与上一个点直接进行连接,擦除使用 clearRect 方法:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line">maskCanvas.onmousemove = <span class="function"><span class="keyword">function</span>(<span class="params">e</span>) </span>{</span><br><span class="line"> curX = e.pageX - maskCanvas.offsetLeft;</span><br><span class="line"> curY = e.pageY - maskCanvas.offsetTop;</span><br><span class="line"> <span class="keyword">if</span> (isPainting) {</span><br><span class="line"> drawPath(curX, curY);</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (isClearing) {</span><br><span class="line"> clear(curX, curY);</span><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">drawPath</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> maskCtx.lineTo(x, y);</span><br><span class="line"> maskCtx.stroke();</span><br><span class="line"> prevX = x;</span><br><span class="line"> prevY = y;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">clear</span>(<span class="params">x, y</span>) </span>{</span><br><span class="line"> maskCtx.clearRect(x, y, <span class="number">8</span>, <span class="number">8</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>最后,点击”生成”按钮,将两层canvas合并并导出图片:</p>
<figure class="highlight javascript"><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">gen.addEventListener(<span class="string">'click'</span>, <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> oriCtx.drawImage(maskCanvas, <span class="number">0</span>, <span class="number">0</span>);</span><br><span class="line"> <span class="keyword">var</span> resImg = oriCanvas.toDataURL();</span><br><span class="line"> <span class="keyword">var</span> resEl = <span class="built_in">document</span>.createElement(<span class="string">'img'</span>);</span><br><span class="line"> resEl.src = resImg;</span><br><span class="line"> <span class="built_in">document</span>.getElementById(<span class="string">'container'</span>).append(resEl);</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/09/27/Canvas绘制图形/" data-id="ck7an8uuf0001bkuoons50m8d" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-Vue源码学习之三" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/09/06/Vue源码学习之三/" class="article-date">
<time datetime="2019-09-06T08:50:35.000Z" itemprop="datePublished">2019-09-06</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/09/06/Vue源码学习之三/">Vue源码学习之三</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>Vue实例的初始化中stateMixin以及数据双向绑定部分在本次博客中学习。</p>
<p>来到 <code>src/core/instance/state.js</code> 文件中,这里主要对 Vue 实例选项的 data\watch\computed\props\methods 属性做了初始化:</p>
<ol>
<li>props</li>
</ol>
<figure class="highlight javascript"><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><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">initProps</span> (<span class="params">vm: Component, propsOptions: Object</span>) </span>{</span><br><span class="line"> <span class="comment">// 在使用new 创建实例时,使用propsData属性</span></span><br><span class="line"> <span class="keyword">const</span> propsData = vm.$options.propsData || {}</span><br><span class="line"> <span class="keyword">const</span> props = vm._props = {}</span><br><span class="line"> <span class="comment">// cache prop keys so that future props updates can iterate using Array</span></span><br><span class="line"> <span class="comment">// instead of dynamic object key enumeration.</span></span><br><span class="line"> <span class="keyword">const</span> keys = vm.$options._propKeys = []</span><br><span class="line"> <span class="keyword">const</span> isRoot = !vm.$parent</span><br><span class="line"> <span class="comment">// root instance props should be converted</span></span><br><span class="line"> <span class="comment">// 如果没有父组件,则将shouldObserve置为false</span></span><br><span class="line"> <span class="keyword">if</span> (!isRoot) {</span><br><span class="line"> toggleObserving(<span class="literal">false</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> propsOptions) {</span><br><span class="line"> keys.push(key)</span><br><span class="line"> <span class="comment">// validateProp设置默认值</span></span><br><span class="line"> <span class="comment">// 当default为undefined时,需要去observe</span></span><br><span class="line"> <span class="keyword">const</span> value = validateProp(key, propsOptions, propsData, vm)</span><br><span class="line"> <span class="comment">/* istanbul ignore else */</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) {</span><br><span class="line"> <span class="comment">// hyphenate将驼峰转换为-格式</span></span><br><span class="line"> <span class="keyword">const</span> hyphenatedKey = hyphenate(key)</span><br><span class="line"> <span class="comment">// isReservedAttribute检查是否是保留字(key,ref,slot,slot-scope,is)</span></span><br><span class="line"> <span class="comment">// config.isReservedAttr: 检查是否是boolean</span></span><br><span class="line"> <span class="keyword">if</span> (isReservedAttribute(hyphenatedKey) ||</span><br><span class="line"> config.isReservedAttr(hyphenatedKey)) {</span><br><span class="line"> warn(</span><br><span class="line"> <span class="string">`"<span class="subst">${hyphenatedKey}</span>" is a reserved attribute and cannot be used as component prop.`</span>,</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> defineReactive(props, key, value, () => {</span><br><span class="line"> <span class="keyword">if</span> (!isRoot && !isUpdatingChildComponent) {</span><br><span class="line"> warn(</span><br><span class="line"> <span class="string">`Avoid mutating a prop directly since the value will be `</span> +</span><br><span class="line"> <span class="string">`overwritten whenever the parent component re-renders. `</span> +</span><br><span class="line"> <span class="string">`Instead, use a data or computed property based on the prop's `</span> +</span><br><span class="line"> <span class="string">`value. Prop being mutated: "<span class="subst">${key}</span>"`</span>,</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> defineReactive(props, key, value)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// static props are already proxied on the component's prototype</span></span><br><span class="line"> <span class="comment">// during Vue.extend(). We only need to proxy props defined at</span></span><br><span class="line"> <span class="comment">// instantiation here.</span></span><br><span class="line"> <span class="keyword">if</span> (!(key <span class="keyword">in</span> vm)) {</span><br><span class="line"> proxy(vm, <span class="string">`_props`</span>, key)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> toggleObserving(<span class="literal">true</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>上面源码中就涉及到了Vue中的observer模式, 在Observer对象中绑定了getter/setter方法去收集依赖、监听变化并进行数据更新。</p>
<p>来到<code>src/core/observer/index/js</code>中看下Observer是如何定义的, 首先是Observer定义:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Observer class that is attached to each observed</span></span><br><span class="line"><span class="comment"> * object. Once attached, the observer converts the target</span></span><br><span class="line"><span class="comment"> * object's property keys into getter/setters that</span></span><br><span class="line"><span class="comment"> * collect dependencies and dispatch updates.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="class"><span class="keyword">class</span> <span class="title">Observer</span> </span>{</span><br><span class="line"> value: any;</span><br><span class="line"> dep: Dep;</span><br><span class="line"> vmCount: number; <span class="comment">// number of vms that have this object as root $data</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">constructor</span> (value: any) {</span><br><span class="line"> <span class="keyword">this</span>.value = value</span><br><span class="line"> <span class="keyword">this</span>.dep = <span class="keyword">new</span> Dep()</span><br><span class="line"> <span class="keyword">this</span>.vmCount = <span class="number">0</span></span><br><span class="line"> <span class="comment">// def即封装的defineProperty,可写可配置</span></span><br><span class="line"> def(value, <span class="string">'__ob__'</span>, <span class="keyword">this</span>)</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(value)) {</span><br><span class="line"> <span class="comment">// hasProto: 能否使用__proto__</span></span><br><span class="line"> <span class="comment">// protoAgrument: 为对象的__proto__赋值</span></span><br><span class="line"> <span class="comment">// arrayMethods: 数组的方法</span></span><br><span class="line"> <span class="keyword">if</span> (hasProto) {</span><br><span class="line"> protoAugment(value, arrayMethods)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 将数组方法按键值对形式赋给value,即value[arrayKey] = arrayMethod</span></span><br><span class="line"> copyAugment(value, arrayMethods, arrayKeys)</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 对于value中每个元素,都去执行observe(value[i])</span></span><br><span class="line"> <span class="keyword">this</span>.observeArray(value)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 如果value不是数组的话,对value中每个属性执行defineReactive()</span></span><br><span class="line"> <span class="keyword">this</span>.walk(value)</span><br><span class="line"> }</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>其中<code>observe</code>方法定义如下,它主要做的事就是去构造Observer实例:<br><figure class="highlight javascript"><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="comment">/**</span></span><br><span class="line"><span class="comment"> * Attempt to create an observer instance for a value,</span></span><br><span class="line"><span class="comment"> * returns the new observer if successfully observed,</span></span><br><span class="line"><span class="comment"> * or the existing observer if the value already has one.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="comment">// 如果存在 __ob__这个属性,说明构造函数的参数已经是一个Observer对象了,直接返回value.__ob__</span></span><br><span class="line"> <span class="comment">// 否则构造一个新的Observer对象</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">observe</span> (<span class="params">value: any, asRootData: ?boolean</span>): <span class="title">Observer</span> | <span class="title">void</span> </span>{</span><br><span class="line"> <span class="keyword">if</span> (!isObject(value) || value <span class="keyword">instanceof</span> VNode) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">let</span> ob: Observer | <span class="keyword">void</span></span><br><span class="line"> <span class="keyword">if</span> (hasOwn(value, <span class="string">'__ob__'</span>) && value.__ob__ <span class="keyword">instanceof</span> Observer) {</span><br><span class="line"> ob = value.__ob__</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (</span><br><span class="line"> shouldObserve &&</span><br><span class="line"> !isServerRendering() &&</span><br><span class="line"> (<span class="built_in">Array</span>.isArray(value) || isPlainObject(value)) &&</span><br><span class="line"> <span class="built_in">Object</span>.isExtensible(value) &&</span><br><span class="line"> !value._isVue</span><br><span class="line"> ) {</span><br><span class="line"> ob = <span class="keyword">new</span> Observer(value)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (asRootData && ob) {</span><br><span class="line"> ob.vmCount++</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ob</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p><code>Observer</code>中的属性 <code>dep</code> 是一个 <code>Dep</code> 实例,<code>Dep</code> 是一个observable, 可以有多个指令去订阅它,其定义如下:</p>
<figure class="highlight javascript"><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></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">Dep</span> </span>{</span><br><span class="line"> <span class="keyword">static</span> target: ?Watcher;</span><br><span class="line"> id: number;</span><br><span class="line"> subs: <span class="built_in">Array</span><Watcher>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">constructor</span> () {</span><br><span class="line"> <span class="keyword">this</span>.id = uid++</span><br><span class="line"> <span class="keyword">this</span>.subs = []</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> addSub (sub: Watcher) {</span><br><span class="line"> <span class="keyword">this</span>.subs.push(sub)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> removeSub (sub: Watcher) {</span><br><span class="line"> remove(<span class="keyword">this</span>.subs, sub)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> depend () {</span><br><span class="line"> <span class="keyword">if</span> (Dep.target) {</span><br><span class="line"> Dep.target.addDep(<span class="keyword">this</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> notify () {</span><br><span class="line"> <span class="comment">// stabilize the subscriber list first</span></span><br><span class="line"> <span class="keyword">const</span> subs = <span class="keyword">this</span>.subs.slice()</span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && !config.async) {</span><br><span class="line"> <span class="comment">// subs aren't sorted in scheduler if not running async</span></span><br><span class="line"> <span class="comment">// we need to sort them now to make sure they fire in correct</span></span><br><span class="line"> <span class="comment">// order</span></span><br><span class="line"> subs.sort(<span class="function">(<span class="params">a, b</span>) =></span> a.id - b.id)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>, l = subs.length; i < l; i++) {</span><br><span class="line"> subs[i].update()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>再来看看 <code>defineReactive</code>,它主要用来给对象定义响应式的属性:</p>
<figure class="highlight javascript"><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><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Define a reactive property on an Object.</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">defineReactive</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params"> obj: Object,</span></span></span><br><span class="line"><span class="function"><span class="params"> key: string,</span></span></span><br><span class="line"><span class="function"><span class="params"> val: any,</span></span></span><br><span class="line"><span class="function"><span class="params"> customSetter?: ?Function,</span></span></span><br><span class="line"><span class="function"><span class="params"> shallow?: boolean</span></span></span><br><span class="line"><span class="function"><span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> dep = <span class="keyword">new</span> Dep()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> property = <span class="built_in">Object</span>.getOwnPropertyDescriptor(obj, key)</span><br><span class="line"> <span class="keyword">if</span> (property && property.configurable === <span class="literal">false</span>) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// cater for pre-defined getter/setters</span></span><br><span class="line"> <span class="keyword">const</span> getter = property && property.get</span><br><span class="line"> <span class="keyword">const</span> setter = property && property.set</span><br><span class="line"> <span class="keyword">if</span> ((!getter || setter) && <span class="built_in">arguments</span>.length === <span class="number">2</span>) {</span><br><span class="line"> val = obj[key]</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> childOb = !shallow && observe(val)</span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(obj, key, {</span><br><span class="line"> enumerable: <span class="literal">true</span>,</span><br><span class="line"> configurable: <span class="literal">true</span>,</span><br><span class="line"> <span class="keyword">get</span>: function reactiveGetter () {</span><br><span class="line"> <span class="keyword">const</span> value = getter ? getter.call(obj) : val</span><br><span class="line"> <span class="keyword">if</span> (Dep.target) {</span><br><span class="line"> dep.depend()</span><br><span class="line"> <span class="keyword">if</span> (childOb) {</span><br><span class="line"> childOb.dep.depend()</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Array</span>.isArray(value)) {</span><br><span class="line"> dependArray(value)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> value</span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">set</span>: function reactiveSetter (newVal) {</span><br><span class="line"> <span class="keyword">const</span> value = getter ? getter.call(obj) : val</span><br><span class="line"> <span class="comment">/* eslint-disable no-self-compare */</span></span><br><span class="line"> <span class="keyword">if</span> (newVal === value || (newVal !== newVal && value !== value)) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* eslint-enable no-self-compare */</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && customSetter) {</span><br><span class="line"> customSetter()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// #7981: for accessor properties without setter</span></span><br><span class="line"> <span class="keyword">if</span> (getter && !setter) <span class="keyword">return</span></span><br><span class="line"> <span class="keyword">if</span> (setter) {</span><br><span class="line"> setter.call(obj, newVal)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> val = newVal</span><br><span class="line"> }</span><br><span class="line"> childOb = !shallow && observe(newVal)</span><br><span class="line"> dep.notify()</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>Reactive 就是一种可观测的数据模型,defineReactive中主要用 get和set去拦截数据的读取与变化。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/09/06/Vue源码学习之三/" data-id="ck7an8uum0005bkuo2pkgr498" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-Vue源码中知识点总结" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/09/06/Vue源码中知识点总结/" class="article-date">
<time datetime="2019-09-06T03:10:35.000Z" itemprop="datePublished">2019-09-06</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/09/06/Vue源码中知识点总结/">Vue源码中知识点总结</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<ol>
<li>native code</li>
</ol>
<figure class="highlight javascript"><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="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">isNative</span> (<span class="params">Ctor: any</span>): <span class="title">boolean</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">typeof</span> Ctor === <span class="string">'function'</span> && <span class="regexp">/native code/</span>.test(Ctor.toString())</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol start="2">
<li>Plain Object</li>
</ol>
<p>简单对象:通过{} 或 new() 创建的对象<br><figure class="highlight javascript"><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="comment">// 判断是否位简单对象</span></span><br><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">isPlainObject</span> (<span class="params">obj: any</span>): <span class="title">boolean</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> _toString.call(obj) === <span class="string">'[object Object]'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<ol start="3">
<li><p>Object.isfrozon<br>判断一个对象是否被冻结。一个对象是冻结的是指它不可扩展,所有属性都是不可配置的,且所有数据属性(即没有getter或setter组件的访问器的属性)都是不可写的。</p>
</li>
<li><p>makeMap</p>
</li>
</ol>
<p>这个方法用于生成一个map并返回一个函数用于判断一个值是否在这个map中</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">makeMap</span> (<span class="params"></span></span></span><br><span class="line"><span class="function"><span class="params"> str: string,</span></span></span><br><span class="line"><span class="function"><span class="params"> expectsLowerCase?: boolean</span></span></span><br><span class="line"><span class="function"><span class="params"></span>): (<span class="params">key: string</span>) => <span class="title">true</span> | <span class="title">void</span> </span>{</span><br><span class="line"> <span class="keyword">const</span> map = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line"> <span class="keyword">const</span> list: <span class="built_in">Array</span><string> = str.split(<span class="string">','</span>)</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < list.length; i++) {</span><br><span class="line"> map[list[i]] = <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> expectsLowerCase</span><br><span class="line"> ? <span class="function"><span class="params">val</span> =></span> map[val.toLowerCase()]</span><br><span class="line"> : <span class="function"><span class="params">val</span> =></span> map[val]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol start="5">
<li>检测浏览器是否支持passive特性</li>
</ol>
<p>关于 passive:<br> <a href="https://www.cnblogs.com/ziyunfei/p/5545439.html" target="_blank" rel="noopener">https://www.cnblogs.com/ziyunfei/p/5545439.html</a><br> <a href="https://blog.csdn.net/dj0379/article/details/52883315" target="_blank" rel="noopener">https://blog.csdn.net/dj0379/article/details/52883315</a></p>
<p>检测方法:</p>
<figure class="highlight javascript"><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="keyword">export</span> <span class="keyword">let</span> supportsPassive = <span class="literal">false</span> <span class="comment">// 全局属性</span></span><br><span class="line"><span class="keyword">if</span> (inBrowser) {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> opts = {}</span><br><span class="line"> <span class="comment">// defineProperty()方法在属性名不存在时会给对象创建属性名,执行下面这段代码后opts={passive: undefined}</span></span><br><span class="line"> <span class="built_in">Object</span>.defineProperty(opts, <span class="string">'passive'</span>, ({</span><br><span class="line"> <span class="keyword">get</span> () {</span><br><span class="line"> <span class="comment">/* istanbul ignore next */</span></span><br><span class="line"> supportsPassive = <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }: <span class="built_in">Object</span>)) <span class="comment">// https://github.com/facebook/flow/issues/285</span></span><br><span class="line"> <span class="built_in">window</span>.addEventListener(<span class="string">'test-passive'</span>, <span class="literal">null</span>, opts) <span class="comment">// 通过注册一个事件来执行opts中的getter</span></span><br><span class="line"> } <span class="keyword">catch</span> (e) {}</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol start="6">
<li>获取原生类型</li>
</ol>
<p>在任何值上调用 Object 原生的 toString() 方法,都会返回一个 [object NativeConstructorName] 格式的字符串。每个类在内部都有一个 [[Class]] 属性,这个属性中就指定了上述字符串中的构造函数名, 如 ‘[object Array]’</p>
<figure class="highlight javascript"><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="function"><span class="keyword">function</span> <span class="title">toRawType</span> (<span class="params">value: any</span>): <span class="title">string</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> _toString.call(value).slice(<span class="number">8</span>, <span class="number">-1</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<ol start="7">
<li>cached方法</li>
</ol>
<figure class="highlight javascript"><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="function"><span class="keyword">function</span> <span class="title">cached</span><<span class="title">F</span>: <span class="title">Function</span>> (<span class="params">fn: F</span>): <span class="title">F</span> </span>{</span><br><span class="line"> <span class="keyword">const</span> cache = <span class="built_in">Object</span>.create(<span class="literal">null</span>)</span><br><span class="line"> <span class="keyword">return</span> (<span class="function"><span class="keyword">function</span> <span class="title">cachedFn</span> (<span class="params">str: string</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> hit = cache[str]</span><br><span class="line"> <span class="keyword">return</span> hit || (cache[str] = fn(str))</span><br><span class="line"> }: any)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>创建了一个对象cache,返回执行函数,在执行函数中对字符串做相应的处理并存在cache中,下次再处理相同的字符串时,首先在cache中查找字符串对应的key,如果能找到则直接读取而无需重新处理,提高了执行速度。这里利用了闭包的特性,即返回的立即执行函数 cachedFn 可以引用起外部函数中的cache对象,即使当 cached 执行完毕后,其活动对象也不会销毁,cachedFn 仍然可以引用 cache,取得其最后一个值即已保存了混村对象的值。</p>
<p>用例:</p>
<figure class="highlight javascript"><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> camelizeRE = <span class="regexp">/-(\w)/g</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> camelize = cached((str: string): <span class="function"><span class="params">string</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> str.replace(camelizeRE, (_, c) => c ? c.toUpperCase() : <span class="string">''</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<ol start="8">
<li>bind polyfill</li>
</ol>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">polyfillBind</span> (<span class="params">fn: Function, ctx: Object</span>): <span class="title">Function</span> </span>{</span><br><span class="line"> <span class="function"><span class="keyword">function</span> <span class="title">boundFn</span> (<span class="params">a</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> l = <span class="built_in">arguments</span>.length</span><br><span class="line"> <span class="keyword">return</span> l</span><br><span class="line"> ? l > <span class="number">1</span></span><br><span class="line"> ? fn.apply(ctx, <span class="built_in">arguments</span>)</span><br><span class="line"> : fn.call(ctx, a)</span><br><span class="line"> : fn.call(ctx)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> boundFn._length = fn.length</span><br><span class="line"> <span class="keyword">return</span> boundFn</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">nativeBind</span> (<span class="params">fn: Function, ctx: Object</span>): <span class="title">Function</span> </span>{</span><br><span class="line"> <span class="keyword">return</span> fn.bind(ctx)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">const</span> bind = <span class="built_in">Function</span>.prototype.bind</span><br><span class="line"> ? nativeBind</span><br><span class="line"> : polyfillBind</span><br></pre></td></tr></table></figure>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/09/06/Vue源码中知识点总结/" data-id="ck7an8uui0002bkuo2rlyhjal" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-Vue源码学习之二" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/08/23/Vue源码学习之二/" class="article-date">
<time datetime="2019-08-23T05:44:03.000Z" itemprop="datePublished">2019-08-23</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/08/23/Vue源码学习之二/">Vue源码学习之二</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>本周继续Vue源码的学习。上周主要看了Vue的构造函数,这周主要研究Vue源码构造函数中的初始化方法,即上篇博客中提到的 initMixin, stateMixin, eventsMixin, lifecycleMixin和 renderMixin</p>
<h2 id="initMixin"><a href="#initMixin" class="headerlink" title="initMixin"></a>initMixin</h2><p>在这里主要是抛出了Vue的初始化方法 <code>_init</code>。 在<code>_init</code>中主要做的有以下几件事:</p>
<ul>
<li><p>设置 <code>_uid</code>, 将实例缓存到 <code>vm</code> 中</p>
</li>
<li><p>环境变量不是 <code>production</code> 时,测试性能</p>
</li>
<li><p>如果 <code>Vue</code> 的初始化选项 <code>options</code> (data、生命周期钩子、methods、mixins等)中有 <code>_isComponent</code>这个选项(用于表明当前实例是否是组件的一个flag),去优化组件实例</p>
</li>
<li><p>否则执行 <code>mergeOptions</code> 这个函数,这个函数的作用是将传入的选项和自身的选项merge</p>
</li>
<li><p>在对选项处理完后,去对生命周期、事件等做初始化,并调用 <code>beforeCreate</code> 和 <code>created</code> 钩子函数<br>来看看具体代码:</p>
</li>
</ul>
<figure class="highlight javascript"><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><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">initMixin</span> (<span class="params">Vue: Class<Component></span>) </span>{</span><br><span class="line"> Vue.prototype._init = <span class="function"><span class="keyword">function</span> (<span class="params">options?: Object</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> vm: Component = <span class="keyword">this</span></span><br><span class="line"> <span class="comment">// a uid</span></span><br><span class="line"> vm._uid = uid++</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> startTag, endTag</span><br><span class="line"> <span class="comment">/* istanbul ignore if */</span></span><br><span class="line"> <span class="comment">// 性能优化</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && config.performance && mark) {</span><br><span class="line"> startTag = <span class="string">`vue-perf-start:<span class="subst">${vm._uid}</span>`</span></span><br><span class="line"> endTag = <span class="string">`vue-perf-end:<span class="subst">${vm._uid}</span>`</span></span><br><span class="line"> mark(startTag)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// a flag to avoid this being observed</span></span><br><span class="line"> vm._isVue = <span class="literal">true</span></span><br><span class="line"> <span class="comment">// merge options</span></span><br><span class="line"> <span class="comment">// _isComponent 用于标识 Vue 实例是否是组件</span></span><br><span class="line"> <span class="keyword">if</span> (options && options._isComponent) {</span><br><span class="line"> <span class="comment">// optimize internal component instantiation</span></span><br><span class="line"> <span class="comment">// since dynamic options merging is pretty slow, and none of the</span></span><br><span class="line"> <span class="comment">// internal component options needs special treatment.</span></span><br><span class="line"> <span class="comment">// </span></span><br><span class="line"> initInternalComponent(vm, options)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> vm.$options = mergeOptions(</span><br><span class="line"> resolveConstructorOptions(vm.constructor),</span><br><span class="line"> options || {},</span><br><span class="line"> vm</span><br><span class="line"> )</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">/* istanbul ignore else */</span></span><br><span class="line"> <span class="comment">// 如果不是在生产环境,初始化proxy</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span>) {</span><br><span class="line"> initProxy(vm)</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> vm._renderProxy = vm</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// expose real self</span></span><br><span class="line"> vm._self = vm</span><br><span class="line"> initLifecycle(vm) <span class="comment">// 生命周期相关的初始化</span></span><br><span class="line"> initEvents(vm) <span class="comment">// 事件相关初始化</span></span><br><span class="line"> initRender(vm) <span class="comment">// 渲染相关初始化</span></span><br><span class="line"> callHook(vm, <span class="string">'beforeCreate'</span>) <span class="comment">// 调用beforeCreate</span></span><br><span class="line"> initInjections(vm) <span class="comment">// resolve injections before data/props</span></span><br><span class="line"> initState(vm) <span class="comment">// 初始化data,进行双向绑定</span></span><br><span class="line"> initProvide(vm) <span class="comment">// resolve provide after data/props</span></span><br><span class="line"> callHook(vm, <span class="string">'created'</span>) <span class="comment">// 调用created</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/* istanbul ignore if */</span></span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> && config.performance && mark) {</span><br><span class="line"> vm._name = formatComponentName(vm, <span class="literal">false</span>)</span><br><span class="line"> mark(endTag)</span><br><span class="line"> measure(<span class="string">`vue <span class="subst">${vm._name}</span> init`</span>, startTag, endTag)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (vm.$options.el) {</span><br><span class="line"> vm.$mount(vm.$options.el)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>了解了大概的初始化流程之后,来具体看一下相关函数:</p>
<figure class="highlight javascript"><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="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">resolveConstructorOptions</span> (<span class="params">Ctor: Class<Component></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> options = Ctor.options</span><br><span class="line"> <span class="keyword">if</span> (Ctor.super) {</span><br><span class="line"> <span class="keyword">const</span> superOptions = resolveConstructorOptions(Ctor.super)</span><br><span class="line"> <span class="keyword">const</span> cachedSuperOptions = Ctor.superOptions</span><br><span class="line"> <span class="keyword">if</span> (superOptions !== cachedSuperOptions) {</span><br><span class="line"> <span class="comment">// super option changed,</span></span><br><span class="line"> <span class="comment">// need to resolve new options.</span></span><br><span class="line"> Ctor.superOptions = superOptions</span><br><span class="line"> <span class="comment">// check if there are any late-modified/attached options (#4976)</span></span><br><span class="line"> <span class="comment">// resolveModifiedOptions 返回</span></span><br><span class="line"> <span class="keyword">const</span> modifiedOptions = resolveModifiedOptions(Ctor)</span><br><span class="line"> <span class="comment">// update base extend options</span></span><br><span class="line"> <span class="keyword">if</span> (modifiedOptions) {</span><br><span class="line"> extend(Ctor.extendOptions, modifiedOptions)</span><br><span class="line"> }</span><br><span class="line"> options = Ctor.options = mergeOptions(superOptions, Ctor.extendOptions)</span><br><span class="line"> <span class="keyword">if</span> (options.name) {</span><br><span class="line"> options.components[options.name] = Ctor</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> options</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>resolveConstructorOptions</code>这个函数用于解析构造函数中的选项 <code>options</code>。<code>Ctor.super</code> 是当 Ctor 是 <code>Vue.extend</code> 构建的子类时,它就会具有 <code>super</code> 这个属性。此时,去对比 Ctor 与其父类的 options ,如果不一样的话,说明父类的 options 已被修改过(比如有mixins),那么就调用 <code>extend</code> 函数将已修改过的部分扩展到 <code>Ctor</code> 的 <code>options</code> 中。</p>
<p>扩展函数 <code>extend</code> 如下,这里是用到这个函数将父类中修改后的选项逐一添加到 Ctor 的选项中。</p>
<figure class="highlight javascript"><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"><span class="keyword">export</span> <span class="function"><span class="keyword">function</span> <span class="title">extend</span> (<span class="params">to: Object, _from: ?Object</span>): <span class="title">Object</span> </span>{</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> _from) {</span><br><span class="line"> to[key] = _from[key]</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> to</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>而 <code>mergeOptions</code> 则是对 options 中各项属性 data、 watch等与父类进行合并。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/08/23/Vue源码学习之二/" data-id="ck7an8uun0006bkuo7aq9ok8a" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-Vue源码学习之一" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/08/17/Vue源码学习之一/" class="article-date">
<time datetime="2019-08-17T05:45:18.000Z" itemprop="datePublished">2019-08-17</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/08/17/Vue源码学习之一/">Vue源码学习之一</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>这个星期开始看Vue 2.6.10版本的源码。首先从入口文件开始,在 <code>package.json</code> 中看到:</p>
<figure class="highlight javascript"><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"><span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="string">"dev"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:web-full-dev"</span>,</span><br><span class="line"> <span class="string">"dev:cjs"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:web-runtime-cjs-dev"</span>,</span><br><span class="line"> <span class="string">"dev:esm"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:web-runtime-esm"</span>,</span><br><span class="line"> <span class="string">"dev:test"</span>: <span class="string">"karma start test/unit/karma.dev.config.js"</span>,</span><br><span class="line"> <span class="string">"dev:ssr"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:web-server-renderer"</span>,</span><br><span class="line"> <span class="string">"dev:compiler"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:web-compiler "</span>,</span><br><span class="line"> <span class="string">"dev:weex"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:weex-framework"</span>,</span><br><span class="line"> <span class="string">"dev:weex:factory"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:weex-factory"</span>,</span><br><span class="line"> <span class="string">"dev:weex:compiler"</span>: <span class="string">"rollup -w -c scripts/config.js --environment TARGET:weex-compiler "</span>,</span><br></pre></td></tr></table></figure>
<p>第一行也就是运行 <code>npm run dev</code>时做的事情,这里是根据<code>scripts/config.js</code> 配置文件去打包项目,环境变量是<code>TARGET=web-full-dev</code>。 找到<code>scripts/config.js</code>文件,找到 <code>web-full-dev</code>对应的配置:</p>
<figure class="highlight javascript"><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">// Runtime+compiler development build (Browser)</span></span><br><span class="line"><span class="string">'web-full-dev'</span>: {</span><br><span class="line"> entry: resolve(<span class="string">'web/entry-runtime-with-compiler.js'</span>),</span><br><span class="line"> dest: resolve(<span class="string">'dist/vue.js'</span>),</span><br><span class="line"> format: <span class="string">'umd'</span>,</span><br><span class="line"> env: <span class="string">'development'</span>,</span><br><span class="line"> alias: { <span class="attr">he</span>: <span class="string">'./entity-decoder'</span> },</span><br><span class="line"> banner</span><br><span class="line">},</span><br></pre></td></tr></table></figure>
<p> 入口文件为 <code>'src/platforms/web/entry-runtime-with-compiler.js</code>, 找到该文件,找到以下代码:</p>
<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"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'./runtime/index'</span></span><br></pre></td></tr></table></figure>
<p> 依次找下去,依据路径<code>src/platforms/web/runtime/index</code> -> <code>src/core/index</code> -> <code>src/core/instance/index.js</code> 找到Vue的构造函数:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Vue</span> (<span class="params">options</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (process.env.NODE_ENV !== <span class="string">'production'</span> &&</span><br><span class="line"> !(<span class="keyword">this</span> <span class="keyword">instanceof</span> Vue)</span><br><span class="line"> ) {</span><br><span class="line"> warn(<span class="string">'Vue is a constructor and should be called with the `new` keyword'</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">this</span>._init(options)</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>在这个文件中还对 <code>Vue</code> 做了初始化:</p>
<figure class="highlight javascript"><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">initMixin(Vue)</span><br><span class="line">stateMixin(Vue)</span><br><span class="line">eventsMixin(Vue)</span><br><span class="line">lifecycleMixin(Vue)</span><br><span class="line">renderMixin(Vue)</span><br></pre></td></tr></table></figure>
<p><code>_init</code> 是 <code>initMixin</code> 中挂载在 <code>Vue</code> 原型上的方法。</p>
<p>而 <code>stateMixin</code>, <code>eventsMixin</code>,<code>lifecycleMixin</code>,<code>renderMixin</code>则分别在 <code>Vue.prototype</code> 挂载了以下方法/属性:</p>
<ul>
<li><p>stateMixin: 挂载$data, $props, $set, $delete, $watch,和数据处理相关</p>
</li>
<li><p>eventsMixin: 和事件处理相关的, $on, $off, $emit, $once</p>
</li>
<li><p>lifecycleMixin: 处理生命周期, 在 Vue.prototype 上挂载 _update, $forceUpdate, $destroy</p>
</li>
<li><p>renderMixin: $nextTick, _render</p>
</li>
</ul>
<p>再回到 <code>src/core/index</code> 看看这个文件中引入 <code>Vue</code> 实例之后做了什么:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 主要挂载一些静态属性和方法</span></span><br><span class="line">initGlobalAPI(Vue)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 挂载$isServer: 是否运行于服务器</span></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(Vue.prototype, <span class="string">'$isServer'</span>, {</span><br><span class="line"> <span class="keyword">get</span>: isServerRendering</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">Object.defineProperty(Vue.prototype, '$ssrContext', {</span><br><span class="line"> <span class="keyword">get</span> () {</span><br><span class="line"> <span class="comment">/* istanbul ignore next */</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">this</span>.$vnode && <span class="keyword">this</span>.$vnode.ssrContext</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// expose FunctionalRenderContext for ssr runtime helper installation</span></span><br><span class="line"><span class="built_in">Object</span>.defineProperty(Vue, <span class="string">'FunctionalRenderContext'</span>, {</span><br><span class="line"> value: FunctionalRenderContext</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">Vue.version = <span class="string">'__VERSION__'</span></span><br></pre></td></tr></table></figure>
<p>再回到<code>src/platforms/web/runtime/index</code>中,这里主要给 Vue 做了一些全局配置以及配置了一些初始化选项。</p>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/08/17/Vue源码学习之一/" data-id="ck7an8uuk0004bkuo0g773f3c" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-element源码学习" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/08/09/element源码学习/" class="article-date">
<time datetime="2019-08-09T07:33:13.000Z" itemprop="datePublished">2019-08-09</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/08/09/element源码学习/">element源码学习之clickoutside</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>clickoutside是element-ui中的一个指令, 用于在点击绑定元素外部时,执行指定的操作。主要原理是监听 <code>mouseup</code> 和 <code>mousedown</code> 事件,在 <code>mouseup</code>时,如果判断到事件所在元素不属于指令所绑定的元素,则执行指令绑定的回调方法。</p>
<p>来看看源码:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> Vue <span class="keyword">from</span> <span class="string">'vue'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// on 是element中封装的一个方法,兼容不同浏览器及服务器端的事件监听</span></span><br><span class="line"><span class="keyword">import</span> { on } <span class="keyword">from</span> <span class="string">'element-ui/src/utils/dom'</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// nodeList 存放所有绑定了clickoutside指令的元素</span></span><br><span class="line"><span class="keyword">const</span> nodeList = [];</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> ctx = <span class="string">'@@clickoutsideContext'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> startClick;</span><br><span class="line"><span class="keyword">let</span> seed = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标按下时,startClick 存储 mousedown 事件</span></span><br><span class="line">!Vue.prototype.$isServer && on(<span class="built_in">document</span>, <span class="string">'mousedown'</span>, e => (startClick = e));</span><br><span class="line"></span><br><span class="line"><span class="comment">// 鼠标松开,对于所有绑定了clickoutside事件的节点,执行节点绑定的回调函数</span></span><br><span class="line">!Vue.prototype.$isServer && on(<span class="built_in">document</span>, <span class="string">'mouseup'</span>, e => {</span><br><span class="line"> nodeList.forEach(<span class="function"><span class="params">node</span> =></span> node[ctx].documentHandler(e, startClick));</span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>上面这段代码中, <code>Vue.prototype.$isSever</code> 用于判断Vue实例是否运行于服务器。</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createDocumentHandler</span>(<span class="params">el, binding, vnode</span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="function"><span class="keyword">function</span>(<span class="params">mouseup = {}, mousedown = {}</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span> (!vnode ||</span><br><span class="line"> !vnode.context ||</span><br><span class="line"> !mouseup.target ||</span><br><span class="line"> !mousedown.target ||</span><br><span class="line"> el.contains(mouseup.target) ||</span><br><span class="line"> el.contains(mousedown.target) ||</span><br><span class="line"> el === mouseup.target ||</span><br><span class="line"> (vnode.context.popperElm &&</span><br><span class="line"> (vnode.context.popperElm.contains(mouseup.target) ||</span><br><span class="line"> vnode.context.popperElm.contains(mousedown.target)))) <span class="keyword">return</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (binding.expression &&</span><br><span class="line"> el[ctx].methodName &&</span><br><span class="line"> vnode.context[el[ctx].methodName]) {</span><br><span class="line"> vnode.context[el[ctx].methodName]();</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> el[ctx].bindingFn && el[ctx].bindingFn();</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>createDocumentHandler</code> 判断需要执行回调的情形,并为指令绑定的节点添加回调方法。判断的情形如下:</p>
<ul>
<li><p><code>!vnode.context</code>:不存在则退出</p>
</li>
<li><p><code>!mouseup.target</code>: 未触发点击事件,退出</p>
</li>
<li><p><code>!mousedown.target</code>: 未触发点击事件,退出</p>
</li>
<li><p><code>el.contains(mouseup.target)</code>, <code>el.contains(mousedown.target)</code>: 绑定的元素包含点击的元素,则退出不执行绑定回调方法</p>
</li>
<li><p><code>el === mouseup.target</code>: 点击绑定的元素,则不执行绑定的回调方法</p>
</li>
<li><p><code>(vnode.context.popperElm &&(vnode.context.popperElm.contains(mouseup.target) ||
vnode.context.popperElm.contains(mousedown.target))))</code>: 如果点击在下拉菜单上,则退出不执行绑定回调方法</p>
</li>
</ul>
<p>如果点击的事件不是上述情况的话,那么给绑定元素的 <code>ctx</code> 属性添加绑定的回调函数,并执行回调函数。</p>
<p>最后 export 出去的是指令的配置,主要是其钩子函数:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 指令绑定时调用</span></span><br><span class="line">bind(el, binding, vnode) {</span><br><span class="line"> <span class="comment">// 将绑定该指令的元素添加到nodeList中</span></span><br><span class="line"> nodeList.push(el);</span><br><span class="line"> <span class="comment">// seed作为标识符</span></span><br><span class="line"> <span class="keyword">const</span> id = seed++;</span><br><span class="line"> el[ctx] = {</span><br><span class="line"> id,</span><br><span class="line"> documentHandler: createDocumentHandler(el, binding, vnode),</span><br><span class="line"> methodName: binding.expression,</span><br><span class="line"> bindingFn: binding.value</span><br><span class="line"> };</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="comment">// vNode更新时调用</span></span><br><span class="line"> update(el, binding, vnode) {</span><br><span class="line"> el[ctx].documentHandler = createDocumentHandler(el, binding, vnode);</span><br><span class="line"> el[ctx].methodName = binding.expression;</span><br><span class="line"> el[ctx].bindingFn = binding.value;</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="comment">// 解绑时,将元素从nodeList中删除</span></span><br><span class="line"> unbind(el) {</span><br><span class="line"> <span class="keyword">let</span> len = nodeList.length;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < len; i++) {</span><br><span class="line"> <span class="keyword">if</span> (nodeList[i][ctx].id === el[ctx].id) {</span><br><span class="line"> nodeList.splice(i, <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">delete</span> el[ctx];</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>在元素绑定指令时,即给元素的ctx属性赋了一个对象,这个对象具有以下属性:</p>
<ul>
<li><p><code>id</code>:给绑定元素的一个唯一的标识符</p>
</li>
<li><p><code>documentHandler</code>: 即前面createDocumentHandler所返回的函数,如前,在 <code>mouseup</code>触发时便会去遍历绑定元素去执行 <code>documentHandler</code></p>
</li>
<li><p><code>methodName</code>: 指令绑定的expression</p>
</li>
<li><p><code>bindingFn</code>: 指令绑定的value</p>
</li>
</ul>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/08/09/element源码学习/" data-id="ck7an8uuo0007bkuojwky35hp" class="article-share-link">Share</a>
</footer>
</div>
</article>
<article id="post-web安全之xss攻击" class="article article-type-post" itemscope itemprop="blogPost">
<div class="article-meta">
<a href="/2019/08/05/web安全之xss攻击/" class="article-date">
<time datetime="2019-08-05T01:53:54.000Z" itemprop="datePublished">2019-08-05</time>
</a>
</div>
<div class="article-inner">
<header class="article-header">
<h1 itemprop="name">
<a class="article-title" href="/2019/08/05/web安全之xss攻击/">web安全之xss攻击</a>
</h1>
</header>
<div class="article-entry" itemprop="articleBody">
<p>上篇blog学习了web安全领域中的 CSRF 攻击,这篇来说一下 XSS。</p>
<p>XSS(Cross Site Script)又名跨站脚本攻击,是攻击者在网页上注入恶意的客户端脚本,通过恶意的脚本篡改客户端网页,从而导致用户隐私泄露或用户浏览器被控制。</p>
<h2 id="XSS-攻击的方式"><a href="#XSS-攻击的方式" class="headerlink" title="XSS 攻击的方式"></a>XSS 攻击的方式</h2><p>XSS可能的攻击方式有:</p>
<ol>
<li>将包含攻击脚本的恶意url包装在图片、游戏外挂或其他具有诱惑力的内容中,当用户点击这个恶意url,其中的脚本被执行即产生了XSS攻击。</li>
</ol>
<p>例如,现在网页上有个链接,其指向地址为 <code>xxx.com?a=b</code>, 用户访问这个链接即向XSS攻击后台服务器发起一个请求,服务器收到请求后返回一段攻击代码,客户端收到返回的恶意代码后在网页执行即完成了一次攻击。</p>
<ol start="2">
<li>将对客户端的攻击脚本植入到服务器上,那么每个正常访问该页面的用户都会受到攻击。</li>
</ol>
<p>例如,当用户提交内容时,服务端监听用户提交,在用户提交时将用户填入的信息保存在服务端,在用户正常访问这个页面时,服务端返回之前用户提交的信息。那么如果用户提交的是一段恶意代码,这段恶意代码就会被保存在服务器端并在所有用户访问这个页面时被执行。</p>
<p>XSS 攻击还有其他方式,如改变页面中某个结构在其中嵌入代码。总结起来就是通过各种方式将可执行脚本植入页面中。</p>
<h2 id="XSS的防御"><a href="#XSS的防御" class="headerlink" title="XSS的防御"></a>XSS的防御</h2><ol>
<li>任何用户的输入都是不可信的,对用户的输入需要做过滤、转义。例如 <code><script></code>需要对 <code><</code> 和 <code>></code> 做转义。</li>
</ol>
<p>当然,用户输入也不止输入框一种形式,还有诸如 <code>xxx.com?a=b&c=d</code>这种通过 url 中查询参数等形式以及post参数等,需要全面考虑各种用户输入的情形。</p>
<ol start="2">
<li><p>除了<code><script></code>之外,攻击者也有可能通过 html 片段在页面中渲染不安全的内容。例如使用 <code>span</code>、<code>p</code>标签等渲染不安全的内容,在 <code>input</code>输入中放置不安全的 <code>value</code>等,在 <code>a</code>标签中植入不安全的链接等。 那么就需要做html编码、url校验与安全性认证等工作去防御 xss 攻击。</p>
</li>
<li><p>对服务端的输出,除了富文本输出外,也需要做过滤与转义之后再输出到页面中。</p>
</li>
</ol>
</div>
<footer class="article-footer">
<a data-url="http://yoursite.com/2019/08/05/web安全之xss攻击/" data-id="ck7an8uuv000bbkuoh13mhzhc" class="article-share-link">Share</a>
</footer>
</div>
</article>
<nav id="page-nav">
<span class="page-number current">1</span><a class="page-number" href="/page/2/">2</a><a class="extend next" rel="next" href="/page/2/">Next »</a>
</nav>
</section>
<aside id="sidebar">
<div class="widget-wrap">
<h3 class="widget-title">Archives</h3>
<div class="widget">
<ul class="archive-list"><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/03/">March 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2020/02/">February 2020</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/09/">September 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/08/">August 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/07/">July 2019</a></li><li class="archive-list-item"><a class="archive-list-link" href="/archives/2019/06/">June 2019</a></li></ul>
</div>
</div>
<div class="widget-wrap">
<h3 class="widget-title">Recent Posts</h3>
<div class="widget">
<ul>
<li>
<a href="/2020/03/01/js继承方式总结/">js继承方式总结</a>
</li>
<li>
<a href="/2020/02/27/前端性能优化总结/">前端性能优化总结'</a>
</li>
<li>
<a href="/2020/02/15/浏览器知识总结/">浏览器知识总结</a>
</li>
<li>
<a href="/2019/09/27/Canvas绘制图形/">Canvas绘制图形</a>
</li>
<li>
<a href="/2019/09/06/Vue源码学习之三/">Vue源码学习之三</a>
</li>
</ul>
</div>
</div>
</aside>
</div>
<footer id="footer">
<div class="outer">
<div id="footer-info" class="inner">
© 2020 Rin's blog<br>
Powered by <a href="http://hexo.io/" target="_blank">Hexo</a>
</div>
</div>
</footer>
</div>
<nav id="mobile-nav">
<a href="/" class="mobile-nav-link">Home</a>
<a href="/archives" class="mobile-nav-link">Archives</a>
</nav>
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.0.3/jquery.min.js"></script>
<link rel="stylesheet" href="/fancybox/jquery.fancybox.css">
<script src="/fancybox/jquery.fancybox.pack.js"></script>
<script src="/js/script.js"></script>
</div>
</body>
</html>