-
Notifications
You must be signed in to change notification settings - Fork 0
/
local-search.xml
609 lines (288 loc) · 305 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>网页性能优化的方法概述</title>
<link href="/2021/12/25/%E7%BD%91%E9%A1%B5%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%9A%84%E6%96%B9%E6%B3%95%E6%A6%82%E8%BF%B0/"/>
<url>/2021/12/25/%E7%BD%91%E9%A1%B5%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96%E7%9A%84%E6%96%B9%E6%B3%95%E6%A6%82%E8%BF%B0/</url>
<content type="html"><![CDATA[<h1 id="网页性能优化的9个方法"><a href="#网页性能优化的9个方法" class="headerlink" title="网页性能优化的9个方法"></a>网页性能优化的9个方法</h1><p><strong>合并操作</strong></p><ul><li> DOM的多个读写操作(或多个写操作),应该放在一起。不要两个读操作之间加入一个写操作。</li><li>不要一条条的改变样式,而要通过改变class,或者csstext属性,一次性的改变样式</li></ul><p><strong>缓存</strong></p><ul><li>如果某个样式是通过重排得到的,那么最好缓存结果。避免下一次用到的时候,浏览器又要重排</li></ul><p><strong>操作非真实DOM</strong></p><ul><li>尽量使用离线DOM,而不是真实的网面dom,来改变元素样式。(比如,操作Document Fragment对象,完成后再把这个对象加入DOM。再比如,使用 cloneNode() 方法,在克隆的节点上进行操作,然后再用克隆的节点替换原始节点。)</li><li>使用虚拟DOM的脚本库,比如React等。</li></ul><p><strong>改变操作dom的属性</strong></p><ul><li>position属性为absolute或fixed的元素,重排的开销会比较小,因为不用考虑它对其他元素的影响。</li><li>只在必要的时候,才将元素的display属性为可见,因为不可见的元素不影响重排和重绘。另外,visibility : hidden的元素只对重绘有影响,不影响重排。</li><li>先将元素设为display: none(需要1次重排和重绘),然后对这个节点进行100次操作,最后再恢复显示(需要1次重排和重绘)。</li></ul><p><strong>自定义渲染</strong></p><ul><li>使用 window.requestAnimationFrame()、window.requestIdleCallback() 这两个方法调节重新渲染</li></ul><hr><p>学习链接:<a href="http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html">http://www.ruanyifeng.com/blog/2015/09/web-page-performance-in-depth.html</a></p>]]></content>
<categories>
<category>场景应用</category>
</categories>
</entry>
<entry>
<title>前端开发中导致页面卡顿的因素</title>
<link href="/2021/12/25/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E4%B8%AD%E5%AF%BC%E8%87%B4%E9%A1%B5%E9%9D%A2%E5%8D%A1%E9%A1%BF%E7%9A%84%E5%9B%A0%E7%B4%A0/"/>
<url>/2021/12/25/%E5%89%8D%E7%AB%AF%E5%BC%80%E5%8F%91%E4%B8%AD%E5%AF%BC%E8%87%B4%E9%A1%B5%E9%9D%A2%E5%8D%A1%E9%A1%BF%E7%9A%84%E5%9B%A0%E7%B4%A0/</url>
<content type="html"><![CDATA[<p>之前在做实习的一个项目的时候碰到了一个让我抓狂的BUG,需求是让我实现一个实时刷新获取消息列表的功能,但是后台返回的接口数据是成百上千的,页面卡的不得了,笔记本的风扇都呼呼的,所以这篇博客是为了总结一下导致页面卡顿的原因。</p><hr><h1 id="一-页面卡顿的原因大体上可以分为两种类型"><a href="#一-页面卡顿的原因大体上可以分为两种类型" class="headerlink" title="一.页面卡顿的原因大体上可以分为两种类型"></a>一.页面卡顿的原因大体上可以分为两种类型</h1><p><strong>1.渲染不及时,页面掉帧</strong></p><ul><li>长时间占用js线程</li><li>页面回流和重绘较多</li><li>资源加载堵塞</li></ul><p>我觉得这就是我上次碰到的哪个问题的原因,每一次请求时间差不多有2s左右,js长时间占用线程,而且加载的数据很多,页面回流与重绘严重,上一次请求还没结束,这一次请求又发起了,也造成了资源加载堵塞,多种原因导致我的页面卡崩溃了。</p><p><strong>2.网页内存占用过高,运行卡顿</strong></p><blockquote><p>内存泄漏导致内存过大</p></blockquote><ul><li>意外的全局变量引起的内存泄漏</li><li>闭包引起的内存泄漏</li><li>被遗忘的定时器</li><li>循环引用</li><li>DOM 删除时没有解绑事件</li><li>没有清理的DOM元素引用</li></ul><blockquote><p>dom节点或事件占用内存过大</p></blockquote><h1 id="二-详细解读-渲染不及时的问题"><a href="#二-详细解读-渲染不及时的问题" class="headerlink" title="二.详细解读-渲染不及时的问题"></a>二.详细解读-渲染不及时的问题</h1><h2 id="1-长时间占用js线程"><a href="#1-长时间占用js线程" class="headerlink" title="1.长时间占用js线程"></a>1.长时间占用js线程</h2><p>浏览器包括js线程和GUI线程,而且二者是互斥的,当长时间占用js线程时,会导致渲染不及时,出现页面卡顿</p><ul><li>1.1同步方式获取数据,会导致gui线程挂起,数据返回后再执行渲染<figure class="highlight arcade"><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><code class="hljs arcade">$.ajax({<br> <span class="hljs-attr">url</span>: <span class="hljs-string">''</span>,<br> <span class="hljs-attr">async</span>: <span class="hljs-literal">false</span><br>})<br></code></pre></td></tr></table></figure></li><li>1.2计算时间过长导致页面渲染不及时</li></ul><p>很明显的例子就是我遇到的哪个BUG,2S的响应时间,不卡才怪。<br><strong>渲染不及时的原因:</strong><br>浏览器的渲染频率一般是60HZ,即要求1帧的时间为1s / 60 = 16.67ms,浏览器显示页面的时候,要处理js逻辑,还要做渲染,每个执行片段不能超过16.67ms。实际上,浏览器内核自身支撑体系运行也需要消耗一些时间,所以留给我们的时间差不多只有10ms。<br><strong>常见的优化方式:</strong></p><ul><li>使用requestIdleCallback和requestAnimationFrame,任务分片<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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Task</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">this</span>.tasks = [];<br>}<br><span class="hljs-comment">//添加一个任务</span><br>Task.prototype.addTask = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">task</span>)</span>{<br> <span class="hljs-built_in">this</span>.tasks.push(task);<br>};<br><span class="hljs-comment">//每次重绘前取一个task执行</span><br>Task.prototype.draw = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> that = <span class="hljs-built_in">this</span>;<br> <span class="hljs-built_in">window</span>.requestAnimationFrame(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> tasks = that.tasks; <span class="hljs-keyword">if</span>(tasks.length){<br> <span class="hljs-keyword">var</span> task = tasks.shift();<br> task();<br> }<br> <span class="hljs-built_in">window</span>.requestAnimationFrame(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{that.draw.call(that)});<br> });<br>};<br></code></pre></td></tr></table></figure></li></ul><h2 id="2-页面回流和重绘较多"><a href="#2-页面回流和重绘较多" class="headerlink" title="2.页面回流和重绘较多"></a>2.页面回流和重绘较多</h2><ul><li>尽量减少layout<br>获取scrollTop、clentWidth等维度属性时都会触发layout以获取实时的值,所以在for循环里面应该把这些值缓存一下</li><li>简化DOM结构<br>当DOM结构越复杂时,需要重绘的元素也就越多。所以dom应该保持简单,特别是那些要做动画的,或者要监听scroll/mousemove事件的。<strong>另外使用flex比使用float在重绘方面会有优势</strong>。</li></ul><h2 id="3-资源加载阻塞"><a href="#3-资源加载阻塞" class="headerlink" title="3.资源加载阻塞"></a>3.资源加载阻塞</h2><ul><li>js资源放在body之前</li><li>行内script阻塞</li><li>css加载会阻塞DOM树渲染(css并不会阻塞DOM树的解析)</li><li>资源过大阻塞<h1 id="三-详细解读-内存过大导致的页面卡顿"><a href="#三-详细解读-内存过大导致的页面卡顿" class="headerlink" title="三.详细解读-内存过大导致的页面卡顿"></a>三.详细解读-内存过大导致的页面卡顿</h1><h2 id="1-内存泄漏导致内存过大"><a href="#1-内存泄漏导致内存过大" class="headerlink" title="1.内存泄漏导致内存过大"></a>1.内存泄漏导致内存过大</h2>浏览器有自己的一套垃圾回收机制,主流垃圾回收机制是标记清除,不过在ie中访问原生dom会采用引用计数方式机制,而如果闲置内存得不到及时回收,就会导致内存泄漏。</li></ul><p><strong>简单介绍下两种垃圾回收机制(GC Garbage Collection)</strong></p><h3 id="标记清除"><a href="#标记清除" class="headerlink" title="标记清除"></a>标记清除</h3><p><strong>定义和用法:</strong><br><strong>当变量进入环境时,将变量标记为进入环境,当变量离开环境时,标记为离开环境。</strong>某一时刻,垃圾回收器会过滤掉环境种的变量,以及被环境变量引用的变量,剩下的就是被视为准备回收的变量。<br>到目前为止,IE、Firefox、Opera、Chrome、Safari的js实现使用的都是标记清除的垃圾回收策略或类似的策略,只不过垃圾收集的时间间隔互不相同。<br><strong>流程:</strong></p><ul><li>浏览器在运行的时候会给存储再内存中的所有变量都加上标记</li><li>去掉环境中的变量以及被环境中引用的变量的标记</li><li>如果还有变量有标记,就会被视为准备删除的变量</li><li>垃圾回收机制完成内存的清除工作,销毁那些带标记的变量,并回收他们所占用的内存空间</li></ul><h3 id="引用计数"><a href="#引用计数" class="headerlink" title="引用计数"></a>引用计数</h3><p><strong>定义和用法:</strong>引用计数是跟踪记录每个值被引用的次数。<br><strong>基本原理:</strong>就是变量的引用次数,被引用一次则加1,当这个引用计数为0时,被视为准备回收的对象。<br><strong>流程:</strong></p><ul><li>声明了一个变量并将一个引用类型的值赋值给这个变量,这个引用类型值引用次数就是1</li><li>同一个值又被赋值另一个变量,这个引用类型的值引用次数加1</li><li>当包含这个引用类型值得变量又被赋值另一个值了,那么这个引用类型的值的引用次数减1</li><li>当引用次数变成0时, 说明这个值需要解除引用</li><li>当垃圾回收机制下次运行时,它就会释放引用次数为0 的值所占用的内存</li></ul><p><strong>常见的造成内存泄漏的原因:</strong></p><ul><li>意外的全局变量引起的内存泄漏</li><li>*解决**:使用严格模式避免<br>实例:<figure class="highlight pgsql"><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><code class="hljs pgsql"><button onclick="createNode()">添加节点</button><br> <button onclick="removeNode()">删除节点</button><br> <div id="wrapper"></div><br> <script><br> var <span class="hljs-type">text</span> = [];<br> <span class="hljs-keyword">function</span> createNode() { <br> <span class="hljs-type">text</span>.push(<span class="hljs-built_in">new</span> <span class="hljs-keyword">Array</span>(<span class="hljs-number">1000000</span>).<span class="hljs-keyword">join</span>(<span class="hljs-string">'x'</span>)); <br> var textNode = document.createTextNode("新节点"),<br> div = document.createElement(<span class="hljs-string">'div'</span>);<br> div.appendChild(textNode);<br> document.getElementById("wrapper").appendChild(div); <br> }<br> <br> <span class="hljs-keyword">function</span> removeNode() {<br> var <span class="hljs-keyword">wrapper</span> = document.getElementById("wrapper"),<br> len = <span class="hljs-keyword">wrapper</span>.childNodes.length;<br> <span class="hljs-keyword">if</span> (len > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">wrapper</span>.removeChild(<span class="hljs-keyword">wrapper</span>.childNodes[len - <span class="hljs-number">1</span>]); <br> }<br> }<br> </script><br></code></pre></td></tr></table></figure>text变量在createNode中引用,导致text不能被回收</li><li>闭包引起的内存泄漏</li></ul><p>实例:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onclick</span>=<span class="hljs-string">"replaceThing()"</span>></span>第二次点我就有泄漏<span class="hljs-tag"></<span class="hljs-name">button</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript"></span><br><span class="javascript"> <span class="hljs-keyword">var</span> theThing = <span class="hljs-literal">null</span>;</span><br><span class="javascript"> <span class="hljs-keyword">var</span> replaceThing = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{</span><br><span class="javascript"> <span class="hljs-keyword">var</span> originalThing = theThing;</span><br><span class="javascript"> <span class="hljs-keyword">var</span> unused = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{</span><br><span class="javascript"> <span class="hljs-keyword">if</span> (originalThing) {</span><br><span class="javascript"> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"hi"</span>);</span><br><span class="javascript"> };</span><br><span class="javascript"> }</span><br><span class="javascript"> theThing = {</span><br><span class="javascript"> <span class="hljs-attr">longStr</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Array</span>(<span class="hljs-number">1000000</span>).join(<span class="hljs-string">'*'</span>),</span><br><span class="javascript"> <span class="hljs-attr">someMethod</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">someMethod</span>(<span class="hljs-params"></span>) </span>{</span><br><span class="javascript"> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'someMessage'</span>);</span><br><span class="javascript"> }</span><br><span class="javascript"> };</span><br><span class="javascript"> };</span><br><span class="javascript"> </span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><p>上面那段代码泄漏的原因在于有两个闭包:unused和someMethod,二者共享父级作用域。<br>因为后面的 theThing 是全局变量,someMethod是全局变量的属性,它引用的闭包作用域(unused 和somMethod共享)不会释放,由于originalThing在共享的作用域中,造成originalThing不会释放,随着 replaceThing 不断调用,originalThing 指向前一次的 theThing,而新的theThing.someMethod又会引用originalThing ,从而形成一个闭包引用链,而 longStr是一个大字符串,得不到释放,从而造成内存泄漏。<br><strong>解决方法</strong>:在 replaceThing 的最后添加 originalThing = null</p><ul><li>被遗忘的定时器</li></ul><p>实例:</p><figure class="highlight crmsh"><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><code class="hljs crmsh">var someResource = getData(); <br>setInterval(function() { <br> var <span class="hljs-keyword">node</span> <span class="hljs-title">= document</span>.getElementById('<span class="hljs-keyword">Node</span><span class="hljs-title">'); </span><br><span class="hljs-title"> if</span>(<span class="hljs-keyword">node</span><span class="hljs-title">) { </span><br><span class="hljs-title"> // 处理 node</span> 和 someResource <br> node.innerHTML = JSON.stringify(someResource)); <br> } <br>}, <span class="hljs-number">1000</span>);<br></code></pre></td></tr></table></figure><p>计时器回调函数没被回收(计时器停止才会被回收)</p><ul><li>循环引用</li></ul><p>定义:循环引用就是对象A中包含另一个指向对象B的指针,B中也包含一个指向A的引用。</p><p>因为IE中的BOM、DOM的实现使用了COM,而COM对象使用的垃圾收集机制是引用计数策略。所以会存在循环引用的问题</p><p><strong>解决方法:</strong>手工断开js对象和DOM之间的链接。赋值为null。</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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">var</span> element = <span class="hljs-built_in">document</span>.getElementById(“testId”);<br> element.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{<br> alert(element.id)<br> }<br>}<br></code></pre></td></tr></table></figure><p>element绑定的事件中引用了element上的属性</p><p>onclick事件是一个闭包,闭包可以维持函数内局部变量,使其得不到释放。也就是说element变量得不到释放,每调用一次element都会得不到释放,最终内存泄漏<br><strong>解决问题:</strong></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><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">handle</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">var</span> element = <span class="hljs-built_in">document</span>.getElementById(“testId”);<br> element.onclick = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{<br> alert(element.id)<br> }<br> element = <span class="hljs-literal">null</span><br>}<br></code></pre></td></tr></table></figure><ul><li>DOM删除是没有解绑事件</li><li>没有清除DOM元素的引用</li></ul><h2 id="2-dom节点或事件占用内存过大"><a href="#2-dom节点或事件占用内存过大" class="headerlink" title="2.dom节点或事件占用内存过大"></a>2.dom节点或事件占用内存过大</h2><p><strong>实例:</strong></p><figure class="highlight reasonml"><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><code class="hljs reasonml"><span class="hljs-keyword">function</span> add<span class="hljs-constructor">Dom()</span>{<br> <span class="hljs-keyword">let</span> d = document.create<span class="hljs-constructor">DocumentFragment()</span>;<br> <br> <span class="hljs-keyword">for</span>(var i = <span class="hljs-number">0</span>;i<<span class="hljs-number">30</span>;i++){<br> <span class="hljs-keyword">let</span> li = document.create<span class="hljs-constructor">Element('<span class="hljs-params">li</span>')</span><br> li.add<span class="hljs-constructor">EventListener('<span class="hljs-params">click</span>', <span class="hljs-params">function</span>(<span class="hljs-params">e</span>)</span> {<br> <span class="hljs-keyword">let</span> _this = e.target;<br> <span class="hljs-keyword">let</span> dom = e.target.tagName.<span class="hljs-keyword">to</span><span class="hljs-constructor">LowerCase()</span>;<br> <span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">_this</span>.</span></span>style.color = 'red';<br> })<br> li.innerHTML = `</div><br> <h4><br> 测试图片 <br> </h4><br> <img style = <span class="hljs-string">"height:20px;width:100px"</span> src=<span class="hljs-string">"https://timgsa.baidu.com/timg?image&quality=80&size=b9999_10000&sec=1591105123139&di=90a63b4962d0b4405ce65c2096a676c2&imgtype=0&src=http%3A%2F%2Fimg0.imgtn.bdimg.com%2Fit%2Fu%3D3769023270%2C3433174748%26fm%3D214%26gp%3D0.jpg"</span>/><br> </div>`<br> d.append<span class="hljs-constructor">Child(<span class="hljs-params">li</span>)</span><br> }<br> document.get<span class="hljs-constructor">ElementById('<span class="hljs-params">app</span>')</span>.append<span class="hljs-constructor">Child(<span class="hljs-params">d</span>)</span><br> }<br></code></pre></td></tr></table></figure><p>上面的代码是下拉加载,每次都会添加dom,最终导致内存过大<br><strong>解决办法</strong>:采用虚拟列表和事件委托</p><hr><p>总结:页面卡顿在实际开发过程中有很多场景,可以使用内存泄漏检测工具(sIEve,针对IE)进行检测,也可以使用chrome提供的timeline和profiles,或者performance,这里不再详细介绍。</p><hr><p>查阅:<br><a href="https://blog.csdn.net/ycf74514/article/details/51123263?locationNum=3&fps=1">https://blog.csdn.net/ycf74514/article/details/51123263?locationNum=3&fps=1</a><br><a href="https://blog.csdn.net/c11073138/article/details/84728132">https://blog.csdn.net/c11073138/article/details/84728132</a><br><a href="https://www.cnblogs.com/yanglongbo/articles/9762359.html">https://www.cnblogs.com/yanglongbo/articles/9762359.html</a></p>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>页面重绘与回流</title>
<link href="/2021/12/25/%E9%A1%B5%E9%9D%A2%E9%87%8D%E7%BB%98%E4%B8%8E%E5%9B%9E%E6%B5%81/"/>
<url>/2021/12/25/%E9%A1%B5%E9%9D%A2%E9%87%8D%E7%BB%98%E4%B8%8E%E5%9B%9E%E6%B5%81/</url>
<content type="html"><![CDATA[<h1 id="一-什么是页面的重绘与回流"><a href="#一-什么是页面的重绘与回流" class="headerlink" title="一.什么是页面的重绘与回流"></a>一.什么是页面的重绘与回流</h1><p>浏览器在渲染一个页面的时候,从加载到完成,首先是构建DOM树,然后根据DOM节点的几何属性生成渲染树(<strong>不包括display:none,head节点但是会包括visibility:hidden节点</strong>),当渲染树构建完成,页面就根据DOM树开始布局了,渲染树也根据设置的样式对应的渲染这些节点。在这个过程中,回流与dom树和渲染树有关,重绘与渲染树有关。</p><p>比如我们删除一个dom节点,修改一个元素的宽高,这样就会导致页面的布局发生变化,DOM树的结构发生变化,引起dom树的重构,重构完成之后就会导致渲染树的重新渲染,这个过程就叫做<strong>回流</strong></p><p>当我们修改一个元素的颜色,这并不会影响页面的布局,但是渲染树会重新渲染页面的样式颜色,这就是<strong>重绘</strong></p><p>回流的代价是远远大于重绘的,回流一定导致重绘,但是重绘不一定导致回流。</p><h1 id="二-常见的场景"><a href="#二-常见的场景" class="headerlink" title="二.常见的场景"></a>二.常见的场景</h1><p><strong>回流常见于元素的尺寸,布局,隐藏等Dom结构发生改变的情况</strong></p><ul><li>1.添加或者删除可见的dom元素</li><li>2.元素位置改变</li><li>3.元素尺寸改变(边距,填充,边框,高度和宽度)</li><li>4.内容改变(内容物引起的元素大小发生变化)</li><li>5.页面渲染初始化</li><li>6.浏览器尺寸改变</li><li>7.计算元素的偏移量属性(浏览器为了确保属性值的正确性会回流得到最新值,所以最好使用一个变量记录一下)</li></ul><p><strong>重绘常见于元素的颜色的样式发生改变的情况</strong></p><ul><li>1.改变字体</li><li>2.增加或者移除样式表</li><li>3.内容变化(input输入框)</li><li>4.激活CSS伪类</li><li>5.设置style属性值</li><li>6.计算offsetWidth和offsetHeight属性</li></ul><h1 id="三-如何优化浏览器的回流与重绘"><a href="#三-如何优化浏览器的回流与重绘" class="headerlink" title="三.如何优化浏览器的回流与重绘"></a>三.如何优化浏览器的回流与重绘</h1><h2 id="1-将那些改变样式的操作集合在一次完事,直接改变className或者cssText"><a href="#1-将那些改变样式的操作集合在一次完事,直接改变className或者cssText" class="headerlink" title="1.将那些改变样式的操作集合在一次完事,直接改变className或者cssText"></a>1.将那些改变样式的操作集合在一次完事,直接改变className或者cssText</h2><ul><li>使用cssText<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs dart"><span class="hljs-keyword">const</span> el = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'test'</span>); <br>el.style.cssText += <span class="hljs-string">'border-left: 1px; border-right: 2px; padding: 5px;'</span>; <br></code></pre></td></tr></table></figure></li><li>修改CSS的class<figure class="highlight dart"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs dart"><span class="hljs-keyword">const</span> el = <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'test'</span>); <br>el.className += <span class="hljs-string">' active'</span>; <br></code></pre></td></tr></table></figure><h2 id="2-让要操作的元素进行离线处理,处理完事以后再一起更新"><a href="#2-让要操作的元素进行离线处理,处理完事以后再一起更新" class="headerlink" title="2.让要操作的元素进行离线处理,处理完事以后再一起更新"></a>2.让要操作的元素进行离线处理,处理完事以后再一起更新</h2></li><li>(1)使用DocumentFragment进行缓存操作,引发一次回流和重绘</li><li>(2)使用display:none,只引发两次回流和重绘。道理跟上面的一样。因为<strong>display:none的元素不会出现在render树</strong><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">function</span> append<span class="hljs-constructor">DataToElement(<span class="hljs-params">appendToElement</span>, <span class="hljs-params">data</span>)</span> {<br> <span class="hljs-keyword">let</span> li;<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < data.length; i++) {<br> li = document.create<span class="hljs-constructor">Element('<span class="hljs-params">li</span>')</span>;<br> li.textContent = 'text';<br> appendToElement.append<span class="hljs-constructor">Child(<span class="hljs-params">li</span>)</span>;<br> }<br>}<br>const ul = document.get<span class="hljs-constructor">ElementById('<span class="hljs-params">list</span>')</span>;<br>ul.style.display = 'none';<br>append<span class="hljs-constructor">DataToElement(<span class="hljs-params">ul</span>, <span class="hljs-params">data</span>)</span>;<br>ul.style.display = 'block';<br></code></pre></td></tr></table></figure></li><li>(3)使用cloneNode和replaceChild技术,引发一次回流和重绘(<strong>将原始元素拷贝到一个脱离文档流的节点中,修改节点之后,再替换原始元素</strong>)<figure class="highlight reasonml"><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><code class="hljs reasonml">const ul = document.get<span class="hljs-constructor">ElementById('<span class="hljs-params">list</span>')</span>;<br>const clone = ul.clone<span class="hljs-constructor">Node(<span class="hljs-params">true</span>)</span>;<br>append<span class="hljs-constructor">DataToElement(<span class="hljs-params">clone</span>, <span class="hljs-params">data</span>)</span>;<br>ul.parentNode.replace<span class="hljs-constructor">Child(<span class="hljs-params">clone</span>, <span class="hljs-params">ul</span>)</span>;<br></code></pre></td></tr></table></figure><h2 id="3-不要经常访问会引起浏览器flush队列的属性,非要高频访问的话建议缓存到变量;"><a href="#3-不要经常访问会引起浏览器flush队列的属性,非要高频访问的话建议缓存到变量;" class="headerlink" title="3.不要经常访问会引起浏览器flush队列的属性,非要高频访问的话建议缓存到变量;"></a>3.不要经常访问会引起浏览器flush队列的属性,非要高频访问的话建议缓存到变量;</h2><h2 id="4-将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;"><a href="#4-将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;" class="headerlink" title="4.将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;"></a>4.将需要多次重排的元素,position属性设为absolute或fixed,这样此元素就脱离了文档流,它的变化不会影响到其他元素。例如有动画效果的元素就最好设置为绝对定位;</h2><h2 id="5-尽量不要使用表格布局,如果没有定宽,表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。"><a href="#5-尽量不要使用表格布局,如果没有定宽,表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。" class="headerlink" title="5.尽量不要使用表格布局,如果没有定宽,表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。"></a>5.尽量不要使用表格布局,如果没有定宽,表格一列的宽度由最宽的一列决定,那么很可能在最后一行的宽度超出之前的列宽,引起整体回流造成table可能需要多次计算才能确定好其在渲染树中节点的属性,通常要花3倍于同等元素的时间。</h2><h2 id="6-避免触发同步布局事件"><a href="#6-避免触发同步布局事件" class="headerlink" title="6.避免触发同步布局事件"></a>6.避免触发同步布局事件</h2>现代浏览器都会通过队列化修改并批量执行来优化重排过程。浏览器会将修改操作放入到队列里,直到过了一段时间或者操作达到了一个阈值,才清空队列。但是!<strong>当你获取布局信息的操作的时候,会强制队列刷新</strong>,比如当你访问以下属性或者使用以下方法:</li><li>offsetTop、offsetLeft、offsetWidth、offsetHeight</li><li>scrollTop、scrollLeft、scrollWidth、scrollHeight</li><li>clientTop、clientLeft、clientWidth、clientHeight</li><li>getComputedStyle()</li><li>getBoundingClientRect<figure class="highlight maxima"><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><code class="hljs maxima">function initP() {<br> <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = <span class="hljs-number">0</span>; i < paragraphs.<span class="hljs-built_in">length</span>; i++) {<br> paragraphs[i].<span class="hljs-built_in">style</span>.<span class="hljs-built_in">width</span> = <span class="hljs-built_in">box</span>.offsetWidth + 'px';<br> }<br>}<br></code></pre></td></tr></table></figure>改为<figure class="highlight maxima"><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><code class="hljs maxima">const <span class="hljs-built_in">width</span> = <span class="hljs-built_in">box</span>.offsetWidth;<br>function initP() {<br> <span class="hljs-keyword">for</span> (<span class="hljs-built_in">let</span> i = <span class="hljs-number">0</span>; i < paragraphs.<span class="hljs-built_in">length</span>; i++) {<br> paragraphs[i].<span class="hljs-built_in">style</span>.<span class="hljs-built_in">width</span> = <span class="hljs-built_in">width</span> + 'px';<br> }<br>}<br></code></pre></td></tr></table></figure>以上属性和方法都需要返回最新的布局信息,因此浏览器不得不清空队列,触发回流重绘来返回正确的值。因此,我们在修改样式的时候,<strong>最好避免使用上面列出的属性,他们都会刷新渲染队列</strong>。如果要使用它们,最好将值缓存起来<h2 id="7-css3硬件加速,使用部分css3的属性不会引发页面的回流与重绘或者造成的影响比较小"><a href="#7-css3硬件加速,使用部分css3的属性不会引发页面的回流与重绘或者造成的影响比较小" class="headerlink" title="7.css3硬件加速,使用部分css3的属性不会引发页面的回流与重绘或者造成的影响比较小"></a>7.css3硬件加速,使用部分css3的属性不会引发页面的回流与重绘或者造成的影响比较小</h2><h1 id="四-浏览器渲染的过程"><a href="#四-浏览器渲染的过程" class="headerlink" title="四.浏览器渲染的过程"></a>四.浏览器渲染的过程</h1></li></ul><p><img src="/images/pasted-32.png" alt="upload successful"><br>渲染过程大致如下:<br>1.解析HTML,生成DOM树,解析CSS,生成CSSOM树<br>2.将DOM树和CSSOM树结合,生成渲染树<br>3.回流(Layout):根据生成的渲染树,进行回流得到节点信息(位置,大小)<br>4.重绘(Painting):根据渲染树以及回流得到的几何信息,得到节点的绝对像素<br>5.Display:将像素发送给GPU,展示在页面上<br><strong>生成渲染树</strong></p><p><img src="/images/pasted-33.png" alt="upload successful"></p><ul><li>1.从DOM树的根节点开始遍历每个可见节点。</li><li>2.对于每个<strong>可见的节点</strong>,找到CSSOM树中对应的规则,并应用它们。</li><li>3.根据每个可见节点以及其对应的样式,组合生成渲染树。</li><li>*不可见的节点:**(渲染树只包含可见的节点)</li><li>一些不会被渲染出来的点,比如:script,meta,link等</li><li>一些通过css进行隐藏的节点。比如display:none。注意,<strong>利用visibility和opacity隐藏的节点,还是会显示在渲染树上的</strong>。只有display:none的节点才不会显示在渲染树上。</li></ul><hr><p>学习链接:</p><p>1.<a href="https://www.cnblogs.com/dujingjie/p/5784890.html">https://www.cnblogs.com/dujingjie/p/5784890.html</a></p><p>2.<a href="https://www.cnblogs.com/wanan-01/p/7732340.html">https://www.cnblogs.com/wanan-01/p/7732340.html</a></p><p>3.<a href="https://zhuanlan.zhihu.com/p/22181897">https://zhuanlan.zhihu.com/p/22181897</a></p><p>4.<a href="https://zhuanlan.zhihu.com/p/52076790">https://zhuanlan.zhihu.com/p/52076790</a></p>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>Css概述</title>
<link href="/2021/12/25/Css%E6%A6%82%E8%BF%B0/"/>
<url>/2021/12/25/Css%E6%A6%82%E8%BF%B0/</url>
<content type="html"><![CDATA[<p>前言:我把CSS样式的一些基础知识总结了一下,作为自己的复习</p><hr><h1 id="1-内联与块"><a href="#1-内联与块" class="headerlink" title="1.内联与块"></a>1.内联与块</h1><p><strong>内联元素</strong>:内联元素不会占用一整行,他只会占用他自己元素大小的空间<br><strong>块级元素</strong>:会独占一行的元素,无论他的内容有多少他都会独占一整行<br>a元素可以包含除了他本身以外的任意元素,p元素不能包含其他任何块元素</p><h1 id="2-CSS选择器"><a href="#2-CSS选择器" class="headerlink" title="2.CSS选择器"></a>2.CSS选择器</h1><table><thead><tr><th>选择器</th><th>语法</th></tr></thead><tbody><tr><td>元素选择器</td><td>标签名{}</td></tr><tr><td>id选择器</td><td>#id属性值{}</td></tr><tr><td>类选择器</td><td>.class属性名</td></tr><tr><td>选择器分组</td><td>选择器1,选择器2,选择器3{}(通过选择器分组可以同时选中多个选择器对应的元素)</td></tr><tr><td>通配选择器</td><td>*{}(它可以用来选中页面中的所有元素)</td></tr><tr><td>复合选择器</td><td>选择器1选择器2选择器3{}(可以选中同时满足多个选择器的元素)</td></tr><tr><td>后代元素选择器</td><td>祖先元素 后代元素{}(选中指定元素的指定后代元素)</td></tr><tr><td>子元素选择器</td><td>父元素>子元素(选中指定父元素的指定子元素)</td></tr><tr><td>伪类选择器</td><td>a:link ->为没有访问过的链接添加样式,a:visited ->为访问过的链接修改样式,a:hover ->为鼠标移入链接时修改状态,a:active ->表示的是超链接被点击的状态,:focus伪类->获取焦点</td></tr><tr><td>伪元素控制样式</td><td>:first-letter表示设置第一个字符的样式,:before表示元素最前边的部分,一般before都需要结合content这个样式一起使用,通过content可以向before或after的位置添加一些元素,:after同理</td></tr><tr><td>属性选择器</td><td>[属性名]选取含有指定属性的元素,[属性名=”属性值”]选取含有指定属性值的元素,[属性名^=”属性值”]选取属性值以指定内容开头的元素,[属性名$=”属性值”]选取属性值以指定内容结尾的元素,[属性名*=”属性值”]选取属性值以包含指定内容的元素。(可以根据元素中的属性或属性值来选取指定元素)</td></tr><tr><td>子元素的伪类</td><td>first-child 可以选中第一个子元素,last-child 可以选中最后一个子元素,nth-child 可以选中任意位置的子元素:该选择器后边可以指定一个参数,指定要选中第几个子元素,even 表示偶数位置的子元素,odd 表示奇数位置的子元素</td></tr><tr><td>兄弟选择器</td><td>(可上网搜索语法)</td></tr><tr><td>否定伪类</td><td>:not(选择器)(可以从已选中的元素中剔除某些元素)</td></tr></tbody></table><h1 id="3-选择器优先级"><a href="#3-选择器优先级" class="headerlink" title="3.选择器优先级"></a>3.选择器优先级</h1><p>(1)内联样式——1000</p><p>(2)id选择器——100</p><p>(3)类和伪类——10</p><p>(4)元素选择器——1</p><p>(5)通配选择器——0</p><p>(6)继承的样式——无优先级</p><p><strong>当选择器中包含有多种选择器是,需要将多种选择器的优先级相加然后再比较。但是注意,选择器优先级计算不会超过他的最大数量级,如果选择器的优先级一样,则使用靠后的样式。</strong></p><p><strong>并集选择器的优先级是单独计算。</strong></p><p><strong>可以在样式的最后添加一个!important,则此时该样式将会获得一个最高的优先级,将会优先于所有的样式显示甚至超过内联样式。</strong><br>#4.链接标签的伪类顺序<br>最好以link、visited、hover、active的顺序写,否则由于这四个选择器的优先级是一样的,会造成一些样式的覆盖。<br>#5.盒子模型<br><img src="https://upload-images.jianshu.io/upload_images/24026585-91162257c86526dd.png?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240" alt="CSS的盒子模型"><br><strong>盒子的可见框大小由内容区,内边距和边框共同决定</strong></p><p><strong>内边距</strong>:由padding属性来设置,内边距会影响盒子的可见框大小,元素的背景会延申到内边距</p><p><strong>外边距</strong>:外边距指的是当前盒子于其他盒子之间的距离,他不会影响盒子可见框的大小,而会影响到盒子的位置。通过margin属性来设置。margin值可以设置为一个负值,那么则会导致指定位置的边距向相反方向移动,值还可以设置为auto,可以使用 margin:0 auto 来使得元素在父元素中水平居中</p><p><strong>外边距的重叠</strong>:垂直外边距的重叠,在网页中垂直方向的相邻外边距会发生外边距的重叠,这种情况是指兄弟元素之间的相邻外边距会取最大值而不是取和,注意是相邻外边距,如果有其他元素使得两个元素分开,则不会重叠。而如果父子元素的外边距相邻了,那么子元素的外边距将会设置给父元素。</p><p><strong>内联元素</strong>:不能设置width和height,但是可以设置水平竖直方向的内边距和边框,水平方向的内边距以及边框会影响布局,而竖直方向的则不会影响,会产生覆盖的效果。内联元素支持水平方向的外边距,但是不支持垂直方向的外边距,并且水平方向的外边距不会重叠,而是求和。</p><h1 id="6-元素在文档流中的特点"><a href="#6-元素在文档流中的特点" class="headerlink" title="6.元素在文档流中的特点"></a>6.元素在文档流中的特点</h1><h2 id="块元素:"><a href="#块元素:" class="headerlink" title="块元素:"></a>块元素:</h2><p>(1)块元素在文档流中会独占一行,块元素会自上向下排列</p><p>(2)块元素在文档流中默认宽度是父元素的100%</p><p>(3)当元素的宽度的值为auto时,此时指定内边距不会影响可见框的大小,而是会自动修改宽度,以适应内边距</p><p>(4)块元素在文档流的高度默认被内容撑开</p><h2 id="内联元素:"><a href="#内联元素:" class="headerlink" title="内联元素:"></a>内联元素:</h2><p>(1)内联元素在文档流中只占自身的大小,会默认从左到右排列,如果一行中不足以容纳所有的内联元素,则换到下一行,继续自左向右</p><p>(2)在文档流中内联元素的高度和宽度默认都被内容撑开<br>#7.盒模型的浮动<br>当我们设置一个元素脱离文档流之后,它下边的元素会立即向上移动,而浮动的元素会尽量的向页面的左上或者右上漂浮,直到遇到父元素的边框或者其他的浮动元素。<br><strong>如果浮动元素上边是一个没有浮动的元素,则浮动元素不会超过块元素。浮动的元素不会超过他上边的兄弟元素,最多一边齐而且浮动的元素不会盖住文字,文字会自动环绕在浮动元素的周围,所以我们可以通过浮动来设置文字环绕图片的效果</strong>在文档流中,子元素的宽度默认占据父元素的全部,但是当元素设置浮动之后,会完全的脱离文档流,高度和宽度都是被内容撑开的。<strong>而且在内联元素开启浮动以后就会变成块元素,就可以随便设置宽度和高度了。</strong></p><h1 id="8-高度坍塌问题"><a href="#8-高度坍塌问题" class="headerlink" title="8.高度坍塌问题"></a>8.高度坍塌问题</h1><h2 id="高度塌陷"><a href="#高度塌陷" class="headerlink" title="高度塌陷"></a>高度塌陷</h2><p>当我们为子元素设置浮动以后,子元素就会脱离文档流,此时将会导致子元素无法撑起父元素的高度,导致父元素的高度塌陷,由于父元素的高度塌陷了,就会导致父元素下面的所有元素向上移动,从而导致布局混乱。</p><h2 id="探讨解决高度塌陷的方法"><a href="#探讨解决高度塌陷的方法" class="headerlink" title="探讨解决高度塌陷的方法"></a>探讨解决高度塌陷的方法</h2><p><strong>我们可以打开元素的BFC,当开启元素的BFC以后</strong></p><ul><li>(1)父元素的垂直外边距不会和子元素重叠</li><li>(2)开启BFC的元素不会被浮动元素所覆盖</li><li>(3)开启BFC的元素可以包含浮动的子元素</li></ul><p><strong>如何开启元素的BFC:</strong></p><ul><li>1.设置元素浮动</li><li>2.设置元素的绝对定位</li></ul><p><strong>使用以上两种方式开启虽然可以撑开父元素,但是会导致父元素的宽度丢失,而且使用这种方式也会导致下边的元素上移,不能解决问题。</strong></p><ul><li>3.设置元素为inline-block</li></ul><p><strong>这种方式可以解决问题,但是会导致宽度丢失</strong></p><ul><li>4.将元素的overflow设置为一个非visible的值</li></ul><p><strong>这是比较推荐的方法,将overflow设置为hidden是副作用最小的方式</strong></p><h2 id="解决高度坍塌的改良办法"><a href="#解决高度坍塌的改良办法" class="headerlink" title="解决高度坍塌的改良办法"></a>解决高度坍塌的改良办法</h2><p>我们有时希望清除掉其他元素浮动对当前元素产生的影响,这时可以使用clear来完成功能,clear可以用来清除其他浮动元素对当前元素的影响,none是默认值不会清除浮动,left会清除左侧浮动元素对当前元素的影响,right会清除右侧浮动元素对当前元素的影响,而both则会清除两侧浮动元素中,对他影响最大的那个元素的浮动。</p><p><strong>(1)我们可以直接在高度塌陷的父元素的最后添加一个空白的div,由于这个div没有浮动,所以他是可以撑开父元素的高度的,基本没有副作用</strong></p><p><strong>(2)可以通过after伪类向元素的最后一个添加一个空白的块元素,然后对其清除浮动,这样做和添加一个div的原理是一样的,几乎没有副作用</strong></p><figure class="highlight avrasm"><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><code class="hljs avrasm"><span class="hljs-symbol">.clearfix:</span>after{<br><span class="hljs-comment">/*添加一个内容*/</span><br><span class="hljs-symbol">content:</span><span class="hljs-string">""</span><span class="hljs-comment">;</span><br><span class="hljs-comment">/*转换为一个块元素*/</span><br><span class="hljs-symbol">display:</span>block<span class="hljs-comment">;</span><br><span class="hljs-comment">/*清除两侧的浮动*/</span><br><span class="hljs-symbol">clear:</span>both<span class="hljs-comment">;</span><br>}<br></code></pre></td></tr></table></figure><p><strong>可以向需要处理的元素中添加这个样式就不会出现高度塌陷问题了</strong></p><h1 id="9-定位属性"><a href="#9-定位属性" class="headerlink" title="9.定位属性"></a>9.定位属性</h1><h2 id="static"><a href="#static" class="headerlink" title="static"></a>static</h2><p>默认值</p><h2 id="relative"><a href="#relative" class="headerlink" title="relative"></a>relative</h2><p>1.当开启了元素的相对定位以后,而不设置偏移量,元素不会发生任何变化</p><p><strong>2.相对定位是相对于元素在文档流中原来的位置进行定位</strong></p><p><strong>3.相对定位的元素不会脱离文档流</strong></p><p><strong>4.相对定位的元素会使元素提升一个层次</strong><br>5.相对定位不会改变元素的性质,块还是块,内联还是内联</p><h2 id="absolute"><a href="#absolute" class="headerlink" title="absolute"></a>absolute</h2><p><strong>1.当开启了元素绝对定位,会使元素脱离文档流</strong></p><p>2.当开启了元素的绝对定位以后,而不设置偏移量,元素不会发生任何变化</p><p><strong>3.绝对定位是相对于离他最近的开启了定位的祖先元素进行定位的(一般情况,开启了子元素的绝对定位都会同时开启父元素的相对定位)如果所有的祖先元素都没有开启定位,则会相对于浏览器窗口进行定位</strong></p><p>4.绝对定位的元素会提升一个层级</p><p><strong>5.绝对定位会改变元素的性质,内联元素会变成块元素,块元素的高度和宽度会被内容撑开</strong></p><h2 id="fixed"><a href="#fixed" class="headerlink" title="fixed"></a>fixed</h2><p>固定定位也是一种特殊的绝对定位,大部分特点和绝对定位一样,但是固定定位永远都会相对于浏览器窗口进行定位,并且永远会固定在浏览器窗口的某个位置,不会随着滚动条移动</p><p>如果定位元素的层级是一样的,那么下边的元素会盖住上边的,我们还可以通过z-index属性来设置元素的层级,可以为z-index指定一个正整数作为值,该值将会作为当前元素的层级,层级越高越优先显示,<strong>但是对于没有开启定位的元素不能使用z-index,而且父元素的层级再高也不会盖住子元素</strong></p>]]></content>
<categories>
<category>Css</category>
</categories>
</entry>
<entry>
<title>Cookie与Webstorage</title>
<link href="/2021/12/25/cookie/"/>
<url>/2021/12/25/cookie/</url>
<content type="html"><![CDATA[<h1 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h1><p><strong>cookie</strong>是往返于客户端与服务端的一小段存储信息,由响应头的Set-cookie设置,客户端浏览器在接收到响应之后就会在cookie中存储配置信息与数据,并且在向服务端发起请求的时候都会在请求头中带上这一段cookie传给服务端。</p><p><img src="/images/pasted-31.png" alt="upload successful"></p><p><strong>LocalStorage</strong>是WebStorage的一种,可以在浏览器中长期保存的一种本地存储类型,除非被清除,或者设定的过期时间到了,否则会一直保存在浏览器中<br><strong>SessionStorage</strong>是另一种WebStorage,但是保存数据的周期与LocalStorage不同,他只是可以将数据在当前会话中保存下来,<strong>刷新页面数据依旧存在,但是当页面关闭之后,SessionStorage中的数据就会被清空</strong></p><h1 id="三者的异同"><a href="#三者的异同" class="headerlink" title="三者的异同"></a>三者的异同</h1><table><thead><tr><th></th><th>Cookie</th><th>localStorage</th><th>sessionStorage</th></tr></thead><tbody><tr><td>数据的生命期</td><td>一般由服务器生成,可设置失效时间。如果在浏览器端生成Cookie,默认是关闭浏览器后失效</td><td>除非被清除,否则永久保存</td><td>仅在当前会话下有效,关闭页面或浏览器后被清除</td></tr><tr><td>存放数据大小</td><td>4K左右</td><td>一般为5MB</td><td>一般为5MB</td></tr><tr><td>与服务器端通信</td><td>每次都会携带在HTTP头中,如果使用cookie保存过多数据会带来性能问题</td><td>仅在客户端(即浏览器)中保存,不参与和服务器的通信</td><td>仅在客户端(即浏览器)中保存,不参与和服务器的通信</td></tr><tr><td>易用性</td><td>需要程序员自己封装,源生的Cookie接口不友好</td><td>源生接口可以接受,亦可再次封装来对Object和Array有更好的支持</td><td>源生接口可以接受,亦可再次封装来对Object和Array有更好的支持</td></tr></tbody></table><hr><p>学习链接:<a href="https://zhuanlan.zhihu.com/p/56300597">DOM总结:数据存储(cookie,storage) - 知乎 (zhihu.com)</a></p>]]></content>
</entry>
<entry>
<title>Form Data与Request Payload的区别</title>
<link href="/2021/12/25/Form-Data%E4%B8%8ERequest-Payload%E7%9A%84%E5%8C%BA%E5%88%AB/"/>
<url>/2021/12/25/Form-Data%E4%B8%8ERequest-Payload%E7%9A%84%E5%8C%BA%E5%88%AB/</url>
<content type="html"><![CDATA[<p>前端向后端发起get请求的时候,get会把传递的参数拼接在url的后面,所以get请求一般适用于数据量不大的情况之下,在数据量比较大的时候,我们就需要选择Post请求的方式了.</p><h1 id="方式一-Form-Data形式"><a href="#方式一-Form-Data形式" class="headerlink" title="方式一:Form Data形式"></a>方式一:Form Data形式</h1><p>当POST请求的请求头里设置Content-Type: application/x-www-form-urlencoded(默认), 参数在请求体以标准的Form Data的形式提交,以&符号拼接,参数格式为key=value&key=value&key=value…</p><p><img src="/images/pasted-27.png" alt="upload successful"></p><p><img src="/images/pasted-28.png" alt="upload successful"></p><h1 id="方式二-Request-Payload形式"><a href="#方式二-Request-Payload形式" class="headerlink" title="方式二:Request Payload形式"></a>方式二:Request Payload形式</h1><p>如果使用AJAX原生POST请求,请求头里设置Content-Type:application/json,请求的参数会显示在Request Payload中,参数格式为JSON格式:{“key”:”value”,”key”:”value”…},这种方式可读性会更好。</p><p><img src="/images/pasted-29.png" alt="upload successful"></p><p><img src="/images/pasted-30.png" alt="upload successful"><br><strong>Form Data和Request Payload的区别:</strong></p><p>1.如果请求头里设置Content-Type: application/x-www-form-urlencoded,那么这个请求被认为是表单请求,参数出现在Form Data里,格式为key=value&key=value&key=value…</p><p>2.原生的AJAX请求头里设置Content-Type:application/json,或者使用默认的请求头Content-Type:text/plain;参数会显示在Request payload块里提交。</p>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>应用鉴权</title>
<link href="/2021/12/25/%E5%BA%94%E7%94%A8%E9%89%B4%E6%9D%83/"/>
<url>/2021/12/25/%E5%BA%94%E7%94%A8%E9%89%B4%E6%9D%83/</url>
<content type="html"><![CDATA[<p>应用鉴权就是当一个用户进入APP时,我们需要判断他所拥有的权利,根据权力来判断他所能进行的一个行为,最为常见的就是购物网站的登录以及购物支付等操作。</p><hr><h1 id="一-鉴权的需求背景"><a href="#一-鉴权的需求背景" class="headerlink" title="一.鉴权的需求背景"></a>一.鉴权的需求背景</h1><p>Http的请求是无状态的,就是说在一个Http请求中的请求方和响应方都是无法维护状态,是一次性的,所以我们就不知道请求前后都发生了什么。所以我们需要标记的功能,而浏览器的sessionStorage,localStorage,全局变量等限制太多,就有了cookie,session,token等鉴权的操作。</p><h1 id="二-cookie"><a href="#二-cookie" class="headerlink" title="二.cookie"></a>二.cookie</h1><p>cookie也是一种前端存储的方式,但是他和sessionStorage,localStorage等本地存储的不同在于,浏览器向服务端发起请求的时候,cooike是自动传过去的,可以做到前端无感知,出错的概率更低<br><strong>过程:</strong><br>1.浏览器向服务器发起请求并传送数据,由服务器接收数据然后设置cooike放进响应头(Set-Cookie),浏览器接收到响应之后就会自动存储进cookie</p><p>2.在之后的每一次请求中浏览器都会自动的在请求头之中设置cookie字段,发送给服务端<br><strong>配置:</strong><br><strong>1.Domain/Path</strong><br>cookie 是要限制空间范围的,通过 Domain(域)/ Path(路径)两级。</p><blockquote><p>Domain属性指定浏览器发出 HTTP 请求时,哪些域名要附带这个 Cookie。如果没有指定该属性,浏览器会默认将其设为当前 URL 的一级域名,比如 <a href="https://link.zhihu.com/?target=http://www.example.com/">www.example.com</a> 会设为 <a href="https://link.zhihu.com/?target=http://example.com/">example.com</a>,而且以后如果访问<a href="https://link.zhihu.com/?target=http://example.com/">example.com</a>的任何子域名,HTTP 请求也会带上这个 Cookie。如果服务器在Set-Cookie字段指定的域名,不属于当前域名,浏览器会拒绝这个 Cookie。<br>Path属性指定浏览器发出 HTTP 请求时,哪些路径要附带这个 Cookie。只要浏览器发现,Path属性是 HTTP 请求路径的开头一部分,就会在头信息里面带上这个 Cookie。比如,PATH属性是/,那么请求/docs路径也会包含该 Cookie。当然,前提是域名必须一致。</p></blockquote><p><strong>2.Expires / Max-Age</strong><br>cookie 还可以限制时间范围,通过 Expires、Max-Age 中的一种。</p><blockquote><p>Expires属性指定一个具体的到期时间,到了指定时间以后,浏览器就不再保留这个 Cookie。它的值是 UTC 格式。如果不设置该属性,或者设为null,Cookie 只在当前会话(session)有效,浏览器窗口一旦关闭,当前 Session 结束,该 Cookie 就会被删除。另外,浏览器根据本地时间,决定 Cookie 是否过期,由于本地时间是不精确的,所以没有办法保证 Cookie 一定会在服务器指定的时间过期。<br>Max-Age属性指定从现在开始 Cookie 存在的秒数,比如60 * 60 * 24 * 365(即一年)。过了这个时间以后,浏览器就不再保留这个 Cookie。<br>如果同时指定了Expires和Max-Age,那么Max-Age的值将优先生效。<br>如果Set-Cookie字段没有指定Expires或Max-Age属性,那么这个 Cookie 就是 Session Cookie,即它只在本次对话存在,一旦用户关闭浏览器,浏览器就不会再保留这个 Cookie。</p></blockquote><p><strong>3.Secure / HttpOnly</strong><br>cookie 可以限制使用方式。</p><blockquote><p>Secure属性指定浏览器只有在加密协议 HTTPS 下,才能将这个 Cookie 发送到服务器。另一方面,如果当前协议是 HTTP,浏览器会自动忽略服务器发来的Secure属性。该属性只是一个开关,不需要指定值。如果通信是 HTTPS 协议,该开关自动打开。<br>HttpOnly属性指定该 Cookie 无法通过 JavaScript 脚本拿到,主要是Document.cookie属性、XMLHttpRequest对象和 Request API 都拿不到该属性。这样就防止了该 Cookie 被脚本读到,只有浏览器发出 HTTP 请求时,才会带上该 Cookie。</p></blockquote><p><strong>Http头对cookie的读写:</strong><br>响应会携带一个Set-Cookie头,一个Set-Cookie只能设置一条cookie,格式为cookie键值+配置键值,如果想要一次设置多个cookie,我们可以多写几个Set-Cookie在头里面。</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">Set-Cookie: <span class="hljs-attribute">username</span>=jimu; <span class="hljs-attribute">domain</span>=jimu.com; <span class="hljs-attribute">path</span>=/blog; <span class="hljs-attribute">Expires</span>=Wed, 21 Oct 2015 07:28:00 GMT; Secure; HttpOnly<br></code></pre></td></tr></table></figure><p>而当浏览器请求服务器的时候,就不再需要发送配置内容了,只需要发送键值对就可以</p><figure class="highlight routeros"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs routeros">Cookie: <span class="hljs-attribute">username</span>=jimu; <span class="hljs-attribute">height</span>=180; <span class="hljs-attribute">weight</span>=80<br></code></pre></td></tr></table></figure><p><strong>前端对cookie的读写操作:</strong><br>如果服务端设置的cookie并没有设置httpOnly,那么我们就可以调用document.cookie来对cookie进行读写操作,但一次调用document.cookie就只能操作一个cookie</p><h1 id="三-session"><a href="#三-session" class="headerlink" title="三.session"></a>三.session</h1><p>在上面我们介绍了cookie,可以了解到cookie其实是浏览器存储的一种实现,可以看作应用鉴权的基石。但是它只是一个存储信息的工具,我们还需要判断其中的信息是不是安全的操作者,这时候我们就需要session了。<br><strong>典型的session登录流程:</strong></p><p><img src="/images/pasted-19.png" alt="upload successful"><br>1.浏览器登录发送账号密码,服务端查用户库,校验用户</p><p>2.服务端把用户登录状态存为 Session,生成一个 sessionId</p><p>3.通过登录接口返回,把 sessionId set 到 cookie 上,此后浏览器再请求业务接口,sessionId 随 cookie 带上</p><p>4.服务端查 sessionId 校验 session</p><p>5.成功后正常做业务处理,返回结果</p><p><strong>session的存储与过期销毁:</strong><br>由于session是用来验证的,所以服务端仅仅只是给浏览器的cookie中添加一个sessionid,所以也需要自己保存一下,存储的方式:</p><p>1.Redis(推荐):内存型数据库,redis中文官方网站。以 key-value 的形式存,正合 sessionId-sessionData 的场景;且访问快。</p><p>2.内存:直接放到变量里。一旦服务重启就没了</p><p>3.数据库:普通数据库。性能不高。</p><p>而session也可以手动设置过期时间,一到过期时间就直接清空存储的内存就好了</p><p><strong>session的分布式问题:</strong><br>由于服务端是集群,而用户请求过来会走一次负载均衡,不一定会打到哪台机器上。一旦用户后续接口请求到的机器和他登录请求的机器不一样,或者登录请求的机器出问题了,那session就会失效。<br>常见的解决方式:</p><p>1.把session集中存储到独立的redis或者普通数据库中,就可以把session存储到一个库里(<strong>存储角度</strong>)</p><p>2.让相同 IP 的请求在负载均衡时都打到同一台机器上。以 nginx 为例,可以配置 ip_hash 来实现。(<strong>分布角度</strong>)</p><h1 id="四-token"><a href="#四-token" class="headerlink" title="四.token"></a>四.token</h1><p><img src="/images/pasted-20.png" alt="upload successful"></p><p>1.用户登录,服务端校验账号密码,获得用户信息</p><p>2.把用户信息、token 配置编码成 token,通过 cookie set 到浏览器</p><p>3.此后用户请求业务接口,通过 cookie 携带 token</p><p>4.接口校验 token 有效性,进行正常业务接口处理</p><p>目前主流的token存储还是在cookie中进行,但是为了防止伪造token造成的安全问题,我们还需要一些加密算法来生成签名,来保证数据安全性</p><p><img src="/images/pasted-21.png" alt="upload successful"><br><strong>JWT</strong><br>由于以上的方法增加了cookie的数量,所以JSON web Token得以被使用。</p><p><img src="/images/pasted-22.png" alt="upload successful"><br><strong>refresh token</strong><br>token,作为权限守护者,最重要的就是「安全」。</p><p>业务接口用来鉴权的 token,我们称之为 access token。越是权限敏感的业务,我们越希望 access token 有效期足够短,以避免被盗用。但过短的有效期会造成 access token 经常过期,过期后怎么办呢?</p><p>一种办法是,让用户重新登录获取新 token,显然不够友好,要知道有的 access token 过期时间可能只有几分钟。</p><p>另外一种办法是,再来一个 token,一个专门生成 access token 的 token,我们称为 refresh token。</p><p>1.access token 用来访问业务接口,由于有效期足够短,盗用风险小,也可以使请求方式更宽松灵活</p><p>2.refresh token 用来获取 access token,有效期可以长一些,通过独立服务和严格的请求方式增加安全性;由于不常验证,也可以如前面的 session 一样处理</p><p><img src="/images/pasted-23.png" alt="upload successful"><br>如果refresh token过期了,就只能重新登陆了。</p><h1 id="五-session与token对比"><a href="#五-session与token对比" class="headerlink" title="五.session与token对比"></a>五.session与token对比</h1><p><strong>客户端存cookie与存放于其他地方</strong></p><p>1.出了浏览器环境之外就没有cookie了;</p><p>2.cookie是浏览器在域下自动携带的。很容易引发CSFR攻击</p><p>存放在别的地方可以解决部分问题<br><strong>服务端存储数据于不存</strong></p><p>1.存数据的话可以缩短认证字符串的长度,减小请求体积</p><p>2.不存数据就不会出现分布式处理的问题,降低硬件成本,避免查库带来的验证延迟</p><h1 id="六-单点登录"><a href="#六-单点登录" class="headerlink" title="六.单点登录"></a>六.单点登录</h1><p>前面我们已经知道了,在同域下的客户端/服务端认证系统中,通过客户端携带凭证,维持一段时间内的登录状态。</p><p>但当我们业务线越来越多,就会有更多业务系统分散到不同域名下,就需要「一次登录,全线通用」的能力,叫做「单点登录」。</p><p>在<strong>主域名相同</strong>的情况下,就可以直接把cookie设置为主域名就可以实现了。</p><p>如果<strong>主域名不同</strong>,我们就需要独立的认证服务,称为SSO。</p><p><img src="/images/pasted-24.png" alt="upload successful"></p><ul><li>用户进入 A 系统,没有登录凭证(ticket),A 系统给他跳到 SSO</li><li>SSO 没登录过,也就没有 sso 系统下没有凭证(注意这个和前面 A ticket 是两回事),输入账号密码登录</li><li>SSO 账号密码验证成功,通过接口返回做两件事:一是种下 sso 系统下凭证(记录用户在 SSO 登录状态);二是下发一个 ticket</li><li>客户端拿到 ticket,保存起来,带着请求系统 A 接口</li><li>系统 A 校验 ticket,成功后正常处理业务请求</li><li>此时用户第一次进入系统 B,没有登录凭证(ticket),B 系统给他跳到 SSO</li><li>SSO 登录过,系统下有凭证,不用再次登录,只需要下发 ticket</li><li>客户端拿到 ticket,保存起来,带着请求系统 B 接口</li></ul><p>如果是在浏览器之下实现,我们需要考虑其他的东西</p><p><img src="/images/pasted-25.png" alt="upload successful"><br>对浏览器来说,SSO 域下返回的数据要怎么存,才能在访问 A 的时候带上?浏览器对跨域有严格限制,cookie、localStorage 等方式都是有域限制的。</p><p>这就需要也只能由 A 提供 A 域下存储凭证的能力。一般我们是这么做的:</p><p><img src="/images/pasted-26.png" alt="upload successful"><br>图中我们通过颜色把浏览器当前所处的域名标记出来。注意图中灰底文字说明部分的变化。</p><ul><li>1.在 SSO 域下,SSO 不是通过接口把 ticket 直接返回,而是通过一个带 code 的 URL 重定向到系统 A 的接口上,这个接口通常在 A 向 SSO 注册时约定</li><li>2.浏览器被重定向到 A 域下,带着 code 访问了 A 的 callback 接口,callback 接口通过 code 换取 ticket</li><li>3.这个 code 不同于 ticket,code 是一次性的,暴露在 URL 中,只为了传一下换 ticket,换完就失效</li><li>4.callback 接口拿到 ticket 后,在自己的域下 set cookie 成功</li><li>5.在后续请求中,只需要把 cookie 中的 ticket 解析出来,去 SSO 验证就好</li><li>6.访问 B 系统也是一样</li></ul><h1 id="七-总结"><a href="#七-总结" class="headerlink" title="七.总结"></a>七.总结</h1><p>1.HTTP 是无状态的,为了维持前后请求,需要前端存储标记</p><p>2.cookie 是一种完善的标记方式,通过 HTTP 头或 js 操作,有对应的安全策略,是大多数状态管理方案的基石</p><p>3.session 是一种状态管理方案,前端通过 cookie 存储 id,后端存储数据,但后端要处理分布式问题</p><p>4.token 是另一种状态管理方案,相比于 session 不需要后端存储,数据全部存在前端,解放后端,释放灵活性</p><p>5.token 的编码技术,通常基于 base64,或增加加密算法防篡改,jwt 是一种成熟的编码方案</p><p>6.在复杂系统中,token 可通过 service token、refresh token 的分权,同时满足安全性和用户体验</p><p>7.session 和 token 的对比就是「用不用cookie」和「后端存不存」的对比</p><p>8.单点登录要求不同域下的系统「一次登录,全线通用」,通常由独立的 SSO 系统记录登录状态、下发 ticket,各业务系统配合存储和认证 ticket</p><hr><p>阅读知乎文章:<a href="https://zhuanlan.zhihu.com/p/395273289">鉴权必须了解的5个兄弟:cookie、session、token、jwt、单点登录 - 知乎 (zhihu.com)</a></p>]]></content>
<categories>
<category>场景应用</category>
</categories>
</entry>
<entry>
<title>JSONP详解</title>
<link href="/2021/12/25/JSONP%E8%AF%A6%E8%A7%A3/"/>
<url>/2021/12/25/JSONP%E8%AF%A6%E8%A7%A3/</url>
<content type="html"><![CDATA[<p>jsonp是一个比较常见的跨域解决方法,这篇博客使用来详解它的用法以及实现原理</p><hr><h1 id="1-实现原理"><a href="#1-实现原理" class="headerlink" title="1.实现原理"></a>1.实现原理</h1><p>script标签的src属性并不被同源策略所约束,所以可以获取任何服务器上脚本并执行,这种性质致使我们可以通过script标签来实现jsonp跨域</p><h1 id="2-实现模式"><a href="#2-实现模式" class="headerlink" title="2.实现模式"></a>2.实现模式</h1><p>假设A网站需要获取B网站中的某些数据<br><strong>程序B</strong></p><figure class="highlight awk"><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><code class="hljs awk"><span class="hljs-regexp">//</span>调用callback函数,并以json数据形式作为阐述传递,完成回调<br><br>callback({message:<span class="hljs-string">"success"</span>});<br></code></pre></td></tr></table></figure><p><strong>程序A</strong></p><figure class="highlight xml"><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><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"><span class="hljs-comment">//回调函数</span></span><br><span class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">callback</span>(<span class="hljs-params">data</span>) </span>{</span><br><span class="javascript"> alert(data.message);</span><br><span class="javascript">}</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://localhost:20002/test.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><p><strong>JSONP的简单实现模式:</strong>创建一个回调函数,然后在远程服务上调用这个函数并且将JSON 数据形式作为参数传递,完成回调。</p><h1 id="3-JQuery对JSONP的实现"><a href="#3-JQuery对JSONP的实现" class="headerlink" title="3.JQuery对JSONP的实现"></a>3.JQuery对JSONP的实现</h1><p><strong>JQuery</strong></p><figure class="highlight xquery"><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><code class="hljs xquery"><span class="xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://code.jquery.com/jquery-latest.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span></span><br><span class="xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span></span><br><span class="javascript"><span class="xml"> $.getJSON(<span class="hljs-string">"http://localhost:20002/MyService.ashx?callback=?"</span>,<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span></span></span><span class="xquery">{</span><br><span class="xquery"> alert(data<span class="hljs-built_in">.name</span> + <span class="hljs-string">" is a a"</span> + data.sex);</span><br><span class="xquery"> }</span><span class="xml">);</span><br><span class="xml"><span class="hljs-tag"></<span class="hljs-name">script</span>></span></span><br></code></pre></td></tr></table></figure><p><strong>要注意的是在url的后面必须添加一个callback参数,这样getJSON方法才会知道是用JSONP方式去访问服务,callback后面的那个问号是内部自动生成的一个回调函数名。这个函数名大家可以debug一下看看,比如jQuery17207481773362960666_1332575486681</strong><br>如果想要加入自己的回调函数名,或者服务规定了回调函数名,可以使用**$.ajax**方法</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://code.jquery.com/jquery-latest.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"> $.ajax({</span><br><span class="javascript"> <span class="hljs-attr">url</span>:<span class="hljs-string">"http://localhost:20002/MyService.ashx?callback=?"</span>, </span><br><span class="javascript"> <span class="hljs-attr">dataType</span>:<span class="hljs-string">"jsonp"</span>,</span><br><span class="javascript"> <span class="hljs-attr">jsonpCallback</span>:<span class="hljs-string">"person"</span>,</span><br><span class="javascript"> <span class="hljs-attr">success</span>:<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{</span><br><span class="javascript"> alert(data.name + <span class="hljs-string">" is a a"</span> + data.sex);</span><br><span class="javascript"> }</span><br><span class="javascript"> });</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>从输入URL到页面展示的详细过程</title>
<link href="/2021/12/25/%E4%BB%8E%E8%BE%93%E5%85%A5URL%E5%88%B0%E9%A1%B5%E9%9D%A2%E5%B1%95%E7%A4%BA%E7%9A%84%E8%AF%A6%E7%BB%86%E8%BF%87%E7%A8%8B/"/>
<url>/2021/12/25/%E4%BB%8E%E8%BE%93%E5%85%A5URL%E5%88%B0%E9%A1%B5%E9%9D%A2%E5%B1%95%E7%A4%BA%E7%9A%84%E8%AF%A6%E7%BB%86%E8%BF%87%E7%A8%8B/</url>
<content type="html"><![CDATA[<p><img src="/images/pasted-14.png" alt="upload successful"></p><h1 id="1-输入网址"><a href="#1-输入网址" class="headerlink" title="1.输入网址"></a>1.输入网址</h1><p>url:统一资源定位符,用于定位互联网综上的资源<br>url构成:协议类型://<主机名>:<端口>/<路径>/文件名?参数名#锚点</p><h1 id="2-浏览器查找域名的IP地址(域名解析)"><a href="#2-浏览器查找域名的IP地址(域名解析)" class="headerlink" title="2.浏览器查找域名的IP地址(域名解析)"></a>2.浏览器查找域名的IP地址(域名解析)</h1><p><strong>浏览器缓存</strong><br>浏览器会检查缓存中有没有这个域名对应的解析过的IP地址,如果缓存中有,这个解析过程就将结束<br><strong>系统缓存</strong><br>浏览器会查看本地硬盘的 hosts 文件,看看其中有没有和这个域名对应的规则,如果有的话就直接使用 hosts 文件里面的 ip 地址。<br><strong>路由器缓存</strong><br>如果系统缓存中也找不到,那么查询请求就会发向路由器,路由器一般会有自己的DNS缓存。<br><strong>ISP DNS 缓存(域名商的域名解析系统)</strong><br>如果在hosts文件中也没有找到对应的条目,浏览器就会发起一个DNS的系统调用,就会向<br> 本地配置的首选DNS服务器(一般是运营商提供的)发起域名解析请求,(通过的是UDP协议向DNS的53端口发起请求,这个请求是递归的请求,也就是运营商的DNS服务器必须得提供给我们该域名的IP地址),运营商的DNS服务器首先查找自身的缓存,找到对应的条目,且没有过期,则解析成功。<br><strong>如果都没有找到,则运营商的DNS代浏览器发起迭代DNS解析请求,查找域名对应 IP(见图)</strong></p><p><img src="/images/pasted-15.png" alt="upload successful"></p><h1 id="3-通过IP向服务器发起TCP连接"><a href="#3-通过IP向服务器发起TCP连接" class="headerlink" title="3.通过IP向服务器发起TCP连接"></a>3.通过IP向服务器发起TCP连接</h1><p><img src="/images/pasted-16.png" alt="upload successful"></p><h1 id="4-服务器接受请求"><a href="#4-服务器接受请求" class="headerlink" title="4.服务器接受请求"></a>4.服务器接受请求</h1><h1 id="5-服务器处理请求返回相应文件"><a href="#5-服务器处理请求返回相应文件" class="headerlink" title="5.服务器处理请求返回相应文件"></a>5.服务器处理请求返回相应文件</h1><p><img src="/images/pasted-17.png" alt="upload successful"></p><h1 id="6-页面渲染"><a href="#6-页面渲染" class="headerlink" title="6.页面渲染"></a>6.页面渲染</h1><p>(1)解析HTML文件,创建DOM树(解析执行JS脚本时,会停止解析后续HTML)</p><p>(2)解析CSS,形成CSS对象模型</p><p>(3)将CSS与DOM合并,构建渲染树</p><p>(4)布局渲染树</p><p>(5)绘制渲染树(可能触发回流和重绘)</p><p><img src="/images/pasted-18.png" alt="upload successful"></p><h1 id="7-页面加载完毕"><a href="#7-页面加载完毕" class="headerlink" title="7.页面加载完毕"></a>7.页面加载完毕</h1><hr><p><a href="https://www.cnblogs.com/xianyulaodi/p/6547807.html">更详细的内容可以跳转到这篇博客</a></p>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>跨域问题</title>
<link href="/2021/12/25/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/"/>
<url>/2021/12/25/%E8%B7%A8%E5%9F%9F%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<h1 id="一-为什么会出现跨域问题"><a href="#一-为什么会出现跨域问题" class="headerlink" title="一.为什么会出现跨域问题"></a>一.为什么会出现跨域问题</h1><p>出于<strong>浏览器的同源策略限制</strong>。同源策略(Sameoriginpolicy)是一种约定,它是浏览器最核心也最基本的安全功能,如果缺少了同源策略,则浏览器的正常功能可能都会受到影响。可以说Web是构建在同源策略基础之上的,浏览器只是针对同源策略的一种实现。<strong>同源策略会阻止一个域的javascript脚本和另外一个域的内容进行交互</strong>。所谓<strong>同源(即指在同一个域)就是两个页面具有相同的协议(protocol),主机(host)和端口号(port)</strong></p><h1 id="二-什么是跨域"><a href="#二-什么是跨域" class="headerlink" title="二.什么是跨域"></a>二.什么是跨域</h1><p>当一个请求url的<strong>协议、端口、域名</strong>三者之间任意一个与当前页面url不同即为跨域</p><h1 id="三-非同源的限制"><a href="#三-非同源的限制" class="headerlink" title="三.非同源的限制"></a>三.非同源的限制</h1><p>1.无法读取非同源网页的Cookie、LocalStorage、indexedDB</p><p>2.无法接触非同源网页的DOM</p><p>3.无法向非同源地址发送AJAX请求</p><h1 id="四-跨域解决方法"><a href="#四-跨域解决方法" class="headerlink" title="四.跨域解决方法"></a>四.跨域解决方法</h1><p><strong>1.设置document.domain解决无法读取非同源网页的 Cookie问题</strong><br>因为浏览器是通过document.domain属性来检查两个页面是否同源,因此只要通过设置相同的document.domain,两个页面就可以共享Cookie(此方案<strong>仅限主域相同,子域不同</strong>的跨域应用场景。)</p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk"><span class="hljs-regexp">//</span> 两个页面都设置<br>document.domain = <span class="hljs-string">'test.com'</span>;<br></code></pre></td></tr></table></figure><p><strong>2.跨文档通信 API:window.postMessage()</strong><br>调用postMessage方法实现父窗口<a href="http://test1.com向子窗口http//test2.com%E5%8F%91%E6%B6%88%E6%81%AF%EF%BC%88%E5%AD%90%E7%AA%97%E5%8F%A3%E5%90%8C%E6%A0%B7%E5%8F%AF%E4%BB%A5%E9%80%9A%E8%BF%87%E8%AF%A5%E6%96%B9%E6%B3%95%E5%8F%91%E9%80%81%E6%B6%88%E6%81%AF%E7%BB%99%E7%88%B6%E7%AA%97%E5%8F%A3%EF%BC%89">http://test1.com向子窗口http://test2.com发消息(子窗口同样可以通过该方法发送消息给父窗口)</a></p><p>它可用于解决以下方面的问题:</p><p>(1)页面和其打开的新窗口的数据传递</p><p>(2)多窗口之间消息传递</p><p>(3)页面与嵌套的iframe消息传递</p><p>(4)上面三个场景的跨域数据传递</p><figure class="highlight reasonml"><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><code class="hljs reasonml"><span class="hljs-comment">// 父窗口打开一个子窗口</span><br>var openWindow = window.<span class="hljs-keyword">open</span>('http:<span class="hljs-comment">//test2.com', 'title');</span><br> <br><span class="hljs-comment">// 父窗口向子窗口发消息(第一个参数代表发送的内容,第二个参数代表接收消息窗口的url)</span><br>openWindow.post<span class="hljs-constructor">Message('Nice <span class="hljs-params">to</span> <span class="hljs-params">meet</span> <span class="hljs-params">you</span>!', '<span class="hljs-params">http</span>:<span class="hljs-operator">/</span><span class="hljs-operator">/</span><span class="hljs-params">test2</span>.<span class="hljs-params">com</span>')</span>;<br></code></pre></td></tr></table></figure><p>调用message事件,监听对方发送的消息</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><code class="hljs javascript"><span class="hljs-comment">// 监听 message 消息</span><br><span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'message'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">e</span>) </span>{<br> <span class="hljs-built_in">console</span>.log(e.source); <span class="hljs-comment">// e.source 发送消息的窗口</span><br> <span class="hljs-built_in">console</span>.log(e.origin); <span class="hljs-comment">// e.origin 消息发向的网址</span><br> <span class="hljs-built_in">console</span>.log(e.data); <span class="hljs-comment">// e.data 发送的消息</span><br>},<span class="hljs-literal">false</span>);<br></code></pre></td></tr></table></figure><p><strong>3.JSONP</strong><br>JSONP 是服务器与客户端跨源通信的常用方法。最大特点就是简单适用,兼容性好(兼容低版本IE),<strong>缺点</strong>是只支持get请求,不支持post请求。</p><p>核心思想:网页通过添加一个<script>元素,向服务器请求 JSON 数据,服务器收到请求后,将数据放在一个指定名字的回调函数的参数位置传回来。<br>(1)原生实现</p><figure class="highlight xml"><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><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"http://test.com/data.php?callback=dosomething"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br>// 向服务器test.com发出请求,该请求的查询字符串有一个callback参数,用来指定回调函数的名字<br> <br>// 处理服务器返回回调函数的数据<br><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dosomething</span>(<span class="hljs-params">res</span>)</span>{</span><br><span class="javascript"> <span class="hljs-comment">// 处理获得的数据</span></span><br><span class="javascript"> <span class="hljs-built_in">console</span>.log(res.data)</span><br><span class="javascript"> }</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><p>(2)jQuery.ajax</p><figure class="highlight awk"><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><code class="hljs awk">$.ajax({<br> url: <span class="hljs-string">'http://www.test.com:8080/login'</span>,<br> type: <span class="hljs-string">'get'</span>,<br> dataType: <span class="hljs-string">'jsonp'</span>, <span class="hljs-regexp">//</span> 请求方式为jsonp<br> jsonpCallback: <span class="hljs-string">"handleCallback"</span>, <span class="hljs-regexp">//</span> 自定义回调函数名<br> data: {}<br>});<br></code></pre></td></tr></table></figure><p>#4.CORS<br><strong>(1)普通跨域:只需要服务端设置Access-Control-Allow-Origin</strong><br><strong>(2)带cookie跨域请求:前后端都需要进行设置</strong><br>#5.web sockets<br>web sockets是一种浏览器的API,它的目标是在一个单独的持久连接上提供全双工、双向通信。(同源策略对web sockets不适用)<br>web sockets原理:在<a href="http://lib.csdn.net/base/javascript" title="JavaScript知识库">js</a>创建了web socket之后,会有一个HTTP请求发送到浏览器以发起连接。取得服务器响应后,建立的连接会使用HTTP升级从HTTP协议交换为web sockt协议。<br><strong>只有在支持web socket协议的服务器上才能正常工作</strong>。</p><p>#五.为什么要有限制跨域行为<br>如果我们登录了网银支付网站,然后有登录了一个其他网站,有坏心思的人就会从这个网站中拿到我们的支付信息,十分危险</p><hr><p><a href="https://www.cnblogs.com/yongshaoye/p/7423881.html">其他跨域补充</a><br><a href="https://juejin.cn/post/6844904126246027278#heading-43">最全跨域讲解</a><br><a href="https://www.ruanyifeng.com/blog/2016/04/cors.html">阮一峰讲CORS</a></p>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>HTTP请求头与响应头</title>
<link href="/2021/12/25/HTTP%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%B8%8E%E5%93%8D%E5%BA%94%E5%A4%B4/"/>
<url>/2021/12/25/HTTP%E8%AF%B7%E6%B1%82%E5%A4%B4%E4%B8%8E%E5%93%8D%E5%BA%94%E5%A4%B4/</url>
<content type="html"><![CDATA[<h1 id="1-HTTP请求"><a href="#1-HTTP请求" class="headerlink" title="1.HTTP请求"></a>1.HTTP请求</h1><p><strong>HTTP请求报文由3部分组成(请求行+请求头+请求体)</strong></p><p><img src="/images/pasted-12.png" alt="upload successful"><br>1是请求方法,GET和POST是最常见的HTTP方法,初次以外还包括 DELETE、HEAD、OPTIONS、PUT、TRACE,不过现在大部分的浏览器只支持GET和POST</p><p>2是请求对应的URL地址,他和报文头的Host属性,组合起来是一个完整的请求URL</p><p>3是协议民称和版本号</p><p>4是报文头,有若干个属性,形式为key:val,服务端据此获取客户端信息</p><p>5是报文体,它将一个页面表单中的组件值通过param1=val1&parma=2的键值对形式编码成一个格式化串,它承载多个请求参数的数据,不但报文头可以传递请求参数,URL也可以通过/chapter15/user.html? param1=value1&param2=value2”的方式传递数值</p><h2 id="常见HTTP请求报文头属性"><a href="#常见HTTP请求报文头属性" class="headerlink" title="常见HTTP请求报文头属性"></a>常见HTTP请求报文头属性</h2><p><strong>Accpet</strong>:告诉服务端,客户端接收什么类型的响应<br><strong>Referer</strong>:表示这是请求是从哪个URL进来的,比如想在网上购物,但是不知道选择哪家电商平台,你就去问度娘,说哪家电商的东西便宜啊,然后一堆东西弹出在你面前,第一给就是某宝,当你从这里进入某宝的时候,这个请求报文的Referer就是<a href="www.baidu.com">www.baidu.com</a><br><strong>Cache-Control</strong>:对缓存进行控制,如一个请求希望响应的内容在客户端缓存一年,或不被缓可以通过这个报文头设置<br><strong>Accept-Encoding</strong>:这个属性是用来告诉服务器能接受什么编码格式,包括字符编码,压缩形式(一般都是压缩形式)<br><strong>Host</strong>:指定要请求的资源所在的主机和端口<br><strong>User-Agent</strong> :告诉服务器,客户端使用的操作系统、浏览器版本和名称</p><h1 id="2-HTTP响应报文"><a href="#2-HTTP响应报文" class="headerlink" title="2.HTTP响应报文"></a>2.HTTP响应报文</h1><p><strong>响应报文与请求报文一样,由三个部分组成(响应行,响应头,响应体)</strong></p><p><img src="/images/pasted-13.png" alt="upload successful"><br>1.报文协议及版本;<br>2.状态码及状态描述;<br>3.响应报文头,也是由多个属性组成;<br>4.响应报文体,即我们要的数据。</p><h2 id="常见的HTTP响应报文属性"><a href="#常见的HTTP响应报文属性" class="headerlink" title="常见的HTTP响应报文属性"></a>常见的HTTP响应报文属性</h2><p><strong>Cache-Control:</strong>响应输出到客户端后,服务端通过该属性告诉客户端该怎么控制响应内容的缓存<br><strong>ETag:</strong>表示你请求资源的版本,如果该资源发生啦变化,那么这个属性也会跟着变<br><strong>Location:</strong>在重定向中或者创建新资源时使用<br><strong>Set-Cookie:</strong>服务端可以设置客户端的cookie</p><hr><p><a href="https://www.cnblogs.com/widget90/p/7650890.html">更多请求头和响应头属性</a></p>]]></content>
<categories>
<category>计算机网络基础</category>
</categories>
</entry>
<entry>
<title>Fetch</title>
<link href="/2021/12/25/Fetch/"/>
<url>/2021/12/25/Fetch/</url>
<content type="html"><![CDATA[<p>前言:就像我之前所说的,由于发现了自己在网络方面的知识不足,所以决定充充电,发现除了Ajax之外还有Fetch这么一个东西</p><hr><h1 id="1-什么是Fetch"><a href="#1-什么是Fetch" class="headerlink" title="1.什么是Fetch"></a>1.什么是Fetch</h1><p><a href="https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API">Fetch API</a> 提供了一个 JavaScript 接口,用于访问和操纵 HTTP 管道的一些具体部分,例如请求和响应。它还提供了一个全局 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WindowOrWorkerGlobalScope/fetch" title="fetch()"><code>fetch()</code></a> 方法,该方法提供了一种简单,合理的方式来跨网络异步获取资源。</p><p>这种功能以前是使用 <a href="https://developer.mozilla.org/zh-CN/docs/Web/API/XMLHttpRequest"><code>XMLHttpRequest</code></a> 实现的。Fetch 提供了一个更理想的替代方案,可以很容易地被其他技术使用,例如 [<code>Service Workers</code>]。</p><h1 id="2-Fetch与jquery-ajax的不同"><a href="#2-Fetch与jquery-ajax的不同" class="headerlink" title="2.Fetch与jquery.ajax的不同"></a>2.Fetch与jquery.ajax的不同</h1><p>1)如果Fetch返回的http状态码为400或者500,也就是代表错误的http状态码的情况下, 返回的 Promise 不会被标记为 reject,只有在当网络故障时或请求被阻止时,才会标记为 reject。</p><p>2)<code>fetch()</code>可以<del>不会</del>接受跨域 cookies;**你也可以<del>不能</del>使用 <code>fetch()</code> 建立起跨域会话。<del>其他网站的 <code>[Set-Cookie](https://wiki.developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie)</code> 头部字段将会被无视。</del></p><p>3)<code>fetch</code> 不会发送 cookies。除非你使用了<em>credentials</em> 的<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/WindowOrWorkerGlobalScope/fetch#Parameters">初始化选项</a>。</p><h1 id="3-基本的Fetch使用"><a href="#3-基本的Fetch使用" class="headerlink" title="3.基本的Fetch使用"></a>3.基本的Fetch使用</h1><h2 id="简单的GET请求"><a href="#简单的GET请求" class="headerlink" title="简单的GET请求"></a>简单的GET请求</h2><figure class="highlight sas"><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><code class="hljs sas">fetch(<span class="hljs-string">'http://example.com/movies.json'</span>)<br> .th<span class="hljs-meta">en(</span>functi<span class="hljs-meta">on(</span>response) {<br> <span class="hljs-meta">return</span> response.js<span class="hljs-meta">on(</span>);<br> })<br> .th<span class="hljs-meta">en(</span>functi<span class="hljs-meta">on(</span>myJson) {<br> console<span class="hljs-meta">.log(</span>myJson);<br> });<br></code></pre></td></tr></table></figure><h2 id="简单的POST请求"><a href="#简单的POST请求" class="headerlink" title="简单的POST请求"></a>简单的POST请求</h2><figure class="highlight arcade"><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><code class="hljs arcade">fetch(<span class="hljs-string">'https://www.easy-mock.com/mock/5ca59ba44ba86c23d507bd40/example/getUser'</span>,{<span class="hljs-attr">method</span>:<span class="hljs-string">"post"</span>})<br> .then(<span class="hljs-function"><span class="hljs-params">resp</span>=></span>resp.json()) <span class="hljs-comment">//转换成Json对象</span><br> .then(<span class="hljs-function"><span class="hljs-params">resp</span>=></span>console.log(resp)) <span class="hljs-comment">//输出Json内容</span><br> .catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> console.error(error));<br></code></pre></td></tr></table></figure><h2 id="第二个参数"><a href="#第二个参数" class="headerlink" title="第二个参数"></a>第二个参数</h2><p>一个可以控制不同配置的 init 对象</p><h2 id="上传JSON数据"><a href="#上传JSON数据" class="headerlink" title="上传JSON数据"></a>上传JSON数据</h2><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-keyword">var</span> url = <span class="hljs-string">'https://example.com/profile'</span>;<br><span class="hljs-keyword">var</span> data = {<span class="hljs-attr">username</span>: <span class="hljs-string">'example'</span>};<br><br>fetch(url, {<br> <span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>, <span class="hljs-comment">// or 'PUT'</span><br> body: JSON.stringify(data), <span class="hljs-comment">// data can be `string` or {object}!</span><br> headers: <span class="hljs-keyword">new</span> Headers({<br> <span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span><br> })<br>}).then(<span class="hljs-function"><span class="hljs-params">res</span> =></span> res.json())<br>.catch(<span class="hljs-function"><span class="hljs-params">error</span> =></span> console.error(<span class="hljs-string">'Error:'</span>, error))<br>.then(<span class="hljs-function"><span class="hljs-params">response</span> =></span> console.log(<span class="hljs-string">'Success:'</span>, response));<br></code></pre></td></tr></table></figure><h2 id="上传文件"><a href="#上传文件" class="headerlink" title="上传文件"></a>上传文件</h2><figure class="highlight livescript"><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><code class="hljs livescript"><span class="hljs-keyword">var</span> formData = <span class="hljs-keyword">new</span> FormData();<br><span class="hljs-keyword">var</span> fileField = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"input[type='file']"</span>);<br><br>formData.append(<span class="hljs-string">'username'</span>, <span class="hljs-string">'abc123'</span>);<br>formData.append(<span class="hljs-string">'avatar'</span>, fileField.files[<span class="hljs-number">0</span>]);<br><br>fetch(<span class="hljs-string">'https://example.com/profile/avatar'</span>, {<br> method: <span class="hljs-string">'PUT'</span>,<br> body: formData<br>})<br>.<span class="hljs-keyword">then</span>(response => response.json())<br>.<span class="hljs-keyword">catch</span>(error => <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error))<br>.<span class="hljs-keyword">then</span>(response => <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Success:'</span>, response));<br></code></pre></td></tr></table></figure><h1 id="4-高级的Fetch使用"><a href="#4-高级的Fetch使用" class="headerlink" title="4.高级的Fetch使用"></a>4.高级的Fetch使用</h1><h2 id="超时设置"><a href="#超时设置" class="headerlink" title="超时设置"></a>超时设置</h2><figure class="highlight livescript"><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><code class="hljs livescript"><span class="hljs-keyword">var</span> formData = <span class="hljs-keyword">new</span> FormData();<br><span class="hljs-keyword">var</span> fileField = <span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">"input[type='file']"</span>);<br><br>formData.append(<span class="hljs-string">'username'</span>, <span class="hljs-string">'abc123'</span>);<br>formData.append(<span class="hljs-string">'avatar'</span>, fileField.files[<span class="hljs-number">0</span>]);<br><br>fetch(<span class="hljs-string">'https://example.com/profile/avatar'</span>, {<br> method: <span class="hljs-string">'PUT'</span>,<br> body: formData<br>})<br>.<span class="hljs-keyword">then</span>(response => response.json())<br>.<span class="hljs-keyword">catch</span>(error => <span class="hljs-built_in">console</span>.error(<span class="hljs-string">'Error:'</span>, error))<br>.<span class="hljs-keyword">then</span>(response => <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Success:'</span>, response));<br></code></pre></td></tr></table></figure><h1 id="5-取消请求"><a href="#5-取消请求" class="headerlink" title="5.取消请求"></a>5.取消请求</h1><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript">let controller = <span class="hljs-keyword">new</span> AbortController();<br>let signal = controller.signal;<br><br>let timeoutPromise = <span class="hljs-function"><span class="hljs-params">(timeout)</span> =></span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-params">(resolve, reject)</span> =></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-params">()</span> =></span> {<br> resolve(<span class="hljs-keyword">new</span> Response(<span class="hljs-string">"timeout"</span>, { status: <span class="hljs-number">504</span>, statusText: <span class="hljs-string">"timeout "</span> }));<br> controller.abort();<br> }, timeout);<br> });<br>}<br>let requestPromise = <span class="hljs-function"><span class="hljs-params">(url)</span> =></span> {<br> <span class="hljs-keyword">return</span> fetch(url, {<br> signal: signal<br> });<br>};<br><span class="hljs-built_in">Promise</span>.race([timeoutPromise(<span class="hljs-number">1000</span>), requestPromise(<span class="hljs-string">"https://www.baidu.com"</span>)])<br> .<span class="hljs-keyword">then</span>(resp => {<br> <span class="hljs-built_in">console</span>.log(resp);<br> })<br> .<span class="hljs-keyword">catch</span>(error => {<br> <span class="hljs-built_in">console</span>.log(error);<br> });<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>Ajax</title>
<link href="/2021/12/25/Ajax/"/>
<url>/2021/12/25/Ajax/</url>
<content type="html"><![CDATA[<p>前言:之前做了一个vue移动端的小案例,在这个案例之前呢,我是只会使用axios来在前端进行网络请求,对计算机网络知识也是什么都不懂,这时问题就出现了,我在提交用户选择的表单数据的时候,先是出现了跨域请求,然后又报错500,搞得我手足无措的,所以这两天赶紧学习一下ajax来充充电。</p><hr><h1 id="一-Ajax技术的核心"><a href="#一-Ajax技术的核心" class="headerlink" title="一.Ajax技术的核心"></a>一.Ajax技术的核心</h1><p><strong>XMLHttpRequest对象(简称XHR)</strong></p><h1 id="二-Ajax技术的优点:"><a href="#二-Ajax技术的优点:" class="headerlink" title="二.Ajax技术的优点:"></a>二.Ajax技术的优点:</h1><p><strong>1.无需刷新页面而与服务器进行通讯</strong></p><p><strong>2.允许根据用户事件更新部分内容</strong></p><h1 id="三-Ajax技术的缺点"><a href="#三-Ajax技术的缺点" class="headerlink" title="三.Ajax技术的缺点"></a>三.Ajax技术的缺点</h1><p><strong>1.没有浏览历史,不能回退</strong></p><p><strong>2.存在跨域问题</strong></p><p><strong>3.SEO不友好</strong></p><h1 id="四-XMLHttpRequest对象"><a href="#四-XMLHttpRequest对象" class="headerlink" title="四.XMLHttpRequest对象"></a>四.XMLHttpRequest对象</h1><h2 id="原生JS创建XHR对象(兼容ie7一下版本)"><a href="#原生JS创建XHR对象(兼容ie7一下版本)" class="headerlink" title="原生JS创建XHR对象(兼容ie7一下版本)"></a>原生JS创建XHR对象(兼容ie7一下版本)</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><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 兼容IE早期版本,建立一个XMLHttpRequest对象</span><br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createXHR</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> XMLHttpRequest != <span class="hljs-string">"undefined"</span>) {<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> XMLHttpRequest();<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> ActiveXObject != <span class="hljs-string">"undefined"</span>) {<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> <span class="hljs-built_in">arguments</span>.callee.activeXString != <span class="hljs-string">"string"</span>) {<br> <span class="hljs-keyword">var</span> versions = [<span class="hljs-string">"MSXML2.XMLHttp.6.0"</span>, <span class="hljs-string">"MSXML2.XMLHttp.3.0"</span>,<br> <span class="hljs-string">"MSXML2.XMLHttp"</span>],<br> i, len;<br> <br> <span class="hljs-keyword">for</span> (i = <span class="hljs-number">0</span>, len = versions.length; i < len; i++) {<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">new</span> ActiveXObject(versions[i]);<br> <span class="hljs-built_in">arguments</span>.callee.activeXString = versions[i];<br> <span class="hljs-keyword">break</span>;<br> } <span class="hljs-keyword">catch</span> (ex) {<br> <span class="hljs-comment">//可放入提示文字</span><br> }<br> }<br> }<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> ActiveXObject(<span class="hljs-built_in">arguments</span>.callee.activeXString);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 没有XHR对象可用,抛出错误</span><br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">"No XHR object available."</span>);<br> }<br> }<br></code></pre></td></tr></table></figure><h2 id="XHR的用法"><a href="#XHR的用法" class="headerlink" title="XHR的用法"></a>XHR的用法</h2><h3 id="步骤"><a href="#步骤" class="headerlink" title="步骤"></a>步骤</h3><p><strong>1.创建XHR变量</strong></p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-keyword">let</span> http = <span class="hljs-keyword">new</span> <span class="hljs-constructor">XMLHttpRequest()</span>;<br></code></pre></td></tr></table></figure><p><strong>2.使用open方法配置</strong></p><p><strong>open方法接受三个参数:要发送的请求类型(GET、POST等),请求的URL,表示是否异步发送请求的布尔值</strong></p><figure class="highlight kotlin"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs kotlin">http.<span class="hljs-keyword">open</span>(<span class="hljs-string">'GET'</span> , <span class="hljs-string">'/api/test.php?name=wang'</span> , <span class="hljs-literal">true</span>);<br></code></pre></td></tr></table></figure><p><strong>3.通过send方法提交</strong></p><figure class="highlight abnf"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs abnf">http.send()<span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><h3 id="XHR对象的属性"><a href="#XHR对象的属性" class="headerlink" title="XHR对象的属性"></a>XHR对象的属性</h3><p><strong>在收到响应之后,响应的数据会自动填充XHR对象</strong></p><p><strong>1.responseText:作为响应主体被返回的文本</strong></p><p><strong>2.responseXML:如果响应的内容类型是’text/xml’或者’application/xml’,这个属性中将保存包含着响应数据的HTTP状态</strong></p><p><strong>3.status:HTTP状态的说明</strong></p><p><strong>4.statusText:HTTP状态说明</strong></p><h3 id="XHR的readyState属性"><a href="#XHR的readyState属性" class="headerlink" title="XHR的readyState属性"></a>XHR的readyState属性</h3><p><strong>0:未初始化。尚未调用open方法</strong></p><p><strong>1:启动。已经调用open方法,尚未调用send方法</strong></p><p><strong>2:发送。已经调用send方法,但尚未接受数据</strong></p><p><strong>3:接收。已经接收到部分响应数据</strong></p><p><strong>4:完成。已经接受到全部响应数据,而且已经可以在客户端使用</strong></p><h3 id="取消网络请求"><a href="#取消网络请求" class="headerlink" title="取消网络请求"></a>取消网络请求</h3><p><strong>调用abort()方法来取消异步请求,调用这个方法之后xhr对象会停止触发事件,而且也不再允许访问任何与响应有关的对象属性,在终止请求之后,还应该对XHR对象进行解除引用操作。由于内存原因,不建议重用XHR对象</strong></p><h1 id="五-状态码详解"><a href="#五-状态码详解" class="headerlink" title="五.状态码详解"></a>五.状态码详解</h1><table><thead><tr><th>状态码</th><th>信息</th></tr></thead><tbody><tr><td>200</td><td>表示响应结果请求被正常处理了</td></tr><tr><td>204</td><td>表示请求资源成功,但是没有资源可以返回</td></tr><tr><td>206</td><td>表示请求资源的某一部分,响应中包含content-range</td></tr><tr><td>301</td><td>表示资源的访问路径(URL)被变更</td></tr><tr><td>302</td><td>表示请求的资源已经在其他位置了,但是默认你已经知道了</td></tr><tr><td>303</td><td>表示请求的资源已经在其他位置了,应使用 GET 方法定向获取请求的资源</td></tr><tr><td>304</td><td>表示资源已经请求到到了,但是未能按照正常的方式请求</td></tr><tr><td>307</td><td>临时重定向</td></tr><tr><td>400</td><td>请求报文中存在语法错误,需要修改请求内容重新请求</td></tr><tr><td>401</td><td>需要通过http认证,若之前已进行过 1 次请求,则表示用 户认证失败</td></tr><tr><td>403</td><td>服务器拒绝你的的访问</td></tr><tr><td>404</td><td>服务器上没有请求的资源,可能是地址写错了……</td></tr><tr><td>405</td><td>客户端的请求方法被服务器禁止</td></tr><tr><td>500</td><td>服务器资源发生了错误</td></tr><tr><td>503</td><td>服务器无法请求</td></tr><tr><td>总结</td><td>2xx为请求成功,3xx为URL被改变了,4xx客户端错误,5xx服务器错误</td></tr></tbody></table>]]></content>
<categories>
<category>浏览器</category>
</categories>
</entry>
<entry>
<title>Webpack概述</title>
<link href="/2021/12/25/Webpack%E6%A6%82%E8%BF%B0/"/>
<url>/2021/12/25/Webpack%E6%A6%82%E8%BF%B0/</url>
<content type="html"><![CDATA[<h1 id="一-Webpack是什么"><a href="#一-Webpack是什么" class="headerlink" title="一.Webpack是什么"></a>一.Webpack是什么</h1><p>Webpack是一种前端资源构建工具,一个静态模块打包器,在Webpack看来,前端的所有资源文件都会作为模块处理,它将根据模块的依赖关系进行静态分析,打包生成对应的静态资源。</p><h1 id="二-Webpack的核心概念"><a href="#二-Webpack的核心概念" class="headerlink" title="二.Webpack的核心概念"></a>二.Webpack的核心概念</h1><p>**entry(入口):**指示 webpack 以哪个文件为入口起点开始打包,分析构建内部依赖图。<br><strong>output(输出):</strong>指示webpack打包后的资源bundles输出到哪里去,以及命名<br><strong>loader:</strong>让 webpack 能够去处理那些非 JS 的文件,比如样式文件、图片文件,webpack只能理解Javascript和JSON文件<br><strong>plugins(插件):</strong>loader 用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。包括:打包优化,资源管理,注入环境变量<br><strong>mode(模式):</strong>指示webpack使用相应模式的配置<br><strong>浏览器兼容性(browser compatibility):</strong>webpack 支持所有符合 ES5 标准的浏览器(不支持 IE8 及以下版本)。webpack 的 <code>import()</code> 和 <code>require.ensure()</code> 需要 <code>Promise</code>。如果你想要支持旧版本浏览器,在使用这些表达式之前,还需要 提前加载 polyfill<br><strong>环境(environment):</strong>webpack运行的环境</p><h1 id="三-webpack优化配置"><a href="#三-webpack优化配置" class="headerlink" title="三.webpack优化配置"></a>三.webpack优化配置</h1><h2 id="1-开发环境性能优化"><a href="#1-开发环境性能优化" class="headerlink" title="1.开发环境性能优化"></a>1.开发环境性能优化</h2><hr><p><strong>(1)HMR(模块热替换)</strong><br>HMR: hot module replacement 热模块替换 / 模块热替换</p><p>作用:一个模块发生变化,只会重新打包构建这一个模块(而不是打包所有模块) ,极大提升构建速度</p><p>代码:只需要在 devServer 中设置 hot 为 true,就会自动开启HMR功能<br><strong>(只能在开发者模式下使用)</strong></p><figure class="highlight yaml"><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><code class="hljs yaml"><span class="hljs-attr">devServer:</span> {<br> <span class="hljs-attr">contentBase:</span> <span class="hljs-string">resolve(__dirname</span>, <span class="hljs-string">'build'</span><span class="hljs-string">)</span>,<br> <span class="hljs-attr">compress:</span> <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">port:</span> <span class="hljs-number">3000</span>,<br> <span class="hljs-attr">open:</span> <span class="hljs-literal">true</span>,<br> <span class="hljs-string">//</span> <span class="hljs-string">开启HMR功能</span><br> <span class="hljs-string">//</span> <span class="hljs-string">当修改了webpack配置,新配置要想生效,必须重启webpack服务</span><br> <span class="hljs-attr">hot:</span> <span class="hljs-literal">true</span><br>}<br></code></pre></td></tr></table></figure><p>每种文件实现热模块替换的情况:</p><ul><li><p>样式文件:可以使用HMR功能,因为开发环境下使用的 style-loader 内部默认实现了热模块替换功能</p></li><li><p>js 文件:默认不能使用HMR功能(修改一个 js 模块所有 js 模块都会刷新)</p></li></ul><p>–> 实现 HMR 需要修改 js 代码(添加支持 HMR 功能的代码)</p><figure class="highlight awk"><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><code class="hljs awk"><span class="hljs-regexp">//</span> 绑定<br><span class="hljs-keyword">if</span> (module.hot) {<br> <span class="hljs-regexp">//</span> 一旦 module.hot 为true,说明开启了HMR功能。 --> 让HMR功能代码生效<br> module.hot.accept(<span class="hljs-string">'./print.js'</span>, <span class="hljs-keyword">function</span>() {<br> <span class="hljs-regexp">//</span> 方法会监听 print.js 文件的变化,一旦发生变化,只有这个模块会重新打包构建,其他模块不会。<br> <span class="hljs-regexp">//</span> 会执行后面的回调函数<br> print();<br> });<br>}<br></code></pre></td></tr></table></figure><p><strong>注意:HMR 功能对 js 的处理,只能处理非入口 js 文件的其他文件。</strong></p><ul><li>html文件:默认不能使用 HMR 功能(html 不用做 HMR 功能,因为只有一个 html 文件,不需要再优化)</li></ul><p><strong>使用 HMR 会导致问题:html 文件不能热更新了(不会自动打包构建)</strong></p><p><strong>解决:修改 entry 入口,将 html 文件引入(这样 html 修改整体刷新)</strong></p><figure class="highlight prolog"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs prolog">entry: [<span class="hljs-string">'./src/js/index.js'</span>, <span class="hljs-string">'./src/index.html'</span>]<br></code></pre></td></tr></table></figure><p><strong>(2)source-map</strong><br>source-map:一种提供<strong>源代码到构建后代码的映射</strong>的技术 (如果构建后代码出错了,通过映射可以追踪源代码错误)<br><strong>参数</strong>:<code>[inline-|hidden-|eval-][nosources-][cheap-[module-]]source-map</code><br><strong>代码:</strong></p><figure class="highlight maxima"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs maxima">devtool: '<span class="hljs-built_in">eval</span>-source-<span class="hljs-built_in">map</span>'<br></code></pre></td></tr></table></figure><p>可选方案:[生成source-map的位置|给出的错误代码信息]</p><ul><li><p>source-map:外部,错误代码准确信息 和 源代码的错误位置</p></li><li><p>inline-source-map:内联,只生成一个内联 source-map,错误代码准确信息 和 源代码的错误位置</p></li><li><p>hidden-source-map:外部,错误代码错误原因,但是没有错误位置(为了隐藏源代码),不能追踪源代码错误,只能提示到构建后代码的错误位置</p></li><li><p>eval-source-map:内联,每一个文件都生成对应的 source-map,都在 eval 中,错误代码准确信息 和 源代码的错误位</p></li><li><p>nosources-source-map:外部,错误代码准确信息,但是没有任何源代码信息(为了隐藏源代码)</p></li><li><p>cheap-source-map:外部,错误代码准确信息 和 源代码的错误位置,只能把错误精确到整行,忽略列</p></li><li><p>cheap-module-source-map:外部,错误代码准确信息 和 源代码的错误位置,module 会加入 loader 的 source-map</p></li></ul><p><strong>内联 和 外部的区别:</strong>1. 外部生成了文件,内联没有 2. 内联构建速度更快</p><p><strong>开发/生产环境可做的选择:</strong><br><strong>开发环境:</strong>需要考虑速度快,调试更友好</p><ul><li>速度快( eval > inline > cheap >… )<br>(1)eval-cheap-souce-map<br>(2)eval-source-map</li><li>调试更友好<br>(1)souce-map<br>(2)cheap-module-souce-map<br>(3)cheap-souce-map</li></ul><p><strong>最终得出最好的两种方案 –> eval-source-map(完整度高,内联速度快) / eval-cheap-module-souce-map(错误提示忽略列但是包含其他信息,内联速度快)</strong></p><p><strong>生产环境:</strong>需要考虑源代码要不要隐藏,调试要不要更友好</p><ul><li>内联会让代码体积变大,所以在生产环境不用内联</li><li>隐藏源代码<br>(1)nosources-source-map 全部隐藏<br>(2)hidden-source-map 只隐藏源代码,会提示构建后代码错误信息</li></ul><p><strong>最终得出最好的两种方案 –> source-map(最完整) / cheap-module-souce-map(错误提示一整行忽略列)</strong></p><h2 id="2-生产环境性能优化"><a href="#2-生产环境性能优化" class="headerlink" title="2.生产环境性能优化"></a>2.生产环境性能优化</h2><hr><h3 id="优化打包构建速度"><a href="#优化打包构建速度" class="headerlink" title="优化打包构建速度"></a>优化打包构建速度</h3><p><strong>(1)oneOf</strong><br>oneOf:匹配到loader后就不再向后进行匹配,优化生产环境的打包构建速度<br><strong>代码</strong></p><figure class="highlight gradle"><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><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br></pre></td><td class="code"><pre><code class="hljs gradle">module: {<br> rules: [<br> {<br> <span class="hljs-comment">// js 语法检查</span><br> test: <span class="hljs-regexp">/\.js$/</span>,<br> <span class="hljs-keyword">exclude</span>: <span class="hljs-regexp">/node_modules/</span>,<br> <span class="hljs-comment">// 优先执行</span><br> enforce: <span class="hljs-string">'pre'</span>,<br> loader: <span class="hljs-string">'eslint-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> fix: <span class="hljs-keyword">true</span><br> }<br> },<br> {<br> <span class="hljs-comment">// oneOf 优化生产环境的打包构建速度</span><br> <span class="hljs-comment">// 以下loader只会匹配一个(匹配到了后就不会再往下匹配了)</span><br> <span class="hljs-comment">// 注意:不能有两个配置处理同一种类型文件(所以把eslint-loader提取出去放外面)</span><br> oneOf: [<br> {<br> test: <span class="hljs-regexp">/\.css$/</span>,<br> use: [...commonCssLoader]<br> },<br> {<br> test: <span class="hljs-regexp">/\.less$/</span>,<br> use: [...commonCssLoader, <span class="hljs-string">'less-loader'</span>]<br> },<br> {<br> <span class="hljs-comment">// js 兼容性处理</span><br> test: <span class="hljs-regexp">/\.js$/</span>,<br> <span class="hljs-keyword">exclude</span>: <span class="hljs-regexp">/node_modules/</span>,<br> loader: <span class="hljs-string">'babel-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> presets: [<br> [<br> <span class="hljs-string">'@babel/preset-env'</span>,<br> {<br> useBuiltIns: <span class="hljs-string">'usage'</span>,<br> corejs: {version: <span class="hljs-number">3</span>},<br> targets: {<br> chrome: <span class="hljs-string">'60'</span>,<br> firefox: <span class="hljs-string">'50'</span><br> }<br> }<br> ]<br> ]<br> }<br> },<br> {<br> test: <span class="hljs-regexp">/\.(jpg|png|gif)/</span>,<br> loader: <span class="hljs-string">'url-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> limit: <span class="hljs-number">8</span> * <span class="hljs-number">1024</span>,<br> name: <span class="hljs-string">'[hash:10].[ext]'</span>,<br> outputPath: <span class="hljs-string">'imgs'</span>,<br> esModule: <span class="hljs-keyword">false</span><br> }<br> },<br> {<br> test: <span class="hljs-regexp">/\.html$/</span>,<br> loader: <span class="hljs-string">'html-loader'</span><br> },<br> {<br> <span class="hljs-keyword">exclude</span>: <span class="hljs-regexp">/\.(js|css|less|html|jpg|png|gif)/</span>,<br> loader: <span class="hljs-string">'file-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> outputPath: <span class="hljs-string">'media'</span><br> }<br> }<br> ]<br> }<br> ]<br>},<br></code></pre></td></tr></table></figure><blockquote><p><strong>注意</strong>:如果oneOf中有相同的loader,一定要只在oneOf中只留一个loader</p></blockquote><p><strong>(2)babel缓存:</strong>类似 HMR,将 babel 处理后的资源缓存起来(哪里的 js 改变就更新哪里,其他 js 还是用之前缓存的资源),让第二次打包构建速度更快<br><strong>代码</strong></p><figure class="highlight gradle"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs gradle">{<br> test: <span class="hljs-regexp">/\.js$/</span>,<br> <span class="hljs-keyword">exclude</span>: <span class="hljs-regexp">/node_modules/</span>,<br> loader: <span class="hljs-string">'babel-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> presets: [<br> [<br> <span class="hljs-string">'@babel/preset-env'</span>,<br> {<br> useBuiltIns: <span class="hljs-string">'usage'</span>,<br> corejs: { version: <span class="hljs-number">3</span> },<br> targets: {<br> chrome: <span class="hljs-string">'60'</span>,<br> firefox: <span class="hljs-string">'50'</span><br> }<br> }<br> ]<br> ],<br> <span class="hljs-comment">// 开启babel缓存</span><br> <span class="hljs-comment">// 第二次构建时,会读取之前的缓存</span><br> cacheDirectory: <span class="hljs-keyword">true</span><br> }<br>}<br></code></pre></td></tr></table></figure><p><strong>文件资源缓存:</strong>文件名不变,就不会重新请求,而是再次用之前缓存的资源<br>1.<strong>hash</strong>:每次wepack 打包时会生成一个唯一的 hash 值。<br> <strong>问题</strong>:重新打包,所有文件的 hsah 值都改变,会导致所有缓存失效。(可能只改动了一个文件)<br>2.<strong>chunkhash</strong>:根据 chunk 生成的 hash 值。来源于同一个 chunk的 hash 值一样<br><strong>问题</strong>:js 和 css 来自同一个chunk,hash 值是一样的(因为 css-loader 会将 css 文件加载到 js 中,所以同属于一个chunk)<br>3.<strong>contenthash</strong>: 根据文件的内容生成 hash 值。不同文件 hash 值一定不一样(文件内容修改,文件名里的 hash 才会改变)<br>修改 css 文件内容,打包后的 css 文件名 hash 值就改变,而 js 文件没有改变 hash 值就不变,这样 css 和 js 缓存就会分开判断要不要重新请求资源 –> 让代码上线运行缓存更好使用<br><strong>(3)多进程打包</strong><br>多进程打包:某个任务消耗时间较长会卡顿,多进程可以同一时间干多件事,效率更高。<br><strong>优点</strong>是提升打包速度,缺点是每个进程的开启和交流都会有开销(babel-loader消耗时间最久,所以使用thread-loader针对其进行优化)</p><figure class="highlight gradle"><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><code class="hljs gradle">{<br> test: <span class="hljs-regexp">/\.js$/</span>,<br> <span class="hljs-keyword">exclude</span>: <span class="hljs-regexp">/node_modules/</span>,<br> use: [<br> <span class="hljs-comment">/* </span><br><span class="hljs-comment"> thread-loader会对其后面的loader(这里是babel-loader)开启多进程打包。 </span><br><span class="hljs-comment"> 进程启动大概为600ms,进程通信也有开销。(启动的开销比较昂贵,不要滥用)</span><br><span class="hljs-comment"> 只有工作消耗时间比较长,才需要多进程打包</span><br><span class="hljs-comment"> */</span><br> {<br> loader: <span class="hljs-string">'thread-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> workers: <span class="hljs-number">2</span> <span class="hljs-comment">// 进程2个</span><br> }<br> },<br> {<br> loader: <span class="hljs-string">'babel-loader'</span>,<br> <span class="hljs-keyword">options</span>: {<br> presets: [<br> [<br> <span class="hljs-string">'@babel/preset-env'</span>,<br> {<br> useBuiltIns: <span class="hljs-string">'usage'</span>,<br> corejs: { version: <span class="hljs-number">3</span> },<br> targets: {<br> chrome: <span class="hljs-string">'60'</span>,<br> firefox: <span class="hljs-string">'50'</span><br> }<br> }<br> ]<br> ],<br> <span class="hljs-comment">// 开启babel缓存</span><br> <span class="hljs-comment">// 第二次构建时,会读取之前的缓存</span><br> cacheDirectory: <span class="hljs-keyword">true</span><br> }<br> }<br> ]<br>}<br></code></pre></td></tr></table></figure><p><strong>(4) externals</strong><br>externals:让某些库不打包,通过 cdn 引入<br><strong>webpack.config.js 中配置</strong></p><figure class="highlight awk"><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><code class="hljs awk">externals: {<br> <span class="hljs-regexp">//</span> 拒绝jQuery被打包进来(通过cdn引入,速度会快一些)<br> <span class="hljs-regexp">//</span> 忽略的库名 -- npm包名<br> jquery: <span class="hljs-string">'jQuery'</span><br>}<br></code></pre></td></tr></table></figure><p>需要在 index.html 中通过 cdn 引入:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">src</span>=<span class="hljs-string">"https://cdn.bootcss.com/jquery/1.12.4/jquery.min.js"</span>></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><p><strong>(5)dll</strong><br>dll:让某些库单独打包,后直接引入到 build 中。可以在 code split 分割出 node_modules 后再用 dll 更细的分割,优化代码运行的性能。<br><strong>webpack.dll.js 配置:(将 jquery 单独打包)</strong></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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">/*</span><br><span class="hljs-comment"> node_modules的库会打包到一起,但是很多库的时候打包输出的js文件就太大了</span><br><span class="hljs-comment"> 使用dll技术,对某些库(第三方库:jquery、react、vue...)进行单独打包</span><br><span class="hljs-comment"> 当运行webpack时,默认查找webpack.config.js配置文件</span><br><span class="hljs-comment"> 需求:需要运行webpack.dll.js文件</span><br><span class="hljs-comment"> --> webpack --config webpack.dll.js(运行这个指令表示以这个配置文件打包)</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">const</span> { resolve } = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>);<br><span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack'</span>);<br><br><span class="hljs-built_in">module</span>.exports = {<br> <span class="hljs-attr">entry</span>: {<br> <span class="hljs-comment">// 最终打包生成的[name] --> jquery</span><br> <span class="hljs-comment">// ['jquery] --> 要打包的库是jquery</span><br> <span class="hljs-attr">jquery</span>: [<span class="hljs-string">'jquery'</span>]<br> },<br> <span class="hljs-attr">output</span>: {<br> <span class="hljs-comment">// 输出出口指定</span><br> <span class="hljs-attr">filename</span>: <span class="hljs-string">'[name].js'</span>, <span class="hljs-comment">// name就是jquery</span><br> <span class="hljs-attr">path</span>: resolve(__dirname, <span class="hljs-string">'dll'</span>), <span class="hljs-comment">// 打包到dll目录下</span><br> <span class="hljs-attr">library</span>: <span class="hljs-string">'[name]_[hash]'</span>, <span class="hljs-comment">// 打包的库里面向外暴露出去的内容叫什么名字</span><br> },<br> <span class="hljs-attr">plugins</span>: [<br> <span class="hljs-comment">// 打包生成一个manifest.json --> 提供jquery的映射关系(告诉webpack:jquery之后不需要再打包和暴露内容的名称)</span><br> <span class="hljs-keyword">new</span> webpack.DllPlugin({<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'[name]_[hash]'</span>, <span class="hljs-comment">// 映射库的暴露的内容名称</span><br> <span class="hljs-attr">path</span>: resolve(__dirname, <span class="hljs-string">'dll/manifest.json'</span>) <span class="hljs-comment">// 输出文件路径</span><br> })<br> ],<br> <span class="hljs-attr">mode</span>: <span class="hljs-string">'production'</span><br>};<br></code></pre></td></tr></table></figure><p><strong>webpack.config.js 配置:(告诉 webpack 不需要再打包 jquery,并将之前打包好的 jquery 跟其他打包好的资源一同输出到 build 目录下)</strong></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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-comment">// 引入插件</span><br><span class="hljs-keyword">const</span> webpack = <span class="hljs-built_in">require</span>(<span class="hljs-string">'webpack'</span>);<br><span class="hljs-keyword">const</span> AddAssetHtmlWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'add-asset-html-webpack-plugin'</span>);<br><br><span class="hljs-comment">// plugins中配置:</span><br>plugins: [<br> <span class="hljs-keyword">new</span> HtmlWebpackPlugin({<br> <span class="hljs-attr">template</span>: <span class="hljs-string">'./src/index.html'</span><br> }),<br> <span class="hljs-comment">// 告诉webpack哪些库不参与打包,同时使用时的名称也得变</span><br> <span class="hljs-keyword">new</span> webpack.DllReferencePlugin({<br> <span class="hljs-attr">manifest</span>: resolve(__dirname, <span class="hljs-string">'dll/manifest.json'</span>)<br> }),<br> <span class="hljs-comment">// 将某个文件打包输出到build目录下,并在html中自动引入该资源</span><br> <span class="hljs-keyword">new</span> AddAssetHtmlWebpackPlugin({<br> <span class="hljs-attr">filepath</span>: resolve(__dirname, <span class="hljs-string">'dll/jquery.js'</span>)<br> })<br>]<br></code></pre></td></tr></table></figure><hr><h3 id="优化代码运行的性能"><a href="#优化代码运行的性能" class="headerlink" title="优化代码运行的性能"></a>优化代码运行的性能</h3><p><strong>(1)缓存</strong></p><p><strong>(2)tree shaking(树摇)</strong><br>tree shaking:去除无用代码</p><p><strong>前提</strong>:1. 必须使用 ES6 模块化 2. 开启 production 环境 (这样就自动会把无用代码去掉)</p><p><strong>作用</strong>:减少代码体积</p><p><strong>在 package.json 中配置</strong>:</p><p>“sideEffects”: false 表示所有代码都没有副作用(都可以进行 tree shaking)<br>这样会导致的<strong>问题</strong>:可能会把 css / @babel/polyfill 文件干掉(副作用)<br>所以可以配置:”sideEffects”: [“<em>.css”, “</em>.less”] 不会对css/less文件tree shaking处理<br><strong>(3)code split(代码分割)</strong><br>代码分割:将打包输出的一个大的 bundle.js 文件拆分成多个小文件,这样可以并行加载多个文件,比加载一个文件更快。<br><strong>多入口拆分</strong></p><figure class="highlight awk"><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><code class="hljs awk">entry: {<br> <span class="hljs-regexp">//</span> 多入口:有一个入口,最终输出就有一个bundle<br> index: <span class="hljs-string">'./src/js/index.js'</span>,<br> test: <span class="hljs-string">'./src/js/test.js'</span><br> },<br> output: {<br> <span class="hljs-regexp">//</span> [name]:取文件名<br> filename: <span class="hljs-string">'js/[name].[contenthash:10].js'</span>,<br> path: resolve(__dirname, <span class="hljs-string">'build'</span>)<br> },<br></code></pre></td></tr></table></figure><p><strong>optimization</strong></p><ul><li>将 node_modules 中的代码单独打包(大小超过30kb)</li><li>自动分析多入口chunk中,有没有公共的文件。如果有会打包成单独一个chunk(比如两个模块中都引入了jquery会被打包成单独的文件)(大小超过30kb)<figure class="highlight properties"><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><code class="hljs properties"><span class="hljs-attr">optimization</span>: <span class="hljs-string">{</span><br> <span class="hljs-attr">splitChunks</span>: <span class="hljs-string">{</span><br> <span class="hljs-attr">chunks</span>: <span class="hljs-string">'all'</span><br> <span class="hljs-attr">}</span><br> <span class="hljs-attr">},</span><br></code></pre></td></tr></table></figure></li><li><em>import动态引入语法</em>*<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><code class="hljs javascript"><span class="hljs-comment">/*</span><br><span class="hljs-comment"> 通过js代码,让某个文件被单独打包成一个chunk</span><br><span class="hljs-comment"> import动态导入语法:能将某个文件单独打包(test文件不会和index打包在同一个文件而是单独打包)</span><br><span class="hljs-comment"> webpackChunkName:指定test单独打包后文件的名字</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">import</span>(<span class="hljs-comment">/* webpackChunkName: 'test' */</span><span class="hljs-string">'./test'</span>)<br> .then(<span class="hljs-function">(<span class="hljs-params">{ mul, count }</span>) =></span> {<br> <span class="hljs-comment">// 文件加载成功~</span><br> <span class="hljs-comment">// eslint-disable-next-line</span><br> <span class="hljs-built_in">console</span>.log(mul(<span class="hljs-number">2</span>, <span class="hljs-number">5</span>));<br> })<br> .catch(<span class="hljs-function">() =></span> {<br> <span class="hljs-comment">// eslint-disable-next-line</span><br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'文件加载失败~'</span>);<br> });<br></code></pre></td></tr></table></figure></li><li><em>(4)lazy loading(懒加载/预加载)</em>*</li></ul><p>1.<strong>懒加载</strong>:当文件需要使用时才加载(需要代码分割)。但是如果资源较大,加载时间就会较长,有延迟。<br>2.<strong>正常加载</strong>:可以认为是并行加载(同一时间加载多个文件)没有先后顺序,先加载了不需要的资源就会浪费时间。<br>3.<strong>预加载 prefetch(兼容性很差)</strong>:会在使用之前,提前加载。等其他资源加载完毕,浏览器空闲了,再偷偷加载这个资源。这样在使用时已经加载好了,速度很快。所以在懒加载的基础上加上预加载会更好。<br><strong>代码</strong></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><code class="hljs javascript"><span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'btn'</span>).onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-comment">// 将import的内容放在异步回调函数中使用,点击按钮,test.js才会被加载(不会重复加载)</span><br> <span class="hljs-comment">// webpackPrefetch: true表示开启预加载</span><br> <span class="hljs-keyword">import</span>(<span class="hljs-comment">/* webpackChunkName: 'test', webpackPrefetch: true */</span><span class="hljs-string">'./test'</span>).then(<span class="hljs-function">(<span class="hljs-params">{ mul }</span>) =></span> {<br> <span class="hljs-built_in">console</span>.log(mul(<span class="hljs-number">4</span>, <span class="hljs-number">5</span>));<br> });<br> <span class="hljs-keyword">import</span>(<span class="hljs-string">'./test'</span>).then(<span class="hljs-function">(<span class="hljs-params">{ mul }</span>) =></span> {<br> <span class="hljs-built_in">console</span>.log(mul(<span class="hljs-number">2</span>, <span class="hljs-number">5</span>))<br> })<br>};<br></code></pre></td></tr></table></figure><p><strong>(5)pwa(离线可访问技术)</strong><br>pwa:离线可访问技术(渐进式网络开发应用程序),使用 serviceworker 和 workbox 技术。优点是离线也能访问,缺点是兼容性差。</p><p><strong>webpack.config.js 中配置</strong>:</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><code class="hljs javascript"><span class="hljs-keyword">const</span> WorkboxWebpackPlugin = <span class="hljs-built_in">require</span>(<span class="hljs-string">'workbox-webpack-plugin'</span>); <span class="hljs-comment">// 引入插件</span><br><br><span class="hljs-comment">// plugins中加入:</span><br><span class="hljs-keyword">new</span> WorkboxWebpackPlugin.GenerateSW({<br> <span class="hljs-comment">/*</span><br><span class="hljs-comment"> 1. 帮助serviceworker快速启动</span><br><span class="hljs-comment"> 2. 删除旧的 serviceworker</span><br><span class="hljs-comment"></span><br><span class="hljs-comment"> 生成一个 serviceworker 配置文件</span><br><span class="hljs-comment"> */</span><br> <span class="hljs-attr">clientsClaim</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">skipWaiting</span>: <span class="hljs-literal">true</span><br>})<br></code></pre></td></tr></table></figure><p><strong>index.js 中还需要写一段代码来激活它的使用:</strong></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><code class="hljs javascript"><span class="hljs-comment">/*</span><br><span class="hljs-comment"> 1. eslint不认识 window、navigator全局变量</span><br><span class="hljs-comment"> 解决:需要修改package.json中eslintConfig配置</span><br><span class="hljs-comment"> "env": {</span><br><span class="hljs-comment"> "browser": true // 支持浏览器端全局变量</span><br><span class="hljs-comment"> }</span><br><span class="hljs-comment"> 2. sw代码必须运行在服务器上</span><br><span class="hljs-comment"> --> nodejs</span><br><span class="hljs-comment"> 或--></span><br><span class="hljs-comment"> npm i serve -g</span><br><span class="hljs-comment"> serve -s build 启动服务器,将打包输出的build目录下所有资源作为静态资源暴露出去</span><br><span class="hljs-comment">*/</span><br><span class="hljs-keyword">if</span> (<span class="hljs-string">'serviceWorker'</span> <span class="hljs-keyword">in</span> navigator) { <span class="hljs-comment">// 处理兼容性问题</span><br> <span class="hljs-built_in">window</span>.addEventListener(<span class="hljs-string">'load'</span>, <span class="hljs-function">() =></span> {<br> navigator.serviceWorker<br> .register(<span class="hljs-string">'/service-worker.js'</span>) <span class="hljs-comment">// 注册serviceWorker</span><br> .then(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sw注册成功了~'</span>);<br> })<br> .catch(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'sw注册失败了~'</span>);<br> });<br> });<br>}<br></code></pre></td></tr></table></figure><hr><p><strong>文章学习地址:</strong><a href="http://www.woc12138.com/article/45">Webpack入门-学习总结 | Woc12138</a></p><p><strong>视频学习地址:</strong><a href="https://www.bilibili.com/video/BV1e7411j7T5?p=1">尚硅谷最新版Webpack5实战教程(从入门到精通)_哔哩哔哩_bilibili</a></p>]]></content>
<categories>
<category>Webpack</category>
</categories>
</entry>
<entry>
<title>异步函数async/awit</title>
<link href="/2021/12/25/%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0async-awit/"/>
<url>/2021/12/25/%E5%BC%82%E6%AD%A5%E5%87%BD%E6%95%B0async-awit/</url>
<content type="html"><![CDATA[<h1 id="async"><a href="#async" class="headerlink" title="async"></a>async</h1><p><strong>用法</strong>:async作为一个关键字放到函数前面,用于表示函数是一个异步函数,因为async就是异步的意思, 异步函数也就意味着该函数的执行不会阻塞后面代码的执行。<br><strong>async修饰函数返回值</strong>:async函数返回的是一个promise对象,函数原来的返回值是返回的promise的resolved函数的参数,而抛出的异常会是返回的promise的rejected的参数,这样我们就可以实现promise一样的效果了。</p><h1 id="awit"><a href="#awit" class="headerlink" title="awit"></a>awit</h1><p><strong>用法</strong>:awit只能在async函数中使用,会使本来异步调用的函数变成同步阻塞,并且调用的函数需要本来返回一个promise。<br><strong>awit调用函数的返回值</strong>:该函数本来应该返回一个promise,但是当使用awit调用之后会返回这个promise中resolved函数传递的参数。</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>JS判断一个字符串中是否包含另一个字符串的方法</title>
<link href="/2021/12/25/JS%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E6%98%AF%E5%90%A6%E5%8C%85%E5%90%AB%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95/"/>
<url>/2021/12/25/JS%E5%88%A4%E6%96%AD%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E4%B8%AD%E6%98%AF%E5%90%A6%E5%8C%85%E5%90%AB%E5%8F%A6%E4%B8%80%E4%B8%AA%E5%AD%97%E7%AC%A6%E4%B8%B2%E7%9A%84%E6%96%B9%E6%B3%95/</url>
<content type="html"><![CDATA[<h1 id="1-String对象的方法"><a href="#1-String对象的方法" class="headerlink" title="1.String对象的方法"></a>1.String对象的方法</h1><p><strong>indexOf()方法</strong></p><figure class="highlight axapta"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs axapta"><span class="hljs-built_in">var</span> <span class="hljs-built_in">str</span> = <span class="hljs-string">"123"</span>;<br>console.log(<span class="hljs-built_in">str</span>.indexOf(<span class="hljs-string">"3"</span>) != <span class="hljs-number">-1</span> ); <span class="hljs-comment">// true</span><br></code></pre></td></tr></table></figure><p>传入一个字符串,匹配不上返回-1,匹配上了返回字串首次出现的位置.<br><strong>search()方法</strong></p><figure class="highlight openscad"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs openscad">var <span class="hljs-built_in">str</span> = <span class="hljs-string">"123"</span>;<br>console.<span class="hljs-built_in">log</span>(<span class="hljs-built_in">str</span>.<span class="hljs-built_in">search</span>(<span class="hljs-string">"3"</span>) != -<span class="hljs-number">1</span> ); <span class="hljs-comment">// true</span><br></code></pre></td></tr></table></figure><p>search() 方法用于检索字符串中<strong>指定的子字符串,或检索与正则表达式相匹配的子字符串</strong>。如果没有找到任何匹配的子串,则返回 -1。<br><strong>match()方法</strong></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><code class="hljs javascript"><span class="hljs-keyword">var</span> str = <span class="hljs-string">"123"</span>;<br><span class="hljs-keyword">var</span> reg = <span class="hljs-built_in">RegExp</span>(<span class="hljs-regexp">/3/</span>);<br><span class="hljs-keyword">if</span>(str.match(reg)){<br> <span class="hljs-comment">// 包含 </span><br>}<br></code></pre></td></tr></table></figure><p>match() 方法可在字符串内检索指定的值,或找到一个或多个正则表达式的匹配。</p><h1 id="2-RegExp对象的方法"><a href="#2-RegExp对象的方法" class="headerlink" title="2.RegExp对象的方法"></a>2.RegExp对象的方法</h1><p><strong>test()方法</strong></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><code class="hljs javascript"><span class="hljs-keyword">var</span> str = <span class="hljs-string">"123"</span>;<br><span class="hljs-keyword">var</span> reg = <span class="hljs-built_in">RegExp</span>(<span class="hljs-regexp">/3/</span>);<br><span class="hljs-built_in">console</span>.log(reg.test(str)); <span class="hljs-comment">// true</span><br></code></pre></td></tr></table></figure><p>test() 方法用于检索字符串中指定的值。返回 true 或 false。<br><strong>exec()方法</strong></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><code class="hljs javascript"><span class="hljs-keyword">var</span> str = <span class="hljs-string">"123"</span>;<br><span class="hljs-keyword">var</span> reg = <span class="hljs-built_in">RegExp</span>(<span class="hljs-regexp">/3/</span>);<br><span class="hljs-keyword">if</span>(reg.exec(str)){<br> <span class="hljs-comment">// 包含 </span><br>}<br></code></pre></td></tr></table></figure><p>exec() 方法用于检索字符串中的正则表达式的匹配。返回一个数组,其中存放匹配的结果。如果未找到匹配,则返回值为 null。</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>JS数组中的常用方法</title>
<link href="/2021/12/25/JS%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95/"/>
<url>/2021/12/25/JS%E6%95%B0%E7%BB%84%E4%B8%AD%E7%9A%84%E5%B8%B8%E7%94%A8%E6%96%B9%E6%B3%95/</url>
<content type="html"><![CDATA[<h1 id="1-reduce"><a href="#1-reduce" class="headerlink" title="1.reduce"></a>1.reduce</h1><h2 id="(1)reduce的使用方法:reduce-func-first"><a href="#(1)reduce的使用方法:reduce-func-first" class="headerlink" title="(1)reduce的使用方法:reduce(func,first)"></a>(1)reduce的使用方法:reduce(func,first)</h2><p>func是一个函数,会传入四个参数,function(prev,cur,index,arr)<br><strong>prev是上一次迭代后的函数返回值,在第一次迭代之前是reduce传入的第二个参数,如果没有这个参数,那么就是默认为数组的第一个元素</strong><br><strong>cur是执行当前迭代的元素</strong><br><strong>index是当前迭代元素的下标</strong><br><strong>arr是执行这个reduce方法的数组</strong></p><hr><p>first是第一次迭代的时候prev值的大小</p><hr><h2 id="(2)举例"><a href="#(2)举例" class="headerlink" title="(2)举例"></a>(2)举例</h2><figure class="highlight fortran"><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><code class="hljs fortran">let arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br>let <span class="hljs-built_in">sum</span> = arr.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(prev, cur, index, arr)</span></span> {<br> console.<span class="hljs-built_in">log</span>(prev, cur, <span class="hljs-built_in">index</span>);<br> <span class="hljs-keyword">return</span> prev + cur;<br>})<br>console.<span class="hljs-built_in">log</span>(arr, <span class="hljs-built_in">sum</span>);<br></code></pre></td></tr></table></figure><p><img src="/images/pasted-10.png" alt="upload successful"></p><figure class="highlight fortran"><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><code class="hljs fortran">let arr = [<span class="hljs-number">1</span>, <span class="hljs-number">2</span>, <span class="hljs-number">3</span>, <span class="hljs-number">4</span>];<br>let <span class="hljs-built_in">sum</span> = arr.reduce(<span class="hljs-function"><span class="hljs-keyword">function</span><span class="hljs-params">(prev, cur, index, arr)</span></span> {<br> console.<span class="hljs-built_in">log</span>(prev, cur, <span class="hljs-built_in">index</span>);<br> <span class="hljs-keyword">return</span> prev + cur;<br>},<span class="hljs-number">5</span>)<br>console.<span class="hljs-built_in">log</span>(arr, <span class="hljs-built_in">sum</span>);<br></code></pre></td></tr></table></figure><p><img src="/images/pasted-11.png" alt="upload successful"></p><hr><h2 id="(3)应用"><a href="#(3)应用" class="headerlink" title="(3)应用"></a>(3)应用</h2><p>计算每个元素出现次数</p><figure class="highlight vim"><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><code class="hljs vim"><span class="hljs-keyword">let</span> arr = [<span class="hljs-string">'name'</span>,<span class="hljs-string">'age'</span>,<span class="hljs-string">'long'</span>,<span class="hljs-string">'short'</span>,<span class="hljs-string">'long'</span>,<span class="hljs-string">'name'</span>,<span class="hljs-string">'name'</span>] <br><br><span class="hljs-keyword">let</span> arrResult = arr.reduce((<span class="hljs-keyword">pre</span>,cur) =>{<br> console.<span class="hljs-built_in">log</span>(<span class="hljs-keyword">pre</span>,cur)<br> <span class="hljs-keyword">if</span>(cur in <span class="hljs-keyword">pre</span>){<br> <span class="hljs-keyword">pre</span>[cur]++<br> }<span class="hljs-keyword">else</span>{<br> <span class="hljs-keyword">pre</span>[cur] = <span class="hljs-number">1</span><br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">pre</span><br>},{})<br><br>console.<span class="hljs-built_in">log</span>(arrResult)//结果:{name: <span class="hljs-number">3</span>, age: <span class="hljs-number">1</span>, lon<span class="hljs-variable">g:</span> <span class="hljs-number">2</span>, shor<span class="hljs-variable">t:</span> <span class="hljs-number">1</span>}<br></code></pre></td></tr></table></figure><p>去重</p><figure class="highlight coffeescript"><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><code class="hljs coffeescript">let arrResult = arr.reduce(<span class="hljs-function"><span class="hljs-params">(pre,cur)</span> =></span>{<br> <span class="hljs-keyword">if</span>(!pre.includes(cur)){<br> pre.push(cur)<br> }<br> <span class="hljs-keyword">return</span> pre;<br>},[])<br><br><span class="hljs-built_in">console</span>.log(arrResult)<span class="hljs-regexp">//</span>结果:[<span class="hljs-string">"name"</span>, <span class="hljs-string">"age"</span>, <span class="hljs-string">"long"</span>, <span class="hljs-string">"short"</span>]<br><br></code></pre></td></tr></table></figure><h1 id="2-map"><a href="#2-map" class="headerlink" title="2.map"></a>2.map</h1><p>此方法是将数组中的每个元素调用一个提供的函数,<strong>结果作为一个新的数组返回,并没有改变原来的数组</strong></p><h1 id="3-forEach"><a href="#3-forEach" class="headerlink" title="3.forEach"></a>3.forEach</h1><p>与map类似,但是<strong>不会返回新数组,而是会修改原数组</strong></p><h1 id="4-filter"><a href="#4-filter" class="headerlink" title="4.filter"></a>4.filter</h1><p>此方法是将所有元素进行判断,将满足条件的元素作为一个新的数组返回,传入的函数参数返回的是布尔值</p><h1 id="5-every"><a href="#5-every" class="headerlink" title="5.every"></a>5.every</h1><p>此方法是将所有元素进行判断返回一个布尔值,<strong>如果所有元素都满足判断条件,则返回true,否则为false</strong></p><h1 id="6-some"><a href="#6-some" class="headerlink" title="6.some"></a>6.some</h1><p> 此方法是将所有元素进行判断返回一个布尔值,<strong>如果存在元素都满足判断条件,则返回true,若所有元素都不满足判断条件,则返回false</strong></p><h1 id="7-push-pop-shift-unshift-isArray,toString"><a href="#7-push-pop-shift-unshift-isArray,toString" class="headerlink" title="7.push,pop,shift,unshift,isArray,toString"></a>7.push,pop,shift,unshift,isArray,toString</h1><p>push:向数组末尾添加元素<br>pop:把数组末尾元素删除<br>shift:删除数组的第一个元素<br>unshift:将一个或者多个元素添加到数组前面,返回新长度<br>isArray:判断是不是数组<br>concat:将多个数组拼接成数组<br>toString:将数组转化为字符串</p><h1 id="8-join"><a href="#8-join" class="headerlink" title="8.join"></a>8.join</h1><p>可以将数组转化为字符串,但是传入的字符串参数会成为数组字符串之间的间隔</p><h1 id="9-splice-start-num-instead"><a href="#9-splice-start-num-instead" class="headerlink" title="9.splice(start,num,instead)"></a>9.splice(start,num,instead)</h1><p>start:开始的下标<br>num:删除的个数<br>instead:替换的元素</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>原生JS的懒加载实现</title>
<link href="/2021/12/25/%E5%8E%9F%E7%94%9FJS%E7%9A%84%E6%87%92%E5%8A%A0%E8%BD%BD%E5%AE%9E%E7%8E%B0/"/>
<url>/2021/12/25/%E5%8E%9F%E7%94%9FJS%E7%9A%84%E6%87%92%E5%8A%A0%E8%BD%BD%E5%AE%9E%E7%8E%B0/</url>
<content type="html"><![CDATA[<p>在一个网站含有大量的图片,我们在进入一个页面的时候如果一下子全部加载完毕,就会造成很大的资源浪费,而懒加载就是用户浏览到哪里,就加载哪里的图片,这样就可以减少资源的浪费了。</p><hr><h1 id="1-懒加载的实现原理"><a href="#1-懒加载的实现原理" class="headerlink" title="1.懒加载的实现原理"></a>1.懒加载的实现原理</h1><p>一个img标签如果没有src属性,浏览器就不会发起请求,所以我们在图片没有进入可视区域之前就不会给img标签赋值src属性</p><h1 id="2-懒加载的实现方式"><a href="#2-懒加载的实现方式" class="headerlink" title="2.懒加载的实现方式"></a>2.懒加载的实现方式</h1><h2 id="(1)使用scrollTop-innerHeight-offsetTop"><a href="#(1)使用scrollTop-innerHeight-offsetTop" class="headerlink" title="(1)使用scrollTop/innerHeight/offsetTop"></a>(1)使用scrollTop/innerHeight/offsetTop</h2><p><strong>window.innerHeight</strong>:浏览器可视区域高度<br><strong>document.body.scrollTop || document.documentElement.scrollTop</strong>:浏览器滚动条滚过高度(考虑到了兼容)<br><strong>img.offsetTop</strong>:元素距文档顶部的高度 </p><p><img src="/images/pasted-9.png" alt="upload successful"><br><strong>图片的加载条件:</strong>img.offsetTop < window.innerHeight + document.body.scrollTop;</p><p>基础代码:</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"><span class="hljs-keyword">var</span> imgs = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'img'</span>);</span><br><span class="javascript"><span class="hljs-built_in">window</span>.onscroll = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</span><br><span class="javascript"><span class="hljs-keyword">var</span> scrollTop = <span class="hljs-built_in">document</span>.body.scrollTop || <span class="hljs-built_in">document</span>.documentElement.scrollTop;</span><br><span class="javascript"><span class="hljs-keyword">var</span> winTop = <span class="hljs-built_in">window</span>.innerHeight;</span><br><span class="javascript"><span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>;i < imgs.length;i++){</span><br><span class="javascript"><span class="hljs-keyword">if</span>(imgs[i].offsetTop < scrollTop + winTop ){</span><br><span class="javascript">imgs[i].src = imgs[i].getAttribute(<span class="hljs-string">'data-src'</span>);</span><br><span class="javascript">}</span><br><span class="javascript">}</span><br><span class="javascript">}</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><p>但是我们需要优化一下基础代码,为了防止重复加载,我们需要对基础代码中的函数使用函数节流</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"><span class="hljs-keyword">var</span> imgs = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'img'</span>);</span><br><span class="javascript"><span class="hljs-keyword">var</span> lazyload = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</span><br><span class="javascript"><span class="hljs-keyword">var</span> scrollTop = <span class="hljs-built_in">document</span>.body.scrollTop || <span class="hljs-built_in">document</span>.documentElement.scrollTop;</span><br><span class="javascript"><span class="hljs-keyword">var</span> winTop = <span class="hljs-built_in">window</span>.innerHeight;</span><br><span class="javascript"><span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> i=<span class="hljs-number">0</span>;i < imgs.length;i++){</span><br><span class="javascript"><span class="hljs-keyword">if</span>(imgs[i].offsetTop < scrollTop + winTop ){</span><br><span class="javascript">imgs[i].src = imgs[i].getAttribute(<span class="hljs-string">'data-src'</span>);</span><br><span class="javascript">}</span><br><span class="javascript">}</span><br><span class="javascript">}</span><br><span class="javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throttle</span>(<span class="hljs-params">method,delay</span>)</span>{</span><br><span class="javascript"><span class="hljs-keyword">var</span> timer = <span class="hljs-literal">null</span>;</span><br><span class="javascript"><span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</span><br><span class="javascript"><span class="hljs-keyword">var</span> context = <span class="hljs-built_in">this</span>, args=<span class="hljs-built_in">arguments</span>;</span><br><span class="javascript"><span class="hljs-built_in">clearTimeout</span>(timer);</span><br><span class="javascript">timer=<span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</span><br><span class="javascript">method.apply(context,args);</span><br><span class="javascript">},delay);</span><br><span class="javascript">}</span><br><span class="javascript">}</span><br><span class="javascript"><span class="hljs-built_in">window</span>.onscroll = throttle(lazyload,<span class="hljs-number">200</span>);</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><h2 id="(2)使用IntersectionObserver方法"><a href="#(2)使用IntersectionObserver方法" class="headerlink" title="(2)使用IntersectionObserver方法"></a>(2)使用IntersectionObserver方法</h2><p><strong>基本知识</strong></p><figure class="highlight reasonml"><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><code class="hljs reasonml">var io = <span class="hljs-keyword">new</span> <span class="hljs-constructor">IntersectionObserver(<span class="hljs-params">callback</span>, <span class="hljs-params">option</span>)</span>;<br><span class="hljs-comment">//开始观察</span><br>io.observe(document.get<span class="hljs-constructor">ElementById('<span class="hljs-params">example</span>')</span>);<br><span class="hljs-comment">//停止观察</span><br>io.unobserve(element);<br><span class="hljs-comment">// 关闭观察器</span><br>io.disconnect<span class="hljs-literal">()</span>;<br></code></pre></td></tr></table></figure><p><strong>callback</strong>是可见性变化时的回调函数,<strong>option</strong>是配置对象。这个构造函数的返回值是一个观察器实例。构造函数的返回值是一个观察器实例,实例的observe方法可以指定观察哪个DOM节点。</p><p>上面代码中,observe的参数是一个DOM节点对象。如果要观察多个节点,就要多次调用这个方法。<strong>callback函数的参数(entries)是一个数组,每个成员都是一个IntersectionObserverEntry对象。</strong>举例来说,如果同时有两个被观察的对象的可见性发生变化,entries数组就会有两个成员。<strong>intersectionRatio</strong>:目标元素的可见比例,完全可见时为1,完全不可见时小于等于0。<br><strong>完整代码</strong></p><figure class="highlight reasonml"><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><code class="hljs reasonml"><script <span class="hljs-keyword">type</span>=<span class="hljs-string">"text/javascript"</span>><br><span class="hljs-comment">//获取观察器实例 changes是被观察的对象数组</span><br>var observer = <span class="hljs-keyword">new</span> <span class="hljs-constructor">IntersectionObserver(<span class="hljs-params">function</span>(<span class="hljs-params">changes</span>)</span>{ <br>console.log(changes);<br>changes.<span class="hljs-keyword">for</span><span class="hljs-constructor">Each(<span class="hljs-params">function</span>(<span class="hljs-params">index</span>,<span class="hljs-params">item</span>)</span>{<br><span class="hljs-keyword">if</span>(item.intersectionRatio > <span class="hljs-number">0</span><span class="hljs-operator"> && </span>item.intersectionRatio < <span class="hljs-number">1</span>)<br><span class="hljs-comment">//target:被观察的目标元素,是一个 DOM 节点对象</span><br>item.target.src = item.target.dataset.src;<br>});<br>});<br><span class="hljs-keyword">function</span> add<span class="hljs-constructor">Observer()</span>{<br>var listItems = document.query<span class="hljs-constructor">SelectorAll('.<span class="hljs-params">img</span>-<span class="hljs-params">item</span>')</span>;<br>listItems.<span class="hljs-keyword">for</span><span class="hljs-constructor">Each(<span class="hljs-params">function</span>(<span class="hljs-params">item</span>)</span>{<br><span class="hljs-comment">//实例的observe方法可以指定观察哪个DOM节点</span><br><span class="hljs-comment">//开始观察 observe的参数是一个 DOM 节点对象</span><br>observer.observe(item);<br>});<br>}<br>add<span class="hljs-constructor">Observer()</span>;<br></script><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>函数节流</title>
<link href="/2021/12/25/%E5%87%BD%E6%95%B0%E8%8A%82%E6%B5%81/"/>
<url>/2021/12/25/%E5%87%BD%E6%95%B0%E8%8A%82%E6%B5%81/</url>
<content type="html"><![CDATA[<h1 id="一-什么是函数节流"><a href="#一-什么是函数节流" class="headerlink" title="一.什么是函数节流"></a>一.什么是函数节流</h1><p>限制一个函数在一定时间之内只能执行一次(这里做一下与函数防抖的区别:函数防抖是触发事件后在 n 秒内函数只能执行一次,<strong>如果在 n 秒内又触发了事件,则会重新计算函数执行时间</strong>)</p><h1 id="二-函数节流使用场景"><a href="#二-函数节流使用场景" class="headerlink" title="二.函数节流使用场景"></a>二.函数节流使用场景</h1><p>前端开发过程中,<strong>有一些事件或者函数,会被频繁地触发(短时间按内多次触发)</strong>,最常见的例如,onresize,scroll,mousemove ,mousehover 等,这些事件的触发频率很高,不做限制的话,有可能一秒之内执行几十次、几百次,如果在这些函数内部执行了其他函数,<strong>尤其是执行了操作 DOM 的函数(浏览器操作 DOM 是很耗费性能的),那不仅会造成计算机资源的浪费,还会降低程序运行速度,甚至造成浏览器卡死、崩溃</strong>。这种问题显然是致命的。<br>除此之外,<strong>重复的 ajax 调用不仅可能会造成请求数据的混乱,还会造成网络拥塞,占用服务器带宽,增加服务器压力,显然这个问题也是需要解决的</strong>。</p><h1 id="三-函数节流的思路"><a href="#三-函数节流的思路" class="headerlink" title="三.函数节流的思路"></a>三.函数节流的思路</h1><p>主要实现思路就是通过 setTimeout 定时器,通过设置延时时间,在第一次调用时,创建定时器,先设定一个变量true,写入需要执行的函数。第二次执行这个函数时,会判断变量是否true,是则返回。当第一次的定时器执行完函数最后会设定变量为false。那么下次判断变量时则为false,函数会依次运行。目的在于在一定的时间内,保证多次函数的请求只执行第一次点击时的调用</p><h1 id="四-代码实现"><a href="#四-代码实现" class="headerlink" title="四.代码实现"></a>四.代码实现</h1><p><strong>利用时间戳:</strong>判断再次执行事件的时候的时间与之前触发的时间间隔是否大于指定间隔,如果大于等于的话就再次执行事件</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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throttle</span>(<span class="hljs-params">fn,wait</span>)</span>{<br> <span class="hljs-keyword">var</span> pre = <span class="hljs-built_in">Date</span>.now();<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> context = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">var</span> args = <span class="hljs-built_in">arguments</span>;<br> <span class="hljs-keyword">var</span> now = <span class="hljs-built_in">Date</span>.now();<br> <span class="hljs-keyword">if</span>( now - pre >= wait){<br> fn.apply(context,args);<br> pre = <span class="hljs-built_in">Date</span>.now();<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p><strong>定时器:</strong>如果定时器存在,那么直接终止事件的触发,否则触发事件</p><figure class="highlight maxima"><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><code class="hljs maxima">function throttle(fn, <span class="hljs-built_in">delay</span>) {<br> <span class="hljs-built_in">let</span> <span class="hljs-built_in">timer</span>;<br> <span class="hljs-built_in">return</span> function () {<br> <span class="hljs-built_in">var</span> _this = this;<br> <span class="hljs-built_in">var</span> <span class="hljs-built_in">args</span> = arguments;<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">timer</span>) {<br> <span class="hljs-built_in">return</span>;<br> }<br> <span class="hljs-built_in">timer</span> = setTimeout(function () {<br> fn.<span class="hljs-built_in">apply</span>(_this, <span class="hljs-built_in">args</span>); // 这里<span class="hljs-built_in">args</span>接收的是外边返回的函数的参数,不能用arguments<br> // fn.<span class="hljs-built_in">apply</span>(_this, arguments); 需要注意:Chrome <span class="hljs-number">14</span> 以及 Internet Explorer <span class="hljs-number">9</span> 仍然不接受类数组对象。如果传入类数组对象,它们会抛出异常。<br> <span class="hljs-built_in">timer</span> = null; // 在<span class="hljs-built_in">delay</span>后执行完fn之后清空<span class="hljs-built_in">timer</span>,此时<span class="hljs-built_in">timer</span>为假,throttle触发可以进入计时器<br> }, <span class="hljs-built_in">delay</span>)<br> }<br>}<br></code></pre></td></tr></table></figure><h1 id="五-节流的使用场景"><a href="#五-节流的使用场景" class="headerlink" title="五.节流的使用场景"></a>五.节流的使用场景</h1><p>1.懒加载、滚动加载、加载更多或监听滚动条位置;<br>2.百度搜索框,搜索联想功能;<br>3.防止高频点击提交,防止表单重复提交;</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>函数防抖</title>
<link href="/2021/12/25/%E5%87%BD%E6%95%B0%E9%98%B2%E6%8A%96/"/>
<url>/2021/12/25/%E5%87%BD%E6%95%B0%E9%98%B2%E6%8A%96/</url>
<content type="html"><![CDATA[<h1 id="一-什么是函数防抖"><a href="#一-什么是函数防抖" class="headerlink" title="一.什么是函数防抖"></a>一.什么是函数防抖</h1><p>函数防抖(debounce),就是指触发事件后,在 n 秒内函数只能执行一次,<strong>如果触发事件后在 n 秒内又触发了事件,则会重新计算函数延执行时间</strong>(在这里和函数节流区分一下,函数节流是在<strong>触发完事件之后的一段时间之内不能再次触发事件</strong>)。</p><h1 id="二-函数防抖的实现思路"><a href="#二-函数防抖的实现思路" class="headerlink" title="二.函数防抖的实现思路"></a>二.函数防抖的实现思路</h1><p>在触发事件的时候,设置一个定时器,如果之前就已经触发过该事件了,就清除之前设置的定时器,重新设置一个定时器,如果在触发事件之后没有再次触发事件,那么就等待计时结束执行函数。</p><h1 id="三-代码实现"><a href="#三-代码实现" class="headerlink" title="三.代码实现"></a>三.代码实现</h1><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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debounce</span>(<span class="hljs-params">fn, delay</span>) </span>{<br> <span class="hljs-keyword">if</span>(<span class="hljs-keyword">typeof</span> fn!==<span class="hljs-string">'function'</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'fn不是函数'</span>)<br> }<br> <span class="hljs-keyword">let</span> timer; <span class="hljs-comment">// 维护一个 timer</span><br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">var</span> _this = <span class="hljs-built_in">this</span>; <span class="hljs-comment">// 取debounce执行作用域的this(原函数挂载到的对象)</span><br> <span class="hljs-keyword">var</span> args = <span class="hljs-built_in">arguments</span>;<br> <span class="hljs-keyword">if</span> (timer) {<br> <span class="hljs-built_in">clearTimeout</span>(timer);<br> }<br> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> fn.apply(_this, args); <span class="hljs-comment">// 用apply指向调用debounce的对象,相当于_this.fn(args);</span><br> }, delay);<br> };<br>}<br></code></pre></td></tr></table></figure><h1 id="四-函数防抖的使用场景"><a href="#四-函数防抖的使用场景" class="headerlink" title="四.函数防抖的使用场景"></a>四.函数防抖的使用场景</h1><p><strong>1.搜索框搜索输入。只需用户最后一次输入完,再发送请求;</strong><br><strong>2.用户名、手机号、邮箱输入验证;</strong><br><strong>3.浏览器窗口大小改变后,只需窗口调整完后,再执行 resize 事件中的代码,防止重复渲染。</strong></p><h1 id="五-防抖的立即执行与非立即执行"><a href="#五-防抖的立即执行与非立即执行" class="headerlink" title="五.防抖的立即执行与非立即执行"></a>五.防抖的立即执行与非立即执行</h1><p>函数防抖其实是分为 “立即执行版” 和 “非立即执行版” 的,根据字面意思就可以发现他们的差别,<strong>所谓立即执行版就是 触发事件后函数不会立即执行,而是在 n 秒后执行,如果在 n 秒内又触发了事件,则会重新计算函数执行时间。 而 “非立即执行版” 指的是 触发事件后函数会立即执行,然后 n 秒内不触发事件才能继续执行函数的效果</strong></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><code class="hljs javascript"><span class="hljs-comment">/**</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@desc </span>函数防抖---“立即执行版本” 和 “非立即执行版本” 的组合版本</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param </span>func 需要执行的函数</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param </span>wait 延迟执行时间(毫秒)</span><br><span class="hljs-comment"> * <span class="hljs-doctag">@param <span class="hljs-variable">immediate</span></span>---true 表立即执行,false 表非立即执行</span><br><span class="hljs-comment"> **/</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debounce</span>(<span class="hljs-params">func,wait,immediate</span>) </span>{<br> <span class="hljs-keyword">let</span> timer;<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">let</span> context = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">let</span> args = <span class="hljs-built_in">arguments</span>;<br><br> <span class="hljs-keyword">if</span> (timer) <span class="hljs-built_in">clearTimeout</span>(timer);<br> <span class="hljs-keyword">if</span> (immediate) {<br> <span class="hljs-keyword">var</span> callNow = !timer;<br> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> timer = <span class="hljs-literal">null</span>;<br> }, wait)<br> <span class="hljs-keyword">if</span> (callNow) func.apply(context, args)<br> } <span class="hljs-keyword">else</span> {<br> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> func.apply(context, args)<br> }, wait);<br> }<br> }<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>JS原生Background Tasks</title>
<link href="/2021/12/24/JS%E5%8E%9F%E7%94%9FBackground-Tasks/"/>
<url>/2021/12/24/JS%E5%8E%9F%E7%94%9FBackground-Tasks/</url>
<content type="html"><![CDATA[<h1 id="一-window-requestIdleCallback"><a href="#一-window-requestIdleCallback" class="headerlink" title="一.window.requestIdleCallback"></a>一.window.requestIdleCallback</h1><h2 id="1-作用:"><a href="#1-作用:" class="headerlink" title="1.作用:"></a>1.作用:</h2><p>这个方法将会在浏览器的空闲时间调用函数排队。这样可以使操作者在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,比如动画和输入响应,函数一般会按照先进先执行的顺序进行调用,但是如果设置了timeout(超时执行时间),将会导致为了在超时之前执行相应的函数操作而打乱执行顺序。</p><h2 id="2-使用方法"><a href="#2-使用方法" class="headerlink" title="2.使用方法"></a>2.使用方法</h2><p>window.requestIdleCallback(callback,option)<br><strong>参数</strong>:<br>callback:一个在浏览器空闲时即将被调用的函数引用函数会接收到一个名为<strong>IdleDeadline</strong>的参数,这个参数可以获取当前空闲时间以及回调是否在超时时间前已经执行的状态。<strong>(IdleDeadline对象包括didTimeout,是一个布尔值表示任务是否超时;以及timeRemaining(),表示当前帧剩余的时间,是留给任务执行的时间)</strong><br>option:配置项,可以配置timeout,是可选参数(<strong>timeout:如果指定了timeout并具有一个正值,并且尚未通过超时毫秒数调用回调,那么回调会在下一次空闲时期被强制执行,尽管这样很可能会对性能造成负面影响。</strong>)<br><strong>返回值:</strong><br>一个ID,可以通过window.cancelIdleCallback()来结束回调</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></pre></td><td class="code"><pre><code class="hljs javascript">requestIdleCallback(myNonEssentialWork, { <span class="hljs-attr">timeout</span>: <span class="hljs-number">2000</span> });<br><br><span class="hljs-comment">// 任务队列</span><br><span class="hljs-keyword">const</span> tasks = [<br> <span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"第一个任务"</span>);<br> },<br> <span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"第二个任务"</span>);<br> },<br> <span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"第三个任务"</span>);<br> },<br>];<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">myNonEssentialWork</span> (<span class="hljs-params">deadline</span>) </span>{<br> <span class="hljs-comment">// 如果帧内有富余的时间,或者超时</span><br> <span class="hljs-keyword">while</span> ((deadline.timeRemaining() > <span class="hljs-number">0</span> || deadline.didTimeout) && tasks.length > <span class="hljs-number">0</span>) {<br> work();<br> }<br><br> <span class="hljs-keyword">if</span> (tasks.length > <span class="hljs-number">0</span>)<br> requestIdleCallback(myNonEssentialWork);<br> }<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">work</span> (<span class="hljs-params"></span>) </span>{<br> tasks.shift()();<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'执行任务'</span>);<br>}<br></code></pre></td></tr></table></figure><h2 id="3-使用setTimeout模拟实现"><a href="#3-使用setTimeout模拟实现" class="headerlink" title="3.使用setTimeout模拟实现"></a>3.使用setTimeout模拟实现</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><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-built_in">window</span>.requestIdleCallback = <span class="hljs-built_in">window</span>.requestIdleCallback || <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">handler</span>) </span>{<br> <span class="hljs-keyword">let</span> startTime = <span class="hljs-built_in">Date</span>.now();<br> <br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> handler({<br> <span class="hljs-attr">didTimeout</span>: <span class="hljs-literal">false</span>,<br> <span class="hljs-attr">timeRemaining</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.max(<span class="hljs-number">0</span>, <span class="hljs-number">50.0</span> - (<span class="hljs-built_in">Date</span>.now() - startTime));<br> }<br> });<br> }, <span class="hljs-number">1</span>);<br>}<br></code></pre></td></tr></table></figure><p><strong>可以在 ric 中执行任务时需要注意以下几点</strong>:<br>1.执行重计算而非紧急任务<br>2.空闲回调执行时间应该小于 50ms,最好更少<br>3.空闲回调中不要操作 DOM,因为它本来就是利用的重拍重绘后的间隙空闲时间,重新操作 DOM 又会造成重拍重绘,<strong>DOM 操作建议在 rAF 中进行</strong>。同时,操作 DOM 所需要的耗时是不确定的,因为会导致重新计算布局和视图的绘制,所以这类操作不具备可预测性。<br>4.Promise 也不建议在这里面进行,因为 Promise 的回调属性 Event loop 中优先级较高的一种微任务,会在 requestIdleCallback 结束时立即执行,不管此时是否还有富余的时间,这样有很大可能会让一帧超过 16 ms。<br>React 的时间分片便是基于类似 requestIdleCallback 而实现,然而因为 ric 的兼容性及 50ms 流畅问题,React 自制了一个实现: scheduler</p><h1 id="二-window-requestAnimationFrame"><a href="#二-window-requestAnimationFrame" class="headerlink" title="二.window.requestAnimationFrame"></a>二.window.requestAnimationFrame</h1><h2 id="1-作用:-1"><a href="#1-作用:-1" class="headerlink" title="1.作用:"></a>1.作用:</h2><p>通知浏览器,要求在下一次重绘之前调用指定的回调函数更新动画。<strong>如果希望在下一次重绘之前更新下一帧动画,那么需要回调函数自身必须再次调用window.requestAnimationFrame()</strong>,当window.requestAnimationFrame运行在后台标签或者隐藏的iframe时,会被暂停来提高性能</p><h2 id="2-用法"><a href="#2-用法" class="headerlink" title="2.用法"></a>2.用法</h2><p>window.requestAnimationFrame(callback)<br><strong>参数:</strong><br>callback:下一次重绘之前更新动画帧所调用的函数。该回调函数会被传入<strong>DOMHighResTimeStamp</strong>参数,它表示的是开始执行回调函数的时刻<br><strong>返回值:</strong><br>一个ID,可以传入window.cancelAnimationFrame()以取消回调函数。</p><h2 id="3-补充"><a href="#3-补充" class="headerlink" title="3.补充"></a>3.补充</h2><p>以往我们执行动画动画时所采用的操作是setTimeout和setInterval,这种做法的弊端就是:回调函数执行时间是不固定的,有可能刚好就卡在末尾或者不再执行了,会引起丢帧和卡顿</p><p><img src="/images/pasted-8.png" alt="upload successful"></p><blockquote><p>归根到底发生上面这个问题的原因在于时机,也就是浏览器要知道何时对回调函数进行响应。**<code>setTimeout</code> 或 <code>setInterval</code> 是使用定时器来触发回调函数的,而定时器并无法保证能够准确无误的执行,有许多因素会影响它的运行时机,比如说:当有同步代码执行时,会先等同步代码执行完毕,异步队列中没有其他任务,才会轮到自己执行**。并且,我们知道每一次重新渲染的最佳时间大约是 16.6 ms,如果定时器的时间间隔过短,就会造成 <a href="https://links.jianshu.com/go?to=https://link.zhihu.com/?target=https%253A//www.zhangxinxu.com/wordpress/2013/09/css3-animation-requestanimationframe-tween-%25E5%258A%25A8%25E7%2594%25BB%25E7%25AE%2597%25E6%25B3%2595/">过度渲染</a>,增加开销;过长又会延迟渲染,使动画不流畅。</p></blockquote><p>requestAnimationFrame 方法不同与 setTimeout 或 setInterval,<strong>它是由系统来决定回调函数的执行时机的</strong>,会请求浏览器在下一次重新渲染之前执行回调函数。无论设备的刷新率是多少,requestAnimationFrame 的时间间隔都会紧跟屏幕刷新一次所需要的时间;例如某一设备的刷新率是 75 Hz,那这时的时间间隔就是 13.3 ms(1 秒 / 75 次)。需要注意的是这个方法虽然能够保证回调函数在每一帧内只渲染一次,但是如果这一帧有太多任务执行,还是会造成卡顿的;因此它只能保证重新渲染的时间间隔最短是屏幕的刷新时间。</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>documentFragment深入理解</title>
<link href="/2021/12/24/documentFragment%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3/"/>
<url>/2021/12/24/documentFragment%E6%B7%B1%E5%85%A5%E7%90%86%E8%A7%A3/</url>
<content type="html"><![CDATA[<h1 id="先搬一段官方的描述:"><a href="#先搬一段官方的描述:" class="headerlink" title="先搬一段官方的描述:"></a>先搬一段官方的描述:</h1><p>DocumentFragment 表示一个没有父级文件的最小文档对象。它被当做一个轻量版的 Document 使用,用于存储已排好版的或尚未打理好格式的XML片段。最大的区别是因为DocumentFragment不是真实DOM树的一部分,<strong>它的变化不会引起DOM树的重新渲染的操作(reflow)</strong> ,且不会导致性能等问题。<br>DocumentFragment 接口表示文档的一部分(或一段)。更确切地说,它表示一个或多个邻接的 Document 节点和它们的所有子孙节点。 DocumentFragment 节点不属于文档树,继承的 parentNode 属性总是 null。 <strong>不过它有一种特殊的行为,该行为使得它非常有用,即当请求把一个 DocumentFragment 节点插入文档树时,插入的不是 DocumentFragment 自身,而是它的所有子孙节点。</strong>这使得 DocumentFragment 成了有用的占位符,暂时存放那些一次插入文档的节点。它还有利于实现文档的剪切、复制和粘贴操作,尤其是与 Range 接口一起使用时更是如此。 可以用 Document.createDocumentFragment() 方法创建新的空 DocumentFragment 节点</p><hr><p>这样我们就明白了documentFragment的一些特性,它被修改的之后不会去渲染浏览器页面,不会引起浏览器的重绘与回流,我们可以把它作为一个中转站,把多次的dom操作汇聚到这一次来进行,把需要添加的dom元素都先添加到documentFragment中去,减少回流次数,就可以实现性能的优化了。</p><hr><h2 id="基本用法:"><a href="#基本用法:" class="headerlink" title="基本用法:"></a>基本用法:</h2><figure class="highlight xml"><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><code class="hljs xml">//初始显示test1<br><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"test"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>test1<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>test1<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>test1<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">div</span>></span><br></code></pre></td></tr></table></figure><figure class="highlight awk"><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></pre></td><td class="code"><pre><code class="hljs awk">const ul = document.getElementById(<span class="hljs-string">'test'</span>)<br><span class="hljs-regexp">//</span> 创建fragment对象<br>const fragment = document.createDocumentFragment()<br>console.log(fragment.nodeValue); <span class="hljs-regexp">//</span>null<br>console.log(fragment.nodeName); <span class="hljs-regexp">//</span><span class="hljs-comment">#document-fragment</span><br>console.log(fragment.nodeType); <span class="hljs-regexp">//</span><br><span class="hljs-regexp">//</span> 取出ul中的所有子节点并保存到fragment<br>let child;<br><span class="hljs-keyword">while</span>(child=ul.firstChild) {<br> fragment.appendChild(child)<br>}<br><span class="hljs-regexp">//</span>更新fragment中的所有节点(li的内容)<br>Array.prototype.slice.call(fragment.childNodes).forEach(node => {<br> <span class="hljs-keyword">if</span> (node.nodeType===<span class="hljs-number">1</span>) {<span class="hljs-regexp">//</span>取得元素节点<br> node.textContent = <span class="hljs-string">'test2'</span> <span class="hljs-regexp">//</span>重新赋值为test2<br> }<br>})<br><span class="hljs-regexp">//</span> 将fragment插入到ul<br>ul.appendChild(fragment)<br></code></pre></td></tr></table></figure><p>所以使用documentFragment来进行批量跟新的操作可以极大的节省性能。</p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>Js对象判空与对象是否包含某个属性</title>
<link href="/2021/12/24/Js%E5%AF%B9%E8%B1%A1%E5%88%A4%E7%A9%BA%E4%B8%8E%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E5%8C%85%E5%90%AB%E6%9F%90%E4%B8%AA%E5%B1%9E%E6%80%A7/"/>
<url>/2021/12/24/Js%E5%AF%B9%E8%B1%A1%E5%88%A4%E7%A9%BA%E4%B8%8E%E5%AF%B9%E8%B1%A1%E6%98%AF%E5%90%A6%E5%8C%85%E5%90%AB%E6%9F%90%E4%B8%AA%E5%B1%9E%E6%80%A7/</url>
<content type="html"><![CDATA[<h1 id="对象判空"><a href="#对象判空" class="headerlink" title="对象判空"></a>对象判空</h1><p>1.将对象转换为JSON字符串,判断是否为‘{}’</p><figure class="highlight haskell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs haskell"><span class="hljs-title">var</span> b =<span class="hljs-class"><span class="hljs-keyword">data</span>=> (<span class="hljs-type">JSON</span>.<span class="hljs-title">stringify</span>(<span class="hljs-title">data</span>) == "{}");</span><br></code></pre></td></tr></table></figure><p>2.for in循环对象</p><figure class="highlight kotlin"><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><code class="hljs kotlin"><span class="hljs-keyword">var</span> b =<span class="hljs-keyword">data</span>=> {<br><span class="hljs-keyword">for</span>(<span class="hljs-keyword">var</span> key <span class="hljs-keyword">in</span> <span class="hljs-keyword">data</span>){<span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;}<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br></code></pre></td></tr></table></figure><p>3.jquery的isEmptyObject方法</p><figure class="highlight arcade"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs arcade"><span class="hljs-keyword">var</span> b = $.isEmptyObject(data);<br></code></pre></td></tr></table></figure><p>4.Object.getOwnPropertyNames()方法<br>该方法会把对象的属性名全部收集到一个数组之中,并把数组返回,判断数组长度是否为0</p><figure class="highlight kotlin"><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><code class="hljs kotlin"><span class="hljs-keyword">var</span> b =<span class="hljs-keyword">data</span>=> {<br><span class="hljs-keyword">if</span>(Object.getOwnPropertyNames().length==<span class="hljs-number">0</span>){<br><span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>}<br></code></pre></td></tr></table></figure><p>5.使用ES6的Object.keys()方法(与4的方法类似)</p><h1 id="判断对象中是否包含某个属性"><a href="#判断对象中是否包含某个属性" class="headerlink" title="判断对象中是否包含某个属性"></a>判断对象中是否包含某个属性</h1><p>1.判断是否为undefined</p><figure class="highlight applescript"><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><code class="hljs applescript"><span class="hljs-keyword">if</span> (obj2.a){<br> console.<span class="hljs-built_in">log</span>(<span class="hljs-string">"对象有此属性"</span>)<br>}<span class="hljs-keyword">else</span> {<br> console.<span class="hljs-built_in">log</span>(<span class="hljs-string">"对象无此属性"</span>)<br>}<br></code></pre></td></tr></table></figure><p>2.in运算符 (但是如果属性在对象的原型链上存在,那么会返回true)</p><figure class="highlight vim"><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><code class="hljs vim"><span class="hljs-keyword">let</span> obj2 = {<span class="hljs-variable">a:1</span>}<br><span class="hljs-keyword">if</span> (<span class="hljs-string">"a"</span> in obj2){<br> console.<span class="hljs-built_in">log</span>(<span class="hljs-string">"对象或其原型链上有此属性"</span>)<br>}<span class="hljs-keyword">else</span> {<br> console.<span class="hljs-built_in">log</span>(<span class="hljs-string">"对象或其原型链上无此属性"</span>)<br>}<br></code></pre></td></tr></table></figure><p>3.obj.hasOwnProperty() <strong>对象自身属性中</strong>含有某属性,返回true。</p><figure class="highlight stylus"><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><code class="hljs stylus">let obj2 = {<span class="hljs-selector-tag">a</span>:<span class="hljs-number">1</span>}<br><span class="hljs-keyword">if</span> (obj2<span class="hljs-selector-class">.hasOwnProperty</span>(<span class="hljs-string">"a"</span>)){<br> console<span class="hljs-selector-class">.log</span>(<span class="hljs-string">"对象上有此属性"</span>)<br>}<span class="hljs-keyword">else</span> {<br> console<span class="hljs-selector-class">.log</span>(<span class="hljs-string">"对象上无此属性"</span>)<br>}<br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>V8中的JavaScript的内存管理与垃圾回收</title>
<link href="/2021/12/24/V8%E4%B8%AD%E7%9A%84JavaScript%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/"/>
<url>/2021/12/24/V8%E4%B8%AD%E7%9A%84JavaScript%E7%9A%84%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86%E4%B8%8E%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6/</url>
<content type="html"><![CDATA[<h1 id="一-内存的生命周期"><a href="#一-内存的生命周期" class="headerlink" title="一.内存的生命周期"></a>一.内存的生命周期</h1><p>无论任何语言,内存生命周期大体一致,会分为:分配内存,使用内存,销毁内存三个阶段</p><p><img src="/images/pasted-3.png" alt="upload successful"></p><h2 id="1-分配"><a href="#1-分配" class="headerlink" title="1.分配"></a>1.分配</h2><p><strong>1.1静态内存分配</strong></p><p><strong>1.2动态内存分配</strong></p><p>区别:<br><strong>静态内存分配:</strong></p><ul><li>数据大小在编译的时候必须是已知的</li><li>在编译时执行</li><li>分配给栈</li><li>先进后出 </li></ul><p><img src="/images/pasted-4.png" alt="upload successful"></p><h2 id="2-使用"><a href="#2-使用" class="headerlink" title="2.使用"></a>2.使用</h2><p>读写基本变量或对象的属性、传参等操作,都涉及到了内存的使用。</p><h2 id="3-释放"><a href="#3-释放" class="headerlink" title="3.释放"></a>3.释放</h2><p>对于不再使用的内存,应当及时释放。</p><hr><h1 id="二-V8的内存结构"><a href="#二-V8的内存结构" class="headerlink" title="二.V8的内存结构"></a>二.V8的内存结构</h1><p>正在运行的程序由某些内存表示,这些内存成为常驻集<br><img src="/images/pasted-5.png" alt="upload successful"></p><h2 id="1-栈内存"><a href="#1-栈内存" class="headerlink" title="1.栈内存"></a>1.栈内存</h2><p>栈用于静态内存分配(Static Memory Allocation),它具有以下特点:</p><ul><li>操作数据快,因为是在栈顶操作</li><li>数据必须是静态的,数据大小在编译时是已知的</li><li>多线程应用程序中,每个线程可以有一个栈</li><li>堆的内存管理简单,且由操作系统完成</li><li>栈大小有限,可能发生栈溢出(Stack Overflow)</li><li>值大小有限制</li></ul><h2 id="2-堆内存"><a href="#2-堆内存" class="headerlink" title="2.堆内存"></a>2.堆内存</h2><p>堆用于动态内存分配(Dynamic Memory Allocation),与栈不同,程序需要使用指针在堆中查找数据。它的特点是:</p><ul><li>操作速度慢,但容量大</li><li>可以将动态大小的数据存储在此处</li><li>堆在应用程序的线程之间共享</li><li>因为堆的动态特性,堆管理起来比较困难</li><li>值大小没有限制</li></ul><p><strong>堆的内存还可以进一步划分:</strong><br>1.<strong>新生代:</strong>空间小,并且分为了两个半空间,由Minor GC管理,其中数据存活期短<br>2.<strong>老生代:</strong>空间大,由Major GC管理,老生代可以进一步划分为:</p><ul><li>旧指针空间:包含的对象中还存在指针,这个指针指向其他对象</li><li>旧数据空间:包含的对象中仅有数据</li></ul><p>3.<strong>大对象空间:</strong>这里对象的大小超过了其他空间大小限制<br>4.<strong>代码空间:</strong>即时编译器在这里存储已编译的代码块<br>5.<strong>元空间、属性元空间、映射空间:</strong>这些空间中的每个空间都包含相同大小的对象,并且对它们指向的对象有某种约束,从而简化了收集。</p><p><strong>(页(Page):页是从操作系统分配的连续内存块,以上的空间都由一组组的页构成的。)</strong></p><hr><h1 id="三-回收栈内存"><a href="#三-回收栈内存" class="headerlink" title="三.回收栈内存"></a>三.回收栈内存</h1><p>V8会通过移动记录<strong>当前执行状态的指针</strong>来销毁该函数保存在栈中的执行上下文</p><hr><h1 id="四-回收堆内存"><a href="#四-回收堆内存" class="headerlink" title="四.回收堆内存"></a>四.回收堆内存</h1><p>V8中的<strong>垃圾收集器(Garbage Collector)</strong>,它的工作是:跟踪内存的分配和使用,以便当分配的内存不再使用时,自动释放它。并且,这个垃圾收集器是分代的,也就是说,堆中的对象按其年龄分组并在不同阶段清除。</p><p><strong>回收堆内存有两种思路:</strong><br>1.引用计数法<br>2.标记清除法</p><p>在V8中,使用<strong>两个阶段和三种算法</strong>来进行GC:<br>1.<strong>Minor GC:</strong>针对新生代,使用Scavenger和Cheney’s algorithm两种算法<br>2.<strong>Major GC:</strong>针对老生代,使用Mark-Sweep-Compact算法</p><h2 id="两种思路:"><a href="#两种思路:" class="headerlink" title="两种思路:"></a>两种思路:</h2><p><strong>1.引用计数法:</strong></p><p><strong>内存引用(Memory References)</strong>是引用计数法中的一个重要概念。在内存管理的上下文中,如果一个对象可以隐式或显式访问另一个对象,则称该对象引用另一个对象。 例如,JavaScript对象能够引用其原型(隐式引用)和其属性的值(显式引用)。</p><p>引用计数就是当某个对象的引用计数为0的时候,就把这个对象视为可回收垃圾</p><p><strong>缺点:</strong><br>如果出现循环引用的情况,那么就无法进行垃圾回收了。</p><figure class="highlight actionscript"><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><code class="hljs actionscript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">f</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">var</span> o1 = {};<br> <span class="hljs-keyword">var</span> o2 = {};<br> o1.p = o2; <span class="hljs-comment">// o1 references o2</span><br> o2.p = o1; <span class="hljs-comment">// o2 references o1. This creates a cycle.</span><br>}<br><br>f();<br></code></pre></td></tr></table></figure><p><img src="/images/pasted-6.png" alt="upload successful"></p><p><strong>2.标记清除法</strong><br>标记清除法不再把“对象是否不再被需要”简化为对象是否被引用了,它是从根节点开始寻找节点,并标记可以到达的节点<strong>(标记)</strong>,然后释放没有被标记的节点<strong>(清除)。</strong></p><p><strong>标记清除法解决了循环引用的问题。在之前的示例代码中,函数调用返回之后,两个对象从全局对象出发无法获取。因此,它们将会被垃圾回收器回收。</strong></p><blockquote><p>const和let具有块级作用域,当块级作用域消失的时候就可以及时回收内存,提高程序运行效率</p></blockquote><h2 id="V8-GC的两个步骤"><a href="#V8-GC的两个步骤" class="headerlink" title="V8 GC的两个步骤"></a>V8 GC的两个步骤</h2><h3 id="1-Minor-GC"><a href="#1-Minor-GC" class="headerlink" title="1.Minor GC"></a>1.Minor GC</h3><p>Minor GC是针对新生区的垃圾回收。<br>Minor GC的<strong>整体思路</strong>:(这个过程使用到了Scavenger和Cheney’s algorithm。)</p><ul><li>新生代分为两个半区,分别为“To-Space”和“from-Space”,我们先不断地在from-Space上分配内存</li><li>如果from-Space满了,就触发GC</li><li>找出from-Space上的活动对象,如果这个活动对象存活过两个minor GC周期,就把它移到老生代,否则把它们移到To-Space</li><li>清空from-Space</li><li>转换“To-Space”和“from-Space”的角色</li><li>不断重复上述过程</li></ul><h3 id="2-Major-GC"><a href="#2-Major-GC" class="headerlink" title="2.Major GC"></a>2.Major GC</h3><p>Scavenger算法中需要涉及数据迁移,因此适用于小数据,但老生区的数据较大,因此不宜采用该种方法。</p><p>Major GC针对老生区进行垃圾回收,使用的是Mark-Sweep-Compact算法,<strong>思路</strong>为:</p><ul><li>标记:对堆进行深度优先遍历,标记上所有可到达的对象</li><li>清除:所有未被标记的对象的内存地址均为空闲的内存空间,可以用于存储其他对象</li><li>压缩:将所有存活的对象移到一起,以减少碎片化,并提高为新对象分配内存的性能</li></ul><p><strong>全停顿(stop-the-world GC)</strong>:这种类型的GC方式也被称为全停顿,因为 JavaScript 是运行在主线程之上的,一旦执行垃圾回收算法,都需要将正在执行的 JavaScript 脚本暂停下来,待垃圾回收完毕后再恢复脚本执行,会造成性能的下降。</p><p>V8中采取的解决策略:<br>1.增量式编程:GC是在多个增量步骤中完成的,而不是一次性<br>2.并发标记和并发清理/压缩:使用多个帮助线程并发完成的,而不会影响主线程<br>3.懒清理:指的是延迟处理Page中的垃圾,直到需要内存才进行清理。</p><p><img src="/images/pasted-7.png" alt="upload successful"></p><hr><h1 id="五-如何优化内存的使用"><a href="#五-如何优化内存的使用" class="headerlink" title="五.如何优化内存的使用"></a>五.如何优化内存的使用</h1><p>1.不要在循环之中定义函数<br>2.不要在循环中定义对象<br>3.清空数组</p><figure class="highlight ini"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs ini"><span class="hljs-attr">arr.length</span>=<span class="hljs-number">0</span><span class="hljs-comment">;</span><br></code></pre></td></tr></table></figure><hr><p>学习链接:<br><a href="https://juejin.cn/post/6971245488058302477#heading-6">https://juejin.cn/post/6971245488058302477#heading-6</a><br><a href="https://juejin.cn/post/6891614154134667272">https://juejin.cn/post/6891614154134667272</a></p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>JavaScript常见的内存泄漏</title>
<link href="/2021/12/24/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/"/>
<url>/2021/12/24/%E5%86%85%E5%AD%98%E6%B3%84%E6%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="一-什么是内存泄漏"><a href="#一-什么是内存泄漏" class="headerlink" title="一.什么是内存泄漏"></a>一.什么是内存泄漏</h1><p>当应用程序不再需要的内存,由于某种原因未返回给操作系统或者空闲内存,这将会导致程序变慢、卡顿、高延迟</p><h1 id="二-内存泄漏的主要原因"><a href="#二-内存泄漏的主要原因" class="headerlink" title="二.内存泄漏的主要原因"></a>二.内存泄漏的主要原因</h1><p>JavaScript内存泄漏的主要原因在于一些不再需要的引用(Unwanted References)。</p><p>所谓的<strong>Unwanted References</strong>指的是:有一些内存,其实开发人员已经不再需要了,但是由于某种原因,这些内存仍然被标记并保留在活动根目录树中。Unwanted References就是指对这些内存的引用。在JavaScript上下文中,Unwanted References是一些不再使用的变量,这些变量指向了原本可以释放的一些内存。</p><h1 id="三-常见的内存泄漏"><a href="#三-常见的内存泄漏" class="headerlink" title="三.常见的内存泄漏"></a>三.常见的内存泄漏</h1><h2 id="1-全局变量造成的内存泄漏"><a href="#1-全局变量造成的内存泄漏" class="headerlink" title="1.全局变量造成的内存泄漏"></a>1.全局变量造成的内存泄漏</h2><p>在JS的运行机制中,全局变量是直接由根节点所引用的,所以在整个程序的生命周期中都不会被回收</p><ul><li>引用未声明变量<figure class="highlight ada"><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><code class="hljs ada"><span class="hljs-keyword">function</span> <span class="hljs-title">foo</span>(arg) {<br> bar = "this <span class="hljs-keyword">is</span> a hidden global variable<span class="hljs-string">";</span><br><span class="hljs-string">}</span><br></code></pre></td></tr></table></figure></li><li>将变量设置为全局变量window的属性<figure class="highlight ada"><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><code class="hljs ada"><span class="hljs-keyword">function</span> <span class="hljs-title">foo</span>(arg) {<br> window.bar = "this <span class="hljs-keyword">is</span> an explicit global variable<span class="hljs-string">";</span><br><span class="hljs-string">}</span><br></code></pre></td></tr></table></figure></li><li>盲目使用this,导致挂载到全局变量的属性上<figure class="highlight actionscript"><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><code class="hljs actionscript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">foo</span><span class="hljs-params">()</span> </span>{<br> <span class="hljs-keyword">this</span>.variable = <span class="hljs-string">"potential accidental global"</span>;<br>}<br>foo();<br></code></pre></td></tr></table></figure></li></ul><p><strong>综上:</strong><br>1.避免意外地创建全局变量,可以使用严格模式。<br>2.减少创建全局变量<br>3.如果必须使用全局变量来存储大量数据,必须确保在处理完数据后将其放置null或者从新分配</p><h2 id="2-忘记释放的计时器"><a href="#2-忘记释放的计时器" class="headerlink" title="2.忘记释放的计时器"></a>2.忘记释放的计时器</h2><h2 id="3-多处引用"><a href="#3-多处引用" class="headerlink" title="3.多处引用"></a>3.多处引用</h2><p>当多个变量均引用同一个对象时,只要有一个引用没有清除,都将导致被引用的对象无法GC。<br><strong>例子:</strong></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><code class="hljs javascript"><span class="hljs-keyword">var</span> elements = {<br> <span class="hljs-attr">button</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'button'</span>),<br> <span class="hljs-attr">image</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'image'</span>),<br> <span class="hljs-attr">text</span>: <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'text'</span>)<br>};<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">doStuff</span>(<span class="hljs-params"></span>) </span>{<br> image.src = <span class="hljs-string">'http://some.url/image'</span>;<br> button.click();<br> <span class="hljs-built_in">console</span>.log(text.innerHTML);<br> <span class="hljs-comment">// Much more logic</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">removeButton</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-comment">// The button is a direct child of body.</span><br> <span class="hljs-built_in">document</span>.body.removeChild(<span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'button'</span>));<br><br> <span class="hljs-comment">// At this point, we still have a reference to #button in the global</span><br> <span class="hljs-comment">// elements dictionary. In other words, the button element is still in</span><br> <span class="hljs-comment">// memory and cannot be collected by the GC.s</span><br>}<br></code></pre></td></tr></table></figure><p>上面的代码示例中,#button被两个变量引用,一个在DOM树中,另一个在elements对象中。如果将来决定回收button,需要将两个引用都释放,而上面的代码仅仅只释放了DOM中的button引用,而element对象中依然保持着对button的引用,所以button不会被GC</p><p><strong>还有这么一种情况:</strong> 如果我们想要回收某个table,但我们保持着对这个table中某个单元格(cell)的引用,这个时候将导致整个table都保存在内存中,无法GC。</p><h2 id="4-闭包"><a href="#4-闭包" class="headerlink" title="4.闭包"></a>4.闭包</h2><p>闭包(Closure):闭包是一个函数,它可以访问那些定义在它的包围作用域(Enclosing Scope)里的变量,即使这个包围作用域已经结束。因此,闭包具有记忆周围环境(Context)的功能。<br><strong>例子:</strong></p><figure class="highlight haxe"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs haxe"><span class="hljs-keyword">var</span> <span class="hljs-keyword">new</span><span class="hljs-type">Elem</span>;<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">outer</span></span>() {<br> <span class="hljs-keyword">var</span> someText = <span class="hljs-keyword">new</span> <span class="hljs-type">Array</span>(<span class="hljs-number">1000000</span>);<br> <span class="hljs-keyword">var</span> elem = <span class="hljs-keyword">new</span><span class="hljs-type">Elem</span>;<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">inner</span></span>() {<br> <span class="hljs-keyword">if</span> (elem) <span class="hljs-keyword">return</span> someText;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> </span>() {};<br>}<br>setInterval(<span class="hljs-function"><span class="hljs-keyword">function</span> </span>() {<br> <span class="hljs-keyword">new</span><span class="hljs-type">Elem</span> = outer();<br>}, <span class="hljs-number">5</span>);<br></code></pre></td></tr></table></figure><p>在这个例子中,有两个闭包:一个是inner,另一个是匿名函数function () {}。其中,inner闭包引用了someText和elem,并且,inner永远也不会被调用。可是,我们需要注意:<strong>相同父作用域的闭包,他们能够共享context。 也就是说,在这个例子中,inner的someText和elem将和匿名函数function () {}共享。</strong>然而,这个匿名函数之后会被return返回,并且赋值给newElem。只要newElem还引用着这个匿名函数,那么,someText和elem就不会被GC。</p><p>同时,我们还要注意到,outer函数内部执行了var elem = newElem;,而这个newElem引用了上一次调用的outer返回的匿名函数。试想,第n次调用outer将保持着第n-1次调用的outer中的匿名函数,而这个匿名函数由保持着对elem的引用,进而保持着对n-2次的…因此,这将造成内存泄漏。</p><p><strong>解决方案:</strong>setInterval中的参数1的代码改为newElem = outer()();</p><h2 id="5-console-log"><a href="#5-console-log" class="headerlink" title="5.console.log"></a>5.console.log</h2><p>由于我们传给console.log的对象需要在控制台打印出来查看信息,所以这部分对象的内存是不会被垃圾回收的,这样也会造成内存泄漏。</p><hr><p>阅读链接:<br><a href="https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/">https://auth0.com/blog/four-types-of-leaks-in-your-javascript-code-and-how-to-get-rid-of-them/</a></p>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>Vue Router两种路由模式</title>
<link href="/2021/12/24/Vue-Router%E4%B8%A4%E7%A7%8D%E8%B7%AF%E7%94%B1%E6%A8%A1%E5%BC%8F/"/>
<url>/2021/12/24/Vue-Router%E4%B8%A4%E7%A7%8D%E8%B7%AF%E7%94%B1%E6%A8%A1%E5%BC%8F/</url>
<content type="html"><![CDATA[<h1 id="hash模式"><a href="#hash模式" class="headerlink" title="hash模式"></a>hash模式</h1><p>在hash模式下,本质上是通过修改<strong>window.location.href</strong>实现的。前端路由的改变依托于**#<strong>锚点,而锚点后边的值我们可以通过修改</strong>window.location.hash**的值来修改,每一次hash值的变化都会导致触发hashchange这个事件,很重要的一点就是,这样修改页面不会刷新,不会导致浏览器向后端发送请求</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><code class="hljs javascript"><span class="hljs-built_in">window</span>.onhashchange = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">event</span>)</span>{<br> <span class="hljs-built_in">console</span>.log(event.oldURL, event.newURL);<br> <span class="hljs-keyword">let</span> hash = location.hash.slice(<span class="hljs-number">1</span>);<br> <span class="hljs-built_in">document</span>.body.style.color = hash;<br>}<br></code></pre></td></tr></table></figure><h1 id="history模式"><a href="#history模式" class="headerlink" title="history模式"></a>history模式</h1><p>由于Html5标准的发布,<strong>pushState()和replace()这两个API的提供可以让我们改变url的地址并且不会发送请求,不仅如此我们还可以读取浏览器的历史栈,并对历史记录栈进行修改</strong>,以及<strong>popState(),当浏览器跳转到新的状态时,将触发popState事件</strong></p><hr><p><strong>hash与history模式的区别:</strong><br>1.hash模式只能改变锚点后面的url片段,但是pushStatet设置的url可以是与当前url同源的任意url<br>2.history模式会将url修改的和正常请求后端的url一样,会导致浏览器向后台发送请求,而hash模式就不会导致浏览器向后台发送请求<br>3.hash模式下,浏览器会记录下所有发生变化的url,浏览器的前进和后退就都可以使用。history模式下,前进和后退并不会使得页面状态退回或者前进到上一个或者下一个状态</p><hr><p><strong>使用hash需要注意:</strong><br>刷新的时候可能会出现404的情况,所以必须使服务端对每一个可能的path都有相应的映射</p><hr><h1 id="abstract模式"><a href="#abstract模式" class="headerlink" title="abstract模式"></a>abstract模式</h1><p>支持所有javascript运行模式。如果发现没有浏览器的API,路由会自动强制进入这个模式。</p>]]></content>
<categories>
<category>Vue</category>
</categories>
</entry>
<entry>
<title>Vue中Array的变化侦测</title>
<link href="/2021/12/24/Vue%E4%B8%ADArray%E7%9A%84%E5%8F%98%E5%8C%96%E4%BE%A6%E6%B5%8B/"/>
<url>/2021/12/24/Vue%E4%B8%ADArray%E7%9A%84%E5%8F%98%E5%8C%96%E4%BE%A6%E6%B5%8B/</url>
<content type="html"><![CDATA[<p>前言:之前介绍了Vue中Object的变化侦测,Array中的变化侦测和Object中变化侦测还是有一定的不同的,这一篇就来学习一下。</p><hr><h1 id="1-Object与Array变化侦测的不同"><a href="#1-Object与Array变化侦测的不同" class="headerlink" title="1.Object与Array变化侦测的不同"></a>1.Object与Array变化侦测的不同</h1><p>Array.push()方法实际上并不会调用getter/setter,并且在ES6之前JS并没有元编程的能力,无法拦截原型方法,而每一个数组实例调用push等方法都是调用的原型上的方法,所以为了追踪变化我们必须使用一个拦截器来拦截数组调用这些原型方法的操作,这样我们就可以监测原型方法的调用了。</p><h1 id="2-实现拦截器拦截原型方法"><a href="#2-实现拦截器拦截原型方法" class="headerlink" title="2.实现拦截器拦截原型方法"></a>2.实现拦截器拦截原型方法</h1><p>(1)写出拦截器</p><figure class="highlight actionscript"><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><code class="hljs actionscript"><span class="hljs-keyword">const</span> arrayProto=Array.prototype<br>export <span class="hljs-keyword">const</span> arrayMethods=Object.create(arrayProto)<br>;[<br><span class="hljs-string">'push'</span>,<br><span class="hljs-string">'pop'</span>,<br><span class="hljs-string">'shift'</span>,<br><span class="hljs-string">'unshift'</span>,<br><span class="hljs-string">'splice'</span>,<br><span class="hljs-string">'sort'</span>,<br><span class="hljs-string">'reverse'</span><br>].forEach(method=>{<br><span class="hljs-comment">//缓存原始方法</span><br><span class="hljs-keyword">const</span> original=arrayProto[method]<br><span class="hljs-comment">//def函数是我写的一个工具函数,为对象(第一个参数)的,key(第二个参数)设置数值等属性</span><br>def(arrMethods,method,<span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">mutator</span><span class="hljs-params">(<span class="hljs-rest_arg">...args</span>)</span></span>{<br><span class="hljs-keyword">const</span> result =original.apply(<span class="hljs-keyword">this</span>,args)<br><span class="hljs-comment">//获取当前数组的Observer</span><br><span class="hljs-keyword">const</span> ob=<span class="hljs-keyword">this</span>.__ob__<br>ob.dep.notify()<span class="hljs-comment">//向依赖发送消息</span><br><span class="hljs-keyword">return</span> result<br>})<br>})<br></code></pre></td></tr></table></figure><p>(2)使用拦截器覆盖<strong>支持原型指针的数组的原型对象</strong>,或者将拦截器方法挂载到<strong>不支持原型指针的数组属性上</strong></p><h1 id="3-Array是如何收集依赖的"><a href="#3-Array是如何收集依赖的" class="headerlink" title="3.Array是如何收集依赖的"></a>3.Array是如何收集依赖的</h1><p>Array收集依赖的方式和Object是一样的,都是在getter中收集依赖,但是是在拦截器中触发依赖</p><h1 id="4-Array的依赖保存在哪里"><a href="#4-Array的依赖保存在哪里" class="headerlink" title="4.Array的依赖保存在哪里"></a>4.Array的依赖保存在哪里</h1><p>Array的依赖和Object一样是保存在Dep类的对象中,但是和Object的不同在于<strong>Object的Dep对象是保存在defineReactive函数中的一个变量,但是Array的Dep对象是保存在Observer实例上的。</strong>至于原因就是,我们在写拦截器的时候需要在执行push等方法的时候可以获取到Dep对象,然后向数组的依赖发送通知</p><h1 id="5-侦测数组中元素变化"><a href="#5-侦测数组中元素变化" class="headerlink" title="5.侦测数组中元素变化"></a>5.侦测数组中元素变化</h1><figure class="highlight stylus"><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><code class="hljs stylus"><span class="hljs-comment">//observe函数是监听函数</span><br><span class="hljs-function"><span class="hljs-title">observeArray</span><span class="hljs-params">(items)</span></span>{<br><span class="hljs-function"><span class="hljs-title">for</span><span class="hljs-params">( let i=<span class="hljs-number">0</span>;i<items.length;i++)</span></span>{<br><span class="hljs-function"><span class="hljs-title">observe</span><span class="hljs-params">(items[i])</span></span><br>}<br>}<br></code></pre></td></tr></table></figure><h1 id="6-监听新元素的变化"><a href="#6-监听新元素的变化" class="headerlink" title="6.监听新元素的变化"></a>6.监听新元素的变化</h1><p>(1)获取新增元素<br>通过switch case判断执行的操作,将操作的元素放入inserted数组中<br>(2)使用Observer监听新增元素<br>使用observerArray()方法对inserted数组进行监听,监听这些新增元素的变化</p><h1 id="7-关于Array监听的问题"><a href="#7-关于Array监听的问题" class="headerlink" title="7.关于Array监听的问题"></a>7.关于Array监听的问题</h1><p>(1)修改数组第一个元素时是不会被Vue监听到的<br>(2)修改数组长度时也是不会被Vue监听到的</p>]]></content>
<categories>
<category>Vue</category>
</categories>
</entry>
<entry>
<title>Vue跳转路由和页面时的传参</title>
<link href="/2021/12/24/Vue%E8%B7%B3%E8%BD%AC%E8%B7%AF%E7%94%B1%E5%92%8C%E9%A1%B5%E9%9D%A2%E6%97%B6%E7%9A%84%E4%BC%A0%E5%8F%82/"/>
<url>/2021/12/24/Vue%E8%B7%B3%E8%BD%AC%E8%B7%AF%E7%94%B1%E5%92%8C%E9%A1%B5%E9%9D%A2%E6%97%B6%E7%9A%84%E4%BC%A0%E5%8F%82/</url>
<content type="html"><![CDATA[<p>前言:这个问题是我之前做的一个移动端练手小demo时出现的问题,需求是最后点击另一个路由上的提交按钮时,将之前路由用户选择的数据提交给后台,我想不到别的方法,最终在这么一个小demo中使用了vuex,下面是我看其他博客总结的一些方法</p><hr><h1 id="1-路由传值"><a href="#1-路由传值" class="headerlink" title="1.路由传值"></a>1.路由传值</h1><figure class="highlight awk"><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><code class="hljs awk"><span class="hljs-regexp">//</span> 字符串<br>router.push(<span class="hljs-string">'home'</span>)<br><span class="hljs-regexp">//</span> 对象<br>router.push({ path: <span class="hljs-string">'home'</span> })<br><span class="hljs-regexp">//</span> 命名的路由<br>router.push({ name: <span class="hljs-string">'user'</span>, params: { userId: <span class="hljs-string">'123'</span> }})<br><span class="hljs-regexp">//</span> 带查询参数,变成 /register?plan=private<br>router.push({ path: <span class="hljs-string">'register'</span>, query: { plan: <span class="hljs-string">'private'</span> }})<br></code></pre></td></tr></table></figure><p><strong>注意:如果提供了 path,params 会被忽略,注意匹配问题</strong></p><h1 id="2-通过父子组件中的数据传递"><a href="#2-通过父子组件中的数据传递" class="headerlink" title="2.通过父子组件中的数据传递"></a>2.通过父子组件中的数据传递</h1><p><strong>通过parent,chlidren等方法调取用层级关系的组件内的数据和方法</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs awk">this.<span class="hljs-variable">$parent</span>.<span class="hljs-variable">$data</span>.id <span class="hljs-regexp">//</span>获取父元素data中的id<br>this.<span class="hljs-variable">$children</span>.<span class="hljs-variable">$data</span>.id <span class="hljs-regexp">//</span>获取父元素data中的id<br></code></pre></td></tr></table></figure><h1 id="3-通过EventBus传递数据"><a href="#3-通过EventBus传递数据" class="headerlink" title="3.通过EventBus传递数据"></a>3.通过EventBus传递数据</h1><h2 id="EventBus的简介"><a href="#EventBus的简介" class="headerlink" title="EventBus的简介"></a>EventBus的简介</h2><p>EventBus 又称为事件总线。在Vue中可以使用 EventBus 来作为沟通桥梁的概念,就像是所有组件共用相同的事件中心,可以向该中心注册发送事件或接收事件,所以组件都可以上下平行地通知其他组件,但也就是太方便所以若使用不慎,就会造成难以维护的灾难,因此才需要更完善的Vuex作为状态管理中心,将通知的概念上升到共享状态层次。</p><h2 id="简单使用非全局的EventBus"><a href="#简单使用非全局的EventBus" class="headerlink" title="简单使用非全局的EventBus"></a>简单使用非全局的EventBus</h2><p>1)创建一个EventBus.js文件,实质上他是一个不具备dom元素的组件,他仅仅只具有实例的方法,我们需要的也只是他实例中的方法而已</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><code class="hljs javascript"><span class="hljs-comment">// event-bus.js</span><br><span class="hljs-keyword">import</span> Vue <span class="hljs-keyword">from</span> <span class="hljs-string">'vue'</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">const</span> EventBus = <span class="hljs-keyword">new</span> Vue()<br></code></pre></td></tr></table></figure><p>2)发送事件<br>在需要发送事件的组件中import引入EventBus,并且使用EventBus发送事件</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">EventBus</span>.</span></span><span class="hljs-constructor">$emit(’事件名‘,{<span class="hljs-params">num</span>:<span class="hljs-params">this</span>.<span class="hljs-params">num</span>,<span class="hljs-params">deg</span>:<span class="hljs-params">this</span>.<span class="hljs-params">deg</span>})</span>;<br></code></pre></td></tr></table></figure><p>3)接收事件<br>在需要接受EventBus的组件中引入EventBus,并使用EventBus接收事件</p><figure class="highlight coffeescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs coffeescript">EventBus.$<span class="hljs-literal">on</span>(<span class="hljs-string">'事件名'</span>,<span class="hljs-function"><span class="hljs-params">(num,deg)</span>=></span>{} )<br></code></pre></td></tr></table></figure><p>4)只监听一次</p><figure class="highlight reasonml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs reasonml"><span class="hljs-module-access"><span class="hljs-module"><span class="hljs-identifier">EventBus</span>.</span></span><span class="hljs-constructor">$once('事件名',(<span class="hljs-params">num</span>,<span class="hljs-params">deg</span>)</span>=>{} )<br></code></pre></td></tr></table></figure><p>5)移除事件监听者</p><figure class="highlight coffeescript"><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><code class="hljs coffeescript"><span class="hljs-keyword">import</span> { eventBus } <span class="hljs-keyword">from</span> <span class="hljs-string">'./event-bus.js'</span><br>EventBus.$<span class="hljs-literal">off</span>(<span class="hljs-string">'decreased'</span>, {})<br><br></code></pre></td></tr></table></figure><h2 id="全局EventBus的简单使用"><a href="#全局EventBus的简单使用" class="headerlink" title="全局EventBus的简单使用"></a>全局EventBus的简单使用</h2><p>在main.js文件中引入EventBus</p><figure class="highlight php"><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><code class="hljs php"><span class="hljs-keyword">var</span> EventBus = <span class="hljs-keyword">new</span> Vue();<br><span class="hljs-keyword">Object</span>.defineProperties(Vue.prototype, {<br> <span class="hljs-variable">$bus</span>: {<br> get: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">return</span> EventBus<br> }<br> }<br>})<br></code></pre></td></tr></table></figure><p>这样在其他组件中就可以通过vue使用EventBus来发出和接收事件</p><figure class="highlight lasso"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs lasso">this.$bus.$emit(<span class="hljs-string">'nameOfEvent'</span>,{ <span class="hljs-params">...</span> pass some event <span class="hljs-built_in">data</span> <span class="hljs-params">...</span>});<br>this.$bus.$on(<span class="hljs-string">'nameOfEvent'</span>,($event) => {})<br></code></pre></td></tr></table></figure><h1 id="4-params传参和query传参"><a href="#4-params传参和query传参" class="headerlink" title="4.params传参和query传参"></a>4.params传参和query传参</h1><p>上面已经聊到了params传参和query传参,所以我们来说一下他们之间的区别<br>1)用法上:刚query要用path来引入,params要用name来引入,接收参数都是类似的,分别是</p><figure class="highlight stylus"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs stylus">this.<span class="hljs-variable">$route</span><span class="hljs-selector-class">.query</span><span class="hljs-selector-class">.name</span><br>this.<span class="hljs-variable">$route</span><span class="hljs-selector-class">.params</span>.name<br></code></pre></td></tr></table></figure><p>2)展示上:query更加类似于我们ajax中get传参,params则类似于post,说的再简单一点,前者在浏览器地址栏中显示参数,后者则不显示<br>3)对于路由:params是路由的一部分,必须要有。query是拼接在url后面的参数,没有也没关系。params一旦设置在路由,params就是路由的一部分,如果这个路由有params传参,但是在跳转的时候没有传这个参数,会导致跳转失败或者页面会没有内容。<br><strong>比如:跳转/router1/:id</strong></p><figure class="highlight dust"><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><code class="hljs dust"><span class="xml"><span class="hljs-tag"><<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{ name:'router1',params: { id: status}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">}"</span> ></span>正确<span class="hljs-tag"></<span class="hljs-name">router-link</span>></span></span><br><span class="xml"></span><br><span class="xml"><span class="hljs-tag"><<span class="hljs-name">router-link</span> <span class="hljs-attr">:to</span>=<span class="hljs-string">"</span></span></span><span class="hljs-template-variable">{ name:'router1',params: { id2: status}</span><span class="xml"><span class="hljs-tag"><span class="hljs-string">}"</span>></span>错误<span class="hljs-tag"></<span class="hljs-name">router-link</span>></span></span><br></code></pre></td></tr></table></figure><p>4)params、query不设置也可以传参,params不设置的时候,刷新页面或者返回参数会丢失<br>5)query路由无需改动path规则,params需要设置路由规则<strong>比如:path:’detail/:id’</strong></p><h1 id="5-Vuex"><a href="#5-Vuex" class="headerlink" title="5.Vuex"></a>5.Vuex</h1><p>这当然是项目复杂的时候最推荐的方法</p>]]></content>
<categories>
<category>Vue</category>
</categories>
</entry>
<entry>
<title>setInterval和setTimeout的坑</title>
<link href="/2021/12/24/setInterval%E5%92%8CsetTimeout%E7%9A%84%E5%9D%91/"/>
<url>/2021/12/24/setInterval%E5%92%8CsetTimeout%E7%9A%84%E5%9D%91/</url>
<content type="html"><![CDATA[<p>之前使用setInterval进行一个前端轮询的操作,结果把网站卡崩了,来总结一下setInterval和setTimeout的坑</p><hr><h1 id="一-setInterval的坑"><a href="#一-setInterval的坑" class="headerlink" title="一.setInterval的坑"></a>一.setInterval的坑</h1><ul><li>setInterval会无视代码错误。<br>就算代码中遇到了错误,它还是会一直循环下去,如果代码中有错误代码,setInterval就会导致这个错误被隐藏<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><code class="hljs javascript"><span class="hljs-keyword">let</span> count = <span class="hljs-number">1</span>;<br><span class="hljs-built_in">setInterval</span>(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> count++;<br> <span class="hljs-built_in">console</span>.log(count);<br> <span class="hljs-keyword">if</span> (count % <span class="hljs-number">3</span> === <span class="hljs-number">0</span>) <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'setInterval报错'</span>);<br>}, <span class="hljs-number">1000</span>)<br></code></pre></td></tr></table></figure></li><li>setInterval会无视任何情况定时执行<br>这就是我遇到的哪个BUG,由于数据量很庞大,服务器的响应时间很长,就造成了前一个请求还没有完成,下一次请求又发起了,就会造成非常严重的卡顿。此时就应该<strong>使用setTimeout,当用户发出去的请求得到响应或者超时后,再使用setTimeout递归发送下一个请求,这样就不会出现ajax请求堆积的问题了。</strong></li><li>setInterval并不能确保每次调用都能执行<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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> startDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();<br><span class="hljs-keyword">let</span> endData;<br><span class="hljs-comment">// 第一个调用会被略过</span><br><span class="hljs-built_in">setInterval</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'start'</span>);<br> <span class="hljs-built_in">console</span>.log(startDate.getTime());<br> <span class="hljs-built_in">console</span>.log(endDate.getTime());<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'end'</span>);<br>}, <span class="hljs-number">1000</span>);<br><span class="hljs-keyword">while</span> (startDate.getTime() + <span class="hljs-number">2</span> * <span class="hljs-number">1000</span> > (<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()).getTime()) {<br>}<br>endDate = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();<br></code></pre></td></tr></table></figure></li></ul><p>我们可以看到,第一次执行的setInterval函数输出的startDate和endDate差距在2s以上。而我们的setInterval写的是每间隔1s执行一次。因此,我们可以看出,第一次的setInterval函数调用被略过了。</p><p>这说明了:<strong>如果说你的代码执行时间会比较久的话,就会导致setInterval中的一部分函数调用被略过。</strong>因此你的程序如果依赖于setInterval的精确执行的话,那么你就要小心这一点了。</p><p>当然,<strong>其实setTimeout也有这个问题。浏览器的定时器都不是精确执行的。就算你调用setTimeout(fn, 0),它也不能确保马上执行。</strong></p><p><strong>解决方法:</strong></p><figure class="highlight zephir"><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><code class="hljs zephir"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">fn</span> <span class="hljs-params">()</span> </span>{<br> setTimeout(() => {<br> <span class="hljs-comment">// 程序主逻辑代码</span><br> <span class="hljs-comment">// 循环递归调用</span><br> <span class="hljs-function"><span class="hljs-keyword">fn</span><span class="hljs-params">()</span></span>;<br> }, <span class="hljs-number">1000</span>);<br>}<br><span class="hljs-function"><span class="hljs-keyword">fn</span><span class="hljs-params">()</span></span>;<br></code></pre></td></tr></table></figure><ul><li>setInterval不会清除定时器列表<br>每一次重复执行setInterval的回调函数都会导致计时器类加,造成内存的泄漏,最终导致网页的卡顿</li><li><em>解决方法:</em>*<figure class="highlight coffeescript"><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><code class="hljs coffeescript"><span class="hljs-built_in">window</span>.<span class="hljs-built_in">setInterval</span>(<span class="hljs-function"><span class="hljs-params">()</span> =></span> {<br> <span class="hljs-built_in">setTimeout</span>(fun, <span class="hljs-number">0</span>)<br>}, <span class="hljs-number">30000</span>)<br></code></pre></td></tr></table></figure>setTimeout是自带清除定时器的。</li><li>vue 项目的时候,如果页面使用到循环定时器,调用后台接口出现异常,在切换路由地时候并不能清除定时器。<figure class="highlight stylus"><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><code class="hljs stylus"><span class="hljs-function"><span class="hljs-title">beforeRouteLeave</span><span class="hljs-params">(to, from, next)</span></span> {<br>console<span class="hljs-selector-class">.log</span>(<span class="hljs-string">'beforeRouteLeave'</span>)<br>this<span class="hljs-selector-class">.clearTimer</span>()<br><span class="hljs-function"><span class="hljs-title">next</span><span class="hljs-params">()</span></span> <span class="hljs-comment">//一定不要忘记写</span><br>}<br></code></pre></td></tr></table></figure></li><li>setInterval有时会越跑越快<br>推荐使用setTimeout和递归结合代替</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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">var</span> demo = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'做点什么吧'</span>)<br><span class="hljs-built_in">setTimeout</span>(demo, <span class="hljs-number">1000</span>) <br>}<span class="hljs-string">`</span><br></code></pre></td></tr></table></figure>]]></content>
<categories>
<category>Javascript</category>
</categories>
</entry>
<entry>
<title>Vue中Object的变化侦测</title>
<link href="/2021/06/06/Vue%E4%B8%ADObject%E7%9A%84%E5%8F%98%E5%8C%96%E4%BE%A6%E6%B5%8B/"/>
<url>/2021/06/06/Vue%E4%B8%ADObject%E7%9A%84%E5%8F%98%E5%8C%96%E4%BE%A6%E6%B5%8B/</url>
<content type="html"><![CDATA[<p>前言:最近在阅读深入浅出vue这本书,对Vue的响应式原理中变化侦测有了进一步的理解。</p><hr><h1 id="一-什么是变化侦测"><a href="#一-什么是变化侦测" class="headerlink" title="一.什么是变化侦测"></a>一.什么是变化侦测</h1><p>我们在解释一个东西之前必须先搞清楚这个东西到底是什么,页面渲染的时候需要与数据有所交互,而当用户操作的时候这些数据是会发生变化的,当数据变化的同时为了用户和程序的良好交流,我们必须立即将改变的数据渲染到页面上,而Vue这个框架就很好的做到了这一点。</p><h1 id="二-如何侦测Object变化"><a href="#二-如何侦测Object变化" class="headerlink" title="二.如何侦测Object变化"></a>二.如何侦测Object变化</h1><p>1.数据劫持<br>2.观察者模式(发布者Dep-订阅者watcher)</p><h1 id="三-怎么实现数据劫持"><a href="#三-怎么实现数据劫持" class="headerlink" title="三.怎么实现数据劫持"></a>三.怎么实现数据劫持</h1><p>想要搞清怎么实现数据劫持,我们需要先弄清楚怎么追踪数据的变化,每一个数据都有一个setter,当我们修改数据的时候就会触发该数据的setter,然后做出一定的操作。<br>综上我们就知道了数据劫持的关键是为每一个数据添加上setter,包括对象的每一个属性,但是当遇到复杂的对象的成分的时候我们就需要递归的遍历每一个成员对象为他添加上setter等(包含但不只有setter属性)</p><figure class="highlight typescript"><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><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Observer</span> </span>{<br><span class="hljs-title">constructor</span> (<span class="hljs-params">value: <span class="hljs-built_in">any</span></span>) {<br> <span class="hljs-built_in">this</span>.value = value<br> <span class="hljs-built_in">this</span>.dep = <span class="hljs-keyword">new</span> Dep()<br> <span class="hljs-built_in">this</span>.vmCount = <span class="hljs-number">0</span><br> def(value, <span class="hljs-string">'__ob__'</span>, <span class="hljs-built_in">this</span>)<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(value)) {<br> <span class="hljs-keyword">const</span> augment = hasProto<br> ? protoAugment<br> : copyAugment<br> augment(value, arrayMethods, arrayKeys)<br> <span class="hljs-built_in">this</span>.observeArray(value)<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-built_in">this</span>.walk(value)<br> }<br>}<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">defineReactive</span> (<span class="hljs-params"></span></span><br><span class="hljs-params"><span class="hljs-function"> obj: <span class="hljs-built_in">Object</span>,</span></span><br><span class="hljs-params"><span class="hljs-function"> key: <span class="hljs-built_in">string</span>,</span></span><br><span class="hljs-params"><span class="hljs-function"> val: <span class="hljs-built_in">any</span>,</span></span><br><span class="hljs-params"><span class="hljs-function"> customSetter?: ?<span class="hljs-built_in">Function</span>,</span></span><br><span class="hljs-params"><span class="hljs-function"> shallow?: <span class="hljs-built_in">boolean</span></span></span><br><span class="hljs-params"><span class="hljs-function"></span>) </span>{<br> <span class="hljs-keyword">const</span> dep = <span class="hljs-keyword">new</span> Dep()<br><br> <span class="hljs-keyword">const</span> property = <span class="hljs-built_in">Object</span>.getOwnPropertyDescriptor(obj, key)<br> <span class="hljs-keyword">if</span> (property && property.configurable === <span class="hljs-literal">false</span>) {<br> <span class="hljs-keyword">return</span><br> }<br><br> <span class="hljs-comment">// cater for pre-defined getter/setters</span><br> <span class="hljs-keyword">const</span> getter = property && property.get<br> <span class="hljs-keyword">const</span> setter = property && property.set<br><br> <span class="hljs-keyword">let</span> childOb = !shallow && observe(val)<br> <span class="hljs-built_in">Object</span>.defineProperty(obj, key, {<br> <span class="hljs-attr">enumerable</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">configurable</span>: <span class="hljs-literal">true</span>,<br><span class="hljs-comment">//get方法</span><br> <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reactiveGetter</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">const</span> value = getter ? getter.call(obj) : val<br> <span class="hljs-keyword">if</span> (Dep.target) {<br> dep.depend()<br> <span class="hljs-keyword">if</span> (childOb) {<br> childOb.dep.depend()<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">Array</span>.isArray(value)) {<br> dependArray(value)<br> }<br> }<br> }<br> <span class="hljs-keyword">return</span> value<br> },<br><span class="hljs-comment">//set方法</span><br> <span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">reactiveSetter</span> (<span class="hljs-params">newVal</span>) </span>{<br> <span class="hljs-keyword">const</span> value = getter ? getter.call(obj) : val<br> <span class="hljs-comment">/* eslint-disable no-self-compare */</span><br> <span class="hljs-keyword">if</span> (newVal === value || (newVal !== newVal && value !== value)) {<br> <span class="hljs-keyword">return</span><br> }<br> <span class="hljs-comment">/* eslint-enable no-self-compare */</span><br> <span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'production'</span> && customSetter) {<br> customSetter()<br> }<br> <span class="hljs-keyword">if</span> (setter) {<br> setter.call(obj, newVal)<br> } <span class="hljs-keyword">else</span> {<br> val = newVal<br> }<br> childOb = !shallow && observe(newVal)<br> dep.notify()<br> }<br> })<br>}<br>}<br></code></pre></td></tr></table></figure><p>看完精简的核心代码之后就会发现,之前的Vue监测变化使用的是Object.defineProperty方法,但是在ES6出现之后会使用Proxy来实现。</p><h1 id="四-怎么实现观察者模式"><a href="#四-怎么实现观察者模式" class="headerlink" title="四.怎么实现观察者模式"></a>四.怎么实现观察者模式</h1><p>观察者模式有两个比较重要的对象:<br><strong>订阅者watcher</strong>:<br>update方法用于实现当事件发生时,具体要做的事情;<br><strong>发布者Dep</strong>:<br>①subs 数组:存储所有的观察者<br>②addSub():添加观察者<br>③notify():当事件发生,调用所有观察者的 update() 方法<br><strong>并且我们在弄懂这一部分之前还要明白应该如何收集依赖,这时候就明白了不止要为每一个元素设置setter,也要设置getter,就是在引用当前数据的时候所执行的操作,我们就在getter里面收集依赖</strong><br>其实依赖就是watcher,watcher是以恶搞中间角色,当数据变化的时候通知它,然后由它来通知其他地方,至于模拟代码我就不放上来了,看源码是最好选择。至于依赖收集在哪里自然就知道是在Dep中了,模拟代码网上有很多,直接看源码也是可以的。</p><h1 id="五-Object变化侦测的缺点"><a href="#五-Object变化侦测的缺点" class="headerlink" title="五.Object变化侦测的缺点"></a>五.Object变化侦测的缺点</h1><p>由于getter/setter只能侦测一个数据是否被使用和修改,无法侦测新增属性和删除属性,所以这一点需要注意</p>]]></content>
<categories>
<category>Vue</category>
</categories>
</entry>
<entry>
<title>VueRouter路由守卫</title>
<link href="/2021/06/06/VueRouter%E8%B7%AF%E7%94%B1%E5%AE%88%E5%8D%AB/"/>
<url>/2021/06/06/VueRouter%E8%B7%AF%E7%94%B1%E5%AE%88%E5%8D%AB/</url>
<content type="html"><![CDATA[<p>前言:最近学习一个商城项目的时候出现了一个请求,就是当我点击个人主页的时候,如果已经登陆并且没有超时的情况下会跳转到个人中心,但是如果没有登陆,或者登录超时了,那么就会跳转到登录界面,需要完成这个需求我们就必须使用路由守卫。</p><h1 id="1-路由守卫是什么?"><a href="#1-路由守卫是什么?" class="headerlink" title="1.路由守卫是什么?"></a>1.路由守卫是什么?</h1><p>路由守卫简单的来说就是路由在跳转的时候所触发的一些钩子函数</p><h1 id="2-导航守卫的分类"><a href="#2-导航守卫的分类" class="headerlink" title="2.导航守卫的分类"></a>2.导航守卫的分类</h1><p>1)全局前置守卫——发生在路由跳转前</p><p>当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫 resolve 完之前一直处于<strong>等待中</strong>。</p><figure class="highlight reasonml"><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><code class="hljs reasonml">const router = <span class="hljs-keyword">new</span> <span class="hljs-constructor">VueRouter({ <span class="hljs-operator">...</span> })</span><br>router.before<span class="hljs-constructor">Each((<span class="hljs-params">to</span>, <span class="hljs-params">from</span>, <span class="hljs-params">next</span>)</span> => {<br> <span class="hljs-comment">// ...</span><br>})<br></code></pre></td></tr></table></figure><p>2)全局解析守卫——发生在路由跳转前</p><p>可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,<strong>同时在所有组件内守卫</strong>和<strong>异步路由组件被解析之后</strong>,解析守卫就被调用。</p><figure class="highlight reasonml"><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><code class="hljs reasonml">const router = <span class="hljs-keyword">new</span> <span class="hljs-constructor">VueRouter({ <span class="hljs-operator">...</span> })</span>\<br>router.before<span class="hljs-constructor">Resolve((<span class="hljs-params">to</span>, <span class="hljs-params">from</span>, <span class="hljs-params">next</span>)</span> => {<br> <span class="hljs-comment">// ...</span><br>})<br></code></pre></td></tr></table></figure><p>3)全局后置钩子——发生在路由跳转后,组件路由跳转前</p><figure class="highlight coffeescript"><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><code class="hljs coffeescript">router.afterEach(<span class="hljs-function"><span class="hljs-params">(to, <span class="hljs-keyword">from</span>)</span> =></span> {<br> <span class="hljs-regexp">//</span> ...<br>})<br></code></pre></td></tr></table></figure><p>4)路由独享守卫</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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> router = <span class="hljs-keyword">new</span> VueRouter({<br> <span class="hljs-attr">routes</span>: [<br> {<br> <span class="hljs-attr">path</span>: <span class="hljs-string">'/foo'</span>,<br> <span class="hljs-attr">component</span>: Foo,<br> <span class="hljs-attr">beforeEnter</span>: <span class="hljs-function">(<span class="hljs-params">to, <span class="hljs-keyword">from</span>, next</span>) =></span> {<br> <span class="hljs-comment">// ...</span><br> }<br> }<br> ]<br>})<br></code></pre></td></tr></table></figure><p>5)组件内守卫<br>可以在路由组件内直接定义以下路由导航守卫:<br><strong>beforeRouteEnter</strong><br><strong>beforeRouteUpdate (2.2 新增)</strong><br><strong>beforeRouteLeave</strong></p><figure class="highlight awk"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs awk">const Foo = {<br> template: `...`,<br> beforeRouteEnter(to, from, <span class="hljs-keyword">next</span>) {<br> <span class="hljs-regexp">//</span> 在渲染该组件的对应路由被 confirm 前调用<br> <span class="hljs-regexp">//</span> 不!能!获取组件实例 `this`<br> <span class="hljs-regexp">//</span> 因为当守卫执行前,组件实例还没被创建<br> },<br> beforeRouteUpdate(to, from, <span class="hljs-keyword">next</span>) {<br> <span class="hljs-regexp">//</span> 在当前路由改变,但是该组件被复用时调用<br> <span class="hljs-regexp">//</span> 举例来说,对于一个带有动态参数的路径 <span class="hljs-regexp">/foo/</span>:id,在 <span class="hljs-regexp">/foo/</span><span class="hljs-number">1</span> 和 <span class="hljs-regexp">/foo/</span><span class="hljs-number">2</span> 之间跳转的时候,<br> <span class="hljs-regexp">//</span> 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。<br> <span class="hljs-regexp">//</span> 可以访问组件实例 `this`<br> },<br> beforeRouteLeave(to, from, <span class="hljs-keyword">next</span>) {<br> <span class="hljs-regexp">//</span> 导航离开该组件的对应路由时调用<br> <span class="hljs-regexp">//</span> 可以访问组件实例 `this`<br> }<br>}<br></code></pre></td></tr></table></figure><h1 id="3-守卫的回调参数"><a href="#3-守卫的回调参数" class="headerlink" title="3.守卫的回调参数"></a>3.守卫的回调参数</h1><p><strong>to:目标路由对象;</strong><br><strong>from:即将要离开的路由对象;</strong><br><strong>next:他是最重要的一个参数,他相当于佛珠的线,把一个一个珠子逐个串起来。</strong></p><p><img src="/images/pasted-1.png" alt="upload successful"></p><h1 id="4-完整的导航解析"><a href="#4-完整的导航解析" class="headerlink" title="4.完整的导航解析"></a>4.完整的导航解析</h1><p>1.导航被触发。<br>2.在失活的组件里调用 beforeRouteLeave 守卫。<br>3.调用全局的 beforeEach 守卫。<br>4.在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。<br>5.在路由配置里调用 beforeEnter。<br>6.解析异步路由组件。<br>7.在被激活的组件里调用 beforeRouteEnter。<br>8.调用全局的 beforeResolve 守卫 (2.5+)。<br>9.导航被确认。<br>10.调用全局的 afterEach 钩子。<br>11.触发 DOM 更新。<br>12.调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。</p>]]></content>
<categories>
<category>Vue</category>
</categories>
</entry>
<entry>
<title>关于React事件处理函数中this的指向问题</title>
<link href="/2021/06/05/%E5%85%B3%E4%BA%8EReact%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E5%87%BD%E6%95%B0%E4%B8%ADthis%E7%9A%84%E6%8C%87%E5%90%91%E9%97%AE%E9%A2%98/"/>
<url>/2021/06/05/%E5%85%B3%E4%BA%8EReact%E4%BA%8B%E4%BB%B6%E5%A4%84%E7%90%86%E5%87%BD%E6%95%B0%E4%B8%ADthis%E7%9A%84%E6%8C%87%E5%90%91%E9%97%AE%E9%A2%98/</url>
<content type="html"><![CDATA[<h1 id="问题出现:"><a href="#问题出现:" class="headerlink" title="问题出现:"></a>问题出现:</h1><p>我们在给比如按钮绑定点击事件函数的时候,我们在事件函数中使用this的时候会报错,这是由于this.xxx是undefined。</p><p>通过阅读了几篇博客之后我明白这并不是React这个框架的原因,这是JS的一个机制问题。</p><h1 id="问题分析:"><a href="#问题分析:" class="headerlink" title="问题分析:"></a>问题分析:</h1><p>我们之前所知道的是,JS中对象调用方法的时候,方法中的this指向的是调用对象</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><code class="hljs javascript"><span class="hljs-keyword">let</span> obj = {<br> <span class="hljs-attr">tmp</span>:<span class="hljs-string">'Yes!'</span>,<br> <span class="hljs-attr">testLog</span>:<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.tmp);<br> }<br>}; <br>obj.testLog();<br></code></pre></td></tr></table></figure><p>但是呢,如果我们使用一个指针指向该方法的内存区域,再通过这个指针调用这片内存的函数,就会发现this是windows</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><code class="hljs javascript"><span class="hljs-keyword">let</span> obj = {<br> <span class="hljs-attr">tmp</span>:<span class="hljs-string">'Yes!'</span>,<br> <span class="hljs-attr">testLog</span>:<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.tmp);<br> }<br>};<br><span class="hljs-keyword">let</span> tmpLog = obj.testLog;<br>tmpLog();<br></code></pre></td></tr></table></figure><p>所以this.xxx就是windows.xxx是没有定义的。<br>而我们传递的事件函数就是一个函数,而onClick是一个中间变量这样就会导致this的指向丢失。</p><h1 id="解决方法:"><a href="#解决方法:" class="headerlink" title="解决方法:"></a>解决方法:</h1><p><strong>(1)构造函数绑定this(推荐)</strong></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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Button</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br><span class="hljs-function"><span class="hljs-title">constructor</span>(<span class="hljs-params">props</span>)</span> {<br> <span class="hljs-built_in">super</span>(props);<br> <span class="hljs-built_in">this</span>.handleClick = <span class="hljs-built_in">this</span>.handleClick.bind(<span class="hljs-built_in">this</span>);<br> }<br> <span class="hljs-function"><span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'this is:'</span>, <span class="hljs-built_in">this</span>);<br> }<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">return</span> (<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.handleClick}</span>></span></span><br><span class="xml"> Click me</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br> );<br> } <br>}<br></code></pre></td></tr></table></figure><p><strong>(2)调用的时候绑定this</strong></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></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Button</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> <span class="hljs-function"><span class="hljs-title">handleClick</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'this is:'</span>, <span class="hljs-built_in">this</span>);<br> }<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">return</span> (<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{this.handleClick.bind(this)}</span>></span></span><br><span class="xml"> Click me</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br> );<br> }<br>}<br></code></pre></td></tr></table></figure><p><strong>(3)箭头函数:箭头函数中的this只会固定的指向声明时的this指向。</strong></p>]]></content>
<categories>
<category>React</category>
</categories>
</entry>
</search>