-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
468 lines (243 loc) · 344 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Hexo</title>
<link href="http://example.com/atom.xml" rel="self"/>
<link href="http://example.com/"/>
<updated>2024-11-24T17:45:51.387Z</updated>
<id>http://example.com/</id>
<author>
<name>John Doe</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>EvenLoop队列中的执行顺序</title>
<link href="http://example.com/2023/03/28/Javascript/EvenLoop%E9%98%9F%E5%88%97%E4%B8%AD%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/"/>
<id>http://example.com/2023/03/28/Javascript/EvenLoop%E9%98%9F%E5%88%97%E4%B8%AD%E7%9A%84%E6%89%A7%E8%A1%8C%E9%A1%BA%E5%BA%8F/</id>
<published>2023-03-28T12:15:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>前言:谈谈<code>promise.resove</code>,<code>setTimeout</code>,<code>setImmediate</code>,<code>process.nextTick</code>在EvenLoop队列中的执行顺序</p></blockquote><h2 id="1-问题的引出"><a href="#1-问题的引出" class="headerlink" title="1. 问题的引出"></a>1. 问题的引出</h2><p>event loop都不陌生,是指主线程从“任务队列”中循环读取任务,比如</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)},<span class="number">0</span>);</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>)</span><br><span class="line"><span class="comment">//输出2,1</span></span><br></pre></td></tr></table></figure></div><p>在上述的例子中,我们明白首先执行主线程中的同步任务,当主线程任务执行完毕后,再从event loop中读取任务,因此先输出2,再输出1。</p><p>event loop读取任务的先后顺序,取决于任务队列(Job queue)中对于不同任务读取规则的限定。比如下面一个例子:</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>);</span><br><span class="line">}, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line">});</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>);</span><br><span class="line"><span class="comment">//输出为 1 2 3</span></span><br></pre></td></tr></table></figure></div><p>先输出1,没有问题,因为是同步任务在主线程中优先执行,这里的问题是setTimeout和Promise.then任务的执行优先级是如何定义的。</p><h2 id="2-Job-queue中的执行顺序"><a href="#2-Job-queue中的执行顺序" class="headerlink" title="2 . Job queue中的执行顺序"></a>2 . Job queue中的执行顺序</h2><p>在Job queue中的队列分为两种类型:微任务(macro-task)和宏任务(micro-task)。我们举例来看执行顺序的规定,我们设</p><p>macro-task队列包含任务: <em><strong>a1, a2 , a3</strong></em><br>micro-task队列包含任务: <em><strong>b1, b2 , b3</strong></em></p><p>执行顺序为,首先执行marco-task队列开头的任务,也就是 <em><strong>a1</strong></em> 任务,执行完毕后,在执行micro-task队列里的所有任务,也就是依次执行<em><strong>b1, b2 , b3</strong></em>,执行完后清空micro-task中的任务,接着执行marco-task中的第二个任务,依次循环。</p><p>了解完了macro-task和micro-task两种队列的执行顺序之后,我们接着来看,真实场景下这两种类型的队列里真正包含的任务(我们以node V8引擎为例),在node V8中,这两种类型的真实任务顺序如下所示:</p><p>macro-task队列真实包含任务:</p><p><strong>script(主程序代码),setTimeout, setInterval, setImmediate, I/O, UI rendering</strong></p><p>micro-task队列真实包含任务:<br><em><strong>process.nextTick, Promises, Object.observe, MutationObserver</strong></em></p><p>由此我们得到的执行顺序应该为:</p><p><em><strong>script(主程序代码)—>process.nextTick—>Promises…——>setTimeout——>setInterval——>setImmediate——> I/O——>UI rendering</strong></em></p><p>在ES6中macro-task队列又称为ScriptJobs,而micro-task又称PromiseJobs</p><h2 id="3-真实环境中执行顺序的举例"><a href="#3-真实环境中执行顺序的举例" class="headerlink" title="3 . 真实环境中执行顺序的举例"></a>3 . 真实环境中执行顺序的举例</h2><h3 id="1-setTimeout和promise"><a href="#1-setTimeout和promise" class="headerlink" title="(1) setTimeout和promise"></a>(1) setTimeout和promise</h3><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>);</span><br><span class="line">}, <span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="title class_">Promise</span>.<span class="title function_">resolve</span>().<span class="title function_">then</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>);</span><br></pre></td></tr></table></figure></div><p>我们先以第1小节的例子为例,这里遵循的顺序为:</p><p><em><strong>script(主程序代码)——>promise——>setTimeout</strong></em><br>对应的输出依次为:1 ——>2——>3</p><h3 id="2-process-nextTick和promise、setTimeout"><a href="#2-process-nextTick和promise、setTimeout" class="headerlink" title="(2) process.nextTick和promise、setTimeout"></a>(2) process.nextTick和promise、setTimeout</h3><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)},<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve,reject</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line"> <span class="title function_">resolve</span>();</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>)</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)});</span><br><span class="line"></span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">5</span>)});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">6</span>);</span><br><span class="line"><span class="comment">//输出2,6,5,3,4,1</span></span><br></pre></td></tr></table></figure></div><p>这个例子就比较复杂了,这里要注意的一点在定义promise的时候,promise构造部分是同步执行的,这样问题就迎刃而解了。</p><p>首先分析Job queue的执行顺序:</p><p><em><strong>script(主程序代码)——>process.nextTick——>promise——>setTimeout</strong></em></p><p>I) <em><strong>主体部分</strong></em>: 定义promise的构造部分是同步的,<br>因此先输出2 ,主体部分再输出6(同步情况下,就是严格按照定义的先后顺序)</p><p>II)<em><strong>process.nextTick</strong></em>: 输出5</p><p>III)<em><strong>promise</strong></em>: 这里的promise部分,严格的说其实是promise.then部分,输出的是3,4</p><p>IV) <em><strong>setTimeout</strong></em> : 最后输出1</p><p>综合的执行顺序就是: 2——>6——>5——>3——>4——>1</p><h3 id="3-更复杂的例子"><a href="#3-更复杂的例子" class="headerlink" title="(3)更复杂的例子"></a>(3)更复杂的例子</h3><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">1</span>)},<span class="number">0</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="keyword">function</span>(<span class="params">resolve,reject</span>){</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">2</span>);</span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="title function_">resolve</span>()},<span class="number">0</span>)</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">3</span>)</span><br><span class="line">}).<span class="title function_">then</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)});</span><br><span class="line"></span><br><span class="line">process.<span class="title function_">nextTick</span>(<span class="keyword">function</span>(<span class="params"></span>){<span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">5</span>)});</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">6</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//输出的是 2 6 5 1 3 4</span></span><br></pre></td></tr></table></figure></div><p>这种情况跟我们(2)中的例子,区别在于promise的构造中,没有同步的resolve,因此promise.then在当前的执行队列中是不存在的,只有promise从pending转移到resolve,才会有then方法,而这个resolve是在一个setTimout时间中完成的,因此3,4最后输出。</p><blockquote><p>文章转载来源:: <a class="link" href="https://github.com/forthealllight/blog/issues/5" >https://github.com/forthealllight/blog/issues/5<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p></blockquote>]]></content>
<summary type="html">false</summary>
<category term="Javascript" scheme="http://example.com/categories/Javascript/"/>
<category term="EvenLoop" scheme="http://example.com/tags/EvenLoop/"/>
</entry>
<entry>
<title>对象的继承</title>
<link href="http://example.com/2023/03/12/Javascript/%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BB%A7%E6%89%BF/"/>
<id>http://example.com/2023/03/12/Javascript/%E5%AF%B9%E8%B1%A1%E7%9A%84%E7%BB%A7%E6%89%BF/</id>
<published>2023-03-12T21:59:26.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>在Javascript中实现继承主要是依靠原型链来实现的;其基本思想是通过原型实现一个引用类型继承另一个引用类型的属性和方法。</p></blockquote><h4 id="1-原型链"><a href="#1-原型链" class="headerlink" title="1.原型链"></a>1.原型链</h4><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 父类</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">super</span> = <span class="string">'SuperType'</span></span><br><span class="line">}</span><br><span class="line"><span class="title class_">SuperType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayType</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">super</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子类</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params"></span>){</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">sub</span> = <span class="string">'SubType'</span></span><br><span class="line">}</span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">SuperType</span>()</span><br></pre></td></tr></table></figure></div><p>优点: 写法方便简洁,容易理解</p><p>缺点:对象实例共享所有继承的属性和方法;无法向父类构造函数传参</p><h4 id="2-借用构造函数"><a href="#2-借用构造函数" class="headerlink" title="2.借用构造函数"></a>2.借用构造函数</h4><blockquote><p>子类型构造函数的内部调用父类型构造函数</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params"></span>){ </span><br><span class="line"><span class="variable language_">this</span>.<span class="property">colors</span> = [<span class="string">"red"</span>, <span class="string">"blue"</span>, <span class="string">"green"</span>]; </span><br><span class="line">} </span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params"></span>){ </span><br><span class="line"> <span class="comment">//继承了 SuperType </span></span><br><span class="line"> <span class="title class_">SuperType</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>); </span><br><span class="line">} </span><br><span class="line"><span class="keyword">const</span> c1 = <span class="keyword">new</span> <span class="title class_">SubType</span>(); </span><br><span class="line">c1.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"black"</span>); <span class="comment">// red,blue,green,black</span></span><br><span class="line"><span class="keyword">const</span> c2 = <span class="keyword">new</span> <span class="title class_">SubType</span>(); <span class="comment">// red,blue,green</span></span><br></pre></td></tr></table></figure></div><p>优点:解决了原型链继承不能传承和父类原型共享的问题</p><p>缺点:借用构造函数的方法都是在构造函数中定义的,因此无法实现函数复用;在父类的原型中定义的方法,对于子类型而言也是不可见的,所有的类型都只能使用构造函数模式</p><h4 id="3-组合继承"><a href="#3-组合继承" class="headerlink" title="3.组合继承"></a>3.组合继承</h4><blockquote><p>将原型链和借用构造函数的技术组合到一块,从而发挥二者之长的一种继承模式。其背后的思路是<strong>使用原型链实现对原型属性和方法的继承</strong>,而通过<strong>借用构造函数来实现对实例属性的继承</strong>。这样,既通过在原型上定义方法实现了函数复用,又能够保证每个实例都有它自己的属性。所以组合继承避免了原型链和借用构造函数的缺陷,融合了它们的优点,成为 JavaScript 中最常用的继承模式。</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 父类</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SuperType</span>(<span class="params">name</span>){ </span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name; </span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">colors</span> = [<span class="string">"red"</span>, <span class="string">"blue"</span>, <span class="string">"green"</span>]; </span><br><span class="line">} </span><br><span class="line"><span class="title class_">SuperType</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">sayName</span> = <span class="keyword">function</span>(<span class="params"></span>){ <span class="comment">// 原型链继承原型上的属性和方法</span></span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">name</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 子类</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">SubType</span>(<span class="params">name</span>){ </span><br><span class="line"> <span class="title class_">SuperType</span>.<span class="title function_">call</span>(<span class="variable language_">this</span>, name); <span class="comment">// 借用构造函数来传递参数 </span></span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="title class_">SubType</span>.<span class="property"><span class="keyword">prototype</span></span> = <span class="keyword">new</span> <span class="title class_">SuperType</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建实例对象</span></span><br><span class="line"><span class="keyword">var</span> p1 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"Nicholas"</span>, <span class="number">29</span>); </span><br><span class="line">instance1.<span class="property">colors</span>.<span class="title function_">push</span>(<span class="string">"black"</span>); </span><br><span class="line"><span class="title function_">alert</span>(p1.<span class="property">colors</span>); <span class="comment">//"red,blue,green,black" </span></span><br><span class="line">p1.<span class="title function_">sayName</span>(); <span class="comment">//"Nicholas"; </span></span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> p2 = <span class="keyword">new</span> <span class="title class_">SubType</span>(<span class="string">"Greg"</span>, <span class="number">27</span>); </span><br><span class="line"><span class="title function_">alert</span>(p2.<span class="property">colors</span>); <span class="comment">//"red,blue,green" </span></span><br><span class="line">p2.<span class="title function_">sayName</span>(); <span class="comment">//"Greg"; </span></span><br></pre></td></tr></table></figure></div><p>优点:解决了原型链继承和借用构造函数继承的问题</p><p>缺点:无论在什么情况下,都会调用两次父类构造函数(一次在创建子类型的时候,另一次在子类构造函数内)</p><h4 id="4-原型式继承"><a href="#4-原型式继承" class="headerlink" title="4.原型式继承"></a>4.原型式继承</h4><blockquote><p>将需要被继承的对象挂载到函数内部新创建的构造函数原型上并返回该构造函数的实例</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu 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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 模拟Object.create()方法</span></span><br><span class="line"><span class="comment">* o: 需要被继承的对象</span></span><br><span class="line"><span class="comment">* o2: 自定义参数对象,默认为空</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">object</span>(<span class="params">o,o2={}</span>){</span><br><span class="line"> <span class="comment">// 创建一个构造函数F为接收自定义参数</span></span><br><span class="line"> <span class="keyword">var</span> F = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> o2) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> key2 <span class="keyword">in</span> o2[key]) {</span><br><span class="line"> <span class="variable language_">this</span>[key] = o2[key][key2]<span class="comment">//跳坑指南: 不能使用this.key,因为点赋值会将变量key转为字符串“key” </span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> };</span><br><span class="line"> F.<span class="property"><span class="keyword">prototype</span></span> = o <span class="comment">// 重写构造函数F原型对象为传入的对象o</span></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title function_">F</span>() <span class="comment">//返回构造函数的实例</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'no-Name'</span>,</span><br><span class="line"> <span class="attr">friend</span>:[<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>],</span><br><span class="line"> <span class="attr">sayName</span>:<span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="variable language_">this</span>.<span class="property">name</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> a1 = <span class="title function_">object</span>(person)</span><br></pre></td></tr></table></figure></div><blockquote><p>ECMAScript 5 通过新增 Object.create()方法规范化了原型式继承。这个方法接收两个参数:一个用作新对象原型的对象和(可选的)一个为新对象定义额外属性的对象</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu 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><span class="line"><span class="comment">// 在传入一个参数的情况下,Object.create()与 object()方法的行为相同</span></span><br><span class="line"> <span class="keyword">var</span> person = { </span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Nicholas"</span>, </span><br><span class="line"> <span class="attr">friends</span>: [<span class="string">"Shelby"</span>, <span class="string">"Court"</span>, <span class="string">"Van"</span>] </span><br><span class="line"> }; </span><br><span class="line"><span class="keyword">var</span> anotherPerson = <span class="title class_">Object</span>.<span class="title function_">create</span>(person); </span><br><span class="line">anotherPerson.<span class="property">name</span> = <span class="string">"Greg"</span>; anotherPerson.<span class="property">friends</span>.<span class="title function_">push</span>(<span class="string">"Rob"</span>); </span><br><span class="line"><span class="keyword">var</span> yetAnotherPerson = <span class="title class_">Object</span>.<span class="title function_">create</span>(person); </span><br><span class="line">yetAnotherPerson.<span class="property">name</span> = <span class="string">"Linda"</span>; </span><br><span class="line">yetAnotherPerson.<span class="property">friends</span>.<span class="title function_">push</span>(<span class="string">"Barbie"</span>); </span><br><span class="line"><span class="title function_">alert</span>(person.<span class="property">friends</span>); <span class="comment">//"Shelby,Court,Van,Rob,Barbie"</span></span><br></pre></td></tr></table></figure></div><h4 id="5-寄生式继承"><a href="#5-寄生式继承" class="headerlink" title="5.寄生式继承"></a>5.寄生式继承</h4><blockquote><p>寄生式(parasitic)继承是与原型式继承紧密相关的一种思路 ,其与工厂模式类似,即创建一个仅用于封装继承过程的函数,该函数在内部以某种方式来增强对象,最后再像真地是它做了所有工作一样返回对象</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">createAnother</span>(<span class="params">original</span>){</span><br><span class="line"> <span class="keyword">var</span> clone = <span class="title function_">object</span>(original) <span class="comment">//创建对象</span></span><br><span class="line"> clone.<span class="property">sayHi</span> = <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">alert</span>(<span class="string">'hi!'</span>)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> clone</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> person = { </span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Jiangwen"</span>, </span><br><span class="line"> <span class="attr">friends</span>: [<span class="string">"A"</span>, <span class="string">"B"</span>, <span class="string">"C"</span>] </span><br><span class="line">}; </span><br><span class="line"><span class="keyword">var</span> anotherPerson = <span class="title function_">createAnother</span>(person); </span><br><span class="line">anotherPerson.<span class="title function_">sayHi</span>(); <span class="comment">// hi</span></span><br></pre></td></tr></table></figure></div><p>在主要考虑对象而不是自定义类型和构造函数的情况下,寄生式继承也是一种有用的模式。前面示范继承模式时使用的 object()函数不是必需的;任何能够返回新对象的函数都适用于此模式。</p><h4 id="6-寄生组式继承"><a href="#6-寄生组式继承" class="headerlink" title="6.寄生组式继承"></a>6.寄生组式继承</h4><blockquote><p>所谓寄生组合式继承,即通过借用构造函数来继承属性,通过原型链的混成形式来继承方法 ; 本质上,就是使用寄生式继承来继承超类型的原型,然后再将结果指定给子类型的原型。</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">inheritPrototype</span>(<span class="params">subType, superType</span>){ </span><br><span class="line"> <span class="keyword">var</span> prototype = <span class="title function_">object</span>(superType.<span class="property"><span class="keyword">prototype</span></span>); <span class="comment">//创建对象 </span></span><br><span class="line">prototype.<span class="property">constructor</span> = subType; <span class="comment">//增强对象 </span></span><br><span class="line"> subType.<span class="property"><span class="keyword">prototype</span></span> = prototype; <span class="comment">//指定对象 </span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h4 id="7-ES6的Class类继承"><a href="#7-ES6的Class类继承" class="headerlink" title="7. ES6的Class类继承"></a>7. ES6的Class类继承</h4><blockquote><p>class通过extends关键字实现继承</p></blockquote><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 父类(动物类)</span></span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Animal</span> {</span><br><span class="line"> <span class="title function_">constructor</span> (<span class="params">kind</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">kind</span> = kind</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">getKind</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'我是一只'</span> + <span class="variable language_">this</span>.<span class="property">kind</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 子类(猫类)</span></span><br><span class="line"> <span class="keyword">class</span> <span class="title class_">Cat</span> <span class="keyword">extends</span> <span class="title class_ inherited__">Animal</span> {</span><br><span class="line"> <span class="title function_">constructor</span> (<span class="params">name</span>) {</span><br><span class="line"> <span class="variable language_">super</span>(<span class="string">'猫'</span>) <span class="comment">// 子类的构造函数中必须先调用super方法</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">getCatInfo</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">name</span> + <span class="string">':'</span> + <span class="variable language_">super</span>.<span class="title function_">getKind</span>() + <span class="string">'...喵喵喵...'</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 对象实例</span></span><br><span class="line"> <span class="keyword">const</span> c = <span class="keyword">new</span> <span class="title class_">Cat</span>(<span class="string">'没头脑'</span>)</span><br><span class="line"> c.<span class="title function_">getCatInfo</span>()</span><br></pre></td></tr></table></figure></div><p>优点:语法简单易懂,操作简便</p><p>缺点:浏览器没有全面兼容class关键字</p>]]></content>
<summary type="html"><blockquote>
<p>在Javascript中实现继承主要是依靠原型链来实现的;其基本思想是通过原型实现一个引用类型继承另一个引用类型的属性和方法。</p>
</blockquote>
<h4 id="1-原型链"><a href="#1-原型链" class="hea</summary>
<category term="Javascript" scheme="http://example.com/categories/Javascript/"/>
<category term="Javascript" scheme="http://example.com/tags/Javascript/"/>
</entry>
<entry>
<title>常用正则</title>
<link href="http://example.com/2023/03/12/Javascript/%E5%B8%B8%E7%94%A8%E6%AD%A3%E5%88%99/"/>
<id>http://example.com/2023/03/12/Javascript/%E5%B8%B8%E7%94%A8%E6%AD%A3%E5%88%99/</id>
<published>2023-03-12T21:59:26.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 匹配邮箱</span></span><br><span class="line"><span class="keyword">let</span> reg = /^([a-zA-Z]|[<span class="number">0</span>-<span class="number">9</span>])(\w|\-)+@[a-zA-<span class="variable constant_">Z0</span>-<span class="number">9</span>]+\.([a-zA-Z]{<span class="number">2</span>,<span class="number">4</span>})$</span><br><span class="line"></span><br><span class="line"><span class="comment">// (新)匹配手机号</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/^1[0-9]{10}$/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// (旧)匹配手机号</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/^1(3|4|5|7|8)[0-9]{9}$/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配8-16位数字和字母密码的正则表达式</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{8,16}$/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配国内电话号码 0510-4305211</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/\d{3}-\d{8}|\d{4}-\d{7}/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配身份证号码</span></span><br><span class="line"><span class="keyword">let</span> reg=<span class="regexp">/(^\d{15}$)|(^\d{18}$)|(^\d{17}(\d|X|x)$)/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配腾讯QQ号</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/[1-9][0-9]{4,}/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配ip地址</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/\d+\.\d+\.\d+\.\d+/</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配中文</span></span><br><span class="line"><span class="keyword">let</span> reg = <span class="regexp">/^[\u4e00-\u9fa5]*$/</span>;</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html"><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span c</summary>
<category term="Javascript" scheme="http://example.com/categories/Javascript/"/>
<category term="正则表达式" scheme="http://example.com/tags/%E6%AD%A3%E5%88%99%E8%A1%A8%E8%BE%BE%E5%BC%8F/"/>
</entry>
<entry>
<title>前端的自我修养</title>
<link href="http://example.com/2023/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%89%8D%E7%AB%AF%E7%9A%84%E8%87%AA%E6%88%91%E4%BF%AE%E5%85%BB/"/>
<id>http://example.com/2023/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%89%8D%E7%AB%AF%E7%9A%84%E8%87%AA%E6%88%91%E4%BF%AE%E5%85%BB/</id>
<published>2023-03-06T18:50:24.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<h2 id="CSS部分"><a href="#CSS部分" class="headerlink" title="CSS部分"></a>CSS部分</h2><h6 id="问:css盒模型是什么?"><a href="#问:css盒模型是什么?" class="headerlink" title="问:css盒模型是什么?"></a>问:css盒模型是什么?</h6><p>答: 在html中所有的元素都可以看成一个盒子,盒子由外边距<code>margin</code>、内边距<code>padding</code>、边框<code>border</code>、内容<code>content</code>四部分构成;盒子的类型分为两种:</p><ul><li><p>标准盒模型:<code>margin border + padding + content</code></p></li><li><p>IE盒模型:<code>margin + content(border + padding)</code></p><p>通过控制css属性<code>box-sizing</code>: <code>默认值(标准盒模型)</code>| <code>border-box(IE盒模型)</code></p><p>总结: 标准盒模型的宽高设置的是内容<code>content</code>, IE盒模型的宽高设置的是<code>border + padding + content</code>三者相加的总和</p></li></ul><h6 id="问:css选择器的优先级?"><a href="#问:css选择器的优先级?" class="headerlink" title="问:css选择器的优先级?"></a>问:css选择器的优先级?</h6><p>答:css的特性包括:继承性、层叠性、优先级;当一个元素有多个样式的时候,显示权重高的样式,权限的优先级分为</p><p><code>important</code> > <code>行内样式</code> > <code>id</code> > <code>类/伪类/属性</code>> <code>标签</code> > <code>全局选择器*</code></p><h6 id="问:隐藏元素的方式有哪些?"><a href="#问:隐藏元素的方式有哪些?" class="headerlink" title="问:隐藏元素的方式有哪些?"></a>问:隐藏元素的方式有哪些?</h6><p>答:<code>display: none</code> | <code>opacity: 0</code> | <code> visibility: hidden</code>,其中只有第一种隐藏方式的元素不占据空间</p><h6 id="问:px-和-rem-的区别是什么?"><a href="#问:px-和-rem-的区别是什么?" class="headerlink" title="问:px 和 rem 的区别是什么?"></a>问:px 和 rem 的区别是什么?</h6><p>答: <code>px</code>属于绝对单位长度,代表显示器上的一个像素单位;<code>rem</code>属于相对单位长度,代表相当于<code>html</code>根节点的值,例如:</p><p>设置根节点的<code>font-size: 62.5%</code>,得到的1rem实际单位长度为16px * 62.5% = 10px, 即<code>1rem = 10px</code></p><h6 id="问:-重排和重绘有什么区别?"><a href="#问:-重排和重绘有什么区别?" class="headerlink" title="问: 重排和重绘有什么区别?"></a>问: 重排和重绘有什么区别?</h6><p>答: </p><p>重排也称回流:浏览器渲染引擎会根据所有元素的样式计算出盒模型在页面中的位置和大小;当对Dom的大小、位置进行修改后,引擎需要进行重新计算,就会触发重排机制,重排必然引起重绘。</p><p>重绘:浏览器渲染引擎计算好盒模型的位置、大小等基本属性后,对每个盒模型的其他特性进行绘制;对Dom的样式进行修改,比如<code>background-color</code>,渲染引擎不需要重新计算几何属性,只需对元素的样式进行绘制,就只会触发重绘机制</p><h6 id="问-元素水平垂直居中的方式?"><a href="#问-元素水平垂直居中的方式?" class="headerlink" title="问: 元素水平垂直居中的方式?"></a>问: 元素水平垂直居中的方式?</h6><p>答: 一般常见的有以下几种方式:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/* 方式1: 定位+margin */</span></span><br><span class="line"> <span class="selector-class">.container</span> {</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.box</span> {</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">right</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">margin</span>: auto;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 方式2: 定位+transform */</span></span><br><span class="line"> <span class="selector-class">.container</span> {</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"> }</span><br><span class="line"> <span class="selector-class">.box</span> {</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"> <span class="attribute">top</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">left</span>: <span class="number">50%</span>;</span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">translate</span>(-<span class="number">50%</span>,-<span class="number">50%</span>);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"><span class="comment">/* 方式3: flex布局 */</span></span><br><span class="line"> <span class="selector-class">.container</span> {</span><br><span class="line"> ...</span><br><span class="line"> <span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">justify-content</span>: center;</span><br><span class="line"> <span class="attribute">align-items</span>: center;</span><br><span class="line"> }</span><br><span class="line"><span class="comment">/* 方式4: grid布局 */</span></span><br><span class="line"><span class="comment">/* 方式5: table布局 */</span></span><br></pre></td></tr></table></figure></div><h6 id="问-css中哪些属性可以继承?"><a href="#问-css中哪些属性可以继承?" class="headerlink" title="问: css中哪些属性可以继承?"></a>问: css中哪些属性可以继承?</h6><p>答:以下元素属性没有设置值时,则默认继承父级属性值:</p><ol><li>字体系列属性: <code>font</code>、<code>font-family</code>、<code>font-weight</code>、<code>font-size</code>、<code>fontstyle</code></li><li>文本属性: <code>color</code>、<code>line-height</code>、<code>word-spacing</code>、<code>letter-spacing</code>、<code> text-transform</code>、<code>text-indent</code>、<code>text-align</code></li><li>元素可见性: <code>visibility</code></li><li>表格布局属性: <code>caption-side</code>、<code>border-collapse</code>、<code>border-spacing</code>、<code>empty-cells</code>、<code>table-layout</code></li><li>列表布局属性:<code>list-style</code></li><li>光标属性:<code>cursor</code></li></ol><h2 id="HTML部分"><a href="#HTML部分" class="headerlink" title="HTML部分"></a>HTML部分</h2><h2 id="javascript部分"><a href="#javascript部分" class="headerlink" title="javascript部分"></a>javascript部分</h2><h6 id="问-js由哪三部分构成?"><a href="#问-js由哪三部分构成?" class="headerlink" title="问: js由哪三部分构成?"></a>问: js由哪三部分构成?</h6><p>答:<code>ECMAScript</code>(核心)、<code>DOM</code>(文档对象模型)、<code>BOM</code>(浏览器对象模型)</p><p>问: js对数据类的检测方式有哪些?</p><h6 id="问:js有哪些内置对象?"><a href="#问:js有哪些内置对象?" class="headerlink" title="问:js有哪些内置对象?"></a>问:js有哪些内置对象?</h6><p>答:常见的有<code>String</code> 、<code>Number</code>、<code>Boolean</code>、<code>Array</code>、<code>Object</code>、<code>Function</code>、<code>Math</code>、<code>Date</code>、<code>RegExp</code> 等…</p><p>答:一般通过以下方式</p><ol><li><code>typeof</code>: 只能检测基本数据类型</li><li><code>instanceof</code>: 只能检测引用数据类型</li><li><code>constructor</code>: 可以判断基本和引用数据类型,但是对于构造函数而言,如果改变它的原型指向,会导致检测结果不正确</li><li><code>Object.prototype.toString.call</code>: 最佳检测方式</li></ol><div class="highlight-container" data-rel="Javascript"><figure class="iseeu 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><span class="line"><span class="keyword">const</span> res_1 = <span class="keyword">typeof</span> <span class="string">'123'</span> <span class="comment">// => string</span></span><br><span class="line"><span class="keyword">const</span> res_2 = [] <span class="keyword">instanceof</span> <span class="title class_">Array</span> <span class="comment">// => true</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">A</span>(<span class="params">name</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = name</span><br><span class="line">}</span><br><span class="line"><span class="keyword">let</span> a = <span class="keyword">new</span> <span class="title function_">A</span>(<span class="string">'jiangwen'</span>)</span><br><span class="line"><span class="keyword">const</span> res_3 = a.<span class="property">constructor</span> === A <span class="comment">// => true</span></span><br><span class="line">A.<span class="property"><span class="keyword">prototype</span></span> = <span class="title class_">Array</span></span><br><span class="line"><span class="keyword">const</span> res_4 = a.<span class="property">constructor</span> === A <span class="comment">// => false , 指向的constructor变为Array</span></span><br><span class="line"><span class="keyword">const</span> res_5 = <span class="title class_">Object</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">toString</span>.<span class="title function_">call</span>(<span class="string">'abc'</span>) <span class="comment">// => [object String]</span></span><br></pre></td></tr></table></figure></div><h6 id="问:什么是闭包,闭包有什么特点?"><a href="#问:什么是闭包,闭包有什么特点?" class="headerlink" title="问:什么是闭包,闭包有什么特点?"></a>问:什么是闭包,闭包有什么特点?</h6><p>答: 闭包在<a class="link" href="https://so.csdn.net/so/search?q=JavaScript%E9%AB%98%E7%BA%A7%E7%A8%8B%E5%BA%8F%E8%AE%BE%E8%AE%A1&spm=1001.2101.3001.7020" >JavaScript高级程序设计<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>(第3版)中是这样描述:<strong>闭包是指有权访问另一个函数作用域中的变量的函数。</strong></p><p>闭包的特点是:</p><ul><li>可以在函数的外部访问到函数内部的局部变量。</li><li>让这些变量始终保存在内存中,不会随着函数的结束而自动销毁。</li></ul><p>使用场景:</p><ul><li>防抖</li><li>节流</li><li>函数嵌套避免全局污染</li></ul><h6 id="问:前端中的内存泄露是什么?"><a href="#问:前端中的内存泄露是什么?" class="headerlink" title="问:前端中的内存泄露是什么?"></a>问:前端中的内存泄露是什么?</h6><p>答: js的垃圾回收机制没有释放已经分配了内存地址的对象,造成长期的内存占用、内存资源浪费,导致运行速度慢,甚至崩溃的情况。会导致内存泄露的因素:</p><ol><li>一些未声明直接赋值的变量</li><li>未清空的定时器</li><li>过渡闭包</li><li>引用的元素没有被清除</li></ol><h6 id="问:什么是原型链?"><a href="#问:什么是原型链?" class="headerlink" title="问:什么是原型链?"></a>问:什么是原型链?</h6><p>答:原型就是一个普通的对象,为构造函数的实例共享属性和方法;所有实例对象中引用的原型都是同一个对象,使用<code>prototype</code>可以把属性、方法挂载到原型对象中进行共享,而内存仅需保存一份;实例对象中的属性<code>__proto__</code>,指向了构造函数的原型对象<code>prototype</code>,一个实例对象在调用属性、方法的时候,会依次从<code>实例本身 => 构造函数的原型对象 => 原型对象的原型对象</code>,进行查找,这些原型关系就构成了原型链。</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/5cdd315c45bc895052.png" alt="img" ></p><p>小结</p><ul><li>所有构造函数的<code>__proto__</code>都指向<code>Function.prototype</code>,包括Function本身</li><li>所有原型对象的<code>__proto__</code>都指向<code>Object.prototype</code>, 而<code>Object.prototype</code>本身的<code>__proto__</code>指向<code>null</code></li></ul><h6 id="问:new-操作符具体做了什么?"><a href="#问:new-操作符具体做了什么?" class="headerlink" title="问:new 操作符具体做了什么?"></a>问:new 操作符具体做了什么?</h6><p>答:具体分为以下步骤:</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">newFn</span>(<span class="params">Fn, ...args</span>) {</span><br><span class="line"> <span class="comment">// 1. 创建一个空对象</span></span><br><span class="line"> <span class="keyword">let</span> newObj = {};</span><br><span class="line"> <span class="comment">// 2. 把空对象和构造函数通过原型链进行关联</span></span><br><span class="line"> newObj.<span class="property">__proto__</span> = <span class="title class_">Fn</span>.<span class="property"><span class="keyword">prototype</span></span></span><br><span class="line"> <span class="comment">// 3. 将构造函数的`this`绑定到空对象上</span></span><br><span class="line"> <span class="keyword">let</span> res = <span class="title class_">Fn</span>.<span class="title function_">apply</span>(newObj, args)</span><br><span class="line"> <span class="comment">// 4. 根据构造函数返回的类型判断;如果是值类型,则返回对象,如果是引用类型,则返回这个引用类型</span></span><br><span class="line"> <span class="keyword">return</span> res <span class="keyword">instanceof</span> <span class="title class_">Object</span> ? res : newObj</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h6 id="问:js是如何实现继承的?"><a href="#问:js是如何实现继承的?" class="headerlink" title="问:js是如何实现继承的?"></a>问:js是如何实现继承的?</h6><p>答: 常见的有以下几种方式</p><ol><li>原型链继承</li><li>借用构造函数继承</li><li>组合式继承</li><li>ES6的class类继承</li></ol><h6 id="问:js中关于this指向的问题?"><a href="#问:js中关于this指向的问题?" class="headerlink" title="问:js中关于this指向的问题?"></a>问:js中关于this指向的问题?</h6><p>答: 分为以下情况</p><ol><li><p>全局对象中的this指向的是<code>window</code></p></li><li><p>全局作用域、普通函数中的this指向全局<code>window</code></p></li><li><p>匿名函数的执行环境具有全局性,因此匿名函数中的this永远指向<code>window</code></p></li><li><p>this永远指向最后调用它的那个对象</p></li><li><p><code>new</code>关键字改变了this的指向</p></li><li><p>不是箭头函数的时候,<code>call</code>、<code>apply</code>、<code>bind</code>可以改变this的指向</p></li><li><p>箭头函数中的this指向,在它定义的时候就已经确定了,箭头函数本身没有this,看外层的函数是否有this,有就是外层函数的this,没有就是指向<code>window</code></p></li></ol><h6 id="问:-async-和-defer-有什么区别?"><a href="#问:-async-和-defer-有什么区别?" class="headerlink" title="问: async 和 defer 有什么区别?"></a>问: async 和 defer 有什么区别?</h6><p>答:它们两个属性都是指定浏览器进行脚本的异步加载,但是加载后的执行时机不同,<code>defer</code>需要所有元素加载完成之后才执行,<code>async</code>是<code>HTML5</code>新增的属性,只要脚本加载完成之后就马上执行,因此<code>async</code>不能确保脚本的执行顺序,而<code>defer</code>则可以。</p><h6 id="问:-ES6的新特性有哪些?"><a href="#问:-ES6的新特性有哪些?" class="headerlink" title="问: ES6的新特性有哪些?"></a>问: ES6的新特性有哪些?</h6><p>答:有以下新特性:</p><ol><li><p>新增了块级作用域(let,const)</p></li><li><p>新增了定义类语法糖(class)</p></li><li><p>新增了基本数据类型(symbol)</p></li><li><p>新增了解构赋值</p></li><li><p>新增了函数参数的默认值</p></li><li><p>新增了数组的API</p></li><li><p>对象和数组新增了扩展运算符(…)</p></li><li><p>新增了<code>promise</code></p><ul><li>把异步操作队列化,解决了回调地狱的问题</li><li>有<code>all</code>,<code>reject</code>,<code>resolve</code>,<code>race</code>方法</li><li>原型有<code>then</code>、<code>catch</code></li><li>三种状态<code>pending</code>|<code>rejected</code>|<code>fulfilled</code>, 状态一旦从<code>pending</code> => <code>rejected</code>或者<code>pending</code> => <code>fulfilled</code>改变,状态就确定了,不可逆</li><li><code>async</code>和<code>await</code>: 同步代码做异步操作,两者必须搭配使用</li></ul></li><li><p>新增了模块化(import、export)</p></li><li><p>新增了set 和map数据结构</p><ul><li>set 数据结构不存在重复</li><li>map 数据结构的key类型不受限制</li></ul></li><li><p>新增了generator构造器</p></li><li><p>新增了箭头函数</p></li></ol><h6 id="问:-箭头函数和普通函数有什么区别?"><a href="#问:-箭头函数和普通函数有什么区别?" class="headerlink" title="问: 箭头函数和普通函数有什么区别?"></a>问: 箭头函数和普通函数有什么区别?</h6><p>答:有以下区别</p><ol><li>箭头函数不能使用<code>new</code>操作符, 来作为构造函数使用</li><li>箭头函数没有原型</li><li>箭头函数没有<code>arguments</code></li><li>箭头函数不能用<code>call</code>、<code>call</code>、<code>call</code>去改变this的执行</li><li>箭头函数的this执行外层函数的第一个this</li></ol><h6 id="问:-如何实现一个深拷贝?"><a href="#问:-如何实现一个深拷贝?" class="headerlink" title="问: 如何实现一个深拷贝?"></a>问: 如何实现一个深拷贝?</h6><p>答:一般可以通过以下方式:</p><ol><li><p>扩展运算符实现</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> oo_1 = { ...o} <span class="comment">// 缺点: 对于对象中的引用数据类型还是浅拷贝</span></span><br></pre></td></tr></table></figure></div></li><li><p><code> JSON.parse(JSON.stringify())</code></p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> oo_1 = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(o)) <span class="comment">// 缺点: 对象中的函数不会拷贝</span></span><br></pre></td></tr></table></figure></div></li><li><p>递归复制</p><div class="highlight-container" data-rel="Javascript"><figure class="iseeu highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* origin: 拷贝源对象</span></span><br><span class="line"><span class="comment">* deep: 是否使用深拷贝</span></span><br><span class="line"><span class="comment">*/</span> </span><br><span class="line"><span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">origin, deep</span>) {</span><br><span class="line"> <span class="keyword">let</span> obj = {}</span><br><span class="line"> <span class="keyword">if</span> (origin <span class="keyword">instanceof</span> <span class="title class_">Array</span>) obj = [];</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> origin) {</span><br><span class="line"> <span class="keyword">let</span> value = origin[key]</span><br><span class="line"> obj[key] = (!!deep && <span class="keyword">typeof</span> value === <span class="string">"object"</span> && value !== <span class="literal">null</span>) ? <span class="title function_">deepClone</span>(value, deep) : value</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> obj</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> oo_1 = <span class="title function_">deepClone</span>(o, <span class="literal">true</span>)</span><br></pre></td></tr></table></figure></div></li></ol><h6 id="问:-说一下事件循环?"><a href="#问:-说一下事件循环?" class="headerlink" title="问: 说一下事件循环?"></a>问: 说一下事件循环?</h6><p>答: js是一个单线程脚本语言,</p><h2 id="经典试题"><a href="#经典试题" class="headerlink" title="经典试题"></a>经典试题</h2><ol><li><p>三次握手和四次挥手详细介绍</p></li><li><p>TCP有哪些手段保证可靠交付</p></li><li><p>URL从输入到页面渲染全流程</p></li><li><p>如何预防中间人攻击</p></li><li><p>DNS解析会出错吗,为什么</p></li><li><p>ES6的Set内部实现</p></li><li><p>如何应对流量劫持</p></li><li><p>算法:top-K问题,分成top-1,top-2,top-K三小问</p></li><li><p>跨域</p></li><li><p>webpack的plugins和loaders的实现原理</p></li><li><p>vue和react谈谈区别和选型考虑</p></li><li><p>webpack如何优化编译速度</p></li><li><p>事件循环机制,node和浏览器的事件循环机制区别</p></li><li><p>单元测试编写有哪些原则</p></li><li><p>一个大型项目如何分配前端开发的工作</p></li><li><p>怼项目</p></li><li><p>typescript有什么好处</p></li><li><p>vue项目中如何约束rxjs数据的类型</p></li><li><p>rxjs高阶数据流定义,常用高阶数据流操作符</p></li><li><p>JWT优缺点</p></li><li><p>选择器优先级</p></li><li><p>基本数据类型</p></li><li><p>RxJS冷热流区别</p></li><li><p>RxJS调试方法</p></li><li><p>nginx负载均衡配置</p></li><li><p>前端性能优化手段</p></li><li><p>针对React的性能优化手段</p></li><li><p>301 302 307 308 401 403</p></li><li><p>vue的nextTick实现原理以及应用场景</p></li><li><p>vue组件间通信</p></li><li><p>谈谈XSS防御,以及Content-Security-Policy细节</p></li><li><p>场景题:一个气球从右上角移动到中间,然后抖动,如何实现</p></li><li><p>场景题:一个关于外边距合并的高度计算</p></li><li><p>mobx-react如何驱动react组件重渲染</p></li><li><p>forceUpdate经历了哪些生命周期,子组件呢?</p></li><li><p>React key场景题:列表使用index做key,删除其中一个后,如何表现?</p></li><li><p>算法:实现setter(obj, ‘a.b.c’ ,val)</p></li><li><p>RxJS相对于其他状态管理方案的优势?</p></li><li><p>手写冒泡排序</p></li><li><p>JWT细节,适用场景</p></li><li><p>跨域</p></li><li><p>方案题:不同前端技术栈的项目,如何实现一套通用组件方案?</p></li><li><p>ES6特性</p><p>闭包和this一起谈谈</p><p>postcss配置</p><p>Promise内部实现原理</p><p>vuex, mobx, redux各自的特点和区别</p><p>react生命周期</p><p>各方面谈谈性能优化</p><p>serviceworker如何保证离线缓存资源更新</p><p>virtual dom有哪些好处</p></li><li><ol><li><p>Vue3 proxy解决了哪些问题?</p></li><li><p>Vue响应式原理</p></li><li><p>发布订阅模式和观察者模式的异同</p></li><li><p>图片懒加载实现</p></li><li><p>css垂直居中</p></li><li><p>CI/CD流程</p></li><li><p>谈谈性能优化</p></li><li><p> react生命周期</p></li></ol></li></ol><p> key的作用</p><p> hooks</p><p> vue和react区别,选型考虑</p><pre><code> canvas优化绘制性能 webpack性能优化手段 事件循环 如何解决同步调用代码耗时太高的问题 手写Promise实现 1. Promise实现原理</code></pre><ol start="2"><li>vue组件间通信<ol start="3"><li>性能优化</li></ol></li><li>vuex数据流动过程<ol start="5"><li>谈谈css预处理器机制</li></ol></li><li>算法:Promise串行</li></ol><h2 id="JavaScript"><a href="#JavaScript" class="headerlink" title="JavaScript"></a>JavaScript</h2><p>1、原型/原型链/构造函数/实例/继承</p><p>2、有几种方式可以实现继承</p><p>3、用原型实现继承有什么缺点,怎么解决</p><p>4、arguments</p><p>5、数据类型判断</p><p>6、作用域链、闭包、作用域</p><p>7、Ajax的原生写法</p><p>8、对象深拷贝、浅拷贝</p><p>9、图片懒加载、预加载</p><p>10、实现页面加载进度条</p><p>11、this关键字</p><p>12、函数式编程</p><p>13、手动实现parseInt</p><p>14、为什么会有同源策略</p><p>15、怎么判断两个对象是否相等</p><p>16、事件模型 </p><ul><li>事件委托、代理</li><li>如何让事件先冒泡后捕获</li></ul><p>17、window的onload事件和domcontentloaded</p><p>18、for…in迭代和for…of有什么区别</p><p>19、函数柯里化</p><p>20、call apply区别,原生实现bind </p><ul><li>call,apply,bind 三者用法和区别:角度可为参数、绑定规则(显示绑定和强绑定),运行效率、运行情况。</li></ul><p>21、async/await</p><p>22、立即执行函数和使用场景</p><p>23、设计模式(要求说出如何实现,应用,优缺点)/单例模式实现</p><p>24、iframe的缺点有哪些</p><p>25、数组问题 </p><ul><li>数组去重</li><li>数组常用方法</li><li>查找数组重复项</li><li>扁平化数组</li><li>按数组中各项和特定值差值排序</li></ul><p>26、BOM属性对象方法</p><p>27、服务端渲染</p><p>28、垃圾回收机制</p><p>29、eventloop </p><ul><li>进程和线程</li><li>任务队列</li></ul><p>30、如何快速让字符串变成已千为精度的数字</p><h3 id="ES6"><a href="#ES6" class="headerlink" title="ES6"></a>ES6</h3><p>1、声明 let、const</p><p>2、解构赋值</p><p>3、声明类与继承:class、extend</p><p>4、Promise的使用与实现</p><p>5、generator(异步编程、yield、next()、await 、async)</p><p>6、箭头函数this指向问题、拓展运算符</p><p>7、map和set有没有用过,如何实现一个数组去重,map数据结构有什么优点?</p><p>8、ES6怎么编译成ES5,css-loader原理,过程</p><p>9、ES6转成ES5的常见例子 </p><ul><li>使用es5实现es6的class</li></ul><h2 id="浏览器"><a href="#浏览器" class="headerlink" title="浏览器"></a>浏览器</h2><p>1、输入url到展示页面过程发生了什么?</p><p>2、重绘与回流 </p><ul><li>重绘(repaint): 当元素样式的改变不影响布局时,浏览器将使用重绘对元素进行更新,此时由于只需要UI层面的重新像素绘制,因此 损耗较少</li><li>回流(reflow): 当元素的尺寸、结构或触发某些属性时,浏览器会重新渲染页面,称为回流。此时,浏览器需要重新经过计算,计算后还需要重新页面布局,因此是较重的操作。会触发回流的操作:<br>* 页面初次渲染<br>* 浏览器窗口大小改变<br>* 元素尺寸、位置、内容发生改变<br>* 元素字体大小变化<br>* 添加或者删除可见的 dom 元素<br>* 激活 CSS 伪类(例如::hover)<br>* 查询某些属性或调用某些方法<br>* clientWidth、clientHeight、clientTop、clientLeft<br>* offsetWidth、offsetHeight、offsetTop、offsetLeft<br>* scrollWidth、scrollHeight、scrollTop、scrollLeft<br>* getComputedStyle()<br>* getBoundingClientRect()<br>* scrollTo()<br><strong>回流必定触发重绘,重绘不一定触发回流。重绘的开销较小,回流的代价较高。</strong></li></ul><p>3、防抖与节流</p><p>4、cookies、session、sessionStorage、localStorage</p><p>5、浏览器内核</p><h2 id="服务端与网络"><a href="#服务端与网络" class="headerlink" title="服务端与网络"></a>服务端与网络</h2><p>1、常见状态码</p><p>2、缓存 </p><ul><li>200 From cache和200 ok</li><li>400,401,403状态码分别代表什么</li><li>浏览器缓存</li></ul><p>3、cookie, session, token</p><p>4、前端持久化的方式、区别</p><p>5、DNS是怎么解析的</p><p>6、cdn</p><p>7、计算机网络的相关协议</p><p>8、http/https/http2.0</p><p>9、get post区别</p><p>10、ajax、 axios库</p><p>11、tcp三次握手,四次挥手流程</p><p>12、跨域</p><p>13、前端安全XSS、CSRF</p><p>14、websocket</p><p>15、Http请求中的keep-alive有了解吗</p><p>16、网络分层</p><p>17、即时通信,除了Ajax和websocket</p><p>18、模块化,commonJS,es6,cmd,amd</p><h2 id="框架(Vue)"><a href="#框架(Vue)" class="headerlink" title="框架(Vue)"></a>框架(Vue)</h2><p>1、vue解决了什么问题</p><p>2、MVVM的理解</p><p>3、如何实现一个自定义组件,不同组件之间如何通信的?</p><p>4、nextTick</p><p>5、生命周期</p><p>6、虚拟dom的原理</p><p>7、双向绑定的原理?数据劫持?</p><p>8、组件通信 </p><ul><li>父->子</li><li>子->父</li><li>非父子组件</li></ul><p>9、Proxy 相比于 defineProperty 的优势</p><p>10、watch computed区别</p><p>11、virtual dom 原理实现</p><p>12、vue-router(hash, HTML5 新增的 pushState </p><ul><li>单页应用,如何实现其路由功能—路由原理</li><li>vue-router如何做用户登录权限等</li><li>你在项目中怎么实现路由的嵌套</li></ul><p>13、vuex的理解</p><h2 id="性能优化"><a href="#性能优化" class="headerlink" title="性能优化"></a>性能优化</h2><ul><li>页面DOM节点太多,会出现什么问题?如何优化?</li><li>如何做性能监测</li></ul><h2 id="SEO和语义化"><a href="#SEO和语义化" class="headerlink" title="SEO和语义化"></a>SEO和语义化</h2><p>微信小程序和h5差异,如果有开发weex的经验,可能会加上weex</p><h2 id="微信小程序"><a href="#微信小程序" class="headerlink" title="微信小程序"></a>微信小程序</h2><p>微信小程序和h5差异,如果有开发weex的经验,可能会加上weex</p><h2 id="Git"><a href="#Git" class="headerlink" title="Git"></a>Git</h2><p>一些基本命令</p><h2 id="打包工具webpack"><a href="#打包工具webpack" class="headerlink" title="打包工具webpack"></a>打包工具webpack</h2><p>1、打包原理</p><p>2、打包插件</p><p>3、webpack热更新原理</p><p>4、优化构建速度</p><h2 id="算法与数据结构"><a href="#算法与数据结构" class="headerlink" title="算法与数据结构"></a>算法与数据结构</h2><p>1、排序算法</p><p>2、动态规划,参见背包问题</p><p>3、二叉树</p><p>4、加油站问题(贪心算法)</p><p>5、二分法</p><p>6、二叉树遍历</p><p>7、单链表反转</p><p>8、取1000个数字里面的质数</p><p>9、找出数组中和为给定值的两个元素,如:[1, 2, 3, 4, 5]中找出和为6的两个元素。</p><p>10、线性顺序存储结构和链式存储结构有什么区别?以及优缺点</p><h2 id="移动端"><a href="#移动端" class="headerlink" title="移动端"></a>移动端</h2><p>1、自适应</p><p>2、pwa</p><p>3、移动端手势</p><h2 id="附加题"><a href="#附加题" class="headerlink" title="附加题"></a>附加题</h2><p>1、无限滚动方案</p><p>2、如何处理兼容性问题</p><p>3、你遇到过最难的问题是什么</p><p>4、ES6 class与ES5 function区别及联系</p><p>5、vue怎么监听数组</p><p>6、写过webpack loader吗</p><p>7、微信网页版登录机制思考</p>]]></content>
<summary type="html">false</summary>
<category term="前端" scheme="http://example.com/categories/%E5%89%8D%E7%AB%AF/"/>
<category term="学习总结" scheme="http://example.com/tags/%E5%AD%A6%E4%B9%A0%E6%80%BB%E7%BB%93/"/>
</entry>
<entry>
<title>满纸荒唐言</title>
<link href="http://example.com/2023/03/06/%E7%94%9F%E6%B4%BB%E9%9A%8F%E8%AE%B0/%E6%BB%A1%E7%BA%B8%E8%8D%92%E5%94%90%E8%A8%80/"/>
<id>http://example.com/2023/03/06/%E7%94%9F%E6%B4%BB%E9%9A%8F%E8%AE%B0/%E6%BB%A1%E7%BA%B8%E8%8D%92%E5%94%90%E8%A8%80/</id>
<published>2023-03-06T18:50:24.000Z</published>
<updated>2024-11-24T17:45:51.391Z</updated>
<content type="html"><![CDATA[<h2 id="阅读书单"><a href="#阅读书单" class="headerlink" title="阅读书单"></a>阅读书单</h2><div class="tabs" id="tab-recommend"><ul class="nav-tabs"><li class="tab active"><a class="#recommend-1">人文社科类</a></li><li class="tab"><a class="#recommend-2">技术类</a></li></ul><div class="tab-content"><div class="tab-pane active" id="recommend-1"><ul><li><input checked="" disabled="" type="checkbox"> 《1984》</li><li><input checked="" disabled="" type="checkbox"> 《乌合之众》</li><li><input checked="" disabled="" type="checkbox"> 《浪潮之巅》</li><li><input checked="" disabled="" type="checkbox"> 《中央帝国财经密码》</li><li><input checked="" disabled="" type="checkbox"> 《三体》</li><li><input checked="" disabled="" type="checkbox"> 《万历十五年》</li><li><input checked="" disabled="" type="checkbox"> 《围城》</li><li><input checked="" disabled="" type="checkbox"> 《明朝那些事》</li><li><input checked="" disabled="" type="checkbox"> 《活着》</li><li><input checked="" disabled="" type="checkbox"> 《兄弟》</li><li><input disabled="" type="checkbox"> 《中国古代文化常识》</li><li><input disabled="" type="checkbox"> 《枪炮、病菌与钢铁》</li><li><input disabled="" type="checkbox"> 《月亮与六便士》</li><li><input disabled="" type="checkbox"> 《从黎明到衰落》</li><li><input disabled="" type="checkbox"> 《国富论》</li><li><input disabled="" type="checkbox"> 《人类简史》</li><li><input disabled="" type="checkbox"> 《自私的基因》</li><li><input disabled="" type="checkbox"> 《学会提问》</li><li><input disabled="" type="checkbox"> 《通往奴役之路》</li><li><input disabled="" type="checkbox"> 《动物庄园》</li></ul></div><div class="tab-pane" id="recommend-2"><ul><li><input disabled="" type="checkbox"> 《鸟哥的Linux私房菜》</li><li><input disabled="" type="checkbox"> 《JavaScript高级程序设计》</li><li><input disabled="" type="checkbox"> 《你不知道的JavaScript》</li><li><input disabled="" type="checkbox"> 《JavaScript高级程序设计》</li></ul></div></div></div><h2 id="阅读摘录"><a href="#阅读摘录" class="headerlink" title="阅读摘录"></a>阅读摘录</h2> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《1984》[英]乔治·奥威尔</p> </div> <div class="notel-content"> <ul><li>战争即和平 自由即奴役 无知即力量。</li><li>谁控制了过去,谁就控制了未来;谁控制了现在,谁就控制了过去。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《善与恶的超越》[德]尼采</p> </div> <div class="notel-content"> <ul><li>与恶龙缠斗过久,自身亦成为恶龙;当你凝视深渊,深渊将回以凝视。</li><li>每一个不曾起舞的日子,都是对生命的辜负。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《回答》[中]北岛</p> </div> <div class="notel-content"> <ul><li>卑鄙是卑鄙者的通行证,高尚是高尚者的墓志铭。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《颂歌》[加]莱昂纳德·科恩</p> </div> <div class="notel-content"> <ul><li>万物皆有裂痕,那是光照进来的地方</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《如果我不曾见过太阳》[美]艾米莉·狄金森</p> </div> <div class="notel-content"> <ul><li>如果我不曾见到太阳,我本可以忍受黑暗。可如今,太阳把我的寂寞照的更荒凉。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《亲爱的安德烈》[中]龙应台</p> </div> <div class="notel-content"> <ul><li>我要求你读书用功,不是因为我要你跟别人比成就,而是因为,我希望你将来会拥有选择的权利,选择有意义、有时间的工作,而不是被迫谋生。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《庄子.秋水》[古]庄子</p> </div> <div class="notel-content"> <ul><li>井蛙不可以语于海者,拘于虚也;夏虫不可以语于冰者,笃于时也</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《月亮与六便士》[英]毛姆</p> </div> <div class="notel-content"> <ul><li>在爱情的事上如果你考虑起自尊心来,那只能有一个原因:实际上你还是最爱自己。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《月亮与六便士》[英]毛姆</p> </div> <div class="notel-content"> <ul><li>在爱情的事上如果你考虑起自尊心来,那只能有一个原因:实际上你还是最爱自己。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>[美]海明威</p> </div> <div class="notel-content"> <ul><li>我们用两年时间学会说话,却要用一辈子学会闭嘴。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>《而已集·小杂感》[中]鲁迅</p> </div> <div class="notel-content"> <ul><li>楼下一个男人病得要死,那间壁的一家唱着留声机;对面是弄孩子。楼上有两人狂笑;还有打牌声。河中的船上有女人哭着她死去的母亲。人类的悲欢并不相通,我只觉得他们吵闹。</li></ul> </div> </div> <div class="note-large red"> <div class="notel-title rounded-t-lg p-3 font-bold text-lg flex flex-row gap-2 items-center"> <i class="notel-icon fa-solid fa-book"></i><p>[中]王小波</p> </div> <div class="notel-content"> <ul><li>人的一切痛苦,本质上都是对自己的无能的愤怒。</li></ul> </div> </div>]]></content>
<summary type="html"><h2 id="阅读书单"><a href="#阅读书单" class="headerlink" title="阅读书单"></a>阅读书单</h2><div class="tabs" id="tab-recommend"><ul class="nav-tabs"><li cla</summary>
<category term="随记" scheme="http://example.com/categories/%E9%9A%8F%E8%AE%B0/"/>
<category term="阅读" scheme="http://example.com/tags/%E9%98%85%E8%AF%BB/"/>
</entry>
<entry>
<title>flex布局最后一行列表左对齐的N种方法</title>
<link href="http://example.com/2023/01/18/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95/"/>
<id>http://example.com/2023/01/18/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95/</id>
<published>2023-01-18T14:25:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>在CSS flex布局中,<code>justify-content</code>属性可以控制列表的水平对齐方式,例如<code>space-between</code>值可以实现两端对齐。但是,如果最后一行的列表的个数不满,则就会出现最后一行没有完全垂直对齐的问题。</p></blockquote><p>如下代码:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between;</span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">24%</span>; <span class="attribute">height</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">15px</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></div><p>然后列表的个数不多不少正好7个:</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></div><p>此时最后一行的小方块的排列就显得很尴尬了:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005711005.png" alt="图片" ></p><p>您可以狠狠地点击这里:最后一行flex列表没有对齐demo</p><p>此时,最后一行应该左对齐排列才是我们想要的效果,如何实现呢?</p><p>其实实现的思路和display:inline-block的两端对齐是一样的。</p><h3 id="二、如果每一行列数是固定的"><a href="#二、如果每一行列数是固定的" class="headerlink" title="二、如果每一行列数是固定的"></a><strong>二、如果每一行列数是固定的</strong></h3><p>如果每一行列数是固定的,则下面两种方法可以实现最后一行左对齐。</p><h4 id="方法一:模拟space-between和间隙"><a href="#方法一:模拟space-between和间隙" class="headerlink" title="方法一:模拟space-between和间隙"></a>方法一:模拟space-between和间隙</h4><p>也就是我们不使用<code>justify-content:space-between</code>声明在模拟两端对齐效果。中间的gap间隙我们使用margin进行控制。</p><p>例如:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; </span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">24%</span>; <span class="attribute">height</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">15px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span><span class="selector-pseudo">:not</span>(<span class="selector-pseudo">:nth-child</span>(<span class="number">4</span>n)) { </span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="built_in">calc</span>(<span class="number">4%</span> / <span class="number">3</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>此时,布局效果是这样的:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005710826.png" alt="图片" ></p><h4 id="方法二:-根据个数最后一个元素动态margin"><a href="#方法二:-根据个数最后一个元素动态margin" class="headerlink" title="方法二:****根据个数最后一个元素动态margin"></a><strong>方法二:****根据个数最后一个元素动态margin</strong></h4><p>由于每一列的数目都是固定的,因此,我们可以计算出不同个数列表应当多大的<code>margin</code>值才能保证完全左对齐。</p><p>例如,假设每行4个元素,结果最后一行只有3个元素,则最后一个元素的<code>margin-right</code>大小是“列表宽度+间隙大小”的话,那最后3个元素也是可以完美左对齐的。</p><p>然后,借助树结构伪类数量匹配技术(这篇文章“伪类匹配列表数目实现微信群头像CSS布局的技巧”中的布局技巧就是借助这种技术实现),我们可以知道最后一行有几个元素。</p><p>例如:</p><ul><li><code>.list:last-child:nth-child(4n - 1)</code>说明最后一行,要么3个元素,要么7个元素……</li><li><code>.list:last-child:nth-child(4n - 2)</code>说明最后一行,要么2个元素,要么6个元素……</li></ul><p>在本例中,一行就4个元素,因此,我们可以有如下CSS设置:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; <span class="comment">/* 两端对齐 */</span> </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between; </span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">24%</span>; </span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">15px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 如果最后一行是3个元素 */</span></span><br><span class="line"><span class="selector-class">.list</span><span class="selector-pseudo">:last-child</span><span class="selector-pseudo">:nth-child</span>(<span class="number">4</span>n - <span class="number">1</span>) { </span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="built_in">calc</span>(<span class="number">24%</span> + <span class="number">4%</span> / <span class="number">3</span>);}</span><br><span class="line"><span class="comment">/* 如果最后一行是2个元素 */</span><span class="selector-class">.list</span><span class="selector-pseudo">:last-child</span><span class="selector-pseudo">:nth-child</span>(<span class="number">4</span>n - <span class="number">2</span>) { </span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="built_in">calc</span>(<span class="number">48%</span> + <span class="number">8%</span> / <span class="number">3</span>);</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><p>效果如下GIF示意,删除列表后,布局依然稳稳地左对齐。</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005709403.gif" alt="图片" ></p><p>眼见为实,您可以狠狠地点击这里:动态匹配数量实现flex子项左对齐demo</p><h3 id="三、如果每一子项宽度不固定"><a href="#三、如果每一子项宽度不固定" class="headerlink" title="三、如果每一子项宽度不固定"></a><strong>三、如果每一子项宽度不固定</strong></h3><p>有时候,每一个flex子项的宽度都是不固定的,这个时候希望最后一行左对齐该如何实现呢?</p><p>由于此时间隙的大小不固定,对齐不严格,因此,我们可以直接让最后一行左对齐即可。具体方法有两个:</p><h4 id="方法一:-最后一项margin-right-auto"><a href="#方法一:-最后一项margin-right-auto" class="headerlink" title="方法一:****最后一项margin-right:auto"></a><strong>方法一:****最后一项margin-right:auto</strong></h4><p>CSS代码如下:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between; </span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 最后一项margin-right:auto */</span></span><br><span class="line"><span class="selector-class">.list</span><span class="selector-pseudo">:last-child</span> { </span><br><span class="line"> <span class="attribute">margin-right</span>: auto;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最终效果如下GIF:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005709618.gif" alt="图片" ></p><h4 id="方法二:-创建伪元素并设置flex-auto或flex-1"><a href="#方法二:-创建伪元素并设置flex-auto或flex-1" class="headerlink" title="方法二:****创建伪元素并设置flex:auto或flex:1"></a><strong>方法二:****创建伪元素并设置flex:auto或flex:1</strong></h4><p>CSS代码如下:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between; </span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 使用伪元素辅助左对齐 */</span></span><br><span class="line"><span class="selector-class">.container</span><span class="selector-pseudo">::after</span> { </span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"> <span class="attribute">flex</span>: auto; <span class="comment">/* 或者flex: 1 */</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最终效果如下GIF:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005709618.gif" alt="图片" ></p><hr><p>这两个方法我合在一个demo页面了,您可以狠狠的点击这里:flex子元素宽度不固定最后一行左对齐demo</p><h3 id="四、如果每一行列数不固定"><a href="#四、如果每一行列数不固定" class="headerlink" title="四、如果每一行列数不固定"></a><strong>四、如果每一行列数不固定</strong></h3><p>如果每一行的列数不固定,则上面的这些方法均不适用,需要使用其他技巧来实现最后一行左对齐。</p><p>这个方法其实很简单,也很好理解,就是使用足够的空白标签进行填充占位,具体的占位数量是由最多列数的个数决定的,例如这个布局最多7列,那我们可以使用7个空白标签进行填充占位,最多10列,那我们需要使用10个空白标签。</p><p>如下HTML示意:</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"container"</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">class</span>=<span class="string">"list"</span>></span><span class="tag"></<span class="name">div</span>></span> </span><br><span class="line"> <span class="tag"><<span class="name">i</span>></span><span class="tag"></<span class="name">i</span>></span><span class="tag"><<span class="name">i</span>></span><span class="tag"></<span class="name">i</span>></span><span class="tag"><<span class="name">i</span>></span><span class="tag"></<span class="name">i</span>></span><span class="tag"><<span class="name">i</span>></span><span class="tag"></<span class="name">i</span>></span><span class="tag"><<span class="name">i</span>></span><span class="tag"></<span class="name">i</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></div><p>相关CSS如下,实现的关键就是占位的<code><i></code>元素宽度和<code>margin</code>大小设置得和<code>.list</code>列表元素一样即可,其他样式都不需要写。</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: flex; </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between; </span><br><span class="line"> <span class="attribute">flex-wrap</span>: wrap; </span><br><span class="line"> <span class="attribute">margin-right</span>: -<span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">height</span>:<span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">15px</span> <span class="number">10px</span> <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line">}</span><br><span class="line"><span class="comment">/* 和列表一样的宽度和margin值 */</span></span><br><span class="line"><span class="selector-class">.container</span> > <span class="selector-tag">i</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">margin-right</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>由于<code><i></code>元素高度为0,因此,并不会影响垂直方向上的布局呈现。</p><p>最后的效果如下GIF图示:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005709595.gif" alt="图片" ></p><h3 id="五、如果列数不固定HTML又不能调整"><a href="#五、如果列数不固定HTML又不能调整" class="headerlink" title="五、如果列数不固定HTML又不能调整"></a><strong>五、如果列数不固定HTML又不能调整</strong></h3><p>然而有时候,由于客观原因,前端重构人员没有办法去调整html结构,同时布局的列表个数又不固定,这个时候该如何实现我们最后一行左对齐效果呢?</p><p>我们不妨可以试试使用Grid布局。</p><p>Grid布局天然有gap间隙,且天然格子对齐排布,因此,实现最后一行左对齐可以认为是天生的效果。</p><p>CSS代码如下:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> { </span><br><span class="line"> <span class="attribute">display</span>: grid; </span><br><span class="line"> <span class="attribute">justify-content</span>: space-between; </span><br><span class="line"> <span class="attribute">grid-template-columns</span>: <span class="built_in">repeat</span>(auto-fill, <span class="number">100px</span>); </span><br><span class="line"> <span class="attribute">grid-gap</span>: <span class="number">10px</span>;</span><br><span class="line">}</span><br><span class="line"><span class="selector-class">.list</span> { </span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">height</span>:<span class="number">100px</span>; </span><br><span class="line"> <span class="attribute">background-color</span>: skyblue; </span><br><span class="line"> <span class="attribute">margin-top</span>: <span class="number">5px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>可以看到CSS代码非常简洁。</p><p>HTML代码就是非常规整非常普通的代码片段:</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><<span class="selector-tag">div</span> class="container"> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>> </span><br><span class="line"> <<span class="selector-tag">div</span> class="list"></<span class="selector-tag">div</span>></span><br><span class="line"></<span class="selector-tag">div</span>></span><br></pre></td></tr></table></figure></div><p>最后的效果如下GIF:</p><p><img lazyload src="/images/loading.svg" data-src="https://jiangwen-markdown-img.oss-cn-fuzhou.aliyuncs.com/CSS/flex%E5%B8%83%E5%B1%80%E6%9C%80%E5%90%8E%E4%B8%80%E8%A1%8C%E5%88%97%E8%A1%A8%E5%B7%A6%E5%AF%B9%E9%BD%90%E7%9A%84N%E7%A7%8D%E6%96%B9%E6%B3%95.assets/640-20230626005709595.gif" alt="图片" ></p><h3 id="六、这几种实现方法点评"><a href="#六、这几种实现方法点评" class="headerlink" title="六、这几种实现方法点评"></a><strong>六、这几种实现方法点评</strong></h3><p>首先最后一行需要左对齐的布局更适合使用CSS grid布局实现,但是,<code>repeat()</code>函数兼容性有些要求,IE浏览器并不支持。如果项目需要兼容IE,则此方法需要斟酌。</p><p>然后,适用范围最广的方法是使用空的元素进行占位,此方法不仅适用于列表个数不固定的场景,对于列表个数固定的场景也可以使用这个方法。但是有些人代码洁癖,看不惯这种空的占位的html标签,则可以试试一开始的两个方法,一是动态计算margin,模拟两端对齐,另外一个是根据列表的个数,动态控制最后一个列表元素的margin值实现左对齐。</p><p>累计6种方法,各有各的优缺点,大家根据自己项目的实际场景,选择合适的方法。</p>]]></content>
<summary type="html">false</summary>
<category term="CSS" scheme="http://example.com/categories/CSS/"/>
<category term="flex" scheme="http://example.com/tags/flex/"/>
</entry>
<entry>
<title>diff算法原理解析</title>
<link href="http://example.com/2022/11/22/Vue/diff%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/"/>
<id>http://example.com/2022/11/22/Vue/diff%E7%AE%97%E6%B3%95%E5%8E%9F%E7%90%86%E8%A7%A3%E6%9E%90/</id>
<published>2022-11-22T11:15:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>前言: <strong>diff算法</strong>可以看作是一种对比算法,对比的对象是<strong>新旧虚拟Dom</strong>。顾名思义,<strong>diff算法</strong>可以找到<strong>新旧虚拟Dom</strong>之间的差异,但<strong>diff算法</strong>中其实并不是只有对比<strong>虚拟DOM</strong>,还有根据对比后的结果更新<strong>真实Dom</strong></p></blockquote><h2 id="虚拟DOM"><a href="#虚拟DOM" class="headerlink" title="虚拟DOM"></a>虚拟DOM</h2><p> 在进行进一步了解之前,我们需要先明确<strong>虚拟DOM</strong>的概念:虚拟DOM就是一个用来描述真实DOM的 <strong>JS对象</strong>,它有6个属性:</p><ol><li>sel:当前节点标签名</li><li>data:节点内的所有属性</li><li>childen:当前节点的子节点</li><li>elm:虚拟节点对应的真实节点</li><li>key:当前节点的唯一标识</li><li>text:当前节点的文本</li></ol><p>结构类似这样:</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> vnode = {</span><br><span class="line"> <span class="attr">sel</span>: <span class="string">'ul'</span>, </span><br><span class="line"> <span class="attr">data</span>: {},</span><br><span class="line"> <span class="attr">children</span>: [ </span><br><span class="line"> {</span><br><span class="line"> <span class="attr">sel</span>: <span class="string">'li'</span>, <span class="attr">data</span>: { <span class="attr">class</span>: <span class="string">'item'</span> }, <span class="attr">text</span>: <span class="string">'son1'</span></span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> <span class="attr">sel</span>: <span class="string">'li'</span>, <span class="attr">data</span>: { <span class="attr">class</span>: <span class="string">'item'</span> }, <span class="attr">text</span>: <span class="string">'son2'</span></span><br><span class="line"> }, </span><br><span class="line"> ],</span><br><span class="line"> <span class="attr">elm</span>: <span class="literal">undefined</span>,</span><br><span class="line"> <span class="attr">key</span>: <span class="literal">undefined</span>,</span><br><span class="line"> <span class="attr">text</span>: <span class="literal">undefined</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>那么虚拟Dom有什么用呢。我们其实可以把虚拟Dom理解成对应真实Dom的一种状态。当真实Dom发生变化后,虚拟Dom可以为我们提供这个真实Dom变化之前和变化之后的状态,我们通过对比这两个状态,即可得出真实Dom真正需要更新的部分,即可实现<strong>最小量</strong>更新。在一些比较复杂的Dom变化场景中,通过对比虚拟Dom后更新真实Dom会比直接更新真实Dom的效率高,这也就是虚拟Dom和diff算法真正存在的意义。</p><h3 id="h函数"><a href="#h函数" class="headerlink" title="h函数"></a>h函数</h3><p>要生成虚拟Dom,我们可以使用 <strong>h函数</strong>,就是render函数里面传入的那个<strong>h函数</strong>。它可以接受多种类型的参数,但其实它内部只干了一件事,就是执行<code>vnode函数</code>。根据传入<strong>h函数</strong>的参数来决定执行<code>vnode函数</code>时传入的参数。那么<code>vnode函数</code>又是干什么的呢?<code>vnode函数</code>其实也只干了一件事,就是把传入<strong>h函数</strong>的参数转化为一个对象,即虚拟Dom。</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vnode.js</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> (<span class="params">sel, data, children, text, elm</span>) {</span><br><span class="line"> <span class="keyword">const</span> key = data.<span class="property">key</span> </span><br><span class="line"> <span class="keyword">return</span> {sel, data, children, text, elm, key}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>执行<strong>h函数</strong>后,内部会通过<code>vnode函数</code>生成虚拟Dom,然后<strong>h函数</strong>返回这个虚拟Dom</p><h2 id="diff对比规则"><a href="#diff对比规则" class="headerlink" title="diff对比规则"></a>diff对比规则</h2><p>明确了<strong>h函数是干什么的</strong>,我们可以简单用<strong>h函数</strong>生成两个不同的虚拟节点,我们将通过一个简易版的diff算法代码介绍diff对比的具体流程。</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 第一个参数是sel 第二个参数是data 第三个参数是children</span></span><br><span class="line"><span class="keyword">const</span> myVnode1 = <span class="title function_">h</span>(<span class="string">"h1"</span>, {}, [</span><br><span class="line"> <span class="title function_">h</span>(<span class="string">"p"</span>, {<span class="attr">key</span>: <span class="string">"a"</span>}, <span class="string">"a"</span>),</span><br><span class="line"> <span class="title function_">h</span>(<span class="string">"p"</span>, {<span class="attr">key</span>: <span class="string">"b"</span>}, <span class="string">"b"</span>),</span><br><span class="line">]);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> myVnode2 = <span class="title function_">h</span>(<span class="string">"h1"</span>, {}, [</span><br><span class="line"> <span class="title function_">h</span>(<span class="string">"p"</span>, {<span class="attr">key</span>: <span class="string">"c"</span>}, <span class="string">"c"</span>),</span><br><span class="line"> <span class="title function_">h</span>(<span class="string">"p"</span>, {<span class="attr">key</span>: <span class="string">"d"</span>}, <span class="string">"d"</span>),</span><br><span class="line">]);</span><br></pre></td></tr></table></figure></div><h3 id="patch"><a href="#patch" class="headerlink" title="patch"></a>patch</h3><p>比较的第一步就是执行<strong>patch</strong>,它相当于对比的入口。既然是对比两个虚拟Dom,那么就将两个虚拟Dom作为参数传入<strong>patch</strong>中。<strong>patch</strong>的主要作用是对比两个虚拟Dom的根节点,并根据对比结果操作真实Dom。</p><p>patch函数的核心代码如下,注意注释。</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// patch.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> vnode <span class="keyword">from</span> <span class="string">"./vnode"</span></span><br><span class="line"><span class="keyword">import</span> patchDetails <span class="keyword">from</span> <span class="string">"./patchVnode"</span></span><br><span class="line"><span class="keyword">import</span> createEle <span class="keyword">from</span> <span class="string">"./createEle"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> 用来对比两个虚拟dom的根节点,并根据对比结果操作真实Dom</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} oldVnode </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} newVnode </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">patch</span>(<span class="params">oldVnode, newVnode</span>) {</span><br><span class="line"> <span class="comment">// 1.判断oldVnode是否为虚拟节点,不是的话转化为虚拟节点</span></span><br><span class="line"> <span class="keyword">if</span>(!oldVnode.<span class="property">sel</span>) {</span><br><span class="line"> <span class="comment">// 转化为虚拟节点</span></span><br><span class="line"> oldVnode = <span class="title function_">vnode</span>(oldVnode.<span class="property">tagName</span>.<span class="title function_">toLowerCase</span>(), {}, [], <span class="literal">undefined</span>, oldVnode)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 2.判断oldVnode和newVnode是否为同一个节点</span></span><br><span class="line"> <span class="keyword">if</span>(oldVnode.<span class="property">key</span> == newVnode.<span class="property">key</span> && oldVnode.<span class="property">sel</span> == newVnode.<span class="property">sel</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'是同一个节点'</span>)</span><br><span class="line"> <span class="comment">// 比较子节点</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldVnode, newVnode)</span><br><span class="line"> }<span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'不是同一个节点'</span>)</span><br><span class="line"> <span class="comment">// 插入newVnode </span></span><br><span class="line"> <span class="keyword">const</span> newNode = <span class="title function_">createEle</span>(newVnode) <span class="comment">// 插入之前需要先将newVnode转化为dom</span></span><br><span class="line"> oldVnode.<span class="property">elm</span>.<span class="property">parentNode</span>.<span class="title function_">insertBefore</span>(newNode, oldVnode.<span class="property">elm</span>) <span class="comment">// 插入操作</span></span><br><span class="line"> <span class="comment">// 删除oldVnode</span></span><br><span class="line"> oldVnode.<span class="property">elm</span>.<span class="property">parentNode</span>.<span class="title function_">removeChild</span>(oldVnode.<span class="property">elm</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>createEle:创建真实dom</strong></p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// createEle.js</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> 根据传入的虚拟Dom生成真实Dom</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} vnode </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> real node</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">createEle</span> (<span class="params">vnode</span>) {</span><br><span class="line"> <span class="keyword">const</span> realNode = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(vnode.<span class="property">sel</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 子节点转换</span></span><br><span class="line"> <span class="keyword">if</span>(vnode.<span class="property">text</span> && (vnode.<span class="property">children</span> == <span class="literal">undefined</span> || (vnode.<span class="property">children</span> && vnode.<span class="property">children</span>.<span class="property">length</span> == <span class="number">0</span>)) ) {</span><br><span class="line"> <span class="comment">// 子节点只含有文本</span></span><br><span class="line"> realNode.<span class="property">innerText</span> = vnode.<span class="property">text</span> </span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(<span class="title class_">Array</span>.<span class="title function_">isArray</span>(vnode.<span class="property">children</span>) && vnode.<span class="property">children</span>.<span class="property">length</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="comment">// 子节点为其他虚拟节点 递归添加node</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < vnode.<span class="property">children</span>.<span class="property">length</span>; i++) {</span><br><span class="line"> <span class="keyword">const</span> childNode = <span class="title function_">createEle</span>(vnode.<span class="property">children</span>[i])</span><br><span class="line"> realNode.<span class="title function_">appendChild</span>(childNode)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 补充vnode的elm属性</span></span><br><span class="line"> vnode.<span class="property">elm</span> = realNode</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> vnode.<span class="property">elm</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h3 id="patchVnode"><a href="#patchVnode" class="headerlink" title="patchVnode"></a>patchVnode</h3><p><strong>patchVnode</strong>用来比较两个虚拟节点的子节点并更新其子节点对应的真实Dom节点</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// patchVnode.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> updateChildren <span class="keyword">from</span> <span class="string">"./updateChildren"</span></span><br><span class="line"><span class="keyword">import</span> createEle <span class="keyword">from</span> <span class="string">"./createEle"</span></span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> 比较两个虚拟节点的子节点(children or text) 并更新其子节点对应的真实dom节点</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} oldVnode </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} newVnode </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">patchDetails</span>(<span class="params">oldVnode, newVnode</span>) {</span><br><span class="line"> <span class="comment">// 判断oldVnode和newVnode是否为同一个对象, 是的话直接不用比了</span></span><br><span class="line"> <span class="keyword">if</span>(oldVnode == newVnode) <span class="keyword">return</span> </span><br><span class="line"></span><br><span class="line"> <span class="comment">// 默认newVnode和oldVnode只有text和children其中之一,真实的源码这里的情况会更多一些,不过大同小异。</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span>(<span class="title function_">hasText</span>(newVnode)) {</span><br><span class="line"> <span class="comment">// newVnode有text但没有children</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * newVnode.text !== oldVnode.text 直接囊括了两种情况</span></span><br><span class="line"><span class="comment"> * 1.oldVnode有text无children 但是text和newVnode的text内容不同</span></span><br><span class="line"><span class="comment"> * 2.oldVnode无text有children 此时oldVnode.text为undefined </span></span><br><span class="line"><span class="comment"> * 两种情况都可以通过innerText属性直接完成dom更新 </span></span><br><span class="line"><span class="comment"> * 情况1直接更新text 情况2相当于去掉了children后加了新的text</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span>(newVnode.<span class="property">text</span> !== oldVnode.<span class="property">text</span>) {</span><br><span class="line"> oldVnode.<span class="property">elm</span>.<span class="property">innerText</span> = newVnode.<span class="property">text</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(<span class="title function_">hasChildren</span>(newVnode)) {</span><br><span class="line"> <span class="comment">// newVnode有children但是没有text</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">if</span>(<span class="title function_">hasText</span>(oldVnode)) {</span><br><span class="line"> <span class="comment">// oldVnode有text但是没有children</span></span><br><span class="line"> </span><br><span class="line"> oldVnode.<span class="property">elm</span>.<span class="property">innerText</span> = <span class="string">''</span> <span class="comment">// 删除oldVnode的text</span></span><br><span class="line"> <span class="comment">// 添加newVnode的children</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">let</span> i = <span class="number">0</span>; i < newVnode.<span class="property">children</span>.<span class="property">length</span>; i++) {</span><br><span class="line"> oldVnode.<span class="property">elm</span>.<span class="title function_">appendChild</span>(<span class="title function_">createEle</span>(newVnode.<span class="property">children</span>[i]))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }<span class="keyword">else</span> <span class="keyword">if</span>(<span class="title function_">hasChildren</span>(oldVnode)) {</span><br><span class="line"> <span class="comment">// oldVnode有children但是没有text</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 对比两个节点的children 并更新对应的真实dom节点</span></span><br><span class="line"> <span class="title function_">updateChildren</span>(oldVnode.<span class="property">children</span>, newVnode.<span class="property">children</span>, oldVnode.<span class="property">elm</span>)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 有children没有text</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">hasChildren</span>(<span class="params">node</span>) {</span><br><span class="line"> <span class="keyword">return</span> !node.<span class="property">text</span> && (node.<span class="property">children</span> && node.<span class="property">children</span>.<span class="property">length</span> > <span class="number">0</span>)</span><br><span class="line">} </span><br><span class="line"></span><br><span class="line"><span class="comment">// 有text没有children</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">hasText</span>(<span class="params">node</span>) {</span><br><span class="line"> <span class="keyword">return</span> node.<span class="property">text</span> && (node.<span class="property">children</span> == <span class="literal">undefined</span> || (node.<span class="property">children</span> && node.<span class="property">children</span>.<span class="property">length</span> == <span class="number">0</span>))</span><br><span class="line">} </span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h3 id="updateChildren"><a href="#updateChildren" class="headerlink" title="updateChildren"></a>updateChildren</h3><p>该方法是diff算法中最复杂的方法(大的要来了)。对应上面patchVnode中oldVnode和newVnode都有children的情况。</p><p>首先我们需要介绍一下这里的对比规则。</p><p>对比过程中会引入四个指针,分别指向oldVnode子节点列表中的第一个节点和最后一个节点(后面我们简称为旧前和旧后)以及指向newVnode子节点列表中的第一个节点和最后一个节点(后面我们简称为新前和新后)</p><p>对比时,每一次对比按照以下顺序进行命中查找</p><ul><li>旧前与新前节点对比(1)</li><li>旧后与新后节点对比(2)</li><li>旧前与新后节点对比(3)</li><li>旧后与新前节点对比(4)</li></ul><p>上述四种情况,如果某一种情况两个指针对应的虚拟Dom相同,那么我们称之为命中。命中后就不会接着查找了,指针会移动,(还有可能会操作真实Dom,3或者4命中时会操作真实Dom移动节点)之后开始下一次对比。如果都没有命中,则去oldVnode子节点列表循环查找当前新前指针所指向的节点,如果查到了,那么操作真实Dom移动节点,没查到则新增真实Dom节点插入。</p><p>这种模式的对比会一直进行,直到满足了终止条件。即旧前指针移动到了旧后指针的后面或者新前指针移动到了新后指针的后面,我们可以理解为旧子节点先处理完毕和新子节点处理完毕。那么我们可以预想到新旧子节点中总会有其一先处理完,对比结束后,我们会根据没有处理完子节点的那一对前后指针决定是要插入真实Dom还是删除真实Dom。</p><p>如果旧子节点先处理完了,新子节点有剩余,说明有要新增的节点。将根据最终新前和新后之间的虚拟节点执行插入操作<br>如果新子节点先处理完了,旧子节点有剩余,说明有要删除的节点。将根据最终旧前和旧后之间的虚拟节点执行删除操作<br>下面将呈现代码,注意注释</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><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><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// updateChildren.js</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> patchDetails <span class="keyword">from</span> <span class="string">"./patchVnode"</span></span><br><span class="line"><span class="keyword">import</span> createEle <span class="keyword">from</span> <span class="string">"./createEle"</span>;</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> 对比子节点列表并更新真实Dom</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} oldCh 旧虚拟Dom子节点列表 </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} newCh 新虚拟Dom子节点列表 </span></span><br><span class="line"><span class="comment"> * <span class="doctag">@param</span> {<span class="type">*</span>} parent 新旧虚拟节点对应的真实Dom</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@returns</span> </span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> <span class="keyword">function</span> <span class="title function_">updateChildren</span>(<span class="params">oldCh, newCh, parent</span>) {</span><br><span class="line"> <span class="comment">// 定义四个指针 旧前 旧后 新前 新后 (四个指针两两一对,每一对前后指针所指向的节点以及其之间的节点为未处理的子节点)</span></span><br><span class="line"> <span class="keyword">let</span> oldStartIndex = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> oldEndIndex = oldCh.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">let</span> newStartIndex = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">let</span> newEndIndex = newCh.<span class="property">length</span> - <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 四个指针对应的节点</span></span><br><span class="line"> <span class="keyword">let</span> oldStartNode = oldCh[oldStartIndex];</span><br><span class="line"> <span class="keyword">let</span> oldEndNode = oldCh[oldEndIndex];</span><br><span class="line"> <span class="keyword">let</span> newStartNode = newCh[newStartIndex];</span><br><span class="line"> <span class="keyword">let</span> newEndNode = newCh[newEndIndex];</span><br><span class="line"></span><br><span class="line"> <span class="comment">// oldCh中每个子节点 key 与 index的哈希表 用于四种对比规则都不匹配的情况下在oldCh中寻找节点</span></span><br><span class="line"> <span class="keyword">const</span> keyMap = <span class="keyword">new</span> <span class="title class_">Map</span>();</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 开始遍历两个children数组进行细节对比</span></span><br><span class="line"><span class="comment"> * 对比规则:旧前-新前 旧后-新后 旧前-新后 旧后-新前</span></span><br><span class="line"><span class="comment"> * 对比之后指针进行移动</span></span><br><span class="line"><span class="comment"> * 直到指针不满足以下条件 意味着有一对前后指针之间再无未处理的子节点 则停止对比 直接操作DOM</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (oldStartIndex <= oldEndIndex && newStartIndex <= newEndIndex) {</span><br><span class="line"> <span class="comment">// 这四种情况是为了让指针在移动的过程中跳过空节点</span></span><br><span class="line"> <span class="keyword">if</span> (oldStartNode == <span class="literal">undefined</span>) {</span><br><span class="line"> oldStartNode = oldCh[++oldStartIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (oldEndNode == <span class="literal">undefined</span>) {</span><br><span class="line"> oldEndNode = oldCh[--oldEndIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (newStartNode == <span class="literal">undefined</span>) {</span><br><span class="line"> newStartNode = newCh[++newStartIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (newEndNode == <span class="literal">undefined</span>) {</span><br><span class="line"> newEndNode = newCh[--newEndIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_">isSame</span>(oldStartNode, newStartNode)) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"method1"</span>);</span><br><span class="line"> <span class="comment">// 旧前-新前是同一个虚拟节点</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 两个子节点再对比他们的子节点并更新dom (递归切入点)</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldStartNode, newStartNode);</span><br><span class="line"> <span class="comment">// 指针移动</span></span><br><span class="line"> oldStartNode = oldCh[++oldStartIndex];</span><br><span class="line"> newStartNode = newCh[++newStartIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_">isSame</span>(oldEndNode, newEndNode)) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"method2"</span>);</span><br><span class="line"> <span class="comment">// 旧后-新后是同一个虚拟节点</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 两个子节点再对比他们的子节点并更新dom (递归切入点)</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldEndNode, newEndNode);</span><br><span class="line"> <span class="comment">// 指针移动</span></span><br><span class="line"> oldEndNode = oldCh[--oldEndIndex];</span><br><span class="line"> newEndNode = newCh[--newEndIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_">isSame</span>(oldStartNode, newEndNode)) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"method3"</span>);</span><br><span class="line"> <span class="comment">// 旧前-新后是同一个虚拟节点</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 两个子节点再对比他们的子节点并更新dom (递归切入点)</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldStartNode, newEndNode);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这一步多一个移动(真实)节点的操作</span></span><br><span class="line"><span class="comment"> * 需要把当前指针所指向的子节点 移动到 oldEndIndex所对应真实节点之后(也就是未处理真实节点的尾部)</span></span><br><span class="line"><span class="comment"> * 注意:这一步是在操作真实节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> parent.<span class="title function_">insertBefore</span>(oldStartNode.<span class="property">elm</span>, oldEndNode.<span class="property">elm</span>.<span class="property">nextSibling</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 指针移动</span></span><br><span class="line"> oldStartNode = oldCh[++oldStartIndex];</span><br><span class="line"> newEndNode = newCh[--newEndIndex];</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="title function_">isSame</span>(oldEndNode, newStartNode)) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"method4"</span>);</span><br><span class="line"> <span class="comment">// 旧后-新前 是同一个虚拟节点</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 两个子节点再对比他们的子节点并更新dom (递归切入点)</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldEndNode, newStartNode);</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 这一步多一个移动(真实)节点的操作</span></span><br><span class="line"><span class="comment"> * 与method3不同在移动位置</span></span><br><span class="line"><span class="comment"> * 需要把当前指针所指向的子节点 移动到 oldStartIndex所对应真实节点之前(也就是未处理真实节点的顶部)</span></span><br><span class="line"><span class="comment"> * 注意:这一步是在操作真实节点</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> parent.<span class="title function_">insertBefore</span>(oldEndNode.<span class="property">elm</span>, oldCh[oldStartIndex].<span class="property">elm</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 指针移动</span></span><br><span class="line"> oldEndNode = oldCh[--oldEndIndex];</span><br><span class="line"> newStartNode = newCh[++newStartIndex];</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"does not match"</span>);</span><br><span class="line"> <span class="comment">// 四种规则都不匹配</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 生成keyMap</span></span><br><span class="line"> <span class="keyword">if</span> (keyMap.<span class="property">size</span> == <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = oldStartIndex; i <= oldEndIndex; i++) {</span><br><span class="line"> <span class="keyword">if</span> (oldCh[i].<span class="property">key</span>) keyMap.<span class="title function_">set</span>(oldCh[i].<span class="property">key</span>, i);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 在oldCh中搜索当前newStartIndex所指向的节点</span></span><br><span class="line"> <span class="keyword">if</span> (keyMap.<span class="title function_">has</span>(newStartNode.<span class="property">key</span>)) {</span><br><span class="line"> <span class="comment">// 搜索到了</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 先获取oldCh中该虚拟节点</span></span><br><span class="line"> <span class="keyword">const</span> oldMoveNode = oldCh[keyMap.<span class="title function_">get</span>(newStartNode.<span class="property">key</span>)];</span><br><span class="line"> <span class="comment">// 两个子节点再对比他们的子节点并更新dom (递归切入点)</span></span><br><span class="line"> <span class="title function_">patchDetails</span>(oldMoveNode, newStartNode);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 移动这个节点(移动的是真实节点)</span></span><br><span class="line"> parent.<span class="title function_">insertBefore</span>(oldMoveNode.<span class="property">elm</span>, oldStartNode.<span class="property">elm</span>);</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 该虚拟节点设置为undefined(还记得最开始的四个条件吗,因为这里会将子节点制空,所以加了那四个条件)</span></span><br><span class="line"> oldCh[keyMap.<span class="title function_">get</span>(newStartNode.<span class="property">key</span>)] = <span class="literal">undefined</span>;</span><br><span class="line"> </span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 没搜索到 直接插入</span></span><br><span class="line"> parent.<span class="title function_">insertBefore</span>(<span class="title function_">createEle</span>(newStartNode), oldStartNode.<span class="property">elm</span>);</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 指针移动</span></span><br><span class="line"> newStartNode = newCh[++newStartIndex];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 插入和删除节点</span></span><br><span class="line"><span class="comment"> * while结束后 有一对前后指针之间仍然有未处理的子节点,那么就会进行插入或者删除操作</span></span><br><span class="line"><span class="comment"> * oldCh的双指针中有未处理的子节点,进行删除操作</span></span><br><span class="line"><span class="comment"> * newCh的双指针中有未处理的子节点,进行插入操作</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">if</span> (oldStartIndex <= oldEndIndex) {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 删除</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = oldStartIndex; i <= oldEndIndex; i++) {</span><br><span class="line"> <span class="comment">// 加判断是因为oldCh[i]有可能为undefined</span></span><br><span class="line"> <span class="keyword">if</span>(oldCh[i]) parent.<span class="title function_">removeChild</span>(oldCh[i].<span class="property">elm</span>);</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (newStartIndex <= newEndIndex) {</span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 插入</span></span><br><span class="line"><span class="comment"> * 这里需要注意的点是从哪里插入,也就是appendChild的第二个参数</span></span><br><span class="line"><span class="comment"> * 应该从oldStartIndex对应的位置插入</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = newStartIndex; i <= newEndIndex; i++) {</span><br><span class="line"> <span class="comment">// oldCh[oldStartIndex]存在是从头部插入</span></span><br><span class="line"> parent.<span class="title function_">insertBefore</span>(<span class="title function_">createEle</span>(newCh[i]), oldCh[oldStartIndex] ? oldCh[oldStartIndex].<span class="property">elm</span> : <span class="literal">undefined</span>);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 判断两个虚拟节点是否为同一个虚拟节点</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">isSame</span>(<span class="params">a, b</span>) {</span><br><span class="line"> <span class="keyword">return</span> a.<span class="property">sel</span> == b.<span class="property">sel</span> && a.<span class="property">key</span> == b.<span class="property">key</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>这里的逻辑稍微比较复杂,需要大家多理几遍,必要的话,自己手画一张图自己移动一下指针。着重需要注意的地方是操作真实Dom时,插入、移动节点应该将节点从哪里插入或者移动到哪里,其实基本插入到oldStartIndex对应的真实Dom的前面,除了第三种命中后的移动节点操作,是移动到oldEndIndex所对应真实节点之后</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>由于diff算法对比的是虚拟Dom,而虚拟Dom是呈树状的,所以我们可以发现,diff算法中充满了递归。总结起来,其实diff算法就是一个 patch —> patchVnode —> updateChildren —> patchVnode —> updateChildren —> patchVnode这样的一个循环递归的过程。</p><p>这里再提一嘴key,我们面试中经常会被问到vue中key的作用。根据上面我们分析的,key的主要作用其实就是对比两个虚拟节点时,判断其是否为相同节点。加了key以后,我们可以更为明确的判断两个节点是否为同一个虚拟节点,是的话判断子节点是否有变更(有变更更新真实Dom),不是的话继续比。如果不加key的话,如果两个不同节点的标签名恰好相同,那么就会被判定为同一个节点(key都为undefined),结果一对比这两个节点的子节点发现不一样,这样会凭空增加很多对真实Dom的操作,从而导致页面更频繁得进行重绘和回流。</p><p>所以我认为合理利用key可以有效减少真实Dom的变动,从而减少页面重绘和回流的频率,进而提高页面更新的效率。</p>]]></content>
<summary type="html">false</summary>
<category term="Vue" scheme="http://example.com/categories/Vue/"/>
<category term="Vue" scheme="http://example.com/tags/Vue/"/>
</entry>
<entry>
<title>实现一个简单的vue</title>
<link href="http://example.com/2022/10/28/Vue/%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84vue/"/>
<id>http://example.com/2022/10/28/Vue/%E5%AE%9E%E7%8E%B0%E4%B8%80%E4%B8%AA%E7%AE%80%E5%8D%95%E7%9A%84vue/</id>
<published>2022-10-28T12:15:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<p>首先我们创建一个<code>index.html</code>文件,引入自定义的<code>my-vue.js</code>,然后实例化对象,并传人对象参数</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"app"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span>{{ str }}<span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h2</span>></span>{{ name }}<span class="tag"></<span class="name">h2</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">input</span> <span class="attr">type</span>=<span class="string">"text"</span> <span class="attr">v-model</span>=<span class="string">"name"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">button</span> @<span class="attr">click</span>=<span class="string">"handleClick"</span>></span>按钮<span class="tag"></<span class="name">button</span>></span></span><br><span class="line"><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="tag"><<span class="name">script</span> <span class="attr">src</span>=<span class="string">"./my-vue.js"</span>></span><span class="tag"></<span class="name">script</span>></span></span><br><span class="line"><span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">new</span> <span class="title class_">Vue</span>({</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">el</span>: <span class="string">'#app'</span>,</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">data</span>: {</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">str</span>: <span class="string">'你好🎉'</span>,</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">name</span>: <span class="string">'jiangwen'</span></span></span><br><span class="line"><span class="language-javascript"> },</span></span><br><span class="line"><span class="language-javascript"> <span class="attr">methods</span>:{</span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">handleClick</span>(<span class="params">e</span>){</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">this</span>.<span class="property">str</span> = <span class="string">'hello👋'</span></span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="variable language_">this</span>.<span class="property">str</span>, <span class="variable language_">this</span>.<span class="property">$data</span>.<span class="property">str</span>);</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> })</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></div><p> 创建一个Vue类</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$data</span> = options.<span class="property">data</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$el</span> = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(options.<span class="property">el</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="模版解析"><a href="#模版解析" class="headerlink" title="模版解析"></a>模版解析</h2><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$data</span> = options.<span class="property">data</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$el</span> = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(options.<span class="property">el</span>)</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="title function_">compile</span>(<span class="variable language_">this</span>.<span class="property">$el</span>)</span><br><span class="line"> }</span><br><span class="line">+ <span class="title function_">compile</span>(<span class="params">node</span>) {</span><br><span class="line">+ node.<span class="property">childNodes</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">e</span> =></span> {</span><br><span class="line">+ <span class="comment">// 节点类型</span></span><br><span class="line">+ <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">1</span>) {</span><br><span class="line">+ <span class="keyword">if</span> (e.<span class="property">childNodes</span>.<span class="property">length</span> > <span class="number">0</span>) {</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="title function_">compile</span>(e) <span class="comment">//递归调用</span></span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line">+ <span class="comment">// 文本类型</span></span><br><span class="line">+ <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">3</span>) {</span><br><span class="line">+ <span class="keyword">const</span> reg = <span class="regexp">/\{\{(.*?)\}\}/g</span></span><br><span class="line">+ <span class="keyword">const</span> text = e.<span class="property">textContent</span></span><br><span class="line">+ e.<span class="property">textContent</span> = text.<span class="title function_">replace</span>(reg, <span class="function">(<span class="params">match, vmKey</span>) =></span> {</span><br><span class="line">+ vmKey = vmKey.<span class="title function_">trim</span>()</span><br><span class="line">+ <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">$data</span>[vmKey]</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="生命周期"><a href="#生命周期" class="headerlink" title="生命周期"></a>生命周期</h2><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> <span class="comment">// 1. 实例创建前钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">beforeCreate</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">beforeCreate</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 创建数据 </span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$data</span> = options.<span class="property">data</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">proxyData</span>()</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">observe</span>()</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$watchEvent</span> = {}</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$methods</span> = options.<span class="property">methods</span></span><br><span class="line"> <span class="comment">// 2. 实例创建完成钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">created</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">created</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 3. dom挂载前钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">beforeMount</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">beforeMount</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 挂载dom</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$el</span> = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(options.<span class="property">el</span>)</span><br><span class="line"> <span class="comment">// 模版解析</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">compile</span>(<span class="variable language_">this</span>.<span class="property">$el</span>)</span><br><span class="line"> <span class="comment">// 4.dom挂载完成钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">mounted</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">mounted</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="添加事件"><a href="#添加事件" class="headerlink" title="添加事件"></a>添加事件</h2><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">compile</span>(<span class="params">node</span>) {</span><br><span class="line"> node.<span class="property">childNodes</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">e</span> =></span> {</span><br><span class="line"> <span class="comment">// 节点类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">childNodes</span>.<span class="property">length</span> > <span class="number">0</span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">compile</span>(e)</span><br><span class="line"> }</span><br><span class="line">+ <span class="comment">// 判断节点元素是否有@click</span></span><br><span class="line">+ <span class="keyword">if</span> (e.<span class="title function_">hasAttribute</span>(<span class="string">'@click'</span>)) {</span><br><span class="line">+ <span class="variable language_">console</span>.<span class="title function_">log</span>(e.<span class="title function_">getAttribute</span>(<span class="string">'@click'</span>));</span><br><span class="line">+ <span class="comment">// 获取事件名称</span></span><br><span class="line">+ <span class="keyword">const</span> vmKey = e.<span class="title function_">getAttribute</span>(<span class="string">'@click'</span>).<span class="title function_">trim</span>()</span><br><span class="line">+ e.<span class="title function_">addEventListener</span>(<span class="string">'click'</span>, <span class="function"><span class="params">event</span> =></span> {</span><br><span class="line">+ <span class="keyword">const</span> fn = <span class="variable language_">this</span>.<span class="property">$methods</span>[vmKey].<span class="title function_">bind</span>(<span class="variable language_">this</span>)</span><br><span class="line">+ <span class="title function_">fn</span>(event)</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 文本类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">3</span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="数据驱动视图更新"><a href="#数据驱动视图更新" class="headerlink" title="数据驱动视图更新"></a>数据驱动视图更新</h2><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><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><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> <span class="comment">// 1. 实例创建前钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">beforeCreate</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">beforeCreate</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 创建数据 </span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$data</span> = options.<span class="property">data</span></span><br><span class="line">+ <span class="variable language_">this</span>.<span class="title function_">proxyData</span>()</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="title function_">observe</span>()</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">$watchEvent</span> = {}</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$methods</span> = options.<span class="property">methods</span></span><br><span class="line"> <span class="comment">// 2. 实例创建完成钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">created</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">created</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 3. dom挂载前钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">beforeMount</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">beforeMount</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 挂载dom</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">$el</span> = <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(options.<span class="property">el</span>)</span><br><span class="line"> <span class="comment">// 模版解析</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="title function_">compile</span>(<span class="variable language_">this</span>.<span class="property">$el</span>)</span><br><span class="line"> <span class="comment">// 4.dom挂载完成钩子🪝</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> options.<span class="property">mounted</span> === <span class="string">'function'</span>) {</span><br><span class="line"> options.<span class="property">mounted</span>.<span class="title function_">bind</span>(<span class="variable language_">this</span>)()</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">compile</span>(<span class="params">node</span>) {</span><br><span class="line"> node.<span class="property">childNodes</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">e</span> =></span> {</span><br><span class="line"> <span class="comment">// 节点类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">1</span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 文本类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">3</span>) {</span><br><span class="line"> <span class="keyword">const</span> reg = <span class="regexp">/\{\{(.*?)\}\}/g</span></span><br><span class="line"> <span class="keyword">const</span> text = e.<span class="property">textContent</span></span><br><span class="line"> e.<span class="property">textContent</span> = text.<span class="title function_">replace</span>(reg, <span class="function">(<span class="params">match, vmKey</span>) =></span> {</span><br><span class="line"> vmKey = vmKey.<span class="title function_">trim</span>()</span><br><span class="line">+ <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="title function_">hasOwnProperty</span>(vmKey)) {</span><br><span class="line">+ <span class="keyword">let</span> watcher = <span class="keyword">new</span> <span class="title class_">Watcher</span>(<span class="variable language_">this</span>, vmKey, e, <span class="string">'textContent'</span>)</span><br><span class="line">+ <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="property">$watchEvent</span>[vmKey]){</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">$watchEvent</span>[vmKey].<span class="title function_">push</span>(watcher)</span><br><span class="line">+ }<span class="keyword">else</span>{</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">$watchEvent</span>[vmKey] = []</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">$watchEvent</span>[vmKey].<span class="title function_">push</span>(watcher)</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">$data</span>[vmKey]</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 同步数据(保持vue实例对象属性和data中的属性同步,可以直接使用this.xxx获取数据)</span></span><br><span class="line">+ <span class="title function_">proxyData</span>(<span class="params"></span>) {</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> <span class="variable language_">this</span>.<span class="property">$data</span>) {</span><br><span class="line">+ <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="variable language_">this</span>, key, {</span><br><span class="line">+ <span class="title function_">get</span>(<span class="params"></span>) {</span><br><span class="line">+ <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="property">$data</span>[key]</span><br><span class="line">+ },</span><br><span class="line">+ <span class="title function_">set</span>(<span class="params">val</span>){</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">$data</span>[key] = val</span><br><span class="line">+ }</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line"> <span class="comment">// 观察者(监控this.$data的所有值)</span></span><br><span class="line">+ <span class="title function_">observe</span>(<span class="params"></span>){</span><br><span class="line">+ <span class="keyword">for</span> (<span class="keyword">const</span> key <span class="keyword">in</span> <span class="variable language_">this</span>.<span class="property">$data</span>) {</span><br><span class="line">+ <span class="keyword">let</span> value = <span class="variable language_">this</span>.<span class="property">$data</span>[key]</span><br><span class="line">+ <span class="keyword">const</span> _this = <span class="variable language_">this</span></span><br><span class="line">+ <span class="title class_">Object</span>.<span class="title function_">defineProperty</span>(<span class="variable language_">this</span>.<span class="property">$data</span>, key, {</span><br><span class="line">+ <span class="title function_">get</span>(<span class="params"></span>){</span><br><span class="line">+ <span class="keyword">return</span> value</span><br><span class="line">+ },</span><br><span class="line">+ <span class="title function_">set</span>(<span class="params">val</span>){</span><br><span class="line">+ value = val</span><br><span class="line">+ <span class="keyword">if</span>(_this.<span class="property">$watchEvent</span>[key]){</span><br><span class="line">+ <span class="comment">// 所有使用该属性值的元素都执行节点更新</span></span><br><span class="line">+ _this.<span class="property">$watchEvent</span>[key].<span class="title function_">forEach</span>(<span class="function">(<span class="params">item, index</span>) =></span> {</span><br><span class="line">+ item.<span class="title function_">update</span>()</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br><span class="line">+}</span><br><span class="line"></span><br><span class="line">+ <span class="keyword">class</span> <span class="title class_">Watcher</span> {</span><br><span class="line">+ <span class="title function_">constructor</span>(<span class="params">vm, key, node, attr</span>){</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">vm</span> = vm</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">key</span> = key</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">node</span> = node</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">attr</span> = attr</span><br><span class="line">+ }</span><br><span class="line">+ <span class="comment">// 更新节点的内容</span></span><br><span class="line">+ <span class="title function_">update</span>(<span class="params"></span>){</span><br><span class="line">+ <span class="variable language_">this</span>.<span class="property">node</span>[<span class="variable language_">this</span>.<span class="property">attr</span>] = <span class="variable language_">this</span>.<span class="property">vm</span>[<span class="variable language_">this</span>.<span class="property">key</span>]</span><br><span class="line">+ }</span><br><span class="line">+ }</span><br></pre></td></tr></table></figure></div><h2 id="双向绑定"><a href="#双向绑定" class="headerlink" title="双向绑定"></a>双向绑定</h2><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">class</span> <span class="title class_">Vue</span> {</span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">options</span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">compile</span>(<span class="params">node</span>) {</span><br><span class="line"> node.<span class="property">childNodes</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">e</span> =></span> {</span><br><span class="line"> <span class="comment">// 节点类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">1</span>) {</span><br><span class="line"> ...</span><br><span class="line">+ <span class="comment">// 判断节点元素是否有v-model</span></span><br><span class="line">+ <span class="keyword">if</span>(e.<span class="title function_">hasAttribute</span>(<span class="string">'v-model'</span>)){</span><br><span class="line">+ <span class="keyword">const</span> vmkey = e.<span class="title function_">getAttribute</span>(<span class="string">'v-model'</span>).<span class="title function_">trim</span>()</span><br><span class="line">+ <span class="keyword">if</span>(<span class="variable language_">this</span>.<span class="title function_">hasOwnProperty</span>(vmkey)){</span><br><span class="line">+ e.<span class="property">value</span> = <span class="variable language_">this</span>[vmkey] <span class="comment">// 将数据赋值给有v-model的节点</span></span><br><span class="line">+ }</span><br><span class="line">+ <span class="comment">// 监听元素input事件,赋值</span></span><br><span class="line">+ e.<span class="title function_">addEventListener</span>(<span class="string">'input'</span>, <span class="function">(<span class="params">event</span>)=></span>{</span><br><span class="line">+ <span class="variable language_">this</span>[vmkey] = e.<span class="property">value</span></span><br><span class="line">+ })</span><br><span class="line">+ }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 文本类型</span></span><br><span class="line"> <span class="keyword">if</span> (e.<span class="property">nodeType</span> === <span class="number">3</span>) {</span><br><span class="line"> ...</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">false</summary>
<category term="Vue" scheme="http://example.com/categories/Vue/"/>
<category term="Vue" scheme="http://example.com/tags/Vue/"/>
</entry>
<entry>
<title>Hello git</title>
<link href="http://example.com/2022/09/28/Git/hello-git/"/>
<id>http://example.com/2022/09/28/Git/hello-git/</id>
<published>2022-09-28T11:45:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<h2 id="git起步"><a href="#git起步" class="headerlink" title="git起步"></a>git起步</h2><h5 id="…在命令行上创建新的存储库"><a href="#…在命令行上创建新的存储库" class="headerlink" title="…在命令行上创建新的存储库"></a>…在命令行上创建新的存储库</h5><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">echo</span> <span class="string">"# article-img"</span> >> README.md</span><br><span class="line">git init</span><br><span class="line">git add README.md</span><br><span class="line">git commit -m <span class="string">"first commit"</span></span><br><span class="line">git branch -M main</span><br><span class="line">git remote add origin git@github.com:jiangwen5945/article-img.git</span><br><span class="line">git push -u origin main</span><br></pre></td></tr></table></figure></div><h5 id="…从命令行推送现有存储库"><a href="#…从命令行推送现有存储库" class="headerlink" title="…从命令行推送现有存储库"></a>…从命令行推送现有存储库</h5><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin git@github.com:jiangwen5945/article-img.git</span><br><span class="line">git branch -M main</span><br><span class="line">git push -u origin main</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">git从入门到删库</summary>
<category term="Git" scheme="http://example.com/categories/Git/"/>
<category term="Git" scheme="http://example.com/tags/Git/"/>
</entry>
<entry>
<title>Hexo入坑指南</title>
<link href="http://example.com/2022/09/28/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/Hexo%E5%85%A5%E5%9D%91%E6%8C%87%E5%8D%97/"/>
<id>http://example.com/2022/09/28/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/Hexo%E5%85%A5%E5%9D%91%E6%8C%87%E5%8D%97/</id>
<published>2022-09-28T11:45:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<p>Welcome to <a class="link" href="https://hexo.io/" >Hexo<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>! This is your very first post. Check <a class="link" href="https://hexo.io/docs/" >documentation<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> for more info. If you get any problems when using Hexo, you can find the answer in <a class="link" href="https://hexo.io/docs/troubleshooting.html" >troubleshooting<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> or you can ask me on <a class="link" href="https://github.com/hexojs/hexo/issues" >GitHub<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>.</p><h2 id="快速开始111"><a href="#快速开始111" class="headerlink" title="快速开始111"></a>快速开始111</h2><h3 id="Create-a-new-post"><a href="#Create-a-new-post" class="headerlink" title="Create a new post"></a>Create a new post</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo new <span class="string">"My New Post"</span></span><br></pre></td></tr></table></figure></div><p>More info: <a class="link" href="https://hexo.io/docs/writing.html" >Writing<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h3 id="Run-server"><a href="#Run-server" class="headerlink" title="Run server"></a>Run server</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo server</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link" href="https://hexo.io/docs/server.html" >Server<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h3 id="Generate-static-files"><a href="#Generate-static-files" class="headerlink" title="Generate static files"></a>Generate static files</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo generate</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link" href="https://hexo.io/docs/generating.html" >Generating<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h3 id="Deploy-to-remote-sites"><a href="#Deploy-to-remote-sites" class="headerlink" title="Deploy to remote sites"></a>Deploy to remote sites</h3><div class="highlight-container" data-rel="Bash"><figure class="iseeu highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ hexo deploy</span><br></pre></td></tr></table></figure></div><p>More info: <a class="link" href="https://hexo.io/docs/one-command-deployment.html" >Deployment<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><h2 id="使用主题"><a href="#使用主题" class="headerlink" title="使用主题"></a>使用主题</h2><blockquote><p>Hexo-theme-Redefine reimagines simplicity, speed, and purity, without sacrificing functionality or design. Its sleek, modern aesthetic is packed with useful features, blending style and practicality seamlessly.<br>Building on the solid foundation of hexo-theme-keep, “Redefine” elevates the style and incorporates valuable writing tools and plugins. It offers extensive customization options, allowing you to tailor every detail to your preferences. With Redefine, your blogging experience becomes unique and effortless, showcasing your personal style and needs.</p></blockquote><h3 id="🌐-Demo"><a href="#🌐-Demo" class="headerlink" title="🌐 Demo"></a>🌐 Demo</h3><ul><li><a class="link" href="https://redefine.ohevan.com/" >Theme Redefine Demo<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine.ohevan.com/showcase" >Redefine Theme Showcase<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li></ul><p>If you are also using Redefine, please go to <a class="link" href="https://redefine.ohevan.com/showcase" >Redefine Theme Showcase<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> to add your blog link.</p><h3 id="⛰️-Features"><a href="#⛰️-Features" class="headerlink" title="⛰️ Features"></a>⛰️ Features</h3><ul><li><a class="link" href="https://redefine-docs.ohevan.com/modules/notes" >Note Module<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/page_templates/friends" >Friend Link Page<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/plugins/mathjax" >Mathjax Support<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li>Code block language display</li><li>Light/Dark mode switching</li><li><a class="link" href="https://redefine-docs.ohevan.com/basic/fontawesome" >Font Awesome 6.2.1 Pro<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> (contains different styles like Duotone/Regular/Thin/Sharp)</li><li><a class="link" href="https://redefine-docs.ohevan.com/dhome/navbar#%E9%93%BE%E6%8E%A5%E5%88%97%E8%A1%A8" >Drop-down menu<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/footer" >Customizable footer<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/footer#%E8%BF%90%E8%A1%8C%E6%97%B6%E9%97%B4" >Site Uptime Display<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/article_customize/banner" >Article Header Image<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/plugins/mermaid" >Mermaid JS support<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li>SEO friendly</li><li><a class="link" href="https://redefine-docs.ohevan.com/plugins/aplayer" >Aplayer support<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/shuoshuo" >Shuoshuo support<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li><a class="link" href="https://redefine-docs.ohevan.com/basic/global#%E8%87%AA%E5%AE%9A%E4%B9%89%E5%AD%97%E4%BD%93" >Customizable Font<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></li><li>Tailwind CSS Included</li></ul><h3 id="☁️-Installation"><a href="#☁️-Installation" class="headerlink" title="☁️ Installation"></a>☁️ Installation</h3><p>The easiest way to install Theme Redefine is by using <strong>npm</strong> (after your hexo version has been up to <code>5.0</code>+)</p><div class="highlight-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> your-hexo-site</span><br><span class="line">$ npm install hexo-theme-redefine@latest</span><br></pre></td></tr></table></figure></div><p>Another method is by <strong>git clone</strong></p><div class="highlight-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> your-hexo-site</span><br><span class="line">$ git <span class="built_in">clone</span> https://github.com/EvanNotFound/hexo-theme-redefine.git themes/redefine</span><br></pre></td></tr></table></figure></div><p>After the installation, go to the <code>_config.yml</code> of your hexo site and set</p><div class="highlight-container" data-rel="Yaml"><figure class="iseeu highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">theme:</span> <span class="string">redefine</span></span><br></pre></td></tr></table></figure></div><h3 id="⏫-Update"><a href="#⏫-Update" class="headerlink" title="⏫ Update"></a>⏫ Update</h3><p>To update hexo-theme-redefine, you can run the same <strong>npm</strong> command</p><div class="highlight-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">$ npm install hexo-theme-redefine@latest</span><br></pre></td></tr></table></figure></div><p>If you installed the theme by <strong>git clone</strong>, you can update the theme by running the following command</p><div class="highlight-container" data-rel="Sh"><figure class="iseeu highlight sh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ <span class="built_in">cd</span> themes/redefine</span><br><span class="line">$ git pull</span><br></pre></td></tr></table></figure></div><h3 id="📄-Documentations"><a href="#📄-Documentations" class="headerlink" title="📄 Documentations"></a>📄 Documentations</h3><p>Please read <a class="link" href="https://redefine-docs.ohevan.com/" >Redefine Docs<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> when installing</p><p>It’s very easy to understand.</p><h3 id="☕-Support"><a href="#☕-Support" class="headerlink" title="☕ Support"></a>☕ Support</h3><p>Feel free to <strong>pull request</strong> and <strong>send issues</strong>.</p><p>If you have any questions, please send an email to <a class="link" href="mailto:contact@ohevan.com" >contact@ohevan.com<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>. I will reply in time.</p><p>Please <strong>give me a star</strong> to support me, thanks!</p><p>Also, if you are using <a class="link" href="https://typora.io/" >Typora<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>, check out <a class="link" href="https://github.com/EvanNotFound/typora-theme-redefine" >Typora Theme Redefine<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> so that you can preview the styles of your blog in Typora.</p><h3 id="💗-Donations"><a href="#💗-Donations" class="headerlink" title="💗 Donations"></a>💗 Donations</h3><p>Thanks to all the people who have donated to me. Your support is my greatest motivation.</p><p>If you like this theme, please give a star. You can also support me by <a class="link" href="https://github.com/EvanNotFound/hexo-theme-redefine/blob/dev/DONATION.md" >donating<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>.</p><p>This is the list of all the people who have donated to me: <a class="link" href="https://github.com/EvanNotFound/hexo-theme-redefine/blob/dev/DONATION.md" >Donation List<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p><p>如果你想要使用OpenAI GPT-4,但不想每月支付 20 美元吗,来看看我的 <a class="link" href="https://gpt.oknice.ca/" >GPT Plus Share<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> GPT Plus 共享站,使用多达 100 个 ChatGPT Plus 账户,每月仅 17 人民币起步!</p><p><a href="https://gpt.oknice.ca/"><img lazyload src="/images/loading.svg" data-src="https://github.com/EvanNotFound/hexo-theme-redefine/assets/68590232/55346629-cd54-45a4-9b31-3f979750b0c0" alt="GPT Billboard" ></a></p><h3 id="🌟-Star-History"><a href="#🌟-Star-History" class="headerlink" title="🌟 Star History"></a>🌟 Star History</h3><p align="center"><a href="https://star-history.com/#EvanNotFound/hexo-theme-redefine&Date"> <picture> <source media="(prefers-color-scheme: dark)" srcset="https://api.star-history.com/svg?repos=EvanNotFound/hexo-theme-redefine&type=Date&theme=dark" /> <source media="(prefers-color-scheme: light)" srcset="https://api.star-history.com/svg?repos=EvanNotFound/hexo-theme-redefine&type=Date" /> <img alt="Star History Chart" lazyload src="/images/loading.svg" data-src="https://api.star-history.com/svg?repos=EvanNotFound/hexo-theme-redefine&type=Date" > </picture></a></p><h3 id="💻-Development"><a href="#💻-Development" class="headerlink" title="💻 Development"></a>💻 Development</h3><p>If you want to contribute to this project, you can clone the <code>dev</code> branch and check out the <a class="link" href="https://redefine-docs.ohevan.com/developer" >Development Docs<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a> to get started.</p><h2 id="进阶优化"><a href="#进阶优化" class="headerlink" title="进阶优化"></a>进阶优化</h2><h3 id="新增文章时自动打开Markdown编辑器"><a href="#新增文章时自动打开Markdown编辑器" class="headerlink" title="新增文章时自动打开Markdown编辑器"></a>新增文章时自动打开Markdown编辑器</h3><blockquote><p>需求:由于每次在使用 <code>hexo n "文章名称"</code> 时还要去打开编辑器,这太麻烦了!我们可以通过一个监听的<code> js</code>代码去监听<code>hexo</code>新建文章的命令,并自动打开相应的 Markdown编辑器来实现联动,这样岂不是即方便且优雅!</p></blockquote><ol><li><p>首先在 <code>hexo/scripts</code> 下新建一个 <code>editArticle.js</code> 文件,如果没有 <code>scripts</code> 文件可以手动创建一个。</p></li><li><p>在<code>editArticle.js</code> 文件并写入如下内容即可</p></li></ol> <div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> { spawn } = <span class="built_in">require</span>(<span class="string">"child_process"</span>);</span><br><span class="line"><span class="keyword">const</span> os = <span class="built_in">require</span>(<span class="string">"os"</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 新建文档时,启动typora打开文章</span></span><br><span class="line">hexo.<span class="title function_">on</span>(<span class="string">"new"</span>, <span class="function">(<span class="params">data</span>) =></span> {</span><br><span class="line"> <span class="keyword">const</span> filename = data.<span class="property">path</span>;</span><br><span class="line"> <span class="keyword">const</span> typoraPath = <span class="title function_">getTyporaPath</span>();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (typoraPath) {</span><br><span class="line"> <span class="title function_">spawn</span>(typoraPath, [filename]);</span><br><span class="line"> }</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 根据当前的操作系统,设置启动应用的路径(编辑器可执行文件的位置,要根据自己实际情况更改)</span></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getTyporaPath</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> platform = os.<span class="title function_">type</span>();</span><br><span class="line"> <span class="keyword">if</span> (platform === <span class="string">"Darwin"</span>) {</span><br><span class="line"> <span class="comment">// MacOS</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"/Applications/Typora.app/Contents/MacOS/Typora"</span>;</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (platform === <span class="string">"Windows_NT"</span>) {</span><br><span class="line"> <span class="comment">// Windows</span></span><br><span class="line"> <span class="keyword">return</span> <span class="string">"D:\\Typora\\Typora.exe"</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(<span class="string">"Unsupported platform:"</span>, platform);</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">null</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">This is the excerpt of the post</summary>
<category term="Hexo" scheme="http://example.com/categories/Hexo/"/>
<category term="Hexo" scheme="http://example.com/tags/Hexo/"/>
</entry>
<entry>
<title>vue项目优化经验总结</title>
<link href="http://example.com/2022/04/16/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/vue%E9%A1%B9%E7%9B%AE%E4%BC%98%E5%8C%96%E7%BB%8F%E9%AA%8C%E6%80%BB%E7%BB%93/"/>
<id>http://example.com/2022/04/16/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/vue%E9%A1%B9%E7%9B%AE%E4%BC%98%E5%8C%96%E7%BB%8F%E9%AA%8C%E6%80%BB%E7%BB%93/</id>
<published>2022-04-16T18:50:24.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>前言:首先可以通过安装 <code>npm i webpack-bundle-analyzer</code>插件,在webpack中引入并配置后,执行<code>npm run build</code>打包构建后,浏览器会自动打开分析结果</p></blockquote><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// vue.config.js</span></span><br><span class="line"><span class="keyword">const</span> { defineConfig } = <span class="built_in">require</span>(<span class="string">'@vue/cli-service'</span>)</span><br><span class="line"><span class="keyword">const</span> { <span class="title class_">BundleAnalyzerPlugin</span> } = <span class="built_in">require</span>(<span class="string">'webpack-bundle-analyzer'</span>)</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title function_">defineConfig</span>({</span><br><span class="line"> <span class="attr">chainWebpack</span>: <span class="function"><span class="params">config</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> === <span class="string">'production'</span>) {</span><br><span class="line"> config.<span class="title function_">plugin</span>(<span class="string">'webpack-report'</span>).<span class="title function_">use</span>(<span class="title class_">BundleAnalyzerPlugin</span>, [{ </span><br><span class="line"> <span class="attr">analyzerMode</span>: <span class="string">'static'</span> }])</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><p><img lazyload src="/images/loading.svg" data-src="/Users/jiangwen/Downloads/_Users_jiangwen_GitHub_admin_dist_report.html%20(1).png" alt="_Users_jiangwen_GitHub_admin_dist_report.html (1)" ></p><p>可以根据得到的分析结果,对项目进行有针对性的优化…</p><h2 id="懒加载"><a href="#懒加载" class="headerlink" title="懒加载"></a>懒加载</h2><ol><li><p>路由懒加载</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> <span class="title class_">VueRouter</span>({</span><br><span class="line"><span class="attr">routes</span>: [</span><br><span class="line">{<span class="attr">path</span>: <span class="string">'/foo'</span>, <span class="attr">component</span>: <span class="function">() =></span> <span class="keyword">import</span>(<span class="string">'./Foo.vue'</span>)}</span><br><span class="line">]</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div></li><li><p>图片懒加载:可以使用 <code>vue-lazyload</code> 插件</p></li></ol><h2 id="缓存优化"><a href="#缓存优化" class="headerlink" title="缓存优化"></a>缓存优化</h2><ol><li>使用<code>keep-alive</code>缓存组件</li></ol><h2 id="打包优化"><a href="#打包优化" class="headerlink" title="打包优化"></a>打包优化</h2><ol><li>按需引入UI组件库</li><li>开启<code>gzip</code>打包压缩</li><li>关闭源码映射,不生成<code>sourcemap</code>文件</li></ol><h2 id="首屏优化"><a href="#首屏优化" class="headerlink" title="首屏优化"></a>首屏优化</h2><ol><li><p>数据加载使用loding骨架屏</p></li><li><p>采用<strong>SSR</strong>服务端渲染</p></li></ol><h2 id="代码层面的优化"><a href="#代码层面的优化" class="headerlink" title="代码层面的优化"></a>代码层面的优化</h2><ol><li><p>避免<code>v-if</code> 和 <code>v-for</code> 同级使用,在vue2 中 v-for 的 优先级比 v-if 高,容易导致渲染错误</p></li><li><p>使用<code>v-for</code>时设置key的值,并且使用数据中的唯一标识,尽量不使用index作为标识,有利于dom定位和diff算法</p></li><li><p>根据场景合理选择使用<code>v-if</code> 或 <code>v-show</code></p></li><li><p>将无状态的<strong>组件标记为函数式组件</strong>(静态组件)</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><!-- 只接收父组件传过来的值,自己不做处理,无状态,不创建实例 --></span><br><span class="line"><template></span><br><span class="line"> <div class="page"></span><br><span class="line"> <span v-if='prop.show'>1<span></span><br><span class="line"> <span v-else>2<span></span><br><span class="line"> </div></span><br><span class="line"></template></span><br><span class="line"></span><br><span class="line"><script></span><br><span class="line">export default{</span><br><span class="line"> props: ['show']</span><br><span class="line"> }</span><br><span class="line"></script></span><br></pre></td></tr></table></figure></div></li><li><p><strong>事件销毁</strong>:Vue组件在销毁时,会自动解绑它的全部指令事件及监听器,但仅限于组件本身。<br>比如定时器等最好在销毁阶段手动销毁,避免内存泄漏</p></li><li><p><strong>子组件分割</strong>:将子组件中耗时的任务交给组件自己管理,不影响整体页面的加载</p></li><li><p><strong>变量本地化</strong>:减少使用 <code>this.xxx</code> 的形式获取数据,可以用一个变量先获取数据,然后在这个变量上处理数据。</p></li><li><p><strong>模块化、组件化</strong></p></li><li><p>长列表优化(插件:vue-virtual-scroller )</p><ol><li>纯粹做数据展示,不需要热更新的场景🎬:处于data中的数据会被监视,发生变化时数据就发生变化<br>所以采用object.freeze(数据)方法冻结数据。</li><li>采用虚拟滚动,只渲染视口部分的数据,也就是说渲染固定的DOM节点个数</li></ol></li></ol>]]></content>
<summary type="html"><blockquote>
<p>前言:首先可以通过安装 <code>npm i webpack-bundle-analyzer</code>插件,在webpack中引入并配置后,执行<code>npm run build</code>打包构建后,浏览器会自动打开分析结果 </p></summary>
<category term="开发" scheme="http://example.com/categories/%E5%BC%80%E5%8F%91/"/>
<category term="优化, gzip" scheme="http://example.com/tags/%E4%BC%98%E5%8C%96%EF%BC%8C-gzip/"/>
</entry>
<entry>
<title>开启gzip压缩</title>
<link href="http://example.com/2022/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%BC%80%E5%90%AFgzip%E5%8E%8B%E7%BC%A9/"/>
<id>http://example.com/2022/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%BC%80%E5%90%AFgzip%E5%8E%8B%E7%BC%A9/</id>
<published>2022-03-06T18:50:24.000Z</published>
<updated>2024-11-24T17:45:51.391Z</updated>
<content type="html"><![CDATA[<ol><li><p>首先安装插件 <code>npm i compression-webpack-plugin@10.0.0 </code></p></li><li><p>添加 vue.config.js 项目配置</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> { defineConfig } = <span class="built_in">require</span>(<span class="string">'@vue/cli-service'</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">CompressionPlugin</span> = <span class="built_in">require</span>(<span class="string">"compression-webpack-plugin"</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title function_">defineConfig</span>({</span><br><span class="line"> <span class="attr">configureWebpack</span>: <span class="function"><span class="params">config</span> =></span> {</span><br><span class="line"> <span class="comment">// 开发环境不配置</span></span><br><span class="line"> <span class="keyword">if</span> (process.<span class="property">env</span>.<span class="property">NODE_ENV</span> !== <span class="string">'production'</span>) <span class="keyword">return</span></span><br><span class="line"> <span class="comment">// 生产环境才去配置</span></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="attr">plugins</span>: [</span><br><span class="line"> <span class="keyword">new</span> <span class="title class_">CompressionPlugin</span>({</span><br><span class="line"> <span class="comment">// filename: "[path][base].gz", // 这种方式是默认的,多个文件压缩就有多个.gz文件,建议使用下方的写法</span></span><br><span class="line"> <span class="attr">filename</span>: <span class="string">'[path][base].gz[query]'</span>, <span class="comment">// 使得多个.gz文件合并成一个文件,这种方式压缩后的文件少,建议使用</span></span><br><span class="line"> <span class="attr">algorithm</span>: <span class="string">'gzip'</span>, <span class="comment">// 官方默认压缩算法也是gzip</span></span><br><span class="line"> <span class="attr">test</span>: <span class="regexp">/\.js$|\.css$|\.html$|\.ttf$|\.eot$|\.woff$/</span>, <span class="comment">// 使用正则给匹配到的文件做压缩</span></span><br><span class="line"> <span class="attr">threshold</span>: <span class="number">10240</span>, <span class="comment">//以字节为单位压缩超过此大小的文件,使用默认值10240吧</span></span><br><span class="line"> <span class="attr">minRatio</span>: <span class="number">0.8</span>, <span class="comment">// 最小压缩比率,官方默认0.8</span></span><br><span class="line"> <span class="comment">//是否删除原有静态资源文件,即只保留压缩后的.gz文件,建议这个置为false,还保留源文件。以防:</span></span><br><span class="line"> <span class="comment">// 假如出现访问.gz文件访问不到的时候,还可以访问源文件双重保障</span></span><br><span class="line"> <span class="attr">deleteOriginalAssets</span>: <span class="literal">false</span></span><br><span class="line"> })</span><br><span class="line"> ]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div></li><li><p>项目执行 <code>npm run build</code> 打包构建,目录dist/js 文件就会生成 xxx.js.gz 文件。说明压缩成功</p></li><li><p>配置 nginx 服务开启 gzip</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 80;</span><br><span class="line"> listen 443 ssl http2;</span><br><span class="line"> server_name xxx.com;</span><br><span class="line"> </span><br><span class="line"> location ~* \.(css|js)$ {</span><br><span class="line"> gzip_static on;</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> gzip on;</span><br><span class="line"> gzip_min_length 1k;</span><br><span class="line"> gzip_comp_level 9;</span><br><span class="line"> gzip_types text/plain application/javascript application/x-javascript text/css application/xml text/javascript application/x-httpd-php image/jpeg image/gif image/png;</span><br><span class="line"> gzip_vary on;</span><br><span class="line"> gzip_disable "MSIE [1-6]\.";</span><br><span class="line"> </span><br><span class="line"> #....其他配置</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li><li><p>在浏览器中查看配置成功后的状态,项目体积减少,页面加载速度得到明显提升</p></li></ol><p><img lazyload src="/images/loading.svg" data-src="/Users/jiangwen/Library/Application%20Support/typora-user-images/image-20230504211812296.png" alt="image-20230504211812296" ></p>]]></content>
<summary type="html"><ol>
<li><p>首先安装插件 <code>npm i compression-webpack-plugin@10.0.0 </code></p>
</li>
<li><p>添加 vue.config.js 项目配置</p>
<div class="highlight-co</summary>
<category term="开发" scheme="http://example.com/categories/%E5%BC%80%E5%8F%91/"/>
<category term="优化, gzip" scheme="http://example.com/tags/%E4%BC%98%E5%8C%96%EF%BC%8C-gzip/"/>
</entry>
<entry>
<title>实现移动端Retina屏幕1px边框</title>
<link href="http://example.com/2021/11/18/CSS/%E5%AE%9E%E7%8E%B0%E7%A7%BB%E5%8A%A8%E7%AB%AFRetina%E5%B1%8F%E5%B9%951px%E8%BE%B9%E6%A1%86/"/>
<id>http://example.com/2021/11/18/CSS/%E5%AE%9E%E7%8E%B0%E7%A7%BB%E5%8A%A8%E7%AB%AFRetina%E5%B1%8F%E5%B9%951px%E8%BE%B9%E6%A1%86/</id>
<published>2021-11-18T11:25:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<blockquote><p>前言:在Reina(视网膜)屏幕的手机上,使用CSS设置的1px的边框实际会比视觉稿粗很多,因为不同的手机有不同的像素密度,因此在css中的1px并不等于移动设备的1px。在window对象中有一个<code>devicePixelRatio</code>属性(物理像素 / 设备独立像素),他可以反应css中的像素与设备的像素比。</p></blockquote><h2 id="使用background-image-x2F-border-image实现"><a href="#使用background-image-x2F-border-image实现" class="headerlink" title="使用background-image/border-image实现"></a>使用background-image/border-image实现</h2><p>你要先准备一张符合你要求的图片。然后将边框模拟在背景上。</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/* background-image方案 */</span></span><br><span class="line"><span class="selector-class">.border-bottom-1px</span> {</span><br><span class="line"> <span class="attribute">border-width</span>: <span class="number">0</span> <span class="number">0</span> <span class="number">1px</span> <span class="number">0</span>;</span><br><span class="line"> -webkit-<span class="attribute">border-image</span>: <span class="built_in">url</span>(<span class="string">linenew.png</span>) <span class="number">0</span> <span class="number">0</span> <span class="number">2</span> <span class="number">0</span> stretch;</span><br><span class="line"> <span class="attribute">border-image</span>: <span class="built_in">url</span>(<span class="string">linenew.png</span>) <span class="number">0</span> <span class="number">0</span> <span class="number">2</span> <span class="number">0</span> stretch;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/* border-image方案 */</span></span><br><span class="line"><span class="selector-class">.background-image-1px</span> {</span><br><span class="line"> <span class="attribute">background</span>: <span class="built_in">url</span>(<span class="string">../img/line.png</span>) repeat-x left bottom;</span><br><span class="line"> -webkit-<span class="attribute">background-size</span>: <span class="number">100%</span> <span class="number">1px</span>;</span><br><span class="line"> <span class="attribute">background-size</span>: <span class="number">100%</span> <span class="number">1px</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>优点:</strong></p><ul><li><p>可以设置单条,多条边框</p></li><li><p>没有性能瓶颈的问题</p></li></ul><p><strong>缺点:</strong></p><ul><li><p>修改颜色麻烦, 需要替换图片</p></li><li><p>圆角需要特殊处理,并且边缘会模糊</p></li></ul><h2 id="多背景渐变实现"><a href="#多背景渐变实现" class="headerlink" title="多背景渐变实现"></a>多背景渐变实现</h2><p>与background-image方案类似,只是将图片替换为css3渐变。设置1px的渐变背景,50%有颜色,50%透明。</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.background-gradient-1px</span> {</span><br><span class="line"> <span class="attribute">background</span>:</span><br><span class="line"> <span class="built_in">linear-gradient</span>(<span class="number">#000</span>, <span class="number">#000</span> <span class="number">100%</span>, transparent <span class="number">100%</span>) left / <span class="number">1px</span> <span class="number">100%</span> no-repeat,</span><br><span class="line"> <span class="built_in">linear-gradient</span>(<span class="number">#000</span>, <span class="number">#000</span> <span class="number">100%</span>, transparent <span class="number">100%</span>) right / <span class="number">1px</span> <span class="number">100%</span> no-repeat,</span><br><span class="line"> <span class="built_in">linear-gradient</span>(<span class="number">#000</span>,<span class="number">#000</span> <span class="number">100%</span>, transparent <span class="number">100%</span>) top / <span class="number">100%</span> <span class="number">1px</span> no-repeat,</span><br><span class="line"> <span class="built_in">linear-gradient</span>(<span class="number">#000</span>,<span class="number">#000</span> <span class="number">100%</span>, transparent <span class="number">100%</span>) bottom / <span class="number">100%</span> <span class="number">1px</span> no-repeat</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>优点:</strong></p><ul><li><p>可以实现单条、多条边框</p></li><li><p>边框的颜色随意设置</p></li></ul><p><strong>缺点:</strong></p><ul><li><p>代码量不少</p></li><li><p>圆角没法实现</p></li><li><p>多背景图片有兼容性问题</p></li></ul><h2 id="使用box-shadow模拟边框"><a href="#使用box-shadow模拟边框" class="headerlink" title="使用box-shadow模拟边框"></a>使用box-shadow模拟边框</h2><p>利用css 对阴影处理的方式实现0.5px的效果</p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.box-shadow-1px</span> {</span><br><span class="line"> <span class="attribute">box-shadow</span>: inset <span class="number">0px</span> -<span class="number">1px</span> <span class="number">1px</span> -<span class="number">1px</span> <span class="number">#c8c7cc</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>优点:</strong></p><ul><li><p>代码量少</p></li><li><p>所有场景都能满足</p></li></ul><p><strong>缺点:</strong></p><ul><li>边框有阴影,颜色变浅</li></ul><h2 id="viewport-rem-实现"><a href="#viewport-rem-实现" class="headerlink" title="viewport + rem 实现"></a>viewport + rem 实现</h2><p>同时通过设置对应viewport的rem基准值,这种方式就可以像以前一样轻松愉快的写1px了。</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment"><!-- 在devicePixelRatio = 2 时,输出viewport:--></span></span><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=0.5, maximum-scale=0.5, minimum-scale=0.5, user-scalable=no"</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="comment"><!-- 在devicePixelRatio = 3 时,输出viewport:--></span></span><br><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"viewport"</span> <span class="attr">content</span>=<span class="string">"initial-scale=0.33, maximum-scale=0.33, minimum-scale=0.33, user-scalable=no"</span>></span></span><br></pre></td></tr></table></figure></div><p>这种兼容方案相对比较完美,<strong>适合新的项目</strong>,老的项目修改成本过大。可以看看《使用Flexible实现手淘H5页面的终端适配》</p><p><strong>优点:</strong></p><ul><li><p>所有场景都能满足</p></li><li><p>一套代码,可以兼容基本所有布局</p></li></ul><p><strong>缺点:</strong></p><ul><li>老项目修改代价过大,只适用于新项目</li></ul><h2 id="伪类-transform-实现(推荐)"><a href="#伪类-transform-实现(推荐)" class="headerlink" title="伪类 + transform 实现(推荐)"></a>伪类 + transform 实现(推荐)</h2><p>对于老项目,有没有什么办法能兼容1px的尴尬问题了,个人认为伪类+transform是比较完美的方法了。<br>原理是把原先元素的 border 去掉,然后利用 :before 或者 :after 重做 border ,并 transform 的 scale 缩小一半,原先的元素相对定位,新做的 border 绝对定位。</p><p><strong>单条border样式设置:</strong></p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><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><span class="line"><span class="selector-class">.scale-1px</span>{</span><br><span class="line"></span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">border</span>:none;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.scale-1px</span><span class="selector-pseudo">:after</span>{</span><br><span class="line"></span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">bottom</span>: <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">background</span>: <span class="number">#000</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">height</span>: <span class="number">1px</span>;</span><br><span class="line"></span><br><span class="line"> -webkit-<span class="attribute">transform</span>: <span class="built_in">scaleY</span>(<span class="number">0.5</span>);</span><br><span class="line"></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scaleY</span>(<span class="number">0.5</span>);</span><br><span class="line"></span><br><span class="line"> -webkit-<span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">transform-origin</span>: <span class="number">0</span> <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>四条boder样式设置:</strong></p><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.scale-1px</span>{</span><br><span class="line"></span><br><span class="line"> <span class="attribute">position</span>: relative;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">margin-bottom</span>: <span class="number">20px</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">border</span>:none;</span><br><span class="line"></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.scale-1px</span><span class="selector-pseudo">:after</span>{</span><br><span class="line"></span><br><span class="line"> <span class="attribute">content</span>: <span class="string">''</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">position</span>: absolute;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">top</span>: <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">left</span>: <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">border</span>: <span class="number">1px</span> solid <span class="number">#000</span>;</span><br><span class="line"></span><br><span class="line"> -webkit-<span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">box-sizing</span>: border-box;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">width</span>: <span class="number">200%</span>;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">height</span>: <span class="number">200%</span>;</span><br><span class="line"></span><br><span class="line"> -webkit-<span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">0.5</span>);</span><br><span class="line"></span><br><span class="line"> <span class="attribute">transform</span>: <span class="built_in">scale</span>(<span class="number">0.5</span>);</span><br><span class="line"></span><br><span class="line"> -webkit-<span class="attribute">transform-origin</span>: left top;</span><br><span class="line"></span><br><span class="line"> <span class="attribute">transform-origin</span>: left top;</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>最好在使用前也判断一下,结合 JS 代码,判断是否 Retina 屏:</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span>(<span class="variable language_">window</span>.<span class="property">devicePixelRatio</span> && devicePixelRatio >= <span class="number">2</span>){</span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">querySelector</span>(<span class="string">'ul'</span>).<span class="property">className</span> = <span class="string">'scale-1px'</span>;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p><strong>优点:</strong></p><ul><li><p>所有场景都能满足</p></li><li><p>支持圆角(伪类和本体类都需要加border-radius)</p></li></ul><p><strong>缺点:</strong></p><ul><li>对于已经使用伪类的元素(例如clearfix),可能需要多层嵌套</li></ul><p>🔗: <a class="link" href="https://mp.weixin.qq.com/s?__biz=MjM5MDA2MTI1MA==&mid=2649088476&idx=3&sn=44893ca9980310c02a8b1b63f2145fd5&chksm=be5bc671892c4f6791cd6a60dbcd72918c682cf5e5cf36baf421e9829d7f48014f92cc50fc2a&scene=27" >参考链接<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a></p>]]></content>
<summary type="html">false</summary>
<category term="CSS" scheme="http://example.com/categories/CSS/"/>
<category term="移动端适配" scheme="http://example.com/tags/%E7%A7%BB%E5%8A%A8%E7%AB%AF%E9%80%82%E9%85%8D/"/>
<category term="H5" scheme="http://example.com/tags/H5/"/>
</entry>
<entry>
<title>等高布局的几种实现方式</title>
<link href="http://example.com/2021/11/18/CSS/%E7%AD%89%E9%AB%98%E5%B8%83%E5%B1%80/"/>
<id>http://example.com/2021/11/18/CSS/%E7%AD%89%E9%AB%98%E5%B8%83%E5%B1%80/</id>
<published>2021-11-18T11:25:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<h2 id="table"><a href="#table" class="headerlink" title="table"></a>table</h2><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">display</span>: table;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.left</span> {</span><br><span class="line"><span class="attribute">display</span>: table-cell;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">30%</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="flex"><a href="#flex" class="headerlink" title="flex"></a>flex</h2><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"> <span class="selector-class">.container</span> {</span><br><span class="line"><span class="attribute">display</span>: flex;</span><br><span class="line"> <span class="attribute">flex-direction</span>: row;</span><br><span class="line">}</span><br><span class="line"></span><br></pre></td></tr></table></figure></div><h2 id="grid"><a href="#grid" class="headerlink" title="grid"></a>grid</h2><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">display</span>: grid;</span><br><span class="line"> <span class="attribute">grid-auto-flow</span>: column;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="负maigin"><a href="#负maigin" class="headerlink" title="负maigin"></a>负maigin</h2><div class="highlight-container" data-rel="Css"><figure class="iseeu highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-class">.container</span> {</span><br><span class="line"> <span class="attribute">overflow</span>: hidden;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="selector-class">.left</span> {</span><br><span class="line"><span class="attribute">margin</span>: -<span class="number">99999px</span></span><br><span class="line">padding: <span class="number">99999px</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">false</summary>
<category term="CSS" scheme="http://example.com/categories/CSS/"/>
<category term="H5" scheme="http://example.com/tags/H5/"/>
<category term="布局" scheme="http://example.com/tags/%E5%B8%83%E5%B1%80/"/>
</entry>
<entry>
<title>10万级数据渲染优化方案</title>
<link href="http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/10%E4%B8%87%E7%BA%A7%E6%95%B0%E6%8D%AE%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/"/>
<id>http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/10%E4%B8%87%E7%BA%A7%E6%95%B0%E6%8D%AE%E6%B8%B2%E6%9F%93%E4%BC%98%E5%8C%96%E6%96%B9%E6%A1%88/</id>
<published>2021-11-18T11:25:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<p>假设我们需要在页面上加载获取到的10万条数据📊,在不采取优化方案的情况下它的耗时为 482 ms</p><div class="highlight-container" data-rel="Html"><figure class="iseeu highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><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></pre></td><td class="code"><pre><span class="line"> <span class="tag"><<span class="name">div</span> <span class="attr">id</span>=<span class="string">"container"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br><span class="line"></span><br><span class="line"> <span class="tag"><<span class="name">script</span>></span><span class="language-javascript"></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">'container'</span>)</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> <span class="title function_">getList</span> = (<span class="params"></span>) => {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title class_">Promise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">resolve</span>()</span></span><br><span class="line"><span class="language-javascript"> }, <span class="number">200</span>);</span></span><br><span class="line"><span class="language-javascript"> }).<span class="title function_">then</span>(<span class="function">() =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">let</span> arr = []</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < <span class="number">100000</span>; i++) {</span></span><br><span class="line"><span class="language-javascript"> arr[i] = { <span class="attr">id</span>: i, <span class="attr">text</span>: i }</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">return</span> arr</span></span><br><span class="line"><span class="language-javascript"> })</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"> <span class="comment">// 不优化情况:</span></span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> <span class="title function_">renderList</span> = (<span class="params"></span>) => {</span></span><br><span class="line"><span class="language-javascript"> <span class="title function_">getList</span>().<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">time</span>(<span class="string">'不优化'</span>)</span></span><br><span class="line"><span class="language-javascript"> res.<span class="title function_">forEach</span>(<span class="function"><span class="params">item</span> =></span> {</span></span><br><span class="line"><span class="language-javascript"> <span class="keyword">const</span> div = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'div'</span>)</span></span><br><span class="line"><span class="language-javascript"> div.<span class="property">innerHTML</span> = <span class="string">`<span><span class="subst">${item.text}</span></span>`</span></span></span><br><span class="line"><span class="language-javascript"> container.<span class="title function_">appendChild</span>(div)</span></span><br><span class="line"><span class="language-javascript"> });</span></span><br><span class="line"><span class="language-javascript"> <span class="variable language_">console</span>.<span class="title function_">timeEnd</span>(<span class="string">'不优化'</span>)</span></span><br><span class="line"><span class="language-javascript"> })</span></span><br><span class="line"><span class="language-javascript"> }</span></span><br><span class="line"><span class="language-javascript"></span><span class="tag"></<span class="name">script</span>></span></span><br></pre></td></tr></table></figure></div><p>如果使用了<strong>分页优化</strong>后的耗时为212 ms</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 优化情况(分页 + requestAnimationFrame + createDocumentFragment)</span></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">renderList2</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">time</span>(<span class="string">'分页优化'</span>)</span><br><span class="line"> <span class="title function_">getList</span>().<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"> <span class="keyword">const</span> total = res.<span class="property">length</span> <span class="comment">// 总条数</span></span><br><span class="line"> <span class="keyword">const</span> limit = <span class="number">10000</span> <span class="comment">// 每页数量</span></span><br><span class="line"> <span class="keyword">const</span> page = <span class="number">0</span> <span class="comment">// 起始页</span></span><br><span class="line"> <span class="keyword">const</span> totalPage = <span class="title class_">Math</span>.<span class="title function_">ceil</span>(total / limit)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">render</span> = (<span class="params">page</span>) => {</span><br><span class="line"> <span class="keyword">if</span>(totalPage<= page)<span class="keyword">return</span></span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(<span class="function">()=></span>{ <span class="comment">// requestAnimationFrame减少重排</span></span><br><span class="line"> <span class="keyword">const</span> fragment = <span class="variable language_">document</span>.<span class="title function_">createDocumentFragment</span>() <span class="comment">// 文档碎片</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = page * limit; i < page * limit + limit; i++) {</span><br><span class="line"> <span class="keyword">const</span> item = res[i];</span><br><span class="line"> <span class="keyword">const</span> div = <span class="variable language_">document</span>.<span class="title function_">createElement</span>(<span class="string">'div'</span>)</span><br><span class="line"> div.<span class="property">innerHTML</span> = <span class="string">`<span><span class="subst">${ item.text }</span></span>`</span></span><br><span class="line"> fragment.<span class="title function_">appendChild</span>(div)</span><br><span class="line"> }</span><br><span class="line"> container.<span class="title function_">appendChild</span>(fragment)</span><br><span class="line"> <span class="title function_">render</span>(page + <span class="number">1</span>)</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">render</span>(page)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">timeEnd</span>(<span class="string">'分页优化'</span>)</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><p>如果dom结构更复杂,差距将更大</p>]]></content>
<summary type="html">false</summary>
<category term="CSS" scheme="http://example.com/categories/CSS/"/>
<category term="移动端" scheme="http://example.com/tags/%E7%A7%BB%E5%8A%A8%E7%AB%AF/"/>
<category term="兼容" scheme="http://example.com/tags/%E5%85%BC%E5%AE%B9/"/>
</entry>
<entry>
<title>后台管理系统的权限控制与管理</title>
<link href="http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E7%9A%84%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6%E4%B8%8E%E7%AE%A1%E7%90%86/"/>
<id>http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%90%8E%E5%8F%B0%E7%AE%A1%E7%90%86%E7%B3%BB%E7%BB%9F%E7%9A%84%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6%E4%B8%8E%E7%AE%A1%E7%90%86/</id>
<published>2021-11-18T11:25:00.000Z</published>
<updated>2024-11-24T17:45:51.391Z</updated>
<content type="html"><![CDATA[<blockquote><p>前言: 前端权限的控制本质上来说, 就是控制端的<strong>视图层的展示</strong>和前端所发送的<strong>请求</strong>。但是只有前端权限控制没有后端权限控制是万万不可的。 前端权限控制只可以说是达到锦上添花的效果。</p></blockquote><h2 id="前端权限的意义"><a href="#前端权限的意义" class="headerlink" title="前端权限的意义"></a>前端权限的意义</h2><p>进行前端权限的控制, 主要有这几方面的好处</p><ul><li>降低非法操作的可能性</li><li>尽可能排除不必要清求, 减轻服务器压力</li><li>提高用户体验</li></ul><h2 id="前端权限控制思路"><a href="#前端权限控制思路" class="headerlink" title="前端权限控制思路"></a>前端权限控制思路</h2><h3 id="菜单的控制"><a href="#菜单的控制" class="headerlink" title="菜单的控制"></a>菜单的控制</h3><p>在登录请求中, 会得到权限数据, 当然, 这个需要后端返回数据的支持. 前端根据权限数据, 展示对应的菜单. 点击菜单, 才能查看相关的界面</p><h3 id="界面的控制"><a href="#界面的控制" class="headerlink" title="界面的控制"></a>界面的控制</h3><p>如果用户没有登录, 手动在地址栏敲入管理界面的地址, 则需要跳转到登录界面<br>如果用户已经登录, 如果手动敲入非权限内的地址, 则需要跳转404 界面</p><h3 id="按钮的控制"><a href="#按钮的控制" class="headerlink" title="按钮的控制"></a>按钮的控制</h3><p>在某个菜单的界面中, 还得根据权限数据, 展示出可进行操作的按钮,比如删除, 修改, 增加</p><h3 id="请求和响应的控制"><a href="#请求和响应的控制" class="headerlink" title="请求和响应的控制"></a>请求和响应的控制</h3><p>如果用户通过非常规操作, 比如通过浏览器调试工具将某些禁用的按钮变成启用状态, 此时发的请求, 也应该被前端所拦截</p><h2 id="三、实现步骤"><a href="#三、实现步骤" class="headerlink" title="三、实现步骤"></a>三、实现步骤</h2><h3 id="3-1-权限菜单栏控制"><a href="#3-1-权限菜单栏控制" class="headerlink" title="3.1 权限菜单栏控制"></a>3.1 权限菜单栏控制</h3><p>用户登录之后服务端返回一个数据,这个数据有菜单列表和<code>token</code>,我们把这个数据放入到<code>vuex</code>中,然后主页根据<code>vuex</code>中的数据进行菜单列表的渲染</p><p><strong>问题:</strong> 刷新界面<code>vuex</code>数据消失,菜单栏消失</p><p><strong>解决:</strong> 将数据存储在<code>sessionStorage</code>中,并让其和<code>vuex</code>中的数据保持同步</p><h3 id="3-2-界面的控制"><a href="#3-2-界面的控制" class="headerlink" title="3.2 界面的控制"></a>3.2 界面的控制</h3><p>登录成功后,将<code>token</code>数据存储在<code>sessionStorage</code>中,判断是否登录</p><h3 id="1-路由导航守卫"><a href="#1-路由导航守卫" class="headerlink" title="1. 路由导航守卫"></a>1. 路由导航守卫</h3><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">router.beforeEach((to, from, next) => {</span><br><span class="line">if(to.path !== '/login') {</span><br><span class="line">const token = sessionStorage.getItem('tokem')</span><br><span class="line">if(!toke){</span><br><span class="line">next('/login')</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">next()</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><p><strong>问题:</strong> 这样用户在登录之后就可以访问其他界面了,但如果用户<code>A</code>登录之后他只能访问<code>a</code>页面,他不能访问<code>b</code>页面,但是这时候他还是可以通过地址栏输入进入到<code>b</code>页面</p><p><strong>解决:</strong> 当然我们也可以设置路由导航守卫,但是如果有多个页面,设置会非常不方便,并且对于用户<code>A</code>来说,它是不用访问<code>b</code>页面的,这时候我们何不对<code>A</code>不显示<code>b</code>页面,这个时候我们就用到了<code>动态路由</code></p><h3 id="2-动态路由"><a href="#2-动态路由" class="headerlink" title="2. 动态路由"></a>2. 动态路由</h3><p>根据当前用户所拥有的的权限数据来动态添加所需要的路由</p><ol><li><p>先定义好所有的路由规则</p></li><li><p>登录成功之后动态添加路由,注意这个<code>initDynamicRoutes</code>的方法需要暴露出去在登录页面调用</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">export</span> <span class="keyword">function</span> <span class="title function_">initDynamicRoutes</span>(<span class="params"></span>){</span><br><span class="line"> <span class="comment">// 根据二级权限, 对路由规则进行动态添加</span></span><br><span class="line"><span class="keyword">const</span> currentRoutes = router.<span class="property">options</span>.<span class="property">routes</span> <span class="comment">// 获取当前路由</span></span><br><span class="line"><span class="keyword">const</span> rightList = store.<span class="property">state</span>.<span class="property">rightList</span> <span class="comment">// 拥有权限的列表</span></span><br><span class="line">rightList.<span class="title function_">forEach</span>(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line">item.<span class="property">children</span>.<span class="title function_">forEach</span>(<span class="function"><span class="params">item</span> =></span> {</span><br><span class="line">cinst temp = ruleMapping[item.<span class="property">path</span>]</span><br><span class="line">temp.<span class="property">meta</span> = item.<span class="property">rights</span> <span class="comment">// 设置路由元信息</span></span><br><span class="line">currentRouters[<span class="number">2</span>].<span class="property">children</span>.<span class="title function_">push</span>(temp) <span class="comment">// 添加动态路由</span></span><br><span class="line">})</span><br><span class="line">})</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div></li></ol><p>这样当用户A在地址栏输入自己不能访问的路由时,则不会跳转到该页面,跳转到404页面</p><p><strong>问题:</strong> 如果我们重新刷新的话动态路由就会消失,动态路由是在登录成功之后才会调用的,刷新的时候并没有调用,所以动态路由没有添加上</p><p><strong>解决:</strong> 可以在<code>app.vue</code>中的<code>created中</code>调用添加动态路由的方法</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">export default {</span><br><span class="line">name: 'app',</span><br><span class="line">created() {</span><br><span class="line">initDynamicRoutes()</span><br><span class="line">}</span><br><span class="line">}</span><br></pre></td></tr></table></figure></div><h2 id="3-3-按钮的控制"><a href="#3-3-按钮的控制" class="headerlink" title="3.3 按钮的控制"></a>3.3 按钮的控制</h2><p>虽然用户可以看到某些界面了, 但是这个界面的一些按钮该用户可能是没有权限的。 因此, 我们需要对组件中的一些按钮进行控制, 用户不具备权限的按钮就隐藏或者禁用, 而在这块的实现中, 可以把该逻辑放到自定义指令中,比如我们可以根据后端返回的数据<code>right</code>来判断用户有什么权限</p><div class="highlight-container" data-rel="Json"><figure class="iseeu highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"> <span class="comment">// => rights: ['view']</span></span><br><span class="line"><span class="punctuation">{</span></span><br><span class="line">id<span class="punctuation">:</span> <span class="number">1001</span><span class="punctuation">,</span></span><br><span class="line"> name<span class="punctuation">:</span> '商品管理',</span><br><span class="line"> children<span class="punctuation">:</span> <span class="punctuation">[</span></span><br><span class="line"> <span class="punctuation">{</span></span><br><span class="line"> id<span class="punctuation">:</span> <span class="number">101</span></span><br><span class="line"> name<span class="punctuation">:</span> '商品列表'</span><br><span class="line"> path: 'goods'</span><br><span class="line"> rights<span class="punctuation">:</span> <span class="punctuation">[</span>'view'<span class="punctuation">,</span> 'add'<span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br><span class="line"> <span class="punctuation">]</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure></div><p>创建自定义指令:permission.js</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Vue</span> <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> router <span class="keyword">from</span> <span class="string">'@/router.js'</span></span><br><span class="line"></span><br><span class="line"><span class="title class_">Vue</span>.<span class="title function_">directive</span>(<span class="string">'permission'</span>, {</span><br><span class="line"><span class="attr">inserted</span>: <span class="keyword">function</span>(<span class="params">el, binding</span>) {</span><br><span class="line"><span class="keyword">const</span> action = binding.<span class="property">value</span>.<span class="property">action</span> <span class="comment">// 对应自定义指令中的add</span></span><br><span class="line"><span class="keyword">const</span> currentRight = router.<span class="property">currentRoute</span>.<span class="property">meta</span> <span class="comment">//获取当前路由元信息</span></span><br><span class="line"><span class="keyword">if</span>(currentRight && currentRight.<span class="title function_">indexOf</span>(action) == -<span class="number">1</span>) { <span class="comment">// 判断当前路由对应的组件中,用户是否具备action的权限</span></span><br><span class="line"><span class="comment">// 没有权限</span></span><br><span class="line"><span class="keyword">const</span> type = binding.<span class="property">value</span>.<span class="property">effect</span></span><br><span class="line"><span class="comment">// 根据类型参数 type 对元素进行禁用或移除操作</span></span><br><span class="line"><span class="keyword">if</span>(type === <span class="string">'disabled'</span>) { </span><br><span class="line">el.<span class="property">disabled</span> = <span class="literal">true</span></span><br><span class="line">el.<span class="property">classList</span>.<span class="title function_">add</span>(<span class="string">'is-disabled'</span>)</span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line">el.<span class="property">parentNode</span>.<span class="title function_">removeChild</span>(ed)</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><p>添加自定义指令 控制按钮</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><el-button</span><br><span class="line"> type='primary'</span><br><span class="line"> v-permission = '{action: "add", effect: "disabled"}'</span><br><span class="line">> 添加用户</span><br><span class="line"></el-button></span><br></pre></td></tr></table></figure></div><h2 id="3-4-请求和响应的控制"><a href="#3-4-请求和响应的控制" class="headerlink" title="3.4 请求和响应的控制"></a>3.4 请求和响应的控制</h2><h3 id="请求控制"><a href="#请求控制" class="headerlink" title="请求控制"></a>请求控制</h3><p>如果发出了非权限内的请求, 应该直接在前端范围内阻止:比如a用户是不能够操作该页面的按钮的,但是他通过f12调试把按钮改为可点击,如果我们不对这个请求进行处理,那么这个请求就会发送出去</p> <div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> axios <span class="keyword">from</span> <span class="string">'axios'</span></span><br><span class="line"><span class="keyword">import</span> <span class="title class_">Vue</span> <span class="keyword">from</span> <span class="string">'vue'</span></span><br><span class="line"><span class="keyword">import</span> router <span class="keyword">from</span> <span class="string">'../router'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> actionMapping = {</span><br><span class="line"><span class="attr">get</span>: <span class="string">'view'</span>,</span><br><span class="line"><span class="attr">post</span>: <span class="string">'add'</span>,</span><br><span class="line"><span class="attr">put</span>: <span class="string">'edit'</span>,</span><br><span class="line"><span class="attr">delete</span>: <span class="string">'delete'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 请求拦截</span></span><br><span class="line">axios.<span class="property">interceptors</span>.<span class="property">request</span>.<span class="title function_">use</span>(<span class="function"><span class="params">req</span> =></span> {</span><br><span class="line"><span class="keyword">const</span> currentUrl = req.<span class="property">url</span></span><br><span class="line"> <span class="keyword">if</span>(currentUrl !== <span class="string">'login'</span>) { <span class="comment">// 不是登录的请求,在请求头中加入token</span></span><br><span class="line"> req.<span class="property">headers</span>.<span class="property">Authorization</span> = <span class="variable language_">sessionStorage</span>.<span class="title function_">getItem</span>(<span class="string">'token'</span>)</span><br><span class="line"><span class="keyword">const</span> method = req.<span class="property">method</span> <span class="comment">// 获取请求方法</span></span><br><span class="line"><span class="keyword">const</span> action = actionMapping[method] <span class="comment">// 获取请求方法约定的action</span></span><br><span class="line"><span class="comment">// 判断 action 是否存在当前路由的权限中</span></span><br><span class="line"><span class="keyword">const</span> rights = router.<span class="property">currentRouter</span>.<span class="property">meta</span></span><br><span class="line"><span class="keyword">if</span>(rights && rights.<span class="title function_">indexOf</span>(action) == -<span class="number">1</span>) {</span><br><span class="line"><span class="comment">// 没有权限</span></span><br><span class="line"><span class="keyword">return</span> <span class="title class_">Promise</span>.<span class="title function_">reject</span>(<span class="keyword">new</span> <span class="title class_">Error</span>(<span class="string">'没有权限'</span>))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> req</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><h3 id="响应控制"><a href="#响应控制" class="headerlink" title="响应控制"></a>响应控制</h3><p>得到了服务器返回的状态码401, 代表token 超时或者被篡改了,此时应该强制跳转到登录界面</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">axios.<span class="property">interceptors</span>.<span class="property">response</span>.<span class="title function_">use</span>(<span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"> <span class="keyword">if</span>(res.<span class="property">data</span>.<span class="property">meta</span>.<span class="property">status</span> === <span class="number">401</span>){</span><br><span class="line"> router.<span class="title function_">push</span>(<span class="string">'/login'</span>)</span><br><span class="line"> <span class="variable language_">sessionStorage</span>.<span class="title function_">clear</span>()</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">location</span>.<span class="title function_">reload</span>()</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> res</span><br><span class="line">})</span><br></pre></td></tr></table></figure></div><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>前端权限的实现必须要后端提供数据支持, 返回的权限数据的结构, 前后端需要沟通协商怎样的数据便用起来才最方便</p><h3 id="菜单控制"><a href="#菜单控制" class="headerlink" title="菜单控制"></a>菜单控制</h3><ul><li>权限的数据需要在多组件之间共享, 因此采用<code>vuex</code></li><li>防止刷新界面, 权限数据丢失, 所以需要存在<code>sessionStorage</code>, 并目要保证两者的同步</li></ul><h3 id="界面控制"><a href="#界面控制" class="headerlink" title="界面控制"></a>界面控制</h3><ul><li>路由的导航守卫可以防止跳过登录界面</li><li>动态路由可以让不具备权限的界面的路由规则压根就不存在</li></ul><h3 id="按钮控制"><a href="#按钮控制" class="headerlink" title="按钮控制"></a>按钮控制</h3><ul><li>路由规则中可以增加路由元数据meta</li><li>通过路由对象可以得到当前的路由规则以及存在此规则中的meta 数据</li><li>自定义指令可以很方便的实现按钮控制</li></ul><h3 id="请求和响应控制"><a href="#请求和响应控制" class="headerlink" title="请求和响应控制"></a>请求和响应控制</h3><ul><li><p>请求拦截器和响应拦截器的使用</p></li><li><p>请求方式和风格的约定<code>restful</code></p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// restful风格请求</span></span><br><span class="line"><span class="comment">// get: view</span></span><br><span class="line"><span class="comment">// post: add</span></span><br><span class="line"><span class="comment">// put: edit</span></span><br><span class="line"><span class="comment">// delete: delete</span></span><br><span class="line"></span><br></pre></td></tr></table></figure></div></li></ul>]]></content>
<summary type="html">false</summary>
<category term="开发" scheme="http://example.com/categories/%E5%BC%80%E5%8F%91/"/>
<category term="权限控制" scheme="http://example.com/tags/%E6%9D%83%E9%99%90%E6%8E%A7%E5%88%B6/"/>
</entry>
<entry>
<title>移动常见兼容问题</title>
<link href="http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E7%A7%BB%E5%8A%A8%E5%B8%B8%E8%A7%81%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98/"/>
<id>http://example.com/2021/11/18/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E7%A7%BB%E5%8A%A8%E5%B8%B8%E8%A7%81%E5%85%BC%E5%AE%B9%E9%97%AE%E9%A2%98/</id>
<published>2021-11-18T11:25:00.000Z</published>
<updated>2024-11-24T17:45:51.391Z</updated>
<summary type="html">false</summary>
<category term="CSS" scheme="http://example.com/categories/CSS/"/>
<category term="移动端" scheme="http://example.com/tags/%E7%A7%BB%E5%8A%A8%E7%AB%AF/"/>
<category term="兼容" scheme="http://example.com/tags/%E5%85%BC%E5%AE%B9/"/>
</entry>
<entry>
<title>动态切换elementui主题</title>
<link href="http://example.com/2021/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2elementui%E4%B8%BB%E9%A2%98/"/>
<id>http://example.com/2021/03/06/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/%E5%8A%A8%E6%80%81%E5%88%87%E6%8D%A2elementui%E4%B8%BB%E9%A2%98/</id>
<published>2021-03-06T18:50:24.000Z</published>
<updated>2024-11-24T17:45:51.391Z</updated>
<content type="html"><![CDATA[<h4 id="准备自定义的主题"><a href="#准备自定义的主题" class="headerlink" title="准备自定义的主题"></a>准备自定义的主题</h4><blockquote><p>在<a class="link" href="https://element.eleme.cn/#/zh-CN/theme" >element ui<i class="fa-solid fa-arrow-up-right ml-[0.2em] font-light align-text-top text-[0.7em] link-icon"></i></a>官网主题页下载自定义后的主题,多套主题存放本地,两者有不同的命名空间,如写两套主题,一套叫 <code>theme-dark</code>,一套叫 <code>theme-light</code> ,主题都在它的 <code>.theme-dark</code>或<code>.theme-light</code> 的命名空间下</p></blockquote><h4 id="批量为css文件扩展命名空间"><a href="#批量为css文件扩展命名空间" class="headerlink" title="批量为css文件扩展命名空间"></a>批量为css文件扩展命名空间</h4><blockquote><p>使用gulp-css-wrap将这个主题的每个元素外面包裹一个class 来做命名空间。</p></blockquote><ol><li><p>创建一个新的项目<code>css-wrap</code>,并初始化搭建gulp环境</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">// 1.安装gulp:</span><br><span class="line">npm install gulp</span><br><span class="line">// 2.安装gulp-clean-css</span><br><span class="line">npm install gulp-clean-css</span><br><span class="line">// 3.安装gulp-css-wrap</span><br><span class="line">npm install gulp-css-wrap</span><br></pre></td></tr></table></figure></div></li><li><p>在项目根目录下创建一个名为 gulpfile.js 的文件</p><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">var</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>)</span><br><span class="line"><span class="keyword">var</span> cleanCSS = <span class="built_in">require</span>(<span class="string">'gulp-clean-css'</span>)</span><br><span class="line"><span class="keyword">var</span> cssWrap = <span class="built_in">require</span>(<span class="string">'gulp-css-wrap'</span>)</span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'css-wrap'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> gulp.<span class="title function_">src</span>(path.<span class="title function_">resolve</span>(<span class="string">'./theme/index.css'</span>))</span><br><span class="line"> <span class="comment">/* 找需要添加命名空间的css文件,支持正则表达式 */</span></span><br><span class="line"> .<span class="title function_">pipe</span>(<span class="title function_">cssWrap</span>({</span><br><span class="line"> <span class="attr">selector</span>: <span class="string">'.custom-dark'</span> <span class="comment">/* 添加的命名空间 */</span></span><br><span class="line"> }))</span><br><span class="line"> .<span class="title function_">pipe</span>(<span class="title function_">cleanCSS</span>())</span><br><span class="line"> .<span class="title function_">pipe</span>(gulp.<span class="title function_">dest</span>(<span class="string">'src/theme'</span>)) <span class="comment">/* 存放的目录 */</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure></div></li><li><p>将生成在<code>scr/theme</code>下的css和字体文件复制到所需项目中</p></li></ol><h4 id="项目中动态切换主题"><a href="#项目中动态切换主题" class="headerlink" title="项目中动态切换主题"></a>项目中动态切换主题</h4><ol><li>在<code>main.js</code>中引入全部所需的css主题样式</li></ol><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> <span class="string">'element-ui/lib/theme-chalk/index.css'</span>; <span class="comment">// 基础css不能少,否则一些组件会丢失样式</span></span><br><span class="line"><span class="keyword">import</span> <span class="string">"../src/styles/theme/dark.css"</span>;</span><br><span class="line"><span class="keyword">import</span> <span class="string">"../src/styles/theme/light.css"</span>;</span><br></pre></td></tr></table></figure></div><ol start="2"><li>设置<code>App.vue</code>入口页面中<code>#app</code>的元素的类名为默认主题的名称</li></ol><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><template></span><br><span class="line"> <div id="app" class="custom-dark"></span><br><span class="line"> <router-view></router-view></span><br><span class="line"> </div></span><br><span class="line"></template></span><br></pre></td></tr></table></figure></div><ol start="3"><li>修改<code>#app</code>元素的类目切换主题</li></ol><div class="highlight-container" data-rel="Js"><figure class="iseeu highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">"app"</span>).<span class="title function_">setAttribute</span>(<span class="string">"class"</span>, <span class="string">'custom-'</span> + <span class="variable language_">this</span>.<span class="property">theme</span>);</span><br></pre></td></tr></table></figure></div>]]></content>
<summary type="html">false</summary>
<category term="开发" scheme="http://example.com/categories/%E5%BC%80%E5%8F%91/"/>
<category term="elementui" scheme="http://example.com/tags/elementui/"/>
</entry>
<entry>
<title>HTTP报文结构分析</title>
<link href="http://example.com/2021/01/01/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/HTTP%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90/"/>
<id>http://example.com/2021/01/01/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/HTTP%E6%8A%A5%E6%96%87%E7%BB%93%E6%9E%84%E5%88%86%E6%9E%90/</id>
<published>2021-01-01T18:00:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<h2 id="请求报文"><a href="#请求报文" class="headerlink" title="请求报文"></a>请求报文</h2><p>HTTP请求报文由请求行、请求头、空行和请求内容4个部分构成</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1556086946814.png" alt="1556086946814" ></p><h3 id="请求行:"><a href="#请求行:" class="headerlink" title="请求行:"></a>请求行:</h3><p>请求行由请求方法字段、URL字段、协议版本字段三部分构成,字段之间由空格隔开</p><p>常用的请求方法有:GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT</p><h3 id="请求头:"><a href="#请求头:" class="headerlink" title="请求头:"></a>请求头:</h3><p>请求头由key/value对组成,每行为一对,key和value之间通过冒号(:)分割。</p><p>请求头的作用主要是用于通知服务端有关于客户端的请求信息</p><p>常见的请求头(Request Headers)有:</p><ul><li><strong>User-Agent:</strong>生成请求的浏览器类型</li><li><strong>Accept:</strong>客户端可识别的响应内容类型列表;星号*用于按范围将类型分组</li><li><strong>Accept-Language:</strong> 客户端可接受的自然语言</li><li><strong>Accept-Encoding:</strong> 客户端可接受的编码压缩格式</li><li><strong>Accept-Charset:</strong> 可接受的字符集</li><li><strong>Host:</strong> 请求的主机名,允许多个域名绑定同一IP地址</li><li><strong>connection:</strong>连接方式(close或keeplive)</li><li><strong>Cookie:</strong> 存储在客户端的扩展字段</li></ul><h3 id="空行"><a href="#空行" class="headerlink" title="空行:"></a>空行:</h3><p>最后一个请求头之后就是空行,用于告诉服务端以下内容不再是请求头的内容了。</p><h3 id="请求内容"><a href="#请求内容" class="headerlink" title="请求内容:"></a>请求内容:</h3><p>请求内容主要用于POST请求,与POST请求方法配套的请求头一般有Content-Type(标识请求内容的类型)和Content-Length(标识请求内容的长度)</p><h2 id="响应报文"><a href="#响应报文" class="headerlink" title="响应报文"></a>响应报文</h2><p>HTTP响应报文由状态行、响应头、空行和响应内容4个部分构成</p><p><img lazyload src="/images/loading.svg" data-src="C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\1556087134966.png" alt="1556087134966" ></p><h3 id="状态行:"><a href="#状态行:" class="headerlink" title="状态行:"></a>状态行:</h3><p>由HTTP协议版本、状态码、状态码描述三部分构成,它们之间由空格隔开</p><p>状态码由3位数字组成,第一位标识响应的类型,常用的5大类状态码如下:</p><ul><li>1xx:表示服务器已接收了客户端的请求,客户端可以继续发送请求</li><li>2xx:表示服务器已成功接收到请求并进行处理</li><li>3xx:表示服务器要求客户端重定向</li><li>4xx:表示客户端的请求有非法内容</li><li>5xx:标识服务器未能正常处理客户端的请求而出现意外错误</li></ul><h3 id="响应头"><a href="#响应头" class="headerlink" title="响应头:"></a>响应头:</h3><p>一般情况下,响应头会包含以下,甚至更多的信息。</p><p>Location:服务器返回给客户端,用于重定向到新的位置</p><p>Server: 包含服务器用来处理请求的软件信息及版本信息</p><p>Vary:标识不可缓存的请求头列表</p><p>Connection: 连接方式。</p><p>对于请求端来讲:close是告诉服务端,断开连接,不用等待后续的求请了。keep-live则是告诉服务端,在完成本次请求的响应后,保持连接,等待本次连接后的后续请求。</p><p>对于响应端来讲:close表示连接已经关闭。keeplive则表示连接保持中,可以继续处理后续请求。Keep-Alive表示如果请求端保持连接,则该请求头部信息表明期望服务端保持连接多长时间(秒),例如300秒,应该这样写Keep-Alive: 300</p><h3 id="空行-1"><a href="#空行-1" class="headerlink" title="空行"></a>空行</h3><p>最后一个响应头之后就是空行,用于告诉请求端以下内容不再是响应头的内容了。</p><h3 id="响应内容"><a href="#响应内容" class="headerlink" title="响应内容"></a>响应内容</h3><p>服务端返回给请求端的文本信息。</p>]]></content>
<summary type="html">false</summary>
<category term="浏览器" scheme="http://example.com/categories/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
<category term="http" scheme="http://example.com/tags/http/"/>
</entry>
<entry>
<title>URL的输入到浏览器解析</title>
<link href="http://example.com/2021/01/01/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/URL%E7%9A%84%E8%BE%93%E5%85%A5%E5%88%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E8%A7%A3%E6%9E%90/"/>
<id>http://example.com/2021/01/01/%E5%BC%80%E5%8F%91%E5%AD%A6%E4%B9%A0/URL%E7%9A%84%E8%BE%93%E5%85%A5%E5%88%B0%E6%B5%8F%E8%A7%88%E5%99%A8%E8%A7%A3%E6%9E%90/</id>
<published>2021-01-01T18:00:00.000Z</published>
<updated>2024-11-24T17:45:51.387Z</updated>
<content type="html"><![CDATA[<p>URL的输入到浏览器解析,一般分为以下几个步骤:</p><ul><li>DNS解析</li><li>发起TCP连接</li><li>发送HTTP请求</li><li>服务器处理请求并返回HTTP报文</li><li>浏览器解析渲染页面</li><li>连接结束</li></ul><p>下面我们来看看具体的细节….</p><h2 id="DNS解析"><a href="#DNS解析" class="headerlink" title="DNS解析"></a>DNS解析</h2><p>DNS解析实际上就是寻找你所需要的资源的过程。假设你输入<code>www.baidu.com</code>,而这个网址并不是百度的真实地址,互联网中每一台机器都有唯一标识的IP地址,这个才是关键,但是它不好记,乱七八糟一串数字谁记得住啊,所以就需要一个网址和IP地址的转换,也就是DNS解析。下面看看具体的解析过程</p><h3 id="具体解析"><a href="#具体解析" class="headerlink" title="具体解析"></a>具体解析</h3><p>DNS解析其实是一个递归的过程 </p><p><img lazyload src="/images/loading.svg" data-src="https://user-gold-cdn.xitu.io/2019/4/28/16a634c9285cb545?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="DNS解析" ></p><p> 输入<code>www.google.com</code>网址后,首先在本地的域名服务器中查找,没找到去<code>.</code>根域名服务器查找,没有再去<code>com</code>顶级域名服务器查找,如此的类推下去,直到找到IP地址,然后把它记录在本地,供下次使用。大致过程就是</p><div class="highlight-container" data-rel="Plaintext"><figure class="iseeu highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">. => .com => google.com. => www.google.com.</span><br></pre></td></tr></table></figure></div><p>(这个<code>.</code>对应的就是根域名服务器,默认情况下网址的最后一位都是<code>.</code>,通常都会省略,浏览器在请求DNS的时候会自动加上)</p><h3 id="DNS优化"><a href="#DNS优化" class="headerlink" title="DNS优化"></a>DNS优化</h3><p>既然已经懂得了解析的具体过程,我们可以看到上述一共经过了N个过程,每个过程有一定的消耗和时间的等待,因此我们得想办法解决一下这个问题!</p><h4 id="DNS缓存"><a href="#DNS缓存" class="headerlink" title="DNS缓存"></a>DNS缓存</h4><p>DNS存在着多级缓存,从离浏览器的距离排序的话,有以下几种: 浏览器缓存,系统缓存,路由器缓存,IPS服务器缓存,根域名服务器缓存,顶级域名服务器缓存,主域名服务器缓存。</p><ul><li>在你的chrome浏览器中输入:chrome://net-internals/#dns,你可以看到chrome浏览器的DNS缓存。</li><li>系统缓存主要存在/etc/hosts(Linux系统)中</li></ul><h4 id="DNS负载均衡"><a href="#DNS负载均衡" class="headerlink" title="DNS负载均衡"></a>DNS负载均衡</h4><p>不知道你们有没有注意这样一件事,你访问<code>baidu.com</code>的时候,每次响应的并非是同一个服务器(IP地址不同),一般大公司都有成百上千台服务器来支撑访问,假设只有一个服务器,那它的性能和存储量要多大才能支撑这样大量的访问呢?DNS可以返回一个合适的机器的IP给用户,例如可以根据每台机器的负载量,该机器离用户地理位置的距离等等,这种过程就是DNS负载均衡</p><h2 id="发起TCP连接"><a href="#发起TCP连接" class="headerlink" title="发起TCP连接"></a>发起TCP连接</h2><p>TCP提供一种可靠的传输,这个过程涉及到三次握手,四次挥手,下面我们详细看看 TCP提供一种面向连接的,可靠的字节流服务。 其首部的数据格式如下 </p><p><img lazyload src="/images/loading.svg" data-src="https://user-gold-cdn.xitu.io/2019/4/28/16a634dbf162e53c?imageView2/0/w/1280/h/960/format/webp/ignore-error/1" alt="TCP首部" ></p><h3 id="字段分析"><a href="#字段分析" class="headerlink" title="字段分析"></a>字段分析</h3><ul><li>源端口:源端口和IP地址的作用是标识报文的返回地址。</li><li>目的端口:端口指明接收方计算机上的应用程序接口。</li></ul><blockquote><p>TCP报头中的源端口号和目的端口号同IP数据报中的源IP与目的IP唯一确定一条TCP连接。</p></blockquote><ul><li>序号:是TCP可靠传输的关键部分。序号是该报文段发送的数据组的第一个字节的序号。在TCP传送的流中,每一个字节都有一个序号。比如一个报文段的序号为300,报文段数据部分共有100字节,则下一个报文段的序号为400。所以序号确保了TCP传输的有序性。</li><li>确认号:即ACK,指明下一个期待收到的字节序号,表明该序号之前的所有数据已经正确无误的收到。确认号只有当ACK标志为1时才有效。比如建立连接时,SYN报文的ACK标志位为0。</li><li>首部长度/数据偏移:占4位,它指出TCP报文的数据距离TCP报文段的起始处有多远。由于首部可能含有可选项内容,因此TCP报头的长度是不确定的,报头不包含任何任选字段则长度为20字节,4位首部长度字段所能表示的最大值为1111,转化为10进制为15,15*32/8=60,故报头最大长度为60字节。首部长度也叫数据偏移,是因为首部长度实际上指示了数据区在报文段中的起始偏移值。</li><li>保留:占6位,保留今后使用,但目前应都位0。</li><li>控制位:URG ACK PSH RST SYN FIN,共6个,每一个标志位表示一个控制功能。<ul><li>紧急URG:当URG=1,表明紧急指针字段有效。告诉系统此报文段中有紧急数据</li><li>确认ACK:仅当ACK=1时,确认号字段才有效。TCP规定,在连接建立后所有报文的传输都必须把ACK置1。</li><li>推送PSH:当两个应用进程进行交互式通信时,有时在一端的应用进程希望在键入一个命令后立即就能收到对方的响应,这时候就将PSH=1。</li><li>复位RST:当RST=1,表明TCP连接中出现严重差错,必须释放连接,然后再重新建立连接。</li><li>同步SYN:在连接建立时用来同步序号。当SYN=1,ACK=0,表明是连接请求报文,若同意连接,则响应报文中应该使SYN=1,ACK=1。</li><li>终止FIN:用来释放连接。当FIN=1,表明此报文的发送方的数据已经发送完毕,并且要求释放。</li></ul></li><li>窗口:滑动窗口大小,用来告知发送端接受端的缓存大小,以此控制发送端发送数据的速率,从而达到流量控制。窗口大小时一个16bit字段,因而窗口大小最大为65535。</li><li>校验和:奇偶校验,此校验和是对整个的 TCP 报文段,包括 TCP 头部和 TCP 数据,以 16 位字进行计算所得。由发送端计算和存储,并由接收端进行验证。</li><li>紧急指针:只有当 URG 标志置 1 时紧急指针才有效。紧急指针是一个正的偏移量,和顺序号字段中的值相加表示紧急数据最后一个字节的序号。 TCP 的紧急方式是发送端向另一端发送紧急数据的一种方式。</li><li>选项和填充:最常见的可选字段是最长报文大小,又称为MSS(Maximum Segment Size),每个连接方通常都在通信的第一个报文段(为建立连接而设置SYN标志为1的那个段)中指明这个选项,它表示本端所能接受的最大报文段的长度。选项长度不一定是32位的整数倍,所以要加填充位,即在这个字段中加入额外的零,以保证TCP头是32的整数倍。</li><li>数据部分: TCP 报文段中的数据部分是可选的。在一个连接建立和一个连接终止时,双方交换的报文段仅有 TCP 首部。如果一方没有数据要发送,也使用没有任何数据的首部来确认收到的数据。在处理超时的许多情况中,也会发送不带任何数据的报文段。</li></ul><blockquote><p>需要注意的是: </p><p>(A)<strong>不要将确认序号ack与标志位中的ACK搞混了。</strong> </p><p>(B)<strong>确认序号ack=发起方Req+1,两端配对。</strong></p></blockquote><h4 id="三次握手"><a href="#三次握手" class="headerlink" title="三次握手"></a>三次握手</h4><h5 id="第一次握手:"><a href="#第一次握手:" class="headerlink" title="第一次握手:"></a>第一次握手:</h5><p>客户端发送syn包(Seq=x)到服务器,并进入SYN_SEND状态,等待服务器确认;</p><h5 id="第二次握手:"><a href="#第二次握手:" class="headerlink" title="第二次握手:"></a>第二次握手:</h5><p>服务器收到syn包,必须确认客户的SYN(ack=x+1),同时自己也发送一个SYN包(Seq=y),即SYN+ACK包,此时服务器进入SYN_RECV状态;</p><h5 id="第三次握手:"><a href="#第三次握手:" class="headerlink" title="第三次握手:"></a>第三次握手:</h5><p>客户端收到服务器的SYN+ACK包,向服务器发送确认包ACK(ack=y+1),此包发送完毕,客户端和服务器进入ESTABLISHED状态,完成三次握手。</p><p>握手过程中传送的包里不包含数据,三次握手完毕后,客户端与服务器才正式开始传送数据。理想状态下,TCP连接一旦建立,在通信双方中的任何一方主动关闭连接之前,TCP 连接都将被一直保持下去。</p><p><img lazyload src="/images/loading.svg" data-src="https://img-blog.csdn.net/20180717202520531?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4OTUwMzE2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="img" ></p><h5 id="为什么会采用三次握手,若采用二次握手可以吗?-四次呢?"><a href="#为什么会采用三次握手,若采用二次握手可以吗?-四次呢?" class="headerlink" title="为什么会采用三次握手,若采用二次握手可以吗? 四次呢?"></a>为什么会采用三次握手,若采用二次握手可以吗? 四次呢?</h5><p>建立连接的过程是利用客户服务器模式,假设主机A为客户端,主机B为服务器端。</p><p>采用三次握手是为了防止失效的连接请求报文段突然又传送到主机B,因而产生错误。失效的连接请求报文段是指:主机A发出的连接请求没有收到主机B的确认,于是经过一段时间后,主机A又重新向主机B发送连接请求,且建立成功,顺序完成数据传输。考虑这样一种特殊情况,主机A第一次发送的连接请求并没有丢失,而是因为网络节点导致延迟达到主机B,主机B以为是主机A又发起的新连接,于是主机B同意连接,并向主机A发回确认,但是此时主机A根本不会理会,主机B就一直在等待主机A发送数据,导致主机B的资源浪费。</p><p>采用两次握手不行,原因就是上面说的失效的连接请求的特殊情况。而在三次握手中, client和server都有一个发syn和收ack的过程, 双方都是发后能收, 表明通信则准备工作OK.</p><p>为什么不是四次握手呢? 大家应该知道通信中著名的蓝军红军约定, 这个例子说明, 通信不可能100%可靠, 而上面的三次握手已经做好了通信的准备工作, 再增加握手, 并不能显著提高可靠性, 而且也没有必要。</p><h4 id="四次挥手"><a href="#四次挥手" class="headerlink" title="四次挥手"></a>四次挥手</h4><p>数据传输完毕后,双方都可释放连接。最开始的时候,客户端和服务器都是处于ESTABLISHED状态,假设客户端主动关闭,服务器被动关闭。</p><p><img lazyload src="/images/loading.svg" data-src="https://img-blog.csdn.net/20180717204202563?watermark/2/text/aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM4OTUwMzE2/font/5a6L5L2T/fontsize/400/fill/I0JBQkFCMA==/dissolve/70" alt="img" ></p><h5 id="第一次挥手:"><a href="#第一次挥手:" class="headerlink" title="第一次挥手:"></a>第一次挥手:</h5><p>客户端发送一个FIN,用来关闭客户端到服务器的数据传送,也就是客户端告诉服务器:我已经不 会再给你发数据了(当然,在fin包之前发送出去的数据,如果没有收到对应的ack确认报文,客户端依然会重发这些数据),但是,此时客户端还可 以接受数据。 FIN=1,其序列号为seq=u(等于前面已经传送过来的数据的最后一个字节的序号加1),此时,客户端进入FIN-WAIT-1(终止等待1)状态。 TCP规定,FIN报文段即使不携带数据,也要消耗一个序号。</p><h5 id="第二次挥手:"><a href="#第二次挥手:" class="headerlink" title="第二次挥手:"></a>第二次挥手:</h5><p>服务器收到FIN包后,发送一个ACK给对方并且带上自己的序列号seq,确认序号为收到序号+1(与SYN相同,一个FIN占用一个序号)。此时,服务端就进入了CLOSE-WAIT(关闭等待)状态。TCP服务器通知高层的应用进程,客户端向服务器的方向就释放了,这时候处于半关闭状态,即客户端已经没有数据要发送了,但是服务器若发送数据,客户端依然要接受。这个状态还要持续一段时间,也就是整个CLOSE-WAIT状态持续的时间。</p><p>此时,客户端就进入FIN-WAIT-2(终止等待2)状态,等待服务器发送连接释放报文(在这之前还需要接受服务器发送的最后的数据)。</p><h5 id="第三次挥手:"><a href="#第三次挥手:" class="headerlink" title="第三次挥手:"></a>第三次挥手:</h5><p>服务器发送一个FIN,用来关闭服务器到客户端的数据传送,也就是告诉客户端,我的数据也发送完了,不会再给你发数据了。由于在半关闭状态,服务器很可能又发送了一些数据,假定此时的序列号为seq=w,此时,服务器就进入了LAST-ACK(最后确认)状态,等待客户端的确认。</p><h5 id="第四次挥手:"><a href="#第四次挥手:" class="headerlink" title="第四次挥手:"></a>第四次挥手:</h5><p>主动关闭方收到FIN后,发送一个ACK给被动关闭方,确认序号为收到序号+1,此时,客户端就进入了TIME-WAIT(时间等待)状态。注意此时TCP连接还没有释放,必须经过2∗MSL(最长报文段寿命)的时间后,当客户端撤销相应的TCB后,才进入CLOSED状态。</p><p>服务器只要收到了客户端发出的确认,立即进入CLOSED状态。同样,撤销TCB后,就结束了这次的TCP连接。可以看到,服务器结束TCP连接的时间要比客户端早一些。</p><p>至此,完成四次挥手。</p><h3 id="常见问题:"><a href="#常见问题:" class="headerlink" title="常见问题:"></a>常见问题:</h3><ul><li><h4 id="为什么客户端最后还要等待2MSL?"><a href="#为什么客户端最后还要等待2MSL?" class="headerlink" title="为什么客户端最后还要等待2MSL?"></a>为什么客户端最后还要等待2MSL?</h4><p> 保证客户端发送的最后一个ACK报文能够到达服务器,因为这个ACK报文可能丢失,站在服务器的角度看来,我已经发送了FIN+ACK报文请求断开了,客户端还没有给我回应,应该是我发送的请求断开报文它没有收到,于是服务器又会重新发送一次,而客户端就能在这个2MSL时间段内收到这个重传的报文,接着给出回应报文,并且会重启2MSL计时器。</p></li><li><h4 id="为什么建立连接是三次握手,关闭连接确是四次挥手呢?"><a href="#为什么建立连接是三次握手,关闭连接确是四次挥手呢?" class="headerlink" title="为什么建立连接是三次握手,关闭连接确是四次挥手呢?"></a>为什么建立连接是三次握手,关闭连接确是四次挥手呢?</h4><p> 建立连接的时候, 服务器在LISTEN状态下,收到建立连接请求的SYN报文后,把ACK和SYN放在一个报文里发送给客户端。 而关闭连接时,服务器收到对方的FIN报文时,仅仅表示对方不再发送数据了但是还能接收数据,而自己也未必全部数据都发送给对方了,所以己方可以立即关闭,也可以发送一些数据给对方后,再发送FIN报文给对方来表示同意现在关闭连接,因此,己方ACK和FIN一般都会分开发送,从而导致多了一次。</p></li><li><h4 id="如果已经建立了连接,但是客户端突然出现故障了怎么办?"><a href="#如果已经建立了连接,但是客户端突然出现故障了怎么办?" class="headerlink" title="如果已经建立了连接,但是客户端突然出现故障了怎么办?"></a>如果已经建立了连接,但是客户端突然出现故障了怎么办?</h4><p>TCP还设有一个保活计时器,显然,客户端如果出现故障,服务器不能一直等下去,白白浪费资源。服务器每收到一次客户端的请求后都会重新复位这个计时器,时间通常是设置为2小时,若两小时还没有收到客户端的任何数据,服务器就会发送一个探测报文段,以后每隔75秒钟发送一次。若一连发送10个探测报文仍然没反应,服务器就认为客户端出了故障,接着就关闭连接。</p></li></ul><h2 id="发送HTTP请求"><a href="#发送HTTP请求" class="headerlink" title="发送HTTP请求"></a>发送HTTP请求</h2><p>发送HTTP请求的过程就是构建HTTP请求报文并通过TCP协议中发送到服务器指定端口 请求报文由<strong>请求行</strong>,<strong>请求报头</strong>,<strong>请求正文</strong>组成。</p><p><em>tips:HTTP的端口为80/8080,而HTTPS的端口为443</em></p><h3 id="请求行"><a href="#请求行" class="headerlink" title="请求行"></a>请求行</h3><p>请求行的格式为<code>Method Request-URL HTTP-Version CRLF</code> <code>eg: GET index.html HTTP/1.1</code> 常用的方法有: <code>GET</code>,<code>POST</code>, <code>PUT</code>, <code>DELETE</code>, <code>OPTIONS</code>, <code>HEAD</code>。</p><h4 id="常见的请求方法区别"><a href="#常见的请求方法区别" class="headerlink" title="常见的请求方法区别"></a>常见的请求方法区别</h4><p><code>POST</code>和<code>GET</code>的主要区别</p><ul><li>GET在浏览器回退时是无害的,而POST会再次提交请求</li><li>GET产生的URL地址可以被Bookmark,而POST不可以</li><li>GET请求会被浏览器主动cache,而POST不会,除非手动设置</li><li>GET请求只能进行url编码,而POST支持多种编码方式</li><li>GET请求参数会被完整保留在浏览器历史记录里,而POST中的参数不会被保留</li><li>GET请求在URL中传送的参数是有长度限制的,而POST么有</li><li>对参数的数据类型,GET只接受ASCII字符,而POST没有限制</li><li>GET比POST更不安全,因为参数直接暴露在URL上,所以不能用来传递敏感信息</li><li>GET参数通过URL传递,POST放在Request body中</li><li>★ GET<code>会产生一个</code>TCP<code>数据包,而</code>POST<code>会产生两个</code>TCP`数据包(Firefox就发送一次)</li><li>对于GET方式的请求,浏览器会把http header和data一并发送出去,服务器响应200(返回数据);而对于POST,浏览器先发送header,服务器响应100 continue,浏览器再发送data,服务器响应200 ok(返回数据)</li></ul>]]></content>
<summary type="html">false</summary>
<category term="浏览器" scheme="http://example.com/categories/%E6%B5%8F%E8%A7%88%E5%99%A8/"/>
<category term="http" scheme="http://example.com/tags/http/"/>
</entry>
</feed>