-
Notifications
You must be signed in to change notification settings - Fork 1
/
atom.xml
413 lines (196 loc) · 373 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
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>青衣墨客</title>
<link href="https://www.qingyi1220.cn/atom.xml" rel="self"/>
<link href="https://www.qingyi1220.cn/"/>
<updated>2023-03-21T14:57:18.775Z</updated>
<id>https://www.qingyi1220.cn/</id>
<author>
<name>Tsing Yi</name>
</author>
<generator uri="https://hexo.io/">Hexo</generator>
<entry>
<title>手写Promise</title>
<link href="https://www.qingyi1220.cn/posts/28885.html"/>
<id>https://www.qingyi1220.cn/posts/28885.html</id>
<published>2023-03-14T16:00:00.000Z</published>
<updated>2023-03-21T14:57:18.775Z</updated>
<content type="html"><![CDATA[<figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * <span class="doctag">@description</span> <span class="variable">MyPromise</span></span></span><br><span class="line"><span class="comment"> * <span class="doctag">@author</span> <span class="variable">TsingYi</span></span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyPromise</span> {</span><br><span class="line"> state = <span class="string">'pending'</span> <span class="comment">//状态,'pending', fulfilled','rejected'</span></span><br><span class="line"> value = <span class="literal">undefined</span> <span class="comment">//成功后的值</span></span><br><span class="line"> reason = <span class="literal">undefined</span> <span class="comment">//失败后的原因</span></span><br><span class="line"></span><br><span class="line"> resolveCallbacks = [] <span class="comment">//pending状态下,储存成功的回调</span></span><br><span class="line"> rejectCallbacks = [] <span class="comment">//pending状态下,储存失败的回调</span></span><br><span class="line"></span><br><span class="line"> <span class="title function_">constructor</span>(<span class="params">fn</span>) {</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">resolveHandler</span> = (<span class="params">value</span>) => {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable language_">this</span>.<span class="property">state</span> === <span class="string">'pending'</span> ? <span class="string">'fulfilled'</span> : <span class="variable language_">this</span>.<span class="property">state</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">value</span> = value</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">resolveCallbacks</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">fn</span>) =></span> <span class="title function_">fn</span>(<span class="variable language_">this</span>.<span class="property">value</span>))</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> <span class="title function_">rejectHandler</span> = (<span class="params">reason</span>) => {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">state</span> = <span class="variable language_">this</span>.<span class="property">state</span> === <span class="string">'pending'</span> ? <span class="string">'rejected'</span> : <span class="variable language_">this</span>.<span class="property">state</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">reason</span> = reason</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">rejectCallbacks</span>.<span class="title function_">forEach</span>(<span class="function">(<span class="params">fn</span>) =></span> <span class="title function_">fn</span>(<span class="variable language_">this</span>.<span class="property">reason</span>))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="title function_">fn</span>(resolveHandler, rejectHandler)</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="title function_">rejectHandler</span>(err)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="title function_">then</span>(<span class="params">fn1, fn2</span>) {</span><br><span class="line"> fn1 = <span class="keyword">typeof</span> fn1 === <span class="string">'function'</span> ? fn1 : <span class="function">(<span class="params">v</span>) =></span> v</span><br><span class="line"> fn2 =</span><br><span class="line"> <span class="keyword">typeof</span> fn2 === <span class="string">'function'</span></span><br><span class="line"> ? fn2</span><br><span class="line"> : <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="keyword">throw</span> err</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="string">'pending'</span>) {</span><br><span class="line"> <span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">resolveCallbacks</span>.<span class="title function_">push</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> newValue = <span class="title function_">fn1</span>(<span class="variable language_">this</span>.<span class="property">value</span>)</span><br><span class="line"> <span class="title function_">resolve</span>(newValue)</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="title function_">reject</span>(err)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">rejectCallbacks</span>.<span class="title function_">push</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> newReason = <span class="title function_">fn2</span>(<span class="variable language_">this</span>.<span class="property">reason</span>)</span><br><span class="line"> <span class="title function_">resolve</span>(newReason)</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="title function_">reject</span>(err)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="string">'fulfilled'</span>) {</span><br><span class="line"> <span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> newValue = <span class="title function_">fn1</span>(<span class="variable language_">this</span>.<span class="property">value</span>)</span><br><span class="line"> <span class="title function_">resolve</span>(newValue)</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="title function_">reject</span>(err)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span>.<span class="property">state</span> === <span class="string">'rejected'</span>) {</span><br><span class="line"> <span class="keyword">const</span> p = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">const</span> newReason = <span class="title function_">fn2</span>(<span class="variable language_">this</span>.<span class="property">reason</span>)</span><br><span class="line"> <span class="title function_">resolve</span>(newReason)</span><br><span class="line"> } <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="title function_">reject</span>(err)</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> <span class="keyword">return</span> p</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">catch</span>(fn) {</span><br><span class="line"> <span class="keyword">return</span> <span class="variable language_">this</span>.<span class="title function_">then</span>(<span class="literal">null</span>, fn)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> p1 = <span class="keyword">new</span> <span class="title class_">MyPromise</span>(<span class="function">(<span class="params">resolve, reject</span>) =></span> {</span><br><span class="line"> <span class="title function_">resolve</span>(<span class="number">100</span>)</span><br><span class="line"> <span class="comment">// reject('err123')</span></span><br><span class="line"> <span class="comment">// setTimeout(() => {</span></span><br><span class="line"> <span class="comment">// resolve(100)</span></span><br><span class="line"> <span class="comment">// }, 500)</span></span><br><span class="line"> <span class="comment">// setTimeout(() => {</span></span><br><span class="line"> <span class="comment">// reject('err123')</span></span><br><span class="line"> <span class="comment">// }, 500)</span></span><br><span class="line">})</span><br><span class="line">p1.<span class="title function_">then</span>(<span class="function">(<span class="params">data1</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'data1'</span>, data1)</span><br><span class="line"> <span class="keyword">return</span> data1 + <span class="number">2</span></span><br><span class="line">})</span><br><span class="line"> .<span class="title function_">catch</span>(<span class="function">(<span class="params">err2</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err2)</span><br><span class="line"> <span class="keyword">return</span> err2 + <span class="number">4</span></span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">then</span>(<span class="function">(<span class="params">data3</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'data3'</span>, data3)</span><br><span class="line"> <span class="keyword">return</span> data3</span><br><span class="line"> })</span><br><span class="line"></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span cl</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="JavaScript" scheme="https://www.qingyi1220.cn/tags/JavaScript/"/>
</entry>
<entry>
<title>Vue中hash路由和history路由原理及优缺点</title>
<link href="https://www.qingyi1220.cn/posts/25593.html"/>
<id>https://www.qingyi1220.cn/posts/25593.html</id>
<published>2023-02-13T16:00:00.000Z</published>
<updated>2023-04-10T06:27:04.920Z</updated>
<content type="html"><![CDATA[<h3 id="hash模式"><a href="#hash模式" class="headerlink" title="hash模式:"></a>hash模式:</h3><ol><li>原理: 在 url 中的 # 之后对应的是 hash 值, 其原理是通过<code>window.onhashChange()</code> 事件监听hash值的变化, 根据路由表对应的hash值来判断加载对应的路由加载对应的组件</li><li>优点:<br>(1) 只需要前端配置路由表, 不需要后端的参与<br>(2) 兼容性好, 浏览器都能支持<br>(3) hash值改变不会向后端发送请求, 完全属于前端路由</li><li>缺点:<br>(1) hash值前面需要加#, 不符合url规范,也不美观</li></ol><h3 id="history模式"><a href="#history模式" class="headerlink" title="history模式:"></a>history模式:</h3><ol><li>原理: 利用H5新增的<code>history.pushState()</code>和<code>window.onpopstate()</code>以及<code>history.replaceState()</code>方法监听和改变url</li><li>优点:<br>(1) 符合url地址规范, 不需要#, 使用起来比较美观</li><li>缺点:<br>(1) 在用户手动输入地址或刷新页面时会发起url请求, 后端需要配置index.html页面用户匹配不到静态资源的情况, 否则会出现404错误<br>(2) 兼容性比较差, 是利用了 HTML5 History对象中新增的 <code>pushState()</code> 和 <code>replaceState()</code> 方法,需要特定浏览器的支持</li></ol>]]></content>
<summary type="html"><h3 id="hash模式"><a href="#hash模式" class="headerlink" title="hash模式:"></a>hash模式:</h3><ol>
<li>原理: 在 url 中的 # 之后对应的是 hash 值, 其原理是通过<code>wind</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://www.qingyi1220.cn/tags/Vue/"/>
<category term="Vue-router" scheme="https://www.qingyi1220.cn/tags/Vue-router/"/>
</entry>
<entry>
<title>手写bind,apply,call</title>
<link href="https://www.qingyi1220.cn/posts/3918.html"/>
<id>https://www.qingyi1220.cn/posts/3918.html</id>
<published>2023-02-12T16:00:00.000Z</published>
<updated>2023-03-21T14:57:18.777Z</updated>
<content type="html"><![CDATA[<h1 id="手写bind-apply-call"><a href="#手写bind-apply-call" class="headerlink" title="手写bind,apply,call"></a>手写bind,apply,call</h1><blockquote><p>apply,call,bind都是js给函数内置的一些api,调用他们可以为函数指定this的执行,同时也可以传参.</p></blockquote><h3 id="使用方式"><a href="#使用方式" class="headerlink" title="使用方式"></a>使用方式</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//call</span></span><br><span class="line">fun.<span class="title function_">call</span>(thisArg, arg1, arg2, ...)</span><br><span class="line"></span><br><span class="line"><span class="comment">//apply </span></span><br><span class="line">func.<span class="title function_">apply</span>(thisArg, [argsArray])</span><br><span class="line"></span><br><span class="line"><span class="comment">//bind</span></span><br><span class="line"><span class="keyword">const</span> newFun = fun.<span class="title function_">bind</span>(thisArg, arg1, arg2, ...)</span><br><span class="line"><span class="title function_">newFun</span>()</span><br></pre></td></tr></table></figure><p>apply和call就是传参不一样,但是两个都是会在调用的时候同时执行调用的函数,但是bind则会返回一个绑定了this的函数.</p><p>我们还需要知道一个事情,就是this的指向.</p><h3 id="this的指向"><a href="#this的指向" class="headerlink" title="this的指向"></a>this的指向</h3><p>this的指向是在函数调用的时候确定下来的,this的指向大致可以分为五种.</p><h4 id="1-默认绑定"><a href="#1-默认绑定" class="headerlink" title="1. 默认绑定"></a>1. 默认绑定</h4><p>默认绑定一般发生在回调函数,函数直接调用</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">test</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//严格模式下是undefined</span></span><br><span class="line"> <span class="comment">//非严格模式下是window</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><br><span class="line">}</span><br><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="comment">//setTimeout的比较特殊</span></span><br><span class="line"> <span class="comment">//严格模式和非严格模式下都是window</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><br><span class="line">})</span><br><span class="line"></span><br><span class="line">arr.<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//严格模式下是undefined</span></span><br><span class="line"> <span class="comment">//非严格模式下是window</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><br><span class="line">})</span><br></pre></td></tr></table></figure><h4 id="2-隐式绑定"><a href="#2-隐式绑定" class="headerlink" title="2. 隐式绑定"></a>2. 隐式绑定</h4><p>通俗点说就是谁调用就是指向谁</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>:<span class="string">'joy'</span>,</span><br><span class="line"> <span class="title function_">getName</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="comment">//obj</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="comment">//joy</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">obj.<span class="title function_">getName</span>()</span><br></pre></td></tr></table></figure><h4 id="3-显示绑定call-apply-bind"><a href="#3-显示绑定call-apply-bind" class="headerlink" title="3. 显示绑定call,apply,bind"></a>3. 显示绑定call,apply,bind</h4><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj1 = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'joy'</span>,</span><br><span class="line"> <span class="title function_">getName</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><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><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> obj2 = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'sam'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">obj1.<span class="property">getName</span>.<span class="title function_">call</span>(obj2) <span class="comment">//obj2 sam</span></span><br><span class="line">obj1.<span class="property">getName</span>.<span class="title function_">apply</span>(obj2) <span class="comment">//obj2 sam</span></span><br><span class="line"><span class="keyword">const</span> fn = obj1.<span class="property">getName</span>.<span class="title function_">bind</span>(obj2)</span><br><span class="line"><span class="title function_">fn</span>()<span class="comment">//obj2 sam</span></span><br></pre></td></tr></table></figure><h4 id="4-new绑定"><a href="#4-new绑定" class="headerlink" title="4. new绑定"></a>4. new绑定</h4><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Vehicle</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">a</span> = <span class="number">2</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><br><span class="line">}</span><br><span class="line"><span class="keyword">new</span> <span class="title class_">Vehicle</span>() <span class="comment">//this指向Vehicle这个new出来的对象</span></span><br></pre></td></tr></table></figure><h4 id="5-es6的箭头函数"><a href="#5-es6的箭头函数" class="headerlink" title="5. es6的箭头函数"></a>5. es6的箭头函数</h4><p> es6的箭头函数比较特殊,箭头函数this为父作用域的this,不是调用时的this.前四种方式,都是调用时确定,也就是动态的,而箭头函数的this指向是静态的,声明的时候就确定了下来.比较符合js的词法作用域</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="variable language_">window</span>.<span class="property">name</span> = <span class="string">'win'</span></span><br><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'joy'</span>,</span><br><span class="line"> <span class="attr">age</span>: <span class="number">12</span>,</span><br><span class="line"> <span class="attr">getName</span>: <span class="function">() =></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="comment">//其父作用域this是window,所以就是window</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="comment">//win </span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">getAge</span>: <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="comment">//通过obj.getAge调用,这里面this是指向obj</span></span><br><span class="line"> <span class="built_in">setTimeout</span>(<span class="function">() =></span> {</span><br><span class="line"> <span class="comment">//所以这里this也是指向obj 所以结果是12</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">age</span>) </span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">obj.<span class="title function_">getName</span>()</span><br><span class="line">obj.<span class="title function_">getAge</span>()</span><br></pre></td></tr></table></figure><p>5种this的优先级</p><blockquote><p>箭头函数 -> new绑定 -> 显示绑定call/bind/apply -> 隐式绑定 -> 默认绑定</p></blockquote><h3 id="实现apply"><a href="#实现apply" class="headerlink" title="实现apply"></a>实现apply</h3><ol><li>先给Function原型上扩展个方法并接收2个参数,</li></ol><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myApply</span> = <span class="keyword">function</span> (<span class="params">context, args</span>) {</span><br><span class="line"></span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="2"><li>因为不传context的话,this会指向window的,args也做一下容错处理</li></ol><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myApply</span> = <span class="keyword">function</span> (<span class="params">context, args</span>) {</span><br><span class="line"> <span class="comment">//这里默认不传就是给window,也可以用es6给参数设置默认参数</span></span><br><span class="line"> context = context || <span class="variable language_">window</span></span><br><span class="line"> args = args ? args : []</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="3"><li>需要回想一下绑定this的五种方式,现在要来给调用的函数绑定this了, 这里默认绑定和new肯定用不了,这里就使用隐式绑定去实现显式绑定了</li></ol><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myApply</span> = <span class="keyword">function</span> (<span class="params">context, args</span>) {</span><br><span class="line"> <span class="comment">//这里默认不传就是给window,也可以用es6给参数设置默认参数</span></span><br><span class="line"> context = context || <span class="variable language_">window</span></span><br><span class="line"> args = args ? args : []</span><br><span class="line"> <span class="comment">//给context新增一个独一无二的属性以免覆盖原有属性</span></span><br><span class="line"> <span class="keyword">const</span> key = <span class="title class_">Symbol</span>()</span><br><span class="line"> context[key] = <span class="variable language_">this</span></span><br><span class="line"> <span class="comment">//通过隐式绑定的方式调用函数</span></span><br><span class="line"> context[key](...args)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><ol start="4"><li>最后一步要返回函数调用的返回值,并且把context上的属性删了才不会造成影响</li></ol><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myApply</span> = <span class="keyword">function</span> (<span class="params">context, args</span>) {</span><br><span class="line"> <span class="comment">//这里默认不传就是给window,也可以用es6给参数设置默认参数</span></span><br><span class="line"> context = context || <span class="variable language_">window</span></span><br><span class="line"> args = args ? args : []</span><br><span class="line"> <span class="comment">//给context新增一个独一无二的属性以免覆盖原有属性</span></span><br><span class="line"> <span class="keyword">const</span> key = <span class="title class_">Symbol</span>()</span><br><span class="line"> context[key] = <span class="variable language_">this</span></span><br><span class="line"> <span class="comment">//通过隐式绑定的方式调用函数</span></span><br><span class="line"> <span class="keyword">const</span> result = context[key](...args)</span><br><span class="line"> <span class="comment">//删除添加的属性</span></span><br><span class="line"> <span class="keyword">delete</span> context[key]</span><br><span class="line"> <span class="comment">//返回函数调用的返回值</span></span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样一个简单的apply就实现了,可能会有一些边界问题和错误判断需要完善,这里就不做继续优化了</p><p>既然apply实现了,那么call同样也非常简单了,主要就是传参不一样</p><h4 id="实现call"><a href="#实现call" class="headerlink" title="实现call"></a>实现call</h4><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//传递参数从一个数组变成逐个传参了,不用...扩展运算符的也可以用arguments代替</span></span><br><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myCall</span> = <span class="keyword">function</span> (<span class="params">context, ...args</span>) {</span><br><span class="line"> <span class="comment">//这里默认不传就是给window,也可以用es6给参数设置默认参数</span></span><br><span class="line"> context = context || <span class="variable language_">window</span></span><br><span class="line"> args = args ? args : []</span><br><span class="line"> <span class="comment">//给context新增一个独一无二的属性以免覆盖原有属性</span></span><br><span class="line"> <span class="keyword">const</span> key = <span class="title class_">Symbol</span>()</span><br><span class="line"> context[key] = <span class="variable language_">this</span></span><br><span class="line"> <span class="comment">//通过隐式绑定的方式调用函数</span></span><br><span class="line"> <span class="keyword">const</span> result = context[key](...args)</span><br><span class="line"> <span class="comment">//删除添加的属性</span></span><br><span class="line"> <span class="keyword">delete</span> context[key]</span><br><span class="line"> <span class="comment">//返回函数调用的返回值</span></span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="实现bind"><a href="#实现bind" class="headerlink" title="实现bind"></a>实现bind</h4><p>bind和apply的区别在于,bind是返回一个绑定好的函数,apply是直接调用.其实想一想实现也很简单,就是返回一个函数,里面执行了apply上述的操作而已.不过有一个需要判断的点,因为返回新的函数,要考虑到使用new去调用,并且new的优先级比较高,所以需要判断new的调用,还有一个特点就是bind调用的时候可以传参,调用之后生成的新的函数也可以传参,效果是一样的,所以这一块也要做处理 因为上面已经实现了apply,这里就借用一下,实际上不借用就是把代码copy过来</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title class_">Function</span>.<span class="property"><span class="keyword">prototype</span></span>.<span class="property">myBind</span> = <span class="keyword">function</span> (<span class="params">context, ...args</span>) {</span><br><span class="line"> <span class="keyword">const</span> fn = <span class="variable language_">this</span></span><br><span class="line"> args = args ? args : []</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">function</span> <span class="title function_">newFn</span>(<span class="params">...newFnArgs</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="variable language_">this</span> <span class="keyword">instanceof</span> newFn) {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="title function_">fn</span>(...args, ...newFnArgs)</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> fn.<span class="title function_">apply</span>(context, [...args,...newFnArgs])</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="手写bind-apply-call"><a href="#手写bind-apply-call" class="headerlink" title="手写bind,apply,call"></a>手写bind,apply,call</h1><blockquote>
</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="JavaScript" scheme="https://www.qingyi1220.cn/tags/JavaScript/"/>
</entry>
<entry>
<title>CDN入门配置</title>
<link href="https://www.qingyi1220.cn/posts/16918.html"/>
<id>https://www.qingyi1220.cn/posts/16918.html</id>
<published>2022-12-02T16:00:00.000Z</published>
<updated>2023-03-28T13:37:09.718Z</updated>
<content type="html"><![CDATA[<h2 id="CDN配置原理:"><a href="#CDN配置原理:" class="headerlink" title="CDN配置原理:"></a><strong>CDN配置原理:</strong></h2><p>在第一次使用CDN服务时,往往会遇到一些问题,比如不知道回源HOST是干什么的,该不该配置?HOST 在计算机网络是主机、服务机的意思。顾明思议,回源HOST也就是客户访问CDN节点时,如果节点没有缓存这个资源,要在指定的主机寻找客户想要的目标资源。</p><p><img src="/posts/16918/host.png" alt="CDN入门配置"></p><h2 id="源站和回源HOST的区别:"><a href="#源站和回源HOST的区别:" class="headerlink" title="源站和回源HOST的区别:"></a><strong>源站和回源HOST的区别:</strong></h2><ul><li>源站:源站决定了回源时请求到的具体IP地址。</li><li>回源HOST:回源HOST决定了回源请求访问到该IP地址上的具体站点。</li></ul><p><strong>举个例子</strong>:您要去某单位找张三办事,如果告诉您某单位在 XX路XX号,这就相当于源站。到单位后您需要找张三,就还需要知道张三的房间号,XX楼XX层XX号办公室,这就相当于回源HOST。</p><h2 id="不需要设置回源HOST的应用场景"><a href="#不需要设置回源HOST的应用场景" class="headerlink" title="不需要设置回源HOST的应用场景"></a><strong>不需要设置回源HOST的应用场景</strong></h2><p>以下两种应用场景下,可以不设置回源HOST,或者设置回源HOST为加速域名。</p><ul><li>在您源站地址是加速域名服务器IP的情况下,您的回源HOST类型应为加速域名。</li><li>在您源站地址是OSS域名的情况下,您的回源HOST类型应为源站域名。</li></ul><h2 id="需要设置回源HOST的应用场景"><a href="#需要设置回源HOST的应用场景" class="headerlink" title="需要设置回源HOST的应用场景"></a><strong>需要设置回源HOST的应用场景</strong></h2><p>当您的服务器有多个站点,需要回源的站点不是加速域名对应的站点时,设置回源HOST。设置回源HOST要明白以下两点:</p><p>1、在您的源站选择了域名源站 <a href="http://www.a.com/">www.a.com</a> 的情况下,如果您选择将回源HOST设置为<a href="http://www.b.com/">www.b.com</a> , 则实际回源的是<a href="http://www.a.com/">www.a.com</a> 解析到的服务器上的站点<a href="http://www.b.com/">www.b.com</a> 。</p><p><strong>举例说明:</strong>您添加的加速域名是<a href="http://www.test.com/">www.test.com</a> ,该域名指向服务器 1.1.1.1,添加源站域名填写的域名是 <a href="http://www.a.com/">www.a.com</a> ,回源HOST设置为<a href="http://www.b.com/">www.b.com</a> 。如果<a href="http://www.a.com/">www.a.com</a> 指向服务器1.1.1.1。则实际回源到的是1.1.1.1 服务器上的 <a href="http://www.b.com/">www.b.com</a> 站点;如果<a href="http://www.a.com/">www.a.com</a> 指向服务器 2.2.2.2。则实际回源到的是2.2.2.2 服务器上的 <a href="http://www.b.com/">www.b.com</a> 站点。</p><p>2、在您的源站是IP源站 1.1.1.1的情况下,您选择将回源HOST设置为<a href="http://www.b.com/">www.b.com</a> ,则实际回源的是1.1.1.1对应的主机上的站点<a href="http://www.b.com/">www.b.com</a> 。</p><p><strong>举例说明:</strong>您添加的加速域名是<a href="http://www.test.com/">www.test.com</a> ,该域名指向服务器 1.1.1.1,添加源站域名填写的服务器IP,IP是2.2.2.2 ,回源HOST设置为 <a href="http://www.a.com/">www.a.com</a> 。则实际回源到的是2.2.2.2 服务器上的 <a href="http://www.a.com/">www.a.com</a> 站点。</p>]]></content>
<summary type="html"><h2 id="CDN配置原理:"><a href="#CDN配置原理:" class="headerlink" title="CDN配置原理:"></a><strong>CDN配置原理:</strong></h2><p>在第一次使用CDN服务时,往往会遇到一些问题,比如不知道回</summary>
<category term="网络技术" scheme="https://www.qingyi1220.cn/categories/%E7%BD%91%E7%BB%9C%E6%8A%80%E6%9C%AF/"/>
<category term="CDN" scheme="https://www.qingyi1220.cn/tags/CDN/"/>
</entry>
<entry>
<title>hexo小标题旋转风车</title>
<link href="https://www.qingyi1220.cn/posts/61132.html"/>
<id>https://www.qingyi1220.cn/posts/61132.html</id>
<published>2022-11-29T16:00:00.000Z</published>
<updated>2022-11-29T16:07:04.320Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如果不喜欢<code>Butterfly</code>主题自带的<code>曲别针</code>图标,可以换一种风格。(比如我现在使用的<code>旋转风车</code>)</p><h2 id="操作"><a href="#操作" class="headerlink" title="操作"></a>操作</h2><p>打开主题配置文件<code>_config.butterfly.yml</code></p><p>把<code>beautify</code>的<code>title-prefix-icon</code>选项设置为<code>'\f863'</code></p><p>(如果没有开启图标功能则需要将enable设置为true)</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">beautify:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">title-prefix-icon:</span> <span class="string">'\f863'</span></span><br></pre></td></tr></table></figure><p>在<code>inject</code>的<code>head</code>引入:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span> <span class="string"><style>#article-container.post-content</span> <span class="attr">h1:</span> <span class="string">before,</span> <span class="attr">h2:</span> <span class="string">before,</span> <span class="attr">h3:</span> <span class="string">before,</span> <span class="attr">h4:</span> <span class="string">before,</span> <span class="attr">h5:</span> <span class="string">before,</span> <span class="attr">h6:</span> <span class="string">before</span> { <span class="string">-webkit-animation:</span> <span class="string">avatar_turn_around</span> <span class="string">1s</span> <span class="string">linear</span> <span class="string">infinite;</span> <span class="string">-moz-animation:</span> <span class="string">avatar_turn_around</span> <span class="string">1s</span> <span class="string">linear</span> <span class="string">infinite;</span> <span class="string">-o-animation:</span> <span class="string">avatar_turn_around</span> <span class="string">1s</span> <span class="string">linear</span> <span class="string">infinite;</span> <span class="string">-ms-animation:</span> <span class="string">avatar_turn_around</span> <span class="string">1s</span> <span class="string">linear</span> <span class="string">infinite;</span> <span class="attr">animation:</span> <span class="string">avatar_turn_around</span> <span class="string">1s</span> <span class="string">linear</span> <span class="string">infinite;</span> }<span class="string"></style></span></span><br></pre></td></tr></table></figure><p>重新部署,执行 <code>hexo cl,hexo g,hexo s</code> 三连即可看到效果。</p>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如果不喜欢<code>Butterfly</code>主题自带的<code>曲别针</code>图标,可以换一种风格。(比如我现在使用的<c</summary>
<category term="Hexo" scheme="https://www.qingyi1220.cn/categories/Hexo/"/>
<category term="Butterfly美化" scheme="https://www.qingyi1220.cn/tags/Butterfly%E7%BE%8E%E5%8C%96/"/>
</entry>
<entry>
<title>gulp压缩博客资源</title>
<link href="https://www.qingyi1220.cn/posts/61402.html"/>
<id>https://www.qingyi1220.cn/posts/61402.html</id>
<published>2022-11-27T16:00:00.000Z</published>
<updated>2022-12-06T08:35:14.514Z</updated>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>gulp</code> 能够帮助用户自动压缩静态资源,配合各类下属插件,能够压缩包括 <code>css、js、html</code> 乃至各类格式的图片文件。(图片文件的压缩往往只能节省几十 KB,效果远远不如 <code>imagine</code>、<code>tinypng</code> 等压缩方式,所以此处不再写使用 <code>gulp</code> 压缩图片的内容)。</p><p>如果需要压缩图片的话推荐使用<a href="https://www.67tool.com/images/imgCompress">图片在线压缩</a>工具,将图像转换为WebP格式。WebP这是支持在互联网上无损和有损图像质量压缩的新格式。谷歌公司开发这种格式专为在网上迅速和方便地做好工作。其主要优点是,相对于其他图像格式,文件小,但图像质量相似。</p><h2 id="配置教程"><a href="#配置教程" class="headerlink" title="配置教程"></a>配置教程</h2><ol><li><p>安装 <code>Gulp</code> 插件:在博客根目录 <code>[Blogroot]</code> 打开终端,输入:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install gulp <span class="comment">#安装gulp插件</span></span><br></pre></td></tr></table></figure></li><li><p>安装各个下属插件以实现对各类静态资源的压缩。</p></li></ol><ul><li><p>压缩 HTML:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install gulp-htmlclean <span class="comment"># 清理html</span></span><br><span class="line">npm install gulp-htmlmin <span class="comment"># 压缩html</span></span><br></pre></td></tr></table></figure></li><li><p>压缩 CSS:</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install gulp-minify-css <span class="comment"># 压缩css</span></span><br></pre></td></tr></table></figure></li><li><p>压缩JS</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install gulp-uglify <span class="comment"># 混淆js</span></span><br></pre></td></tr></table></figure></li><li><p>压缩字体包</p><p>字体文件作为最让人又爱又恨的静态资源,为了保证站点文字匹配,全字库字体包一般都有10多M,这对博客的加载速度造成了巨大压力。fontmin 可以遍历博客内用到的字符,并将字体包内这些字符的字体样式提取出来输出为新的字体包。<br>font-min 仅支持压缩 ttf 格式的字体包。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install gulp-fontmin <span class="comment"># 压缩ttf</span></span><br></pre></td></tr></table></figure></li></ul><ol start="3"><li>为 <code>Gulp</code> 创建 <code>gulpfile.js</code> 任务脚本。在博客根目录 <code>[Blogroot]</code> 下新建 <code>gulpfile.js</code>, 打开 <code>[Blogroot]\gulpfile.js</code>, 输入以下内容:</li></ol><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="comment">//用到的各个插件</span></span><br><span class="line"><span class="keyword">const</span> gulp = <span class="built_in">require</span>(<span class="string">'gulp'</span>)</span><br><span class="line"><span class="keyword">const</span> htmlclean = <span class="built_in">require</span>(<span class="string">'gulp-htmlclean'</span>)</span><br><span class="line"><span class="keyword">const</span> htmlmin = <span class="built_in">require</span>(<span class="string">'gulp-htmlmin'</span>)</span><br><span class="line"><span class="keyword">const</span> minifycss = <span class="built_in">require</span>(<span class="string">'gulp-minify-css'</span>)</span><br><span class="line"><span class="keyword">const</span> uglify = <span class="built_in">require</span>(<span class="string">'gulp-uglify'</span>)</span><br><span class="line"><span class="keyword">const</span> fontmin = <span class="built_in">require</span>(<span class="string">'gulp-fontmin'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">//压缩html</span></span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'minify-html'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">return</span> gulp</span><br><span class="line"> .<span class="title function_">src</span>(<span class="string">'./public/**/*.html'</span>)</span><br><span class="line"> .<span class="title function_">pipe</span>(<span class="title function_">htmlclean</span>())</span><br><span class="line"> .<span class="title function_">pipe</span>(</span><br><span class="line"> <span class="title function_">htmlmin</span>({</span><br><span class="line"> <span class="attr">removeComments</span>: <span class="literal">true</span>, <span class="comment">//清除html注释</span></span><br><span class="line"> <span class="attr">collapseWhitespace</span>: <span class="literal">true</span>, <span class="comment">//压缩html</span></span><br><span class="line"> <span class="attr">collapseBooleanAttributes</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">//省略布尔属性的值,例如:<input checked="true"/> ==> <input /></span></span><br><span class="line"> <span class="attr">removeEmptyAttributes</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">//删除所有空格作属性值,例如:<input id="" /> ==> <input /></span></span><br><span class="line"> <span class="attr">removeScriptTypeAttributes</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">//删除<script>的type="text/javascript"</span></span><br><span class="line"> <span class="attr">removeStyleLinkTypeAttributes</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="comment">//删除<style>和<link>的 type="text/css"</span></span><br><span class="line"> <span class="attr">minifyJS</span>: <span class="literal">true</span>, <span class="comment">//压缩页面 JS</span></span><br><span class="line"> <span class="attr">minifyCSS</span>: <span class="literal">true</span>, <span class="comment">//压缩页面 CSS</span></span><br><span class="line"> <span class="attr">minifyURLs</span>: <span class="literal">true</span> <span class="comment">//压缩页面URL</span></span><br><span class="line"> })</span><br><span class="line"> )</span><br><span class="line"> .<span class="title function_">pipe</span>(gulp.<span class="title function_">dest</span>(<span class="string">'./public'</span>))</span><br><span class="line">})</span><br><span class="line"><span class="comment">//压缩css</span></span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'minify-css'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">return</span> gulp</span><br><span class="line"> .<span class="title function_">src</span>([<span class="string">'./public/**/*.css'</span>])</span><br><span class="line"> .<span class="title function_">pipe</span>(</span><br><span class="line"> <span class="title function_">minifycss</span>({</span><br><span class="line"> <span class="attr">compatibility</span>: <span class="string">'ie8'</span></span><br><span class="line"> })</span><br><span class="line"> )</span><br><span class="line"> .<span class="title function_">pipe</span>(gulp.<span class="title function_">dest</span>(<span class="string">'./public'</span>))</span><br><span class="line">})</span><br><span class="line"><span class="comment">// 压缩js</span></span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'minify-js'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">return</span> gulp.<span class="title function_">src</span>(<span class="string">'./public/**/*.js'</span>).<span class="title function_">pipe</span>(<span class="title function_">uglify</span>()).<span class="title function_">pipe</span>(gulp.<span class="title function_">dest</span>(<span class="string">'./public'</span>))</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_">minifyFont</span>(<span class="params">text, cb</span>) {</span><br><span class="line"> gulp</span><br><span class="line"> .<span class="title function_">src</span>(<span class="string">'./public/font/*.ttf'</span>) <span class="comment">//原字体所在目录</span></span><br><span class="line"> .<span class="title function_">pipe</span>(</span><br><span class="line"> <span class="title function_">fontmin</span>({</span><br><span class="line"> <span class="attr">text</span>: text</span><br><span class="line"> })</span><br><span class="line"> )</span><br><span class="line"> .<span class="title function_">pipe</span>(gulp.<span class="title function_">dest</span>(<span class="string">'./public/font/'</span>)) <span class="comment">//压缩后的输出目录</span></span><br><span class="line"> .<span class="title function_">on</span>(<span class="string">'end'</span>, cb)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'mini-font'</span>, <span class="function">(<span class="params">cb</span>) =></span> {</span><br><span class="line"> <span class="keyword">var</span> buffers = []</span><br><span class="line"> gulp</span><br><span class="line"> .<span class="title function_">src</span>([<span class="string">'./public/**/*.html'</span>]) <span class="comment">//HTML文件所在目录请根据自身情况修改</span></span><br><span class="line"> .<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="keyword">function</span> (<span class="params">file</span>) {</span><br><span class="line"> buffers.<span class="title function_">push</span>(file.<span class="property">contents</span>)</span><br><span class="line"> })</span><br><span class="line"> .<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> text = <span class="title class_">Buffer</span>.<span class="title function_">concat</span>(buffers).<span class="title function_">toString</span>(<span class="string">'utf-8'</span>)</span><br><span class="line"> <span class="title function_">minifyFont</span>(text, cb)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"><span class="comment">// 运行gulp命令时依次执行以下任务</span></span><br><span class="line">gulp.<span class="title function_">task</span>(<span class="string">'default'</span>, gulp.<span class="title function_">parallel</span>(<span class="string">'minify-html'</span>, <span class="string">'minify-css'</span>, <span class="string">'minify-js'</span>, <span class="string">'mini-font'</span>))</span><br></pre></td></tr></table></figure><ol start="4"><li>在每次运行完 <code>hexo generate</code> 生成静态页面后,运行 <code>gulp</code> 对其进行压缩。指令流程如下:</li></ol><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hexo clean</span><br><span class="line">hexo generate</span><br><span class="line">gulp</span><br><span class="line">hexo server 或 hexo deploy</span><br></pre></td></tr></table></figure><ol start="5"><li>关于 font-min 的补充说明,在本文中,是通过读取所有编译好的 html 文件(<code>./public/**/*.html</code>)中的字符,然后匹配原有字体包内<code>./public/font/*.ttf</code> 字体样式,输出压缩后的字体包到<code>./public/font/</code> 目录。所以最终引用字体的相对路径应该是 <code>/font/*.ttf</code>。而本地测试时,如果没有运行 <code>gulp</code>,自然也就不会输出压缩字体包到 <code>public</code> 目录,也就看不到字体样式。</li></ol>]]></content>
<summary type="html"><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><code>gulp</code> 能够帮助用户自动压缩静态资源,配合各类下属插件,能够压缩包括 <code>css、js、html</co</summary>
<category term="Hexo" scheme="https://www.qingyi1220.cn/categories/Hexo/"/>
<category term="Hexo优化" scheme="https://www.qingyi1220.cn/tags/Hexo%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>vue引入网络图片跨域问题</title>
<link href="https://www.qingyi1220.cn/posts/12492.html"/>
<id>https://www.qingyi1220.cn/posts/12492.html</id>
<published>2022-11-27T16:00:00.000Z</published>
<updated>2022-12-04T13:07:50.330Z</updated>
<content type="html"><![CDATA[<h1 id="vue请求网络接口中图片报错403解决办法"><a href="#vue请求网络接口中图片报错403解决办法" class="headerlink" title="vue请求网络接口中图片报错403解决办法"></a>vue请求网络接口中图片报错403解决办法</h1><h2 id="造成原因:"><a href="#造成原因:" class="headerlink" title="造成原因:"></a>造成原因:</h2><p>网络接口都是有请求限制的,特别是图片字段值是绝对路径的情况,请求的时候就会报错403 forbidden。</p><h2 id="解决办法:"><a href="#解决办法:" class="headerlink" title="解决办法:"></a>解决办法:</h2><h4 id="方法一:"><a href="#方法一:" class="headerlink" title="方法一:"></a>方法一:</h4><p>这个时候应该使用缓存的方式拿到请求图片的地址进行缓存,只要在请求到的图片链接前面加上<code>https://images.weserv.nl/?url=</code>即可(<code>这是一个专门缓存图片的网址</code>)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">getImages</span>(<span class="params"> _url </span>){</span><br><span class="line"> <span class="keyword">if</span>( _url !== <span class="literal">undefined</span> ){</span><br><span class="line"> <span class="keyword">let</span> _u = _url.<span class="title function_">substring</span>( <span class="number">7</span> );</span><br><span class="line"> <span class="keyword">return</span> <span class="string">'https://images.weserv.nl/?url='</span> + _u;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"><span class="number">123456</span></span><br></pre></td></tr></table></figure><blockquote><p><code>_u</code>:提取http://后面的部分<br><code>_url</code>:是请求接口后返回的图片字段</p></blockquote><p><img src="/posts/12492/1.png"></p><h4 id="方法二:"><a href="#方法二:" class="headerlink" title="方法二:"></a>方法二:</h4><p>在<code>index.html</code> 中增加一个<code>meta</code>标签,不发送HTTP Referer首部 </p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="tag"><<span class="name">meta</span> <span class="attr">name</span>=<span class="string">"referrer"</span> <span class="attr">content</span>=<span class="string">"no-referrer"</span>></span></span><br></pre></td></tr></table></figure><blockquote><p>记得一定要重启服务,不然无法生效</p></blockquote>]]></content>
<summary type="html"><h1 id="vue请求网络接口中图片报错403解决办法"><a href="#vue请求网络接口中图片报错403解决办法" class="headerlink" title="vue请求网络接口中图片报错403解决办法"></a>vue请求网络接口中图片报错403解决办法</</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://www.qingyi1220.cn/tags/Vue/"/>
</entry>
<entry>
<title>Node.js</title>
<link href="https://www.qingyi1220.cn/posts/5063.html"/>
<id>https://www.qingyi1220.cn/posts/5063.html</id>
<published>2022-11-24T16:00:00.000Z</published>
<updated>2022-12-04T13:07:39.218Z</updated>
<content type="html"><![CDATA[<p>文章转载自 <a href="http://www.qfedu.com/teachers/90465.html">Kerwin</a>,如有侵权,请联系作者,本站仅当学习使用。</p><h4 id="一、Node-js基础"><a href="#一、Node-js基础" class="headerlink" title="一、Node.js基础"></a>一、Node.js基础</h4><h5 id="1-认识Node-js"><a href="#1-认识Node-js" class="headerlink" title="1. 认识Node.js"></a>1. 认识Node.js</h5><blockquote><p>Node.js是一个javascript运行环境。它让javascript可以开发后端程序,实现几乎其他后端语言实现的所有功能,可以与PHP、Java、Python、.NET、Ruby等后端语言平起平坐。</p><p>Nodejs是基于V8引擎,V8是Google发布的开源JavaScript引擎,本身就是用于Chrome浏览器的js解释部分,但是Ryan Dahl 这哥们,鬼才般的,把这个V8搬到了服务器上,用于做服务器的软件。</p></blockquote><h6 id="01-nodejs的特性"><a href="#01-nodejs的特性" class="headerlink" title="01 nodejs的特性"></a>01 nodejs的特性</h6><ul><li>Nodejs语法完全是js语法,只要你懂js基础就可以学会Nodejs后端开发</li><li>NodeJs超强的高并发能力,实现高性能服务器</li><li>开发周期短、开发成本低、学习成本低</li></ul><h6 id="02-浏览器环境vs-node环境"><a href="#02-浏览器环境vs-node环境" class="headerlink" title="02 浏览器环境vs node环境"></a>02 浏览器环境vs node环境</h6><p><img src="/posts/5063/image-20220209152247426.webp" alt="image-20220209152247426"></p><p>Node.js 可以解析JS代码(没有浏览器安全级别的限制)提供很多系统级别的API,如:</p><ul><li><p>文件的读写 (File System)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"></span><br><span class="line">fs.<span class="title function_">readFile</span>(<span class="string">'./ajax.png'</span>, <span class="string">'utf-8'</span>, <span class="function">(<span class="params">err, content</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(content)</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li><li><p>进程的管理 (Process)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">main</span>(<span class="params">argv</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(argv)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="title function_">main</span>(process.<span class="property">argv</span>.<span class="title function_">slice</span>(<span class="number">2</span>))</span><br><span class="line"></span><br></pre></td></tr></table></figure></li><li><p>网络通信 (HTTP/HTTPS)</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">"http"</span>)</span><br><span class="line"></span><br><span class="line">http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req,res</span>) =></span> {</span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, {</span><br><span class="line"> <span class="string">"content-type"</span>: <span class="string">"text/plain"</span></span><br><span class="line"> })</span><br><span class="line"> res.<span class="title function_">write</span>(<span class="string">"hello nodejs"</span>)</span><br><span class="line"> res.<span class="title function_">end</span>()</span><br><span class="line">}).<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure></li></ul><h5 id="2-开发环境搭建"><a href="#2-开发环境搭建" class="headerlink" title="2. 开发环境搭建"></a>2. 开发环境搭建</h5><blockquote><p><a href="http://nodejs.cn/download/">http://nodejs.cn/download/</a></p></blockquote><p><img src="/posts/5063/image-20220210095903409.webp" alt="image-20220210095903409"></p><h5 id="3-模块、包、commonJS"><a href="#3-模块、包、commonJS" class="headerlink" title="3. 模块、包、commonJS"></a>3. 模块、包、commonJS</h5><p><img src="/posts/5063/image-20220210100015768.webp" alt="image-20220210100015768"></p><h6 id="02-CommonJS规范"><a href="#02-CommonJS规范" class="headerlink" title="02 CommonJS规范"></a>02 CommonJS规范</h6><img src="/posts/5063/image-20220210101652166.webp" alt="image-20220210101652166" style="zoom: 67%;"><img src="/posts/5063/image-20220210101720533.webp" alt="image-20220210101720533" style="zoom: 67%;"><h6 id="03-modules模块化规范写法"><a href="#03-modules模块化规范写法" class="headerlink" title="03 modules模块化规范写法"></a>03 modules模块化规范写法</h6><p>我们可以把公共的功能 抽离成为一个单独的 js 文件 作为一个模块,默认情况下面这个模块里面的方法或者属性,外面是没法访问的。如果要让外部可以访问模块里面的方法或者属性,就必须在模块里面通过 exports 或者 module.exports 暴露属性或者方法。</p><p>m1.js:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> name = <span class="string">'gp19'</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title function_">sayName</span> = (<span class="params"></span>) => {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(name)</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="string">'module 1'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 接口暴露方法一:</span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = {</span><br><span class="line"> <span class="attr">say</span>: sayName</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="built_in">exports</span>.<span class="property">say</span> = sayName</span><br><span class="line"></span><br><span class="line"><span class="comment">// 错误!</span></span><br><span class="line"><span class="built_in">exports</span> = {</span><br><span class="line"> <span class="attr">say</span>: sayName</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>main.js:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> m1 = <span class="built_in">require</span>(<span class="string">'./m1'</span>)</span><br><span class="line">m1.<span class="title function_">say</span>()</span><br></pre></td></tr></table></figure><h5 id="4-Npm-amp-Yarn"><a href="#4-Npm-amp-Yarn" class="headerlink" title="4. Npm&Yarn"></a>4. Npm&Yarn</h5><h6 id="01-npm的使用"><a href="#01-npm的使用" class="headerlink" title="01 npm的使用"></a>01 npm的使用</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">npm init</span><br><span class="line">npm install 包名 –g (uninstall,update)</span><br><span class="line">npm install 包名 --save-dev (uninstall,update)</span><br><span class="line">npm list -g (不加-g,列举当前目录下的安装包)</span><br><span class="line">npm info 包名(详细信息) npm info 包名 <span class="title function_">version</span>(获取最新版本)</span><br><span class="line">npm install md5@<span class="number">1</span>(安装指定版本)</span><br><span class="line">npm <span class="title function_">outdated</span>( 检查包是否已经过时)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="string">"dependencies"</span>: { <span class="string">"md5"</span>: <span class="string">"^2.1.0"</span> } ^ 表示 如果 直接npm install 将会 安md5</span><br><span class="line"> <span class="number">2.</span>*.* 最新版本</span><br><span class="line"></span><br><span class="line"><span class="string">"dependencies"</span>: { <span class="string">"md5"</span>: <span class="string">"~2.1.0"</span> } ~ 表示 如果 直接npm install 将会 安装md5 <span class="number">2.1</span>.* 最新版本</span><br><span class="line"></span><br><span class="line"><span class="string">"dependencies"</span>: { <span class="string">"md5"</span>: <span class="string">"*"</span> } * 表示 如果 直接npm install 将会 安装 md5 最新版本</span><br></pre></td></tr></table></figure><h6 id="02-全局安装-nrm"><a href="#02-全局安装-nrm" class="headerlink" title="02 全局安装 nrm"></a>02 全局安装 nrm</h6><blockquote><p>NRM (npm registry manager)是npm的镜像源管理工具,有时候国外资源太慢,使用这个就可以快速地在 npm 源间切换。</p></blockquote><p><code>手动切换方法: npm config set registry https://registry.npm.taobao.org</code></p><p><strong>安装 nrm</strong></p><p>在命令行执行命令,npm install -g nrm,全局安装nrm。</p><p><strong>使用 nrm</strong></p><p>执行命令 nrm ls 查看可选的源。 其中,带*的是当前使用的源,上面的输出表明当前源是官方源。</p><p><strong>切换 nrm</strong></p><p>如果要切换到taobao源,执行命令nrm use taobao。</p><p><strong>测试速度</strong></p><p>你还可以通过 nrm test 测试相应源的响应时间。</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">nrm <span class="built_in">test</span></span><br></pre></td></tr></table></figure><blockquote><p> 扩展:</p></blockquote><blockquote><p><img src="/posts/5063/image-20220210114017616.webp" alt="image-20220210114017616"></p></blockquote> <figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g cnpm --registry=https://registry.npmmirror.com</span><br></pre></td></tr></table></figure><h6 id="03-yarn使用"><a href="#03-yarn使用" class="headerlink" title="03 yarn使用"></a>03 yarn使用</h6><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">npm install -g yarn</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">对比<span class="attr">npm</span>:</span><br><span class="line">速度超快: <span class="title class_">Yarn</span> 缓存了每个下载过的包,所以再次使用时无需重复下载。 同时利用并行下载以最大化资源利用率,因此安装速度更快。</span><br><span class="line"> 超级安全: 在执行代码之前,<span class="title class_">Yarn</span> 会通过算法校验每个安装包的完整性。</span><br><span class="line"></span><br><span class="line">开始新项目</span><br><span class="line">yarn init </span><br><span class="line">添加依赖包</span><br><span class="line">yarn add [package] </span><br><span class="line">yarn add [package]@[version] </span><br><span class="line">yarn add [package] --dev </span><br><span class="line">升级依赖包</span><br><span class="line"> yarn upgrade [package]@[version] </span><br><span class="line">移除依赖包</span><br><span class="line"> yarn remove [package]</span><br><span class="line"> </span><br><span class="line">安装项目的全部依赖</span><br><span class="line"> yarn install </span><br></pre></td></tr></table></figure><h5 id="5-内置模块"><a href="#5-内置模块" class="headerlink" title="5. 内置模块"></a>5. 内置模块</h5><h6 id="01-http模块"><a href="#01-http模块" class="headerlink" title="01 http模块"></a>01 http模块</h6><blockquote><p>要使用 HTTP 服务器和客户端,则必须 <code>require('http')</code>。</p></blockquote><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建本地服务器来从其接收数据</span></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, { <span class="string">'Content-Type'</span>: <span class="string">'application/json'</span> });</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">data</span>: <span class="string">'Hello World!'</span></span><br><span class="line"> }));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">server.<span class="title function_">listen</span>(<span class="number">8000</span>);</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建本地服务器来从其接收数据</span></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 监听请求事件</span></span><br><span class="line">server.<span class="title function_">on</span>(<span class="string">'request'</span>, <span class="function">(<span class="params">request, res</span>) =></span> {</span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, { <span class="string">'Content-Type'</span>: <span class="string">'application/json'</span> });</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">data</span>: <span class="string">'Hello World!'</span></span><br><span class="line"> }));</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">server.<span class="title function_">listen</span>(<span class="number">8000</span>);</span><br></pre></td></tr></table></figure><h6 id="02-url模块"><a href="#02-url模块" class="headerlink" title="02 url模块"></a>02 url模块</h6><p><strong>02.1 parse</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">'url'</span>)</span><br><span class="line"><span class="keyword">const</span> urlString = <span class="string">'https://www.baidu.com:443/ad/index.html?id=8&name=mouse#tag=110'</span></span><br><span class="line"><span class="keyword">const</span> parsedStr = url.<span class="title function_">parse</span>(urlString)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(parsedStr)</span><br></pre></td></tr></table></figure><p><strong>02.2 format</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">'url'</span>)</span><br><span class="line"><span class="keyword">const</span> urlObject = {</span><br><span class="line"> <span class="attr">protocol</span>: <span class="string">'https:'</span>,</span><br><span class="line"> <span class="attr">slashes</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">auth</span>: <span class="literal">null</span>,</span><br><span class="line"> <span class="attr">host</span>: <span class="string">'www.baidu.com:443'</span>,</span><br><span class="line"> <span class="attr">port</span>: <span class="string">'443'</span>,</span><br><span class="line"> <span class="attr">hostname</span>: <span class="string">'www.baidu.com'</span>,</span><br><span class="line"> <span class="attr">hash</span>: <span class="string">'#tag=110'</span>,</span><br><span class="line"> <span class="attr">search</span>: <span class="string">'?id=8&name=mouse'</span>,</span><br><span class="line"> <span class="attr">query</span>: { <span class="attr">id</span>: <span class="string">'8'</span>, <span class="attr">name</span>: <span class="string">'mouse'</span> },</span><br><span class="line"> <span class="attr">pathname</span>: <span class="string">'/ad/index.html'</span>,</span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/ad/index.html?id=8&name=mouse'</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> parsedObj = url.<span class="title function_">format</span>(urlObject)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(parsedObj)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>02.3 resolve</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">'url'</span>)</span><br><span class="line"><span class="keyword">var</span> a = url.<span class="title function_">resolve</span>(<span class="string">'/one/two/three'</span>, <span class="string">'four'</span>) ( 注意最后加/ ,不加/的区别 )</span><br><span class="line"><span class="keyword">var</span> b = url.<span class="title function_">resolve</span>(<span class="string">'http://example.com/'</span>, <span class="string">'/one'</span>)</span><br><span class="line"><span class="keyword">var</span> c = url.<span class="title function_">resolve</span>(<span class="string">'http://example.com/one'</span>, <span class="string">'/two'</span>)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a + <span class="string">","</span> + b + <span class="string">","</span> + c)</span><br></pre></td></tr></table></figure><h6 id="03-querystring模块"><a href="#03-querystring模块" class="headerlink" title="03 querystring模块"></a>03 querystring模块</h6><p><strong>03.1 parse</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"><span class="keyword">var</span> qs = <span class="string">'x=3&y=4'</span></span><br><span class="line"><span class="keyword">var</span> parsed = querystring.<span class="title function_">parse</span>(qs)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(parsed)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>03.2 stringify</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"><span class="keyword">var</span> qo = {</span><br><span class="line"> <span class="attr">x</span>: <span class="number">3</span>,</span><br><span class="line"> <span class="attr">y</span>: <span class="number">4</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> parsed = querystring.<span class="title function_">stringify</span>(qo)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(parsed)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><strong>03.3 escape/unescape</strong></p><img src="/posts/5063/image-20220213211406894.webp" alt="image-20220213211406894" style="zoom:67%;"><img src="/posts/5063/image-20220213211423142.webp" alt="image-20220213211423142" style="zoom:67%;"><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"><span class="keyword">var</span> str = <span class="string">'id=3&city=北京&url=https://www.baidu.com'</span></span><br><span class="line"><span class="keyword">var</span> escaped = querystring.<span class="built_in">escape</span>(str)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(escaped)</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"><span class="keyword">var</span> str = <span class="string">'id%3D3%26city%3D%E5%8C%97%E4%BA%AC%26url%3Dhttps%3A%2F%2Fwww.baidu.com'</span></span><br><span class="line"><span class="keyword">var</span> unescaped = querystring.<span class="built_in">unescape</span>(str)</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(unescaped)</span><br></pre></td></tr></table></figure><h6 id="04-http模块补充"><a href="#04-http模块补充" class="headerlink" title="04 http模块补充"></a>04 http模块补充</h6><p><strong>04.1 接口:jsonp</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>)</span><br><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">'url'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> urlObj = url.<span class="title function_">parse</span>(req.<span class="property">url</span>, <span class="literal">true</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (urlObj.<span class="property">pathname</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'/api/user'</span>:</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="string">`<span class="subst">${urlObj.query.cb}</span>({"name": "gp145"})`</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="attr">default</span>:</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="string">'404.'</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">8080</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'localhost:8080'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p><strong>04.2 跨域:CORS</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>)</span><br><span class="line"><span class="keyword">const</span> url = <span class="built_in">require</span>(<span class="string">'url'</span>)</span><br><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> data = <span class="string">''</span></span><br><span class="line"> <span class="keyword">let</span> urlObj = url.<span class="title function_">parse</span>(req.<span class="property">url</span>, <span class="literal">true</span>)</span><br><span class="line"></span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, {</span><br><span class="line"> <span class="string">'content-type'</span>: <span class="string">'application/json;charset=utf-8'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Origin'</span>: <span class="string">'*'</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function">(<span class="params">chunk</span>) =></span> {</span><br><span class="line"> data += chunk</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">responseResult</span>(querystring.<span class="title function_">parse</span>(data))</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">responseResult</span>(<span class="params">data</span>) {</span><br><span class="line"> <span class="keyword">switch</span> (urlObj.<span class="property">pathname</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="string">'/api/login'</span>:</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">message</span>: data</span><br><span class="line"> }))</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> <span class="attr">default</span>:</span><br><span class="line"> res.<span class="title function_">end</span>(<span class="string">'404.'</span>)</span><br><span class="line"> <span class="keyword">break</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">8080</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'localhost:8080'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p><strong>04.3 模拟get</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>)</span><br><span class="line"><span class="keyword">var</span> https = <span class="built_in">require</span>(<span class="string">'https'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 1、接口 2、跨域</span></span><br><span class="line"><span class="keyword">const</span> server = http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">request, response</span>) =></span> {</span><br><span class="line"> <span class="keyword">var</span> url = request.<span class="property">url</span>.<span class="title function_">substr</span>(<span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> data = <span class="string">''</span></span><br><span class="line"></span><br><span class="line"> response.<span class="title function_">writeHeader</span>(<span class="number">200</span>, {</span><br><span class="line"> <span class="string">'content-type'</span>: <span class="string">'application/json;charset=utf-8'</span>,</span><br><span class="line"> <span class="string">'Access-Control-Allow-Origin'</span>: <span class="string">'*'</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> https.<span class="title function_">get</span>(<span class="string">`https://m.lagou.com/listmore.json<span class="subst">${url}</span>`</span>, <span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"></span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function">(<span class="params">chunk</span>) =></span> {</span><br><span class="line"> data += chunk</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =></span> {</span><br><span class="line"> response.<span class="title function_">end</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">ret</span>: <span class="literal">true</span>,</span><br><span class="line"> data</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">server.<span class="title function_">listen</span>(<span class="number">8080</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'localhost:8080'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p><strong>04.4 模拟post:服务器提交(攻击)</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> https = <span class="built_in">require</span>(<span class="string">'https'</span>)</span><br><span class="line"><span class="keyword">const</span> querystring = <span class="built_in">require</span>(<span class="string">'querystring'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> postData = querystring.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">province</span>: <span class="string">'上海'</span>,</span><br><span class="line"> <span class="attr">city</span>: <span class="string">'上海'</span>,</span><br><span class="line"> <span class="attr">district</span>: <span class="string">'宝山区'</span>,</span><br><span class="line"> <span class="attr">address</span>: <span class="string">'同济支路199号智慧七立方3号楼2-4层'</span>,</span><br><span class="line"> <span class="attr">latitude</span>: <span class="number">43.0</span>,</span><br><span class="line"> <span class="attr">longitude</span>: <span class="number">160.0</span>,</span><br><span class="line"> <span class="attr">message</span>: <span class="string">'求购一条小鱼'</span>,</span><br><span class="line"> <span class="attr">contact</span>: <span class="string">'13666666'</span>,</span><br><span class="line"> <span class="attr">type</span>: <span class="string">'sell'</span>,</span><br><span class="line"> <span class="attr">time</span>: <span class="number">1571217561</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> options = {</span><br><span class="line"> <span class="attr">protocol</span>: <span class="string">'https:'</span>,</span><br><span class="line"> <span class="attr">hostname</span>: <span class="string">'ik9hkddr.qcloud.la'</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="string">'POST'</span>,</span><br><span class="line"> <span class="attr">port</span>: <span class="number">443</span>,</span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/index.php/trade/add_item'</span>,</span><br><span class="line"> <span class="attr">headers</span>: {</span><br><span class="line"> <span class="string">'Content-Type'</span>: <span class="string">'application/x-www-form-urlencoded'</span>,</span><br><span class="line"> <span class="string">'Content-Length'</span>: <span class="title class_">Buffer</span>.<span class="title function_">byteLength</span>(postData)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">doPost</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">let</span> data</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> req = https.<span class="title function_">request</span>(options, <span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function"><span class="params">chunk</span> =></span> data += chunk)</span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(data)</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> req.<span class="title function_">write</span>(postData)</span><br><span class="line"> req.<span class="title function_">end</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// setInterval(() => {</span></span><br><span class="line"><span class="comment">// doPost()</span></span><br><span class="line"><span class="comment">// }, 1000)</span></span><br></pre></td></tr></table></figure><p><strong>04.5 爬虫</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> https = <span class="built_in">require</span>(<span class="string">'https'</span>)</span><br><span class="line"><span class="keyword">const</span> http = <span class="built_in">require</span>(<span class="string">'http'</span>)</span><br><span class="line"><span class="keyword">const</span> cheerio = <span class="built_in">require</span>(<span class="string">'cheerio'</span>)</span><br><span class="line"></span><br><span class="line">http.<span class="title function_">createServer</span>(<span class="function">(<span class="params">request, response</span>) =></span> {</span><br><span class="line"> response.<span class="title function_">writeHead</span>(<span class="number">200</span>, {</span><br><span class="line"> <span class="string">'content-type'</span>: <span class="string">'application/json;charset=utf-8'</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> options = {</span><br><span class="line"> <span class="comment">// protocol: 'https:',</span></span><br><span class="line"> <span class="attr">hostname</span>: <span class="string">'i.maoyan.com'</span>,</span><br><span class="line"> <span class="attr">port</span>: <span class="number">443</span>,</span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/'</span>,</span><br><span class="line"> <span class="attr">method</span>: <span class="string">'GET'</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> req = https.<span class="title function_">request</span>(options, <span class="function">(<span class="params">res</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> data = <span class="string">''</span></span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="function">(<span class="params">chunk</span>) =></span> {</span><br><span class="line"> data += chunk</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> res.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">filterData</span>(data)</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">function</span> <span class="title function_">filterData</span>(<span class="params">data</span>) {</span><br><span class="line"> <span class="comment">// console.log(data)</span></span><br><span class="line"> <span class="keyword">let</span> $ = cheerio.<span class="title function_">load</span>(data)</span><br><span class="line"> <span class="keyword">let</span> $movieList = $(<span class="string">'.column.content'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>($movieList)</span><br><span class="line"> <span class="keyword">let</span> movies = []</span><br><span class="line"> $movieList.<span class="title function_">each</span>(<span class="function">(<span class="params">index, value</span>) =></span> {</span><br><span class="line"> movies.<span class="title function_">push</span>({</span><br><span class="line"> <span class="attr">title</span>: $(value).<span class="title function_">find</span>(<span class="string">'.movie-title .title'</span>).<span class="title function_">text</span>(),</span><br><span class="line"> <span class="attr">detail</span>: $(value).<span class="title function_">find</span>(<span class="string">'.detail .actor'</span>).<span class="title function_">text</span>(),</span><br><span class="line"> })</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> response.<span class="title function_">end</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>(movies))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> req.<span class="title function_">end</span>()</span><br><span class="line">}).<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h6 id="05-event模块"><a href="#05-event模块" class="headerlink" title="05 event模块"></a>05 event模块</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">EventEmitter</span> = <span class="built_in">require</span>(<span class="string">'events'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">class</span> <span class="title class_">MyEventEmitter</span> <span class="keyword">extends</span> <span class="title class_ inherited__">EventEmitter</span> {}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> event = <span class="keyword">new</span> <span class="title class_">MyEventEmitter</span>()</span><br><span class="line"></span><br><span class="line">event.<span class="title function_">on</span>(<span class="string">'play'</span>, <span class="function">(<span class="params">movie</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(movie)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">event.<span class="title function_">emit</span>(<span class="string">'play'</span>, <span class="string">'我和我的祖国'</span>)</span><br><span class="line">event.<span class="title function_">emit</span>(<span class="string">'play'</span>, <span class="string">'中国机长'</span>)</span><br></pre></td></tr></table></figure><h6 id="06-fs文件操作模块"><a href="#06-fs文件操作模块" class="headerlink" title="06 fs文件操作模块"></a>06 fs文件操作模块</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 创建文件夹</span></span><br><span class="line">fs.<span class="title function_">mkdir</span>(<span class="string">'./logs'</span>, <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done.'</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">fs.<span class="title function_">rename</span>(<span class="string">'./logs'</span>, <span class="string">'./log'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done'</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">fs.<span class="title function_">rmdir</span>(<span class="string">'./log'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done.'</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">fs.<span class="title function_">writeFile</span>(</span><br><span class="line"> <span class="string">'./logs/log1.txt'</span>,</span><br><span class="line"> <span class="string">'hello'</span>,</span><br><span class="line"> <span class="comment">// 错误优先的回调函数</span></span><br><span class="line"> <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="keyword">if</span> (err) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(err.<span class="property">message</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_">log</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><br><span class="line"><span class="comment">// 给文件追加内容</span></span><br><span class="line">fs.<span class="title function_">appendFile</span>(<span class="string">'./logs/log1.txt'</span>, <span class="string">'\nworld'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done.'</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">fs.<span class="title function_">readFile</span>(<span class="string">'./logs/log1.txt'</span>, <span class="string">'utf-8'</span>, <span class="function">(<span class="params">err, data</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(data)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 删除文件</span></span><br><span class="line">fs.<span class="title function_">unlink</span>(<span class="string">'./logs/log1.txt'</span>, <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done.'</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">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">10</span>; i++) {</span><br><span class="line"> fs.<span class="title function_">writeFile</span>(<span class="string">`./logs/log-<span class="subst">${i}</span>.txt`</span>, <span class="string">`log-<span class="subst">${i}</span>`</span>, <span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'done.'</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">fs.<span class="title function_">readdir</span>(<span class="string">'./'</span>, <span class="function">(<span class="params">err, data</span>) =></span> {</span><br><span class="line"> data.<span class="title function_">forEach</span>(<span class="function">(<span class="params">value, index</span>) =></span> {</span><br><span class="line"> fs.<span class="title function_">stat</span>(<span class="string">`./<span class="subst">${value}</span>`</span>, <span class="function">(<span class="params">err, stats</span>) =></span> {</span><br><span class="line"> <span class="comment">// console.log(value + ':' + stats.size)</span></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(value + <span class="string">' is '</span> + (stats.<span class="title function_">isDirectory</span>() ? <span class="string">'directory'</span> : <span class="string">'file'</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">try</span> {</span><br><span class="line"> <span class="keyword">const</span> content = fs.<span class="title function_">readFileSync</span>(<span class="string">'./logs/log-1.txt'</span>, <span class="string">'utf-8'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(content)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">0</span>)</span><br><span class="line">} <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(e.<span class="property">message</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">fs.<span class="title function_">readFile</span>(<span class="string">'./logs/log-0.txt'</span>, <span class="string">'utf-8'</span>, <span class="function">(<span class="params">err, content</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(content)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">0</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><br><span class="line"><span class="comment">// 异步读取文件:方法二</span></span><br><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>).<span class="property">promises</span></span><br><span class="line">fs.<span class="title function_">readFile</span>(<span class="string">'./logs/log-0.txt'</span>, <span class="string">'utf-8'</span>).<span class="title function_">then</span>(<span class="function"><span class="params">result</span> =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(result)</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>在<code>fs</code>模块中,提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢?</p><p>由于Node环境执行的JavaScript代码是服务器端代码,所以,绝大部分需要在服务器运行期反复执行业务逻辑的代码,<em>必须使用异步代码</em>,否则,同步代码在执行时期,服务器将停止响应,因为JavaScript只有一个执行线程。</p><p>服务器启动时如果需要读取配置文件,或者结束时需要写入到状态文件时,可以使用同步代码,因为这些代码只在启动和结束时执行一次,不影响服务器正常运行时的异步执行。</p><h6 id="07-stream流模块"><a href="#07-stream流模块" class="headerlink" title="07 stream流模块"></a>07 stream流模块</h6><p><code>stream</code>是Node.js提供的又一个仅在服务区端可用的模块,目的是支持“流”这种数据结构。</p><p>什么是流?流是一种抽象的数据结构。想象水流,当在水管中流动时,就可以从某个地方(例如自来水厂)源源不断地到达另一个地方(比如你家的洗手池)。我们也可以把数据看成是数据流,比如你敲键盘的时候,就可以把每个字符依次连起来,看成字符流。这个流是从键盘输入到应用程序,实际上它还对应着一个名字:标准输入流(stdin)。</p><p><img src="/posts/5063/image-20220407085931744.webp" alt="image-20220407085931744"></p><p>如果应用程序把字符一个一个输出到显示器上,这也可以看成是一个流,这个流也有名字:标准输出流(stdout)。流的特点是数据是有序的,而且必须依次读取,或者依次写入,不能像Array那样随机定位。</p><p>有些流用来读取数据,比如从文件读取数据时,可以打开一个文件流,然后从文件流中不断地读取数据。有些流用来写入数据,比如向文件写入数据时,只需要把数据不断地往文件流中写进去就可以了。</p><p>在Node.js中,流也是一个对象,我们只需要响应流的事件就可以了:<code>data</code>事件表示流的数据已经可以读取了,<code>end</code>事件表示这个流已经到末尾了,没有数据可以读取了,<code>error</code>事件表示出错了。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</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> rs = fs.<span class="title function_">createReadStream</span>(<span class="string">'sample.txt'</span>, <span class="string">'utf-8'</span>);</span><br><span class="line"></span><br><span class="line">rs.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="keyword">function</span> (<span class="params">chunk</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'DATA:'</span>)</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(chunk);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">rs.<span class="title function_">on</span>(<span class="string">'end'</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="string">'END'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line">rs.<span class="title function_">on</span>(<span class="string">'error'</span>, <span class="keyword">function</span> (<span class="params">err</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'ERROR: '</span> + err);</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>要注意,<code>data</code>事件可能会有多次,每次传递的<code>chunk</code>是流的一部分数据。</p><p>要以流的形式写入文件,只需要不断调用<code>write()</code>方法,最后以<code>end()</code>结束:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> ws1 = fs.<span class="title function_">createWriteStream</span>(<span class="string">'output1.txt'</span>, <span class="string">'utf-8'</span>);</span><br><span class="line">ws1.<span class="title function_">write</span>(<span class="string">'使用Stream写入文本数据...\n'</span>);</span><br><span class="line">ws1.<span class="title function_">write</span>(<span class="string">'END.'</span>);</span><br><span class="line">ws1.<span class="title function_">end</span>();</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>pipe</code> 就像可以把两个水管串成一个更长的水管一样,两个流也可以串起来。一个<code>Readable</code>流和一个<code>Writable</code>流串起来后,所有的数据自动从<code>Readable</code>流进入<code>Writable</code>流,这种操作叫<code>pipe</code>。</p><p>在Node.js中,<code>Readable</code>流有一个<code>pipe()</code>方法,就是用来干这件事的。</p><p>让我们用<code>pipe()</code>把一个文件流和另一个文件流串起来,这样源文件的所有数据就自动写入到目标文件里了,所以,这实际上是一个复制文件的程序:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> readstream = fs.<span class="title function_">createReadStream</span>(<span class="string">'./1.txt'</span>)</span><br><span class="line"><span class="keyword">const</span> writestream = fs.<span class="title function_">createWriteStream</span>(<span class="string">'./2.txt'</span>)</span><br><span class="line"></span><br><span class="line">readstream.<span class="title function_">pipe</span>(writestream)</span><br></pre></td></tr></table></figure><h6 id="08-zlib"><a href="#08-zlib" class="headerlink" title="08 zlib"></a>08 zlib</h6><img src="/posts/5063/image-20220407105840596.webp" alt="image-20220407105916114" style="zoom:50%;"><img src="/posts/5063/image-20220407105916114.webp" alt="image-20220407105916114" style="zoom:50%;"><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="keyword">const</span> zlib = <span class="built_in">require</span>(<span class="string">'zlib'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> gzip = zlib.<span class="title function_">createGzip</span>()</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> readstream = fs.<span class="title function_">createReadStream</span>(<span class="string">'./note.txt'</span>)</span><br><span class="line"><span class="keyword">const</span> writestream = fs.<span class="title function_">createWriteStream</span>(<span class="string">'./note2.txt'</span>)</span><br><span class="line"></span><br><span class="line">readstream</span><br><span class="line"> .<span class="title function_">pipe</span>(gzip)</span><br><span class="line"> .<span class="title function_">pipe</span>(writestream)</span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="09-crypto"><a href="#09-crypto" class="headerlink" title="09 crypto"></a>09 crypto</h6><p>crypto模块的目的是为了提供通用的加密和哈希算法。用纯JavaScript代码实现这些功能不是不可能,但速度会非常慢。Nodejs用C/C++实现这些算法后,通过cypto这个模块暴露为JavaScript接口,这样用起来方便,运行速度也快。</p><p>MD5是一种常用的哈希算法,用于给任意数据一个“签名”。这个签名通常用一个十六进制的字符串表示:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">'crypto'</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> hash = crypto.<span class="title function_">createHash</span>(<span class="string">'md5'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可任意多次调用update():</span></span><br><span class="line">hash.<span class="title function_">update</span>(<span class="string">'Hello, world!'</span>);</span><br><span class="line">hash.<span class="title function_">update</span>(<span class="string">'Hello, nodejs!'</span>);</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(hash.<span class="title function_">digest</span>(<span class="string">'hex'</span>)); </span><br><span class="line"></span><br></pre></td></tr></table></figure><p><code>update()</code>方法默认字符串编码为<code>UTF-8</code>,也可以传入Buffer。</p><p>如果要计算SHA1,只需要把<code>'md5'</code>改成<code>'sha1'</code>,就可以得到SHA1的结果<code>1f32b9c9932c02227819a4151feed43e131aca40</code>。</p><p>Hmac算法也是一种哈希算法,它可以利用MD5或SHA1等哈希算法。不同的是,Hmac还需要一个密钥:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">const crypto = require('crypto');</span><br><span class="line"></span><br><span class="line">const hmac = crypto.createHmac('sha256', 'secret-key');</span><br><span class="line"></span><br><span class="line">hmac.update('Hello, world!');</span><br><span class="line">hmac.update('Hello, nodejs!');</span><br><span class="line"></span><br><span class="line">console.log(hmac.digest('hex')); // 80f7e22570...</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>只要密钥发生了变化,那么同样的输入数据也会得到不同的签名,因此,可以把Hmac理解为用随机数“增强”的哈希算法。</p><p>AES是一种常用的对称加密算法,加解密都用同一个密钥。crypto模块提供了AES支持,但是需要自己封装好函数,便于使用:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">"crypto"</span>);</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">encrypt</span> (key, iv, data) {</span><br><span class="line"> <span class="keyword">let</span> decipher = crypto.<span class="title function_">createCipheriv</span>(<span class="string">'aes-128-cbc'</span>, key, iv);</span><br><span class="line"> <span class="comment">// decipher.setAutoPadding(true);</span></span><br><span class="line"> <span class="keyword">return</span> decipher.<span class="title function_">update</span>(data, <span class="string">'binary'</span>, <span class="string">'hex'</span>) + decipher.<span class="title function_">final</span>(<span class="string">'hex'</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">decrypt</span> (key, iv, crypted) {</span><br><span class="line"> crypted = <span class="title class_">Buffer</span>.<span class="title function_">from</span>(crypted, <span class="string">'hex'</span>).<span class="title function_">toString</span>(<span class="string">'binary'</span>);</span><br><span class="line"> <span class="keyword">let</span> decipher = crypto.<span class="title function_">createDecipheriv</span>(<span class="string">'aes-128-cbc'</span>, key, iv);</span><br><span class="line"> <span class="keyword">return</span> decipher.<span class="title function_">update</span>(crypted, <span class="string">'binary'</span>, <span class="string">'utf8'</span>) + decipher.<span class="title function_">final</span>(<span class="string">'utf8'</span>);</span><br><span class="line">}</span><br><span class="line">key,iv必须是<span class="number">16</span>个字节</span><br><span class="line"></span><br></pre></td></tr></table></figure><p>可以看出,加密后的字符串通过解密又得到了原始内容。</p><h5 id="6-路由"><a href="#6-路由" class="headerlink" title="6. 路由"></a>6. 路由</h5><h6 id="01-基础"><a href="#01-基础" class="headerlink" title="01 基础"></a>01 基础</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @作者: kerwin</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="keyword">var</span> fs = <span class="built_in">require</span>(<span class="string">"fs"</span>)</span><br><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><br><span class="line"><span class="keyword">function</span> <span class="title function_">render</span>(<span class="params">res, path</span>) {</span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, { <span class="string">"Content-Type"</span>: <span class="string">"text/html;charset=utf8"</span> })</span><br><span class="line"> res.<span class="title function_">write</span>(fs.<span class="title function_">readFileSync</span>(path, <span class="string">"utf8"</span>))</span><br><span class="line"> res.<span class="title function_">end</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> route = {</span><br><span class="line"> <span class="string">"/login"</span>: <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="title function_">render</span>(res, <span class="string">"./static/login.html"</span>)</span><br><span class="line"> },</span><br><span class="line"></span><br><span class="line"> <span class="string">"/home"</span>: <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="title function_">render</span>(res, <span class="string">"./static/home.html"</span>)</span><br><span class="line"> },</span><br><span class="line"> <span class="string">"/404"</span>: <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">404</span>, { <span class="string">"Content-Type"</span>: <span class="string">"text/html;charset=utf8"</span> })</span><br><span class="line"> res.<span class="title function_">write</span>(fs.<span class="title function_">readFileSync</span>(<span class="string">"./static/404.html"</span>, <span class="string">"utf8"</span>))</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="02-获取参数"><a href="#02-获取参数" class="headerlink" title="02 获取参数"></a>02 获取参数</h6><p>get请求</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">"/api/login"</span>:<span class="function">(<span class="params">req,res</span>)=></span>{</span><br><span class="line"> <span class="keyword">const</span> myURL = <span class="keyword">new</span> <span class="title function_">URL</span>(req.<span class="property">url</span>, <span class="string">'http://127.0.0.1:3000'</span>);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(myURL.<span class="property">searchParams</span>.<span class="title function_">get</span>(<span class="string">"username"</span>)) </span><br><span class="line"> <span class="title function_">render</span>(res,<span class="string">`{ok:1}`</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>post请求</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="string">"/api/login"</span>: <span class="function">(<span class="params">req, res</span>) =></span> {</span><br><span class="line"> <span class="keyword">var</span> post = <span class="string">''</span>;</span><br><span class="line"> <span class="comment">// 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中</span></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'data'</span>, <span class="keyword">function</span> (<span class="params">chunk</span>) {</span><br><span class="line"> post += chunk;</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。</span></span><br><span class="line"> req.<span class="title function_">on</span>(<span class="string">'end'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> post = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(post);</span><br><span class="line"> <span class="title function_">render</span>(res, <span class="string">`{ok:1}`</span>)</span><br><span class="line"> });</span><br><span class="line"> }</span><br></pre></td></tr></table></figure><h6 id="03-静态资源处理"><a href="#03-静态资源处理" class="headerlink" title="03 静态资源处理"></a>03 静态资源处理</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">readStaticFile</span>(<span class="params">req, res</span>) {</span><br><span class="line"> <span class="keyword">const</span> myURL = <span class="keyword">new</span> <span class="title function_">URL</span>(req.<span class="property">url</span>, <span class="string">'http://127.0.0.1:3000'</span>)</span><br><span class="line"> <span class="keyword">var</span> filePathname = path.<span class="title function_">join</span>(__dirname, <span class="string">"/static"</span>, myURL.<span class="property">pathname</span>);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (fs.<span class="title function_">existsSync</span>(filePathname)) {</span><br><span class="line"> <span class="comment">// console.log(1111)</span></span><br><span class="line"> res.<span class="title function_">writeHead</span>(<span class="number">200</span>, { <span class="string">"Content-Type"</span>: <span class="string">`<span class="subst">${mime.getType(myURL.pathname.split(<span class="string">"."</span>)[<span class="number">1</span>])}</span>;charset=utf8`</span> })</span><br><span class="line"> res.<span class="title function_">write</span>(fs.<span class="title function_">readFileSync</span>(filePathname, <span class="string">"utf8"</span>))</span><br><span class="line"> res.<span class="title function_">end</span>()</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="二、Express"><a href="#二、Express" class="headerlink" title="二、Express"></a>二、Express</h4><blockquote><p><a href="https://www.expressjs.com.cn/">https://www.expressjs.com.cn/</a></p></blockquote><p>基于 Node.js 平台,快速、开放、极简的 web 开发框架。</p><h5 id="1-特色"><a href="#1-特色" class="headerlink" title="1.特色"></a>1.特色</h5><img src="/posts/5063/image-20220411103139587.webp" alt="image-20220411103139587" style="zoom: 50%;"><h5 id="2-安装"><a href="#2-安装" class="headerlink" title="2.安装"></a>2.安装</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">$ npm install express --save</span><br></pre></td></tr></table></figure><h5 id="3-路由"><a href="#3-路由" class="headerlink" title="3.路由"></a>3.路由</h5><p>路由是指如何定义应用的端点(URIs)以及如何响应客户端的请求。</p><p>路由是由一个 URI、HTTP 请求(GET、POST等)和若干个句柄组成,它的结构如下: app.METHOD(path, [callback…], callback), app 是 express 对象的一个实例, METHOD 是一个 HTTP 请求方法, path 是服务器上的路径, callback 是当路由匹配时要执行的函数。</p><p>下面是一个基本的路由示例:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>);</span><br><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>();</span><br><span class="line"></span><br><span class="line"><span class="comment">// respond with "hello world" when a GET request is made to the homepage</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'hello world'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>路由路径和请求方法一起定义了请求的端点,它可以是字符串、字符串模式或者正则表达式。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 匹配根路径的请求</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/'</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'root'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 /about 路径的请求</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/about'</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'about'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 /random.text 路径的请求</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/random.text'</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'random.text'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>使用字符串模式的路由路径示例:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 匹配 acd 和 abcd</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/ab?cd'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'ab?cd'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 /ab/******</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/ab/:id'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'aaaaaaa'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 abcd、abbcd、abbbcd等</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/ab+cd'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'ab+cd'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 abcd、abxcd、abRABDOMcd、ab123cd等</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/ab*cd'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'ab*cd'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 /abe 和 /abcde</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/ab(cd)?e'</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'ab(cd)?e'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>使用正则表达式的路由路径示例:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">// 匹配任何路径中含有 a 的路径:</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="regexp">/a/</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'/a/'</span>);</span><br><span class="line">});</span><br><span class="line"></span><br><span class="line"><span class="comment">// 匹配 butterfly、dragonfly,不匹配 butterflyman、dragonfly man等</span></span><br><span class="line">app.<span class="title function_">get</span>(<span class="regexp">/.*fly$/</span>, <span class="keyword">function</span>(<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'/.*fly$/'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>可以为请求处理提供多个回调函数,其行为类似 中间件。唯一的区别是这些回调函数有可能调用 next(‘route’) 方法而略过其他路由回调函数。可以利用该机制为路由定义前提条件,如果在现有路径上继续执行没有意义,则可将控制权交给剩下的路径。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">get</span>(<span class="string">'/example/a'</span>, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'Hello from A!'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>使用多个回调函数处理路由(记得指定 next 对象):</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">get</span>(<span class="string">'/example/b'</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'response will be sent by the next function ...'</span>);</span><br><span class="line"> <span class="title function_">next</span>();</span><br><span class="line">}, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'Hello from B!'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>使用回调函数数组处理路由:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> cb0 = <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'CB0'</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cb1 = <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'CB1'</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cb2 = <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'Hello from C!'</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/example/c'</span>, [cb0, cb1, cb2])</span><br></pre></td></tr></table></figure><p>混合使用函数和函数数组处理路由:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> cb0 = <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'CB0'</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> cb1 = <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'CB1'</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/example/d'</span>, [cb0, cb1], <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'response will be sent by the next function ...'</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}, <span class="keyword">function</span> (<span class="params">req, res</span>) {</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">'Hello from D!'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h5 id="4-中间件"><a href="#4-中间件" class="headerlink" title="4.中间件"></a>4.中间件</h5><p>Express 是一个自身功能极简,完全是由路由和中间件构成一个的 web 开发框架:从本质上来说,一个 Express 应用就是在调用各种中间件。</p><p>中间件(Middleware) 是一个函数,它可以访问请求对象(request object (req)), 响应对象(response object (res)), 和 web 应用中处于请求-响应循环流程中的中间件,一般被命名为 next 的变量。</p><p>中间件的功能包括:</p><ul><li>执行任何代码。</li><li>修改请求和响应对象。</li><li>终结请求-响应循环。</li><li>调用堆栈中的下一个中间件。</li></ul><p>如果当前中间件没有终结请求-响应循环,则必须调用 next() 方法将控制权交给下一个中间件,否则请求就会挂起。</p><p>Express 应用可使用如下几种中间件:</p><ul><li>应用级中间件</li><li>路由级中间件</li><li>错误处理中间件</li><li>内置中间件</li><li>第三方中间件</li></ul><p>使用可选则挂载路径,可在应用级别或路由级别装载中间件。另外,你还可以同时装在一系列中间件函数,从而在一个挂载点上创建一个子中间件栈。</p><h6 id="(1)应用级中间件"><a href="#(1)应用级中间件" class="headerlink" title="(1)应用级中间件"></a>(1)应用级中间件</h6><p>应用级中间件绑定到 app 对象 使用 app.use() 和 app.METHOD(), 其中, METHOD 是需要处理的 HTTP 请求的方法,例如 GET, PUT, POST 等等,全部小写。例如:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有挂载路径的中间件,应用的每个请求都会执行该中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Time:'</span>, <span class="title class_">Date</span>.<span class="title function_">now</span>())</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="(2)路由级中间件"><a href="#(2)路由级中间件" class="headerlink" title="(2)路由级中间件"></a>(2)路由级中间件</h6><p>路由级中间件和应用级中间件一样,只是它绑定的对象为 express.Router()。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>()</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>()</span><br><span class="line"><span class="keyword">var</span> router = express.<span class="title class_">Router</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 没有挂载路径的中间件,通过该路由的每个请求都会执行该中间件</span></span><br><span class="line">router.<span class="title function_">use</span>(<span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Time:'</span>, <span class="title class_">Date</span>.<span class="title function_">now</span>())</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 一个中间件栈,显示任何指向 /user/:id 的 HTTP 请求的信息</span></span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">'/user/:id'</span>, <span class="keyword">function</span>(<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Request URL:'</span>, req.<span class="property">originalUrl</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">}, <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'Request Type:'</span>, req.<span class="property">method</span>)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 一个中间件栈,处理指向 /user/:id 的 GET 请求</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">'/user/:id'</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="comment">// 如果 user id 为 0, 跳到下一个路由</span></span><br><span class="line"> <span class="keyword">if</span> (req.<span class="property">params</span>.<span class="property">id</span> == <span class="number">0</span>) <span class="title function_">next</span>(<span class="string">'route'</span>)</span><br><span class="line"> <span class="comment">// 负责将控制权交给栈中下一个中间件</span></span><br><span class="line"> <span class="keyword">else</span> <span class="title function_">next</span>() <span class="comment">//</span></span><br><span class="line">}, <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="comment">// 渲染常规页面</span></span><br><span class="line"> res.<span class="title function_">render</span>(<span class="string">'regular'</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 处理 /user/:id, 渲染一个特殊页面</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">'/user/:id'</span>, <span class="keyword">function</span> (<span class="params">req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(req.<span class="property">params</span>.<span class="property">id</span>)</span><br><span class="line"> res.<span class="title function_">render</span>(<span class="string">'special'</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">app.<span class="title function_">use</span>(<span class="string">'/'</span>, router)</span><br></pre></td></tr></table></figure><h6 id="(3)错误处理中间件"><a href="#(3)错误处理中间件" class="headerlink" title="(3)错误处理中间件"></a>(3)错误处理中间件</h6><p>错误处理中间件和其他中间件定义类似,只是要使用 4 个参数,而不是 3 个,其签名如下: (err, req, res, next)。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(<span class="keyword">function</span>(<span class="params">err, req, res, next</span>) {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">error</span>(err.<span class="property">stack</span>)</span><br><span class="line"> res.<span class="title function_">status</span>(<span class="number">500</span>).<span class="title function_">send</span>(<span class="string">'Something broke!'</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h6 id="(4)内置的中间件"><a href="#(4)内置的中间件" class="headerlink" title="(4)内置的中间件"></a>(4)内置的中间件</h6><p>express.static 是 Express 唯一内置的中间件。它基于 serve-static,负责在 Express 应用中提托管静态资源。每个应用可有多个静态目录。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'public'</span>))</span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'uploads'</span>))</span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'files'</span>))</span><br></pre></td></tr></table></figure><h6 id="(5)第三方中间件"><a href="#(5)第三方中间件" class="headerlink" title="(5)第三方中间件"></a>(5)第三方中间件</h6><p>安装所需功能的 node 模块,并在应用中加载,可以在应用级加载,也可以在路由级加载。</p><p>下面的例子安装并加载了一个解析 cookie 的中间件: cookie-parser</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">$ npm install cookie-parser</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</span><br><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>()</span><br><span class="line"><span class="keyword">var</span> cookieParser = <span class="built_in">require</span>(<span class="string">'cookie-parser'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载用于解析 cookie 的中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">cookieParser</span>())</span><br></pre></td></tr></table></figure><h5 id="5-获取请求参数"><a href="#5-获取请求参数" class="headerlink" title="5. 获取请求参数"></a>5. 获取请求参数</h5><p>get</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">req.<span class="property">query</span></span><br></pre></td></tr></table></figure><p>post</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">urlencoded</span>({<span class="attr">extended</span>:<span class="literal">false</span>}))</span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">json</span>())</span><br><span class="line">req.<span class="property">body</span></span><br></pre></td></tr></table></figure><h5 id="6-利用-Express-托管静态文件"><a href="#6-利用-Express-托管静态文件" class="headerlink" title="6.利用 Express 托管静态文件"></a>6.利用 Express 托管静态文件</h5><p>通过 Express 内置的 express.static 可以方便地托管静态文件,例如图片、CSS、JavaScript 文件等。</p><p>将静态资源文件所在的目录作为参数传递给 express.static 中间件就可以提供静态资源文件的访问了。例如,假设在 public 目录放置了图片、CSS 和 JavaScript 文件,你就可以:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'public'</span>))</span><br></pre></td></tr></table></figure><p>现在,public 目录下面的文件就可以访问了。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/images/kitten.jpg</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/css/style.css</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/js/app.js</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/images/bg.png</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/hello.html</span></span><br></pre></td></tr></table></figure><blockquote><p>所有文件的路径都是相对于存放目录的,因此,存放静态文件的目录名不会出现在 URL 中。</p></blockquote><p>如果你的静态资源存放在多个目录下面,你可以多次调用 express.static 中间件:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'public'</span>))</span><br><span class="line">app.<span class="title function_">use</span>(express.<span class="title function_">static</span>(<span class="string">'files'</span>))</span><br></pre></td></tr></table></figure><p>访问静态资源文件时,express.static 中间件会根据目录添加的顺序查找所需的文件。</p><p>如果你希望所有通过 express.static 访问的文件都存放在一个“虚拟(virtual)”目录(即目录根本不存在)下面,可以通过为静态资源目录指定一个挂载路径的方式来实现,如下所示:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">app.use('/static', express.static('public'))</span><br></pre></td></tr></table></figure><p>现在,你就可以通过带有 “/static” 前缀的地址来访问 public 目录下面的文件了。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/static/images/kitten.jpg</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/static/css/style.css</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/static/js/app.js</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/static/images/bg.png</span></span><br><span class="line"><span class="attr">http</span>:<span class="comment">//localhost:3000/static/hello.html</span></span><br></pre></td></tr></table></figure><h5 id="7-服务端渲染(模板引擎)"><a href="#7-服务端渲染(模板引擎)" class="headerlink" title="7.服务端渲染(模板引擎)"></a>7.服务端渲染(模板引擎)</h5><img src="/posts/5063/image-20220411104609389.webp" alt="image-20220411104609389" style="zoom:50%;"><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">npm i ejs</span><br></pre></td></tr></table></figure><p>需要在应用中进行如下设置才能让 Express 渲染模板文件:</p><ul><li>views, 放模板文件的目录,比如: app.set(‘views’, ‘./views’)</li><li>view engine, 模板引擎,比如: app.set(‘view engine’, ‘ejs’)</li></ul><img src="/posts/5063/image-20220411104652068.webp" alt="image-20220411104652068" style="zoom:50%;"><h4 id="三、MongoDB"><a href="#三、MongoDB" class="headerlink" title="三、MongoDB"></a>三、MongoDB</h4><h5 id="1-关系型与非关系型数据库"><a href="#1-关系型与非关系型数据库" class="headerlink" title="1.关系型与非关系型数据库"></a>1.关系型与非关系型数据库</h5><img src="/posts/5063/image-20220413085332378.webp" alt="image-20220413085332378" style="zoom:67%;"><img src="/posts/5063/image-20220413090707891.webp" alt="image-20220413090707891" style="zoom: 67%;"><p><img src="/posts/5063/image-20220413090406721.webp" alt="image-20220413090406721"></p><p><img src="/posts/5063/image-20220413090614205.webp" alt="image-20220413090614205"></p><h5 id="2-安装数据库"><a href="#2-安装数据库" class="headerlink" title="2.安装数据库"></a>2.安装数据库</h5><p><a href="https://docs.mongodb.com/manual/administration/install-community/">https://docs.mongodb.com/manual/administration/install-community/</a></p><h5 id="3-启动数据库"><a href="#3-启动数据库" class="headerlink" title="3.启动数据库"></a>3.启动数据库</h5><h6 id="(1)windows"><a href="#(1)windows" class="headerlink" title="(1)windows"></a>(1)windows</h6><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">mongod --dbpath d:/data/db</span><br><span class="line">mongo</span><br></pre></td></tr></table></figure><h6 id="(2)mac"><a href="#(2)mac" class="headerlink" title="(2)mac"></a>(2)mac</h6><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">mongod --config /usr/local/etc/mongod.conf</span><br><span class="line">mongo</span><br></pre></td></tr></table></figure><h5 id="4-在命令行中操作数据库"><a href="#4-在命令行中操作数据库" class="headerlink" title="4.在命令行中操作数据库"></a>4.在命令行中操作数据库</h5><img src="/posts/5063/image-20220413090814836.webp" alt="image-20220413090814836" style="zoom:50%;"><img src="/posts/5063/image-20220413090825381.webp" alt="image-20220413090825381" style="zoom:50%;"><img src="/posts/5063/image-20220413090837613.webp" alt="image-20220413090837613" style="zoom:50%;"><img src="/posts/5063/image-20220413090858199.webp" alt="image-20220413090858199" style="zoom:50%;"><img src="/posts/5063/image-20220413090907539.webp" alt="image-20220413090907539" style="zoom:50%;"><img src="/posts/5063/image-20220413090916971.webp" alt="image-20220413090916971" style="zoom:50%;"><h5 id="5-可视化工具进行增删改查"><a href="#5-可视化工具进行增删改查" class="headerlink" title="5.可视化工具进行增删改查"></a>5.可视化工具进行增删改查</h5><p>Robomongo Robo3T adminMongo</p><p><img src="/posts/5063/image-20220413091031852.webp" alt="image-20220413091031852"></p><h5 id="6-nodejs连接操作数据库"><a href="#6-nodejs连接操作数据库" class="headerlink" title="6.nodejs连接操作数据库"></a>6.nodejs连接操作数据库</h5><p>连接数据库</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mongoose = <span class="built_in">require</span>(<span class="string">"mongoose"</span>)</span><br><span class="line"></span><br><span class="line">mongoose.<span class="title function_">connect</span>(<span class="string">"mongodb://127.0.0.1:27017/company-system"</span>)</span><br></pre></td></tr></table></figure><p>创建模型</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mongoose = <span class="built_in">require</span>(<span class="string">"mongoose"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Schema</span> = mongoose.<span class="property">Schema</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">UserType</span> = {</span><br><span class="line"> <span class="attr">username</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">password</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">gender</span>:<span class="title class_">Number</span>,</span><br><span class="line"> <span class="attr">introduction</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">avatar</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">role</span>:<span class="title class_">Number</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">UserModel</span> = mongoose.<span class="title function_">model</span>(<span class="string">"user"</span>,<span class="keyword">new</span> <span class="title class_">Schema</span>(<span class="title class_">UserType</span>))</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">UserModel</span> </span><br></pre></td></tr></table></figure><p>增加数据</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title class_">UserModel</span>.<span class="title function_">create</span>({</span><br><span class="line"> introduction,username,gender,avatar,password,role</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>查询数据</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title class_">UserModel</span>.<span class="title function_">find</span>({<span class="attr">username</span>:<span class="string">"kerwin"</span>},[<span class="string">"username"</span>,<span class="string">"role"</span>,<span class="string">"introduction"</span>,<span class="string">"password"</span>]).<span class="title function_">sort</span>({<span class="attr">createTime</span>:-<span class="number">1</span>}).<span class="title function_">skip</span>(<span class="number">10</span>).<span class="title function_">limit</span>(<span class="number">10</span>)</span><br></pre></td></tr></table></figure><p>更新数据</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title class_">UserModel</span>.<span class="title function_">updateOne</span>({</span><br><span class="line"> _id</span><br><span class="line">},{</span><br><span class="line"> introduction,username,gender,avatar</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>删除数据</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title class_">UserModel</span>.<span class="title function_">deleteOne</span>({_id})</span><br></pre></td></tr></table></figure><h4 id="四、接口规范与业务分层"><a href="#四、接口规范与业务分层" class="headerlink" title="四、接口规范与业务分层"></a>四、接口规范与业务分层</h4><h5 id="1-接口规范"><a href="#1-接口规范" class="headerlink" title="1.接口规范"></a>1.接口规范</h5><img src="/posts/5063/image-20220414094020921.webp" alt="image-20220414094020921" style="zoom: 67%;"><img src="/posts/5063/image-20220414094043782.webp" alt="image-20220414094043782" style="zoom: 67%;"><h5 id="2-业务分层"><a href="#2-业务分层" class="headerlink" title="2.业务分层"></a>2.业务分层</h5><p><img src="/posts/5063/image-20220414094653807.webp" alt="image-20220414094653807"></p><h4 id="五、登录鉴权"><a href="#五、登录鉴权" class="headerlink" title="五、登录鉴权"></a>五、登录鉴权</h4><h5 id="1-Cookie-amp-Session"><a href="#1-Cookie-amp-Session" class="headerlink" title="1. Cookie&Session"></a>1. Cookie&Session</h5><p>「HTTP 无状态」<strong>我们知道,HTTP 是无状态的。也就是说,HTTP 请求方和响应方间无法维护状态,都是一次性的,它不知道前后的请求都发生了什么。但有的场景下,我们需要维护状态。最典型的,一个用户登陆微博,发布、关注、评论,都应是在登录后的用户状态下的。</strong>「标记」那解决办法是什么呢?<img src="/posts/5063/image-20220414095345868.webp" alt="image-20220414095345868"></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">"express"</span>);</span><br><span class="line"><span class="keyword">const</span> session = <span class="built_in">require</span>(<span class="string">"express-session"</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">MongoStore</span> = <span class="built_in">require</span>(<span class="string">"connect-mongo"</span>);</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>();</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(</span><br><span class="line"> <span class="title function_">session</span>({</span><br><span class="line"> <span class="attr">secret</span>: <span class="string">"this is session"</span>, <span class="comment">// 服务器生成 session 的签名</span></span><br><span class="line"> <span class="attr">resave</span>: <span class="literal">true</span>, </span><br><span class="line"> <span class="attr">saveUninitialized</span>: <span class="literal">true</span>, <span class="comment">//强制将为初始化的 session 存储</span></span><br><span class="line"> <span class="attr">cookie</span>: {</span><br><span class="line"> <span class="attr">maxAge</span>: <span class="number">1000</span> * <span class="number">60</span> * <span class="number">10</span>,<span class="comment">// 过期时间</span></span><br><span class="line"> <span class="attr">secure</span>: <span class="literal">false</span>, <span class="comment">// 为 true 时候表示只有 https 协议才能访问cookie</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">rolling</span>: <span class="literal">true</span>, <span class="comment">//为 true 表示 超时前刷新,cookie 会重新计时; 为 false 表示在超时前刷新多少次,都是按照第一次刷新开始计时。</span></span><br><span class="line"> <span class="attr">store</span>: <span class="title class_">MongoStore</span>.<span class="title function_">create</span>({</span><br><span class="line"> <span class="attr">mongoUrl</span>: <span class="string">'mongodb://127.0.0.1:27017/kerwin_session'</span>,</span><br><span class="line"> <span class="attr">ttl</span>: <span class="number">1000</span> * <span class="number">60</span> * <span class="number">10</span> <span class="comment">// 过期时间</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">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req,res,next</span>)=></span>{</span><br><span class="line"> <span class="keyword">if</span>(req.<span class="property">url</span>===<span class="string">"/login"</span>){</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span>(req.<span class="property">session</span>.<span class="property">user</span>){</span><br><span class="line"> req.<span class="property">session</span>.<span class="property">garbage</span> = <span class="title class_">Date</span>();</span><br><span class="line"> <span class="title function_">next</span>();</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> res.<span class="title function_">redirect</span>(<span class="string">"/login"</span>) </span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="2-JSON-Web-Token-JWT"><a href="#2-JSON-Web-Token-JWT" class="headerlink" title="2. JSON Web Token (JWT)"></a>2. JSON Web Token (JWT)</h5><h6 id="(1)介绍"><a href="#(1)介绍" class="headerlink" title="(1)介绍"></a>(1)介绍</h6><p><img src="/posts/5063/image-20220415082822828.webp" alt="image-20220415082822828"></p><p>我为什么要保存这可恶的session呢, 只让每个客户端去保存该多好?</p><p><img src="/posts/5063/image-20220415083015066.webp" alt="image-20220415083015066"></p><p>当然, 如果一个人的token 被别人偷走了, 那我也没办法, 我也会认为小偷就是合法用户, 这其实和一个人的session id 被别人偷走是一样的。</p><p>这样一来, 我就不保存session id 了, 我只是生成token , 然后验证token , 我用我的CPU计算时间获取了我的session 存储空间 !</p><p>解除了session id这个负担, 可以说是无事一身轻, 我的机器集群现在可以轻松地做水平扩展, 用户访问量增大, 直接加机器就行。 这种无状态的感觉实在是太好了!</p><p>缺点:</p><blockquote><ol><li>占带宽,正常情况下要比 session_id 更大,需要消耗更多流量,挤占更多带宽,假如你的网站每月有 10 万次的浏览器,就意味着要多开销几十兆的流量。听起来并不多,但日积月累也是不小一笔开销。实际上,许多人会在 JWT 中存储的信息会更多;</li><li>无法在服务端注销,那么久很难解决劫持问题;</li><li>性能问题,JWT 的卖点之一就是加密签名,由于这个特性,接收方得以验证 JWT 是否有效且被信任。对于有着严格性能要求的 Web 应用,这并不理想,尤其对于单线程环境。</li></ol></blockquote><p>注意:</p><blockquote><p>CSRF攻击的原因是浏览器会自动带上cookie,而不会带上token;</p><p>以CSRF攻击为例:</p><p>cookie:用户点击了链接,cookie未失效,导致发起请求后后端以为是用户正常操作,于是进行扣款操作;<br>token:用户点击链接,由于浏览器不会自动带上token,所以即使发了请求,后端的token验证不会通过,所以不会进行扣款操作;</p></blockquote><h6 id="(2)实现"><a href="#(2)实现" class="headerlink" title="(2)实现"></a>(2)实现</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//jsonwebtoken 封装</span></span><br><span class="line"><span class="keyword">const</span> jsonwebtoken = <span class="built_in">require</span>(<span class="string">"jsonwebtoken"</span>)</span><br><span class="line"><span class="keyword">const</span> secret = <span class="string">"kerwin"</span></span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">JWT</span> = {</span><br><span class="line"> <span class="title function_">generate</span>(<span class="params">value,exprires</span>){</span><br><span class="line"> <span class="keyword">return</span> jsonwebtoken.<span class="title function_">sign</span>(value,secret,{<span class="attr">expiresIn</span>:exprires})</span><br><span class="line"> },</span><br><span class="line"> <span class="title function_">verify</span>(<span class="params">token</span>){</span><br><span class="line"> <span class="keyword">try</span>{</span><br><span class="line"> <span class="keyword">return</span> jsonwebtoken.<span class="title function_">verify</span>(token,secret)</span><br><span class="line"> }<span class="keyword">catch</span>(e){</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="variable constant_">JWT</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//node中间件校验</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req,res,next</span>)=></span>{</span><br><span class="line"> <span class="comment">// 如果token有效 ,next() </span></span><br><span class="line"> <span class="comment">// 如果token过期了, 返回401错误</span></span><br><span class="line"> <span class="keyword">if</span>(req.<span class="property">url</span>===<span class="string">"/login"</span>){</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> token = req.<span class="property">headers</span>[<span class="string">"authorization"</span>].<span class="title function_">split</span>(<span class="string">" "</span>)[<span class="number">1</span>]</span><br><span class="line"> <span class="keyword">if</span>(token){</span><br><span class="line"> <span class="keyword">var</span> payload = <span class="variable constant_">JWT</span>.<span class="title function_">verify</span>(token)</span><br><span class="line"> <span class="comment">// console.log(payload)</span></span><br><span class="line"> <span class="keyword">if</span>(payload){</span><br><span class="line"> <span class="keyword">const</span> newToken = <span class="variable constant_">JWT</span>.<span class="title function_">generate</span>({</span><br><span class="line"> <span class="attr">_id</span>:payload.<span class="property">_id</span>,</span><br><span class="line"> <span class="attr">username</span>:payload.<span class="property">username</span></span><br><span class="line"> },<span class="string">"1d"</span>)</span><br><span class="line"> res.<span class="title function_">header</span>(<span class="string">"Authorization"</span>,newToken)</span><br><span class="line"> <span class="title function_">next</span>()</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> res.<span class="title function_">status</span>(<span class="number">401</span>).<span class="title function_">send</span>({<span class="attr">errCode</span>:<span class="string">"-1"</span>,<span class="attr">errorInfo</span>:<span class="string">"token过期"</span>})</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"> <span class="comment">//生成token</span></span><br><span class="line"><span class="keyword">const</span> token = <span class="variable constant_">JWT</span>.<span class="title function_">generate</span>({</span><br><span class="line"> <span class="attr">_id</span>: result[<span class="number">0</span>].<span class="property">_id</span>,</span><br><span class="line"> <span class="attr">username</span>: result[<span class="number">0</span>].<span class="property">username</span></span><br><span class="line">}, <span class="string">"1d"</span>)</span><br><span class="line"></span><br><span class="line">res.<span class="title function_">header</span>(<span class="string">"Authorization"</span>, token)</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//前端拦截</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * @作者: kerwin</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="keyword">import</span> axios <span class="keyword">from</span> <span class="string">'axios'</span></span><br><span class="line"><span class="comment">// Add a request interceptor</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="keyword">function</span> (<span class="params">config</span>) {</span><br><span class="line"> <span class="keyword">const</span> token = <span class="variable language_">localStorage</span>.<span class="title function_">getItem</span>(<span class="string">"token"</span>)</span><br><span class="line"> config.<span class="property">headers</span>.<span class="property">Authorization</span> = <span class="string">`Bearer <span class="subst">${token}</span>`</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> config;</span><br><span class="line"> }, <span class="keyword">function</span> (<span class="params">error</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Promise</span>.<span class="title function_">reject</span>(error);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"><span class="comment">// Add a response interceptor</span></span><br><span class="line">axios.<span class="property">interceptors</span>.<span class="property">response</span>.<span class="title function_">use</span>(<span class="keyword">function</span> (<span class="params">response</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> {authorization } = response.<span class="property">headers</span></span><br><span class="line"> authorization && <span class="variable language_">localStorage</span>.<span class="title function_">setItem</span>(<span class="string">"token"</span>,authorization)</span><br><span class="line"> <span class="keyword">return</span> response;</span><br><span class="line"> }, <span class="keyword">function</span> (<span class="params">error</span>) {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> {status} = error.<span class="property">response</span></span><br><span class="line"> <span class="keyword">if</span>(status===<span class="number">401</span>){</span><br><span class="line"> <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(<span class="string">"token"</span>)</span><br><span class="line"> <span class="variable language_">window</span>.<span class="property">location</span>.<span class="property">href</span>=<span class="string">"/login"</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">Promise</span>.<span class="title function_">reject</span>(error);</span><br><span class="line"> });</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="六、文件上传管理"><a href="#六、文件上传管理" class="headerlink" title="六、文件上传管理"></a>六、文件上传管理</h4><p>Multer 是一个 node.js 中间件,用于处理 <code>multipart/form-data</code> 类型的表单数据,它主要用于上传文件。</p><p><strong>注意</strong>: Multer 不会处理任何非 <code>multipart/form-data</code> 类型的表单数据。</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install --save multer</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//前后端分离-前端</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> params = <span class="keyword">new</span> <span class="title class_">FormData</span>()</span><br><span class="line">params.<span class="title function_">append</span>(<span class="string">'kerwinfile'</span>, file.<span class="property">file</span>)</span><br><span class="line">params.<span class="title function_">append</span>(<span class="string">'username'</span>, <span class="variable language_">this</span>.<span class="property">username</span>)</span><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"><span class="attr">headers</span>: {</span><br><span class="line"><span class="string">"Content-Type"</span>:<span class="string">"multipart/form-data"</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line">http.<span class="title function_">post</span>(<span class="string">'/api/upload'</span>, params, config).<span class="title function_">then</span>(<span class="function"><span class="params">res</span> =></span> {</span><br><span class="line"><span class="variable language_">this</span>.<span class="property">imgpath</span> = <span class="string">'http://localhost:3000'</span> + res.<span class="property">data</span></span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>Multer 会添加一个 <code>body</code> 对象 以及 <code>file</code> 或 <code>files</code> 对象 到 express 的 <code>request</code> 对象中。 <code>body</code> 对象包含表单的文本域信息,<code>file</code> 或 <code>files</code> 对象包含对象表单上传的文件信息。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">//前后端分离-后端</span></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">'/upload'</span>, upload.<span class="title function_">single</span>(<span class="string">'kerwinfile'</span>),<span class="keyword">function</span>(<span class="params">req, res, next</span>) {</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(req.<span class="property">file</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h4 id="七、APIDOC-API-文档生成工具"><a href="#七、APIDOC-API-文档生成工具" class="headerlink" title="七、APIDOC - API 文档生成工具"></a>七、APIDOC - API 文档生成工具</h4><p>apidoc 是一个简单的 RESTful API 文档生成工具,它从代码注释中提取特定格式的内容生成文档。支持诸如 Go、Java、C++、Rust 等大部分开发语言,具体可使用 <code>apidoc lang</code> 命令行查看所有的支持列表。</p><p>apidoc 拥有以下特点:</p><ol><li>跨平台,linux、windows、macOS 等都支持;</li><li>支持语言广泛,即使是不支持,也很方便扩展;</li><li>支持多个不同语言的多个项目生成一份文档;</li><li>输出模板可自定义;</li><li>根据文档生成 mock 数据;</li></ol><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">npm install -g apidoc</span><br></pre></td></tr></table></figure><h2 id><a href="#" class="headerlink" title></a><img src="/posts/5063/image-20220415085343339.webp" alt="image-20220415085343339"></h2><p>注意:</p><p>(1) 在当前文件夹下 apidoc.json</p><figure class="highlight json"><table><tr><td class="code"><pre><span class="line"><span class="punctuation">{</span></span><br><span class="line"><span class="attr">"name"</span><span class="punctuation">:</span> <span class="string">"****接口文档"</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">"version"</span><span class="punctuation">:</span> <span class="string">"1.0.0"</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">"description"</span><span class="punctuation">:</span> <span class="string">"关于****的接口文档描述"</span><span class="punctuation">,</span></span><br><span class="line"><span class="attr">"title"</span><span class="punctuation">:</span> <span class="string">"****"</span></span><br><span class="line"><span class="punctuation">}</span></span><br></pre></td></tr></table></figure><p>(2)可以利用vscode apidoc snippets 插件创建api</p><h4 id="八、Koa2"><a href="#八、Koa2" class="headerlink" title="八、Koa2"></a>八、Koa2</h4><img src="/posts/5063/image-20220417075653414.webp" alt="image-20220417075653414" style="zoom:50%;"><h5 id="1-简介"><a href="#1-简介" class="headerlink" title="1.简介"></a>1.简介</h5><p>koa 是由 Express 原班人马打造的,致力于成为一个更小、更富有表现力、更健壮的 Web 框架。使用 koa 编写 web 应用,通过组合不同的 generator,可以免除重复繁琐的回调函数嵌套,并极大地提升错误处理的效率。koa 不在内核方法中绑定任何中间件,它仅仅提供了一个轻量优雅的函数库,使得编写 Web 应用变得得心应手。</p><h5 id="2-快速开始"><a href="#2-快速开始" class="headerlink" title="2. 快速开始"></a>2. 快速开始</h5><h6 id="2-1-安装koa2"><a href="#2-1-安装koa2" class="headerlink" title="2.1 安装koa2"></a>2.1 安装koa2</h6><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line"># 初始化package.json</span><br><span class="line">npm init</span><br><span class="line"></span><br><span class="line"># 安装koa2 </span><br><span class="line">npm install koa</span><br></pre></td></tr></table></figure><h6 id="2-2-hello-world-代码"><a href="#2-2-hello-world-代码" class="headerlink" title="2.2 hello world 代码"></a>2.2 hello world 代码</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>( <span class="keyword">async</span> ( ctx ) => {</span><br><span class="line"> ctx.<span class="property">body</span> = <span class="string">'hello koa2'</span> <span class="comment">//json数据</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/posts/5063/image-20220417092053231.webp" alt="image-20220417092053231"></p><h6 id="2-3-启动demo"><a href="#2-3-启动demo" class="headerlink" title="2.3 启动demo"></a>2.3 启动demo</h6><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">node index.js</span><br></pre></td></tr></table></figure><h5 id="3-koa-vs-express"><a href="#3-koa-vs-express" class="headerlink" title="3. koa vs express"></a>3. koa vs express</h5><p>通常都会说 Koa 是洋葱模型,这重点在于中间件的设计。但是按照上面的分析,会发现 Express 也是类似的,不同的是Express 中间件机制使用了 Callback 实现,这样如果出现异步则可能会使你在执行顺序上感到困惑,因此如果我们想做接口耗时统计、错误处理 Koa 的这种中间件模式处理起来更方便些。最后一点响应机制也很重要,Koa 不是立即响应,是整个中间件处理完成在最外层进行了响应,而 Express 则是立即响应。</p><h6 id="3-1更轻量"><a href="#3-1更轻量" class="headerlink" title="3.1更轻量"></a>3.1更轻量</h6><ul><li>koa 不提供内置的中间件;</li><li>koa 不提供路由,而是把路由这个库分离出来了(koa/router)</li></ul><h6 id="3-2-Context对象"><a href="#3-2-Context对象" class="headerlink" title="3.2 Context对象"></a>3.2 Context对象</h6><p>koa增加了一个Context的对象,作为这次请求的上下文对象(在koa2中作为中间件的第一个参数传入)。同时Context上也挂载了Request和Response两个对象。和Express类似,这两个对象都提供了大量的便捷方法辅助开发, 这样的话对于在保存一些公有的参数的话变得更加合情合理</p><h6 id="3-3-异步流程控制"><a href="#3-3-异步流程控制" class="headerlink" title="3.3 异步流程控制"></a>3.3 异步流程控制</h6><p> express采用callback来处理异步, koa v1采用generator,koa v2 采用async/await。</p><p>generator和async/await使用同步的写法来处理异步,明显好于callback和promise,</p><h6 id="3-4-中间件模型"><a href="#3-4-中间件模型" class="headerlink" title="3.4 中间件模型"></a>3.4 中间件模型</h6><p>express基于connect中间件,线性模型;</p><p> koa中间件采用洋葱模型(对于每个中间件,在完成了一些事情后,可以非常优雅的将控制权传递给下一个中间件,并能够等待它完成,当后续的中间件完成处理后,控制权又回到了自己)</p><img src="/posts/5063/image-20220417083817823.webp" alt="image-20220417083817823" style="zoom:50%;"><p> <img src="/posts/5063/image-20220417085913567.webp" alt="image-20220417085913567"></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//同步</span></span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">"express"</span>)</span><br><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">req,res,next</span>)=></span>{</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="title function_">next</span>()</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">"hello"</span>)</span><br><span class="line">})</span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">()=></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><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br><span class="line"><span class="comment">//异步</span></span><br><span class="line"><span class="keyword">var</span> express = <span class="built_in">require</span>(<span class="string">"express"</span>)</span><br><span class="line"><span class="keyword">var</span> app = <span class="title function_">express</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (req,res,next)=>{</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="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)</span><br><span class="line"> res.<span class="title function_">send</span>(<span class="string">"hello"</span>)</span><br><span class="line">})</span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</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="keyword">await</span> <span class="title function_">delay</span>(<span class="number">1</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><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params">time</span>){</span><br><span class="line"> <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><br><span class="line"> <span class="built_in">setTimeout</span>(resolve,<span class="number">1000</span>)</span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//同步</span></span><br><span class="line"><span class="keyword">var</span> koa = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title function_">koa</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">(<span class="params">ctx,next</span>)=></span>{</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="title function_">next</span>()</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)</span><br><span class="line"> ctx.<span class="property">body</span>=<span class="string">"hello"</span></span><br><span class="line">})</span><br><span class="line">app.<span class="title function_">use</span>(<span class="function">()=></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><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</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> koa = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title function_">koa</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx,next)=>{</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="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="number">4</span>)</span><br><span class="line"> ctx.<span class="property">body</span>=<span class="string">"hello"</span></span><br><span class="line">}) </span><br><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</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="keyword">await</span> <span class="title function_">delay</span>(<span class="number">1</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><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">delay</span>(<span class="params">time</span>){</span><br><span class="line"> <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><br><span class="line"> <span class="built_in">setTimeout</span>(resolve,<span class="number">1000</span>)</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h5 id="4-路由"><a href="#4-路由" class="headerlink" title="4. 路由"></a>4. 路由</h5><h6 id="4-1基本用发"><a href="#4-1基本用发" class="headerlink" title="4.1基本用发"></a>4.1基本用发</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Router</span> = <span class="built_in">require</span>(<span class="string">"koa-router"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"><span class="keyword">var</span> router = <span class="keyword">new</span> <span class="title class_">Router</span>()</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">"/list"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>=[<span class="string">"111"</span>,<span class="string">"222"</span>,<span class="string">"333"</span>]</span><br><span class="line">})</span><br><span class="line">app.<span class="title function_">use</span>(router.<span class="title function_">routes</span>()).<span class="title function_">use</span>(router.<span class="title function_">allowedMethods</span>())</span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h6 id="4-2-router-allowedMethods作用"><a href="#4-2-router-allowedMethods作用" class="headerlink" title="4.2 router.allowedMethods作用"></a>4.2 router.allowedMethods作用</h6><p><img src="/posts/5063/image-20220417102845079.webp" alt="image-20220417102845079"></p><h6 id="4-3-请求方式"><a href="#4-3-请求方式" class="headerlink" title="4.3 请求方式"></a>4.3 请求方式</h6><p>Koa-router 请求方式: <code>get</code> 、 <code>put</code> 、 <code>post</code> 、 <code>patch</code> 、 <code>delete</code> 、 <code>del</code> ,而使用方法就是 <code>router.方式()</code> ,比如 <code>router.get()</code> 和 <code>router.post()</code> 。而 <code>router.all()</code> 会匹配所有的请求方法。</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">var</span> <span class="title class_">Router</span> = <span class="built_in">require</span>(<span class="string">"koa-router"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"><span class="keyword">var</span> router = <span class="keyword">new</span> <span class="title class_">Router</span>()</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">"/user"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>=[<span class="string">"aaa"</span>,<span class="string">"bbb"</span>,<span class="string">"ccc"</span>]</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">put</span>(<span class="string">"/user/:id"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"user update"</span>}</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">post</span>(<span class="string">"/user"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"user post"</span>}</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">del</span>(<span class="string">"/user/:id"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"user del"</span>}</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(router.<span class="title function_">routes</span>()).<span class="title function_">use</span>(router.<span class="title function_">allowedMethods</span>())</span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h6 id="4-4-拆分路由"><a href="#4-4-拆分路由" class="headerlink" title="4.4 拆分路由"></a>4.4 拆分路由</h6><p>list.js</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Router</span> = <span class="built_in">require</span>(<span class="string">"koa-router"</span>)</span><br><span class="line"><span class="keyword">var</span> router = <span class="keyword">new</span> <span class="title class_">Router</span>()</span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">"/"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>=[<span class="string">"111"</span>,<span class="string">"222"</span>,<span class="string">"333"</span>]</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">put</span>(<span class="string">"/:id"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"list update"</span>}</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">post</span>(<span class="string">"/"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"list post"</span>}</span><br><span class="line">})</span><br><span class="line">.<span class="title function_">del</span>(<span class="string">"/:id"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>={<span class="attr">ok</span>:<span class="number">1</span>,<span class="attr">info</span>:<span class="string">"list del"</span>}</span><br><span class="line">})</span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router</span><br></pre></td></tr></table></figure><p>index.js</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Router</span> = <span class="built_in">require</span>(<span class="string">"koa-router"</span>)</span><br><span class="line"><span class="keyword">var</span> router = <span class="keyword">new</span> <span class="title class_">Router</span>()</span><br><span class="line"><span class="keyword">var</span> user = <span class="built_in">require</span>(<span class="string">"./user"</span>)</span><br><span class="line"><span class="keyword">var</span> list = <span class="built_in">require</span>(<span class="string">"./list"</span>)</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">'/user'</span>, user.<span class="title function_">routes</span>(), user.<span class="title function_">allowedMethods</span>())</span><br><span class="line">router.<span class="title function_">use</span>(<span class="string">'/list'</span>, list.<span class="title function_">routes</span>(), list.<span class="title function_">allowedMethods</span>())</span><br><span class="line"></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = router</span><br></pre></td></tr></table></figure><p>entry入口</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">"koa"</span>)</span><br><span class="line"><span class="keyword">var</span> router = <span class="built_in">require</span>(<span class="string">"./router/index"</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line">app.<span class="title function_">use</span>(router.<span class="title function_">routes</span>()).<span class="title function_">use</span>(router.<span class="title function_">allowedMethods</span>())</span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><h6 id="4-5-路由前缀"><a href="#4-5-路由前缀" class="headerlink" title="4.5 路由前缀"></a>4.5 路由前缀</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">router.<span class="title function_">prefix</span>(<span class="string">'/api'</span>)</span><br></pre></td></tr></table></figure><h6 id="4-6-路由重定向"><a href="#4-6-路由重定向" class="headerlink" title="4.6 路由重定向"></a>4.6 路由重定向</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">router.<span class="title function_">get</span>(<span class="string">"/home"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="property">body</span>=<span class="string">"home页面"</span></span><br><span class="line">})</span><br><span class="line"><span class="comment">//写法1 </span></span><br><span class="line">router.<span class="title function_">redirect</span>(<span class="string">'/'</span>, <span class="string">'/home'</span>);</span><br><span class="line"><span class="comment">//写法2</span></span><br><span class="line">router.<span class="title function_">get</span>(<span class="string">"/"</span>,<span class="function">(<span class="params">ctx</span>)=></span>{</span><br><span class="line"> ctx.<span class="title function_">redirect</span>(<span class="string">"/home"</span>)</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h5 id="5-静态资源"><a href="#5-静态资源" class="headerlink" title="5. 静态资源"></a>5. 静态资源</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="keyword">static</span> = <span class="built_in">require</span>(<span class="string">'koa-static'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">static</span>(</span><br><span class="line"> path.<span class="title function_">join</span>( __dirname, <span class="string">"public"</span>)</span><br><span class="line">))</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>( <span class="keyword">async</span> ( ctx ) => {</span><br><span class="line"> ctx.<span class="property">body</span> = <span class="string">'hello world'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'[demo] static-use-middleware is starting at port 3000'</span>)</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="6-获取请求参数"><a href="#6-获取请求参数" class="headerlink" title="6. 获取请求参数"></a>6. 获取请求参数</h5><h6 id="6-1get参数"><a href="#6-1get参数" class="headerlink" title="6.1get参数"></a>6.1get参数</h6><p>在koa中,获取GET请求数据源头是koa中request对象中的query方法或querystring方法,query返回是格式化好的参数对象,querystring返回的是请求字符串,由于ctx对request的API有直接引用的方式,所以获取GET请求数据有两个途径。</p><ul><li>是从上下文中直接获取 请求对象ctx.query,返回如 { a:1, b:2 } 请求字符串 ctx.querystring,返回如 a=1&b=2</li><li>是从上下文的request对象中获取 请求对象ctx.request.query,返回如 { a:1, b:2 } 请求字符串 ctx.request.querystring,返回如 a=1&b=2</li></ul><h6 id="6-2post参数"><a href="#6-2post参数" class="headerlink" title="6.2post参数"></a>6.2post参数</h6><p>对于POST请求的处理,koa-bodyparser中间件可以把koa2上下文的formData数据解析到ctx.request.body中</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> bodyParser = <span class="built_in">require</span>(<span class="string">'koa-bodyparser'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 使用ctx.body解析中间件</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">bodyParser</span>())</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="7-ejs模板"><a href="#7-ejs模板" class="headerlink" title="7. ejs模板"></a>7. ejs模板</h5><h6 id="7-1-安装模块"><a href="#7-1-安装模块" class="headerlink" title="7.1 安装模块"></a>7.1 安装模块</h6><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"># 安装koa模板使用中间件</span><br><span class="line">npm install --save koa-views</span><br><span class="line"></span><br><span class="line"># 安装ejs模板引擎</span><br><span class="line">npm install --save ejs</span><br><span class="line"></span><br></pre></td></tr></table></figure><h6 id="7-2-使用模板引擎"><a href="#7-2-使用模板引擎" class="headerlink" title="7.2 使用模板引擎"></a>7.2 使用模板引擎</h6><p><strong>文件目录</strong></p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">├── package.json</span><br><span class="line">├── index.js</span><br><span class="line">└── view</span><br><span class="line"> └── index.ejs</span><br></pre></td></tr></table></figure><p><strong>./index.js文件</strong></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">Koa</span> = <span class="built_in">require</span>(<span class="string">'koa'</span>)</span><br><span class="line"><span class="keyword">const</span> views = <span class="built_in">require</span>(<span class="string">'koa-views'</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="keyword">new</span> <span class="title class_">Koa</span>()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 加载模板引擎</span></span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">views</span>(path.<span class="title function_">join</span>(__dirname, <span class="string">'./view'</span>), {</span><br><span class="line"> <span class="attr">extension</span>: <span class="string">'ejs'</span></span><br><span class="line">}))</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">use</span>( <span class="keyword">async</span> ( ctx ) => {</span><br><span class="line"> <span class="keyword">let</span> title = <span class="string">'hello koa2'</span></span><br><span class="line"> <span class="keyword">await</span> ctx.<span class="title function_">render</span>(<span class="string">'index'</span>, {</span><br><span class="line"> title,</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">app.<span class="title function_">listen</span>(<span class="number">3000</span>)</span><br></pre></td></tr></table></figure><p><strong>./view/index.ejs 模板</strong></p><figure class="highlight html"><table><tr><td class="code"><pre><span class="line"><span class="meta"><!DOCTYPE <span class="keyword">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">html</span>></span></span><br><span class="line"><span class="tag"><<span class="name">head</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">title</span>></span><%= title %><span class="tag"></<span class="name">title</span>></span></span><br><span class="line"><span class="tag"></<span class="name">head</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">h1</span>></span><%= title %><span class="tag"></<span class="name">h1</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">p</span>></span>EJS Welcome to <%= title %><span class="tag"></<span class="name">p</span>></span></span><br><span class="line"><span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">html</span>></span></span><br></pre></td></tr></table></figure><h5 id="8-cookie-amp-session"><a href="#8-cookie-amp-session" class="headerlink" title="8. cookie&session"></a>8. cookie&session</h5><h6 id="8-1-cookie"><a href="#8-1-cookie" class="headerlink" title="8.1 cookie"></a>8.1 cookie</h6><p>koa提供了从上下文直接读取、写入cookie的方法</p><ul><li>ctx.cookies.get(name, [options]) 读取上下文请求中的cookie</li><li>ctx.cookies.set(name, value, [options]) 在上下文中写入cookie</li></ul><h6 id="8-2-session"><a href="#8-2-session" class="headerlink" title="8.2 session"></a>8.2 session</h6><ul><li><p>koa-session-minimal 适用于koa2 的session中间件,提供存储介质的读写接口 。</p> <figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> session = <span class="built_in">require</span>(<span class="string">'koa-session-minimal'</span>)</span><br><span class="line">app.<span class="title function_">use</span>(<span class="title function_">session</span>({</span><br><span class="line"> <span class="attr">key</span>: <span class="string">'SESSION_ID'</span>,</span><br><span class="line"> <span class="attr">cookie</span>: {</span><br><span class="line"> <span class="attr">maxAge</span>:<span class="number">1000</span>*<span class="number">60</span></span><br><span class="line"> }</span><br><span class="line">}))</span><br></pre></td></tr></table></figure> <figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(<span class="keyword">async</span> (ctx, next) => {</span><br><span class="line"> <span class="comment">//排除login相关的路由和接口</span></span><br><span class="line"> <span class="keyword">if</span> (ctx.<span class="property">url</span>.<span class="title function_">includes</span>(<span class="string">"login"</span>)) {</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (ctx.<span class="property">session</span>.<span class="property">user</span>) {</span><br><span class="line"> <span class="comment">//重新设置以下sesssion</span></span><br><span class="line"> ctx.<span class="property">session</span>.<span class="property">mydate</span> = <span class="title class_">Date</span>.<span class="title function_">now</span>()</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"></span><br><span class="line"> ctx.<span class="title function_">redirect</span>(<span class="string">"/login"</span>)</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></li></ul><h5 id="9-JWT"><a href="#9-JWT" class="headerlink" title="9. JWT"></a>9. JWT</h5> <figure class="highlight js"><table><tr><td class="code"><pre><span class="line">app.<span class="title function_">use</span>(<span class="title function_">async</span>(ctx, next) => {</span><br><span class="line"> <span class="comment">//排除login相关的路由和接口</span></span><br><span class="line"> <span class="keyword">if</span> (ctx.<span class="property">url</span>.<span class="title function_">includes</span>(<span class="string">"login"</span>)) {</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">const</span> token = ctx.<span class="property">headers</span>[<span class="string">"authorization"</span>]?.<span class="title function_">split</span>(<span class="string">" "</span>)[<span class="number">1</span>]</span><br><span class="line"> <span class="comment">// console.log(req.headers["authorization"])</span></span><br><span class="line"> <span class="keyword">if</span>(token){</span><br><span class="line"> <span class="keyword">const</span> payload= <span class="variable constant_">JWT</span>.<span class="title function_">verify</span>(token)</span><br><span class="line"> <span class="keyword">if</span>(payload){</span><br><span class="line"> <span class="comment">//重新计算token过期时间</span></span><br><span class="line"> <span class="keyword">const</span> newToken = <span class="variable constant_">JWT</span>.<span class="title function_">generate</span>({</span><br><span class="line"> <span class="attr">_id</span>:payload.<span class="property">_id</span>,</span><br><span class="line"> <span class="attr">username</span>:payload.<span class="property">username</span></span><br><span class="line"> },<span class="string">"10s"</span>)</span><br><span class="line"></span><br><span class="line"> ctx.<span class="title function_">set</span>(<span class="string">"Authorization"</span>,newToken)</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> ctx.<span class="property">status</span> = <span class="number">401</span></span><br><span class="line"> ctx.<span class="property">body</span> = {<span class="attr">errCode</span>:-<span class="number">1</span>,<span class="attr">errInfo</span>:<span class="string">"token过期"</span>}</span><br><span class="line"> }</span><br><span class="line"> }<span class="keyword">else</span>{</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">next</span>()</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h5 id="-1"><a href="#-1" class="headerlink" title></a></h5><h5 id="10-上传文件"><a href="#10-上传文件" class="headerlink" title="10.上传文件"></a>10.上传文件</h5><blockquote><p><a href="https://www.npmjs.com/package/@koa/multer">https://www.npmjs.com/package/@koa/multer</a></p></blockquote><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">npm install --save @koa/multer multer</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> multer = <span class="built_in">require</span>(<span class="string">'@koa/multer'</span>);</span><br><span class="line"><span class="keyword">const</span> upload = <span class="title function_">multer</span>({ <span class="attr">dest</span>: <span class="string">'public/uploads/'</span> })</span><br><span class="line"></span><br><span class="line">router.<span class="title function_">post</span>(<span class="string">"/"</span>,upload.<span class="title function_">single</span>(<span class="string">'avatar'</span>),</span><br><span class="line"><span class="function">(<span class="params">ctx,next</span>)=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(ctx.<span class="property">request</span>.<span class="property">body</span>,ctx.<span class="property">file</span>)</span><br><span class="line"> ctx.<span class="property">body</span>={</span><br><span class="line"> <span class="attr">ok</span>:<span class="number">1</span>,</span><br><span class="line"> <span class="attr">info</span>:<span class="string">"add user success"</span></span><br><span class="line"> }</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="11-操作MongoDB"><a href="#11-操作MongoDB" class="headerlink" title="11.操作MongoDB"></a>11.操作MongoDB</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mongoose = <span class="built_in">require</span>(<span class="string">"mongoose"</span>)</span><br><span class="line"></span><br><span class="line">mongoose.<span class="title function_">connect</span>(<span class="string">"mongodb://127.0.0.1:27017/kerwin_project"</span>)</span><br><span class="line"><span class="comment">//插入集合和数据,数据库kerwin_project会自动创建</span></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> mongoose = <span class="built_in">require</span>(<span class="string">"mongoose"</span>)</span><br><span class="line"><span class="keyword">const</span> <span class="title class_">Schema</span> = mongoose.<span class="property">Schema</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">UserType</span> = {</span><br><span class="line"> <span class="attr">username</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">password</span>:<span class="title class_">String</span>,</span><br><span class="line"> <span class="attr">age</span>:<span class="title class_">Number</span>,</span><br><span class="line"> <span class="attr">avatar</span>:<span class="title class_">String</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">UserModel</span> = mongoose.<span class="title function_">model</span>(<span class="string">"user"</span>,<span class="keyword">new</span> <span class="title class_">Schema</span>(<span class="title class_">UserType</span>))</span><br><span class="line"><span class="comment">// 模型user 将会对应 users 集合, </span></span><br><span class="line"><span class="variable language_">module</span>.<span class="property">exports</span> = <span class="title class_">UserModel</span></span><br></pre></td></tr></table></figure><h4 id="九、MySQL"><a href="#九、MySQL" class="headerlink" title="九、MySQL"></a>九、MySQL</h4><h5 id="1-介绍"><a href="#1-介绍" class="headerlink" title="1.介绍"></a>1.介绍</h5><p>付费的商用数据库:</p><ul><li>Oracle,典型的高富帅;</li><li>SQL Server,微软自家产品,Windows定制专款;</li><li>DB2,IBM的产品,听起来挺高端;</li><li>Sybase,曾经跟微软是好基友,后来关系破裂,现在家境惨淡。</li></ul><p>这些数据库都是不开源而且付费的,最大的好处是花了钱出了问题可以找厂家解决,不过在Web的世界里,常常需要部署成千上万的数据库服务器,当然不能把大把大把的银子扔给厂家,所以,无论是Google、Facebook,还是国内的BAT,无一例外都选择了免费的开源数据库:</p><ul><li>MySQL,大家都在用,一般错不了;</li><li>PostgreSQL,学术气息有点重,其实挺不错,但知名度没有MySQL高;</li><li>sqlite,嵌入式数据库,适合桌面和移动应用。</li></ul><p>作为一个JavaScript全栈工程师,选择哪个免费数据库呢?当然是MySQL。因为MySQL普及率最高,出了错,可以很容易找到解决方法。而且,围绕MySQL有一大堆监控和运维的工具,安装和使用很方便。</p><p><img src="/posts/5063/image-20220420083146539.webp" alt="image-20220420083146539"></p><h5 id="2-与非关系数据库区别"><a href="#2-与非关系数据库区别" class="headerlink" title="2.与非关系数据库区别"></a>2.与非关系数据库区别</h5><p>关系型和非关系型数据库的主要差异是数据存储的方式。关系型数据天然就是表格式的,因此存储在数据表的行和列中。数据表可以彼此关联协作存储,也很容易提取数据。</p><p>与其相反,非关系型数据不适合存储在数据表的行和列中,而是大块组合在一起。非关系型数据通常存储在数据集中,就像文档、键值对或者图结构。你的数据及其特性是选择数据存储和提取方式的首要影响因素。</p><p><strong>关系型数据库最典型的数据结构是表,由二维表及其之间的联系所组成的一个数据组织</strong><br>优点:<br>1、易于维护:都是使用表结构,格式一致;<br>2、使用方便:SQL语言通用,可用于复杂查询;<br>3、复杂操作:支持SQL,可用于一个表以及多个表之间非常复杂的查询。<br>缺点:<br>1、读写性能比较差,尤其是海量数据的高效率读写;<br>2、固定的表结构,灵活度稍欠;<br>3、高并发读写需求,传统关系型数据库来说,硬盘I/O是一个很大的瓶颈。</p><p><strong>非关系型数据库严格上不是一种数据库,应该是一种数据结构化存储方法的集合,可以是文档或者键值对等。</strong></p><p>优点:</p><p>1、格式灵活:存储数据的格式可以是key,value形式、文档形式、图片形式等等,文档形式、图片形式等等,使用灵活,应用场景广泛,而关系型数据库则只支持基础类型。<br>2、速度快:nosql可以使用硬盘或者随机存储器作为载体,而关系型数据库只能使用硬盘;<br>3、高扩展性;<br>4、成本低:nosql数据库部署简单,基本都是开源软件。</p><p>缺点:</p><p>1、不提供sql支持;<br>2、无事务处理;<br>3、数据结构相对复杂,复杂查询方面稍欠。</p><h5 id="3-sql语句"><a href="#3-sql语句" class="headerlink" title="3.sql语句"></a>3.sql语句</h5><p><img src="/posts/5063/image-20220420092527846.webp" alt="image-20220420092527846"></p><p>插入:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">INSERT</span> <span class="keyword">INTO</span> `students`(`id`, `name`, `score`, `gender`) <span class="keyword">VALUES</span> (<span class="keyword">null</span>,<span class="string">'kerwin'</span>,<span class="number">100</span>,<span class="number">1</span>)</span><br><span class="line"><span class="operator">/</span><span class="operator">/</span>可以不设置id,create_time</span><br></pre></td></tr></table></figure><p>更新:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">UPDATE</span> `students` <span class="keyword">SET</span> `name`<span class="operator">=</span><span class="string">'tiechui'</span>,`score`<span class="operator">=</span><span class="number">20</span>,`gender`<span class="operator">=</span><span class="number">0</span> <span class="keyword">WHERE</span> id<span class="operator">=</span><span class="number">2</span>;</span><br></pre></td></tr></table></figure><p>删除:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line"><span class="keyword">DELETE</span> <span class="keyword">FROM</span> `students` <span class="keyword">WHERE</span> id<span class="operator">=</span><span class="number">2</span>;</span><br></pre></td></tr></table></figure><p>查询:</p><figure class="highlight sql"><table><tr><td class="code"><pre><span class="line">查所有的数据所有的字段</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> `students` <span class="keyword">WHERE</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">查所有的数据某个字段</span><br><span class="line"><span class="keyword">SELECT</span> `id`, `name`, `score`, `gender` <span class="keyword">FROM</span> `students` <span class="keyword">WHERE</span> <span class="number">1</span>;</span><br><span class="line"></span><br><span class="line">条件查询</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> `students` <span class="keyword">WHERE</span> score<span class="operator">>=</span><span class="number">80</span>;</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> `students` <span class="keyword">where</span> score<span class="operator">>=</span><span class="number">80</span> <span class="keyword">AND</span> gender<span class="operator">=</span><span class="number">1</span></span><br><span class="line"></span><br><span class="line">模糊查询</span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> `students` <span class="keyword">where</span> name <span class="keyword">like</span> <span class="string">'%k%'</span></span><br><span class="line"></span><br><span class="line">排序</span><br><span class="line"><span class="keyword">SELECT</span> id, name, gender, score <span class="keyword">FROM</span> students <span class="keyword">ORDER</span> <span class="keyword">BY</span> score;</span><br><span class="line"><span class="keyword">SELECT</span> id, name, gender, score <span class="keyword">FROM</span> students <span class="keyword">ORDER</span> <span class="keyword">BY</span> score <span class="keyword">DESC</span>;</span><br><span class="line"></span><br><span class="line">分页查询</span><br><span class="line"><span class="keyword">SELECT</span> id, name, gender, score <span class="keyword">FROM</span> students LIMIT <span class="number">50</span> <span class="keyword">OFFSET</span> <span class="number">0</span></span><br><span class="line"></span><br><span class="line">记录条数</span><br><span class="line"><span class="keyword">SELECT</span> <span class="built_in">COUNT</span>(<span class="operator">*</span>) <span class="keyword">FROM</span> students;</span><br><span class="line"><span class="keyword">SELECT</span> <span class="built_in">COUNT</span>(<span class="operator">*</span>) kerwinnum <span class="keyword">FROM</span> students;</span><br><span class="line"></span><br><span class="line">多表查询</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span> <span class="operator">*</span> <span class="keyword">FROM</span> students, classes;(这种多表查询又称笛卡尔查询,使用笛卡尔查询时要非常小心,由于结果集是目标表的行数乘积,对两个各自有<span class="number">100</span>行记录的表进行笛卡尔查询将返回<span class="number">1</span>万条记录,对两个各自有<span class="number">1</span>万行记录的表进行笛卡尔查询将返回<span class="number">1</span>亿条记录)</span><br><span class="line"><span class="keyword">SELECT</span></span><br><span class="line"> students.id sid,</span><br><span class="line"> students.name,</span><br><span class="line"> students.gender,</span><br><span class="line"> students.score,</span><br><span class="line"> classes.id cid,</span><br><span class="line"> classes.name cname</span><br><span class="line"><span class="keyword">FROM</span> students, classes; (要使用表名.列名这样的方式来引用列和设置别名,这样就避免了结果集的列名重复问题。)</span><br><span class="line"></span><br><span class="line"><span class="keyword">SELECT</span></span><br><span class="line"> s.id sid,</span><br><span class="line"> s.name,</span><br><span class="line"> s.gender,</span><br><span class="line"> s.score,</span><br><span class="line"> c.id cid,</span><br><span class="line"> c.name cname</span><br><span class="line"><span class="keyword">FROM</span> students s, classes c; (<span class="keyword">SQL</span>还允许给表设置一个别名)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">联表查询</span><br><span class="line"><span class="keyword">SELECT</span> s.id, s.name, s.class_id, c.name class_name, s.gender, s.score</span><br><span class="line"><span class="keyword">FROM</span> students s</span><br><span class="line"><span class="keyword">INNER</span> <span class="keyword">JOIN</span> classes c</span><br><span class="line"><span class="keyword">ON</span> s.class_id <span class="operator">=</span> c.id; (连接查询对多个表进行<span class="keyword">JOIN</span>运算,简单地说,就是先确定一个主表作为结果集,然后,把其他表的行有选择性地“连接”在主表结果集上。)</span><br><span class="line"></span><br><span class="line"></span><br></pre></td></tr></table></figure><p><img src="/posts/5063/image-20220420090841742.webp" alt="image-20220420090841742"></p><p>注意:</p><blockquote><ol><li>InnoDB 支持事务,MyISAM 不支持事务。这是 MySQL 将默认存储引擎从 MyISAM 变成 InnoDB 的重要原因之一;</li><li>InnoDB 支持外键,而 MyISAM 不支持。对一个包含外键的 InnoDB 表转为 MYISAM 会失败;</li></ol></blockquote><p><strong>外键约束</strong></p><p>CASCADE<br>在父表上update/delete记录时,同步update/delete掉子表的匹配记录 </p><p>SET NULL<br>在父表上update/delete记录时,将子表上匹配记录的列设为null (要注意子表的外键列不能为not null) </p><p>NO ACTION<br>如果子表中有匹配的记录,则不允许对父表对应候选键进行update/delete操作 </p><p>RESTRICT<br>同no action, 都是立即检查外键约束</p><h5 id="4-nodejs-操作数据库"><a href="#4-nodejs-操作数据库" class="headerlink" title="4.nodejs 操作数据库"></a>4.nodejs 操作数据库</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> express = <span class="built_in">require</span>(<span class="string">'express'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="title function_">express</span>()</span><br><span class="line"><span class="keyword">const</span> mysql2 = <span class="built_in">require</span>(<span class="string">'mysql2'</span>)</span><br><span class="line"><span class="keyword">const</span> port = <span class="number">9000</span></span><br><span class="line"></span><br><span class="line">app.<span class="title function_">get</span>(<span class="string">'/'</span>,<span class="keyword">async</span> (req, res) => {</span><br><span class="line"> <span class="keyword">const</span> config = <span class="title function_">getDBConfig</span>()</span><br><span class="line"> <span class="keyword">const</span> promisePool = mysql2.<span class="title function_">createPool</span>(config).<span class="title function_">promise</span>();</span><br><span class="line"> <span class="comment">// console.log(promisePool)</span></span><br><span class="line"> <span class="keyword">let</span> user = <span class="keyword">await</span> promisePool.<span class="title function_">query</span>(<span class="string">'select * from students'</span>);</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(user)</span><br><span class="line"> <span class="keyword">if</span> (user[<span class="number">0</span>].<span class="property">length</span>) {</span><br><span class="line"> <span class="comment">//存在用户</span></span><br><span class="line"> res.<span class="title function_">send</span>(user[<span class="number">0</span>])</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">//不存在</span></span><br><span class="line"> res.<span class="title function_">send</span>( {</span><br><span class="line"> <span class="attr">code</span>: -<span class="number">2</span>,</span><br><span class="line"> <span class="attr">msg</span>: <span class="string">'user not exsit'</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">app.<span class="title function_">listen</span>(port, <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">`Example app listening at http://localhost:<span class="subst">${port}</span>`</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">getDBConfig</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> <span class="attr">host</span>: <span class="string">'127.0.0.1'</span>,</span><br><span class="line"> <span class="attr">user</span>: <span class="string">'root'</span>,</span><br><span class="line"> <span class="attr">port</span>: <span class="number">3306</span>,</span><br><span class="line"> <span class="attr">password</span>: <span class="string">''</span>,</span><br><span class="line"> <span class="attr">database</span>: <span class="string">'kerwin_test'</span>,</span><br><span class="line"> <span class="attr">connectionLimit</span>: <span class="number">1</span> <span class="comment">//创建一个连接池</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line">查询:</span><br><span class="line">promisePool.<span class="title function_">query</span>(<span class="string">'select * from users'</span>);</span><br><span class="line"></span><br><span class="line">插入:</span><br><span class="line">promisePool.<span class="title function_">query</span>(<span class="string">'INSERT INTO `users`(`id`,`name`,`age`, `password`) VALUES (?,?,?,?)'</span>,[<span class="literal">null</span>,<span class="string">"kerwin"</span>,<span class="number">100</span>,<span class="string">"123456"</span>]);</span><br><span class="line"></span><br><span class="line">更新:</span><br><span class="line">promisePool.<span class="title function_">query</span>(<span class="string">`UPDATE users SET name = ? ,age=? WHERE id = ?`</span>,[<span class="string">"xiaoming2"</span>,<span class="number">20</span>,<span class="number">1</span>])</span><br><span class="line"></span><br><span class="line">删除:</span><br><span class="line">promisePool.<span class="title function_">query</span>(<span class="string">`delete from users where id=?`</span>,[<span class="number">1</span>])</span><br><span class="line"></span><br></pre></td></tr></table></figure><h4 id="十、Socket编程"><a href="#十、Socket编程" class="headerlink" title="十、Socket编程"></a>十、Socket编程</h4><h5 id="1-websocket介绍"><a href="#1-websocket介绍" class="headerlink" title="1.websocket介绍"></a>1.websocket介绍</h5><img src="/posts/5063/image-20220421084242097.webp" alt="image-20220421084242097" style="zoom:50%;"><p><strong>应用场景:</strong></p><ul><li><p>弹幕</p></li><li><p>媒体聊天</p></li><li><p>协同编辑</p></li><li><p>基于位置的应用</p></li><li><p>体育实况更新</p></li><li><p>股票基金报价实时更新</p></li></ul><p>WebSocket并不是全新的协议,而是利用了HTTP协议来建立连接。我们来看看WebSocket连接是如何创建的。</p><p>首先,WebSocket连接必须由浏览器发起,因为请求协议是一个标准的HTTP请求,格式如下:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="variable constant_">GET</span> <span class="attr">ws</span>:<span class="comment">//localhost:3000/ws/chat HTTP/1.1</span></span><br><span class="line"><span class="title class_">Host</span>: localhost</span><br><span class="line"><span class="title class_">Upgrade</span>: websocket</span><br><span class="line"><span class="title class_">Connection</span>: <span class="title class_">Upgrade</span></span><br><span class="line"><span class="title class_">Origin</span>: <span class="attr">http</span>:<span class="comment">//localhost:3000</span></span><br><span class="line"><span class="title class_">Sec</span>-<span class="title class_">WebSocket</span>-<span class="title class_">Key</span>: client-random-string</span><br><span class="line"><span class="title class_">Sec</span>-<span class="title class_">WebSocket</span>-<span class="title class_">Version</span>: <span class="number">13</span></span><br></pre></td></tr></table></figure><p>该请求和普通的HTTP请求有几点不同:</p><ol><li>GET请求的地址不是类似<code>/path/</code>,而是以<code>ws://</code>开头的地址;</li><li>请求头<code>Upgrade: websocket</code>和<code>Connection: Upgrade</code>表示这个连接将要被转换为WebSocket连接;</li><li><code>Sec-WebSocket-Key</code>是用于标识这个连接,并非用于加密数据;</li><li><code>Sec-WebSocket-Version</code>指定了WebSocket的协议版本。</li></ol><p>随后,服务器如果接受该请求,就会返回如下响应:</p><figure class="highlight plaintext"><table><tr><td class="code"><pre><span class="line">HTTP/1.1 101 Switching Protocols</span><br><span class="line">Upgrade: websocket</span><br><span class="line">Connection: Upgrade</span><br><span class="line">Sec-WebSocket-Accept: server-random-string</span><br></pre></td></tr></table></figure><p>该响应代码<code>101</code>表示本次连接的HTTP协议即将被更改,更改后的协议就是<code>Upgrade: websocket</code>指定的WebSocket协议。</p><p>版本号和子协议规定了双方能理解的数据格式,以及是否支持压缩等等。如果仅使用WebSocket的API,就不需要关心这些。</p><p>现在,一个WebSocket连接就建立成功,浏览器和服务器就可以随时主动发送消息给对方。消息有两种,一种是文本,一种是二进制数据。通常,我们可以发送JSON格式的文本,这样,在浏览器处理起来就十分容易。</p><p>为什么WebSocket连接可以实现全双工通信而HTTP连接不行呢?实际上HTTP协议是建立在TCP协议之上的,TCP协议本身就实现了全双工通信,但是HTTP协议的请求-应答机制限制了全双工通信。WebSocket连接建立以后,其实只是简单规定了一下:接下来,咱们通信就不使用HTTP协议了,直接互相发数据吧。</p><p>安全的WebSocket连接机制和HTTPS类似。首先,浏览器用<code>wss://xxx</code>创建WebSocket连接时,会先通过HTTPS创建安全的连接,然后,该HTTPS连接升级为WebSocket连接,底层通信走的仍然是安全的SSL/TLS协议。</p><p><strong>浏览器支持</strong></p><p>很显然,要支持WebSocket通信,浏览器得支持这个协议,这样才能发出<code>ws://xxx</code>的请求。目前,支持WebSocket的主流浏览器如下:</p><ul><li>Chrome</li><li>Firefox</li><li>IE >= 10</li><li>Sarafi >= 6</li><li>Android >= 4.4</li><li>iOS >= 8</li></ul><p><strong>服务器支持</strong></p><p>由于WebSocket是一个协议,服务器具体怎么实现,取决于所用编程语言和框架本身。Node.js本身支持的协议包括TCP协议和HTTP协议,要支持WebSocket协议,需要对Node.js提供的HTTPServer做额外的开发。已经有若干基于Node.js的稳定可靠的WebSocket实现,我们直接用npm安装使用即可。</p><h5 id="2-ws模块"><a href="#2-ws模块" class="headerlink" title="2.ws模块"></a>2.ws模块</h5><p>服务器:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">WebSocket</span> = <span class="built_in">require</span>(<span class="string">"ws"</span>)</span><br><span class="line"><span class="title class_">WebSocketServer</span> = <span class="title class_">WebSocket</span>.<span class="property">WebSocketServer</span></span><br><span class="line"><span class="keyword">const</span> wss = <span class="keyword">new</span> <span class="title class_">WebSocketServer</span>({ <span class="attr">port</span>: <span class="number">8080</span> });</span><br><span class="line">wss.<span class="title function_">on</span>(<span class="string">'connection'</span>, <span class="keyword">function</span> <span class="title function_">connection</span>(<span class="params">ws</span>) {</span><br><span class="line"> ws.<span class="title function_">on</span>(<span class="string">'message'</span>, <span class="keyword">function</span> <span class="title function_">message</span>(<span class="params">data, isBinary</span>) {</span><br><span class="line"> wss.<span class="property">clients</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> <span class="title function_">each</span>(<span class="params">client</span>) {</span><br><span class="line"> <span class="keyword">if</span> (client !== ws && client.<span class="property">readyState</span> === <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) {</span><br><span class="line"> client.<span class="title function_">send</span>(data, { <span class="attr">binary</span>: isBinary });</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"> ws.<span class="title function_">send</span>(<span class="string">'欢迎加入聊天室'</span>);</span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>客户端:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> ws = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">"ws://localhost:8080"</span>)</span><br><span class="line">ws.<span class="property">onopen</span> = <span class="function">()=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"open"</span>)</span><br><span class="line">}</span><br><span class="line">ws.<span class="property">onmessage</span> = <span class="function">(<span class="params">evt</span>)=></span>{</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(evt.<span class="property">data</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>授权验证:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="comment">//前端</span></span><br><span class="line"><span class="keyword">var</span> ws = <span class="keyword">new</span> <span class="title class_">WebSocket</span>(<span class="string">`ws://localhost:8080?token=<span class="subst">${<span class="variable language_">localStorage</span>.getItem(<span class="string">"token"</span>)}</span>`</span>)</span><br><span class="line">ws.<span class="property">onopen</span> = <span class="function">() =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">"open"</span>)</span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">type</span>: <span class="title class_">WebSocketType</span>.<span class="property">GroupList</span></span><br><span class="line"> }))</span><br><span class="line"> }</span><br><span class="line">ws.<span class="property">onmessage</span> = <span class="function">(<span class="params">evt</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(evt.<span class="property">data</span>)</span><br><span class="line">}</span><br><span class="line"><span class="comment">//后端</span></span><br><span class="line"><span class="keyword">const</span> <span class="title class_">WebSocket</span> = <span class="built_in">require</span>(<span class="string">"ws"</span>);</span><br><span class="line"><span class="keyword">const</span> <span class="variable constant_">JWT</span> = <span class="built_in">require</span>(<span class="string">'../util/JWT'</span>);</span><br><span class="line"><span class="title class_">WebSocketServer</span> = <span class="title class_">WebSocket</span>.<span class="property">WebSocketServer</span></span><br><span class="line"><span class="keyword">const</span> wss = <span class="keyword">new</span> <span class="title class_">WebSocketServer</span>({ <span class="attr">port</span>: <span class="number">8080</span> });</span><br><span class="line">wss.<span class="title function_">on</span>(<span class="string">'connection'</span>, <span class="keyword">function</span> <span class="title function_">connection</span>(<span class="params">ws, req</span>) {</span><br><span class="line"> <span class="keyword">const</span> myURL = <span class="keyword">new</span> <span class="title function_">URL</span>(req.<span class="property">url</span>, <span class="string">'http://127.0.0.1:3000'</span>);</span><br><span class="line"> <span class="keyword">const</span> payload = <span class="variable constant_">JWT</span>.<span class="title function_">verify</span>(myURL.<span class="property">searchParams</span>.<span class="title function_">get</span>(<span class="string">"token"</span>))</span><br><span class="line"> <span class="keyword">if</span> (payload) {</span><br><span class="line"> ws.<span class="property">user</span> = payload</span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, ws.<span class="property">user</span>, <span class="string">"欢迎来到聊天室"</span>))</span><br><span class="line"></span><br><span class="line"> <span class="title function_">sendBroadList</span>() <span class="comment">//发送好友列表</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">Error</span>, <span class="literal">null</span>, <span class="string">"token过期"</span>))</span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// console.log(3333,url)</span></span><br><span class="line"> ws.<span class="title function_">on</span>(<span class="string">'message'</span>, <span class="keyword">function</span> <span class="title function_">message</span>(<span class="params">data, isBinary</span>) {</span><br><span class="line"> <span class="keyword">const</span> messageObj = <span class="title class_">JSON</span>.<span class="title function_">parse</span>(data)</span><br><span class="line"> <span class="keyword">switch</span> (messageObj.<span class="property">type</span>) {</span><br><span class="line"> <span class="keyword">case</span> <span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>:</span><br><span class="line"> ws.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, ws.<span class="property">user</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(<span class="title class_">Array</span>.<span class="title function_">from</span>(wss.<span class="property">clients</span>).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="property">user</span>))))</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>:</span><br><span class="line"> wss.<span class="property">clients</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> <span class="title function_">each</span>(<span class="params">client</span>) {</span><br><span class="line"> <span class="keyword">if</span> (client !== ws && client.<span class="property">readyState</span> === <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) {</span><br><span class="line"> client.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, ws.<span class="property">user</span>, messageObj.<span class="property">data</span>));</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> <span class="keyword">case</span> <span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>:</span><br><span class="line"> wss.<span class="property">clients</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> <span class="title function_">each</span>(<span class="params">client</span>) {</span><br><span class="line"> <span class="keyword">if</span> (client.<span class="property">user</span>.<span class="property">username</span> === messageObj.<span class="property">to</span> && client.<span class="property">readyState</span> === <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) {</span><br><span class="line"> client.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>, ws.<span class="property">user</span>, messageObj.<span class="property">data</span>));</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> ws.<span class="title function_">on</span>(<span class="string">"close"</span>,<span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="comment">//删除当前用户</span></span><br><span class="line"> wss.<span class="property">clients</span>.<span class="title function_">delete</span>(ws.<span class="property">user</span>)</span><br><span class="line"> <span class="title function_">sendBroadList</span>() <span class="comment">//发送好用列表</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="keyword">const</span> <span class="title class_">WebSocketType</span> = {</span><br><span class="line"> <span class="title class_">Error</span>: <span class="number">0</span>, <span class="comment">//错误</span></span><br><span class="line"> <span class="title class_">GroupList</span>: <span class="number">1</span>,<span class="comment">//群列表</span></span><br><span class="line"> <span class="title class_">GroupChat</span>: <span class="number">2</span>,<span class="comment">//群聊</span></span><br><span class="line"> <span class="title class_">SingleChat</span>: <span class="number">3</span><span class="comment">//私聊</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">createMessage</span>(<span class="params">type, user, data</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="title class_">JSON</span>.<span class="title function_">stringify</span>({</span><br><span class="line"> <span class="attr">type</span>: type,</span><br><span class="line"> <span class="attr">user</span>: user,</span><br><span class="line"> <span class="attr">data</span>: data</span><br><span class="line"> });</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sendBroadList</span>(<span class="params"></span>){</span><br><span class="line"> wss.<span class="property">clients</span>.<span class="title function_">forEach</span>(<span class="keyword">function</span> <span class="title function_">each</span>(<span class="params">client</span>) {</span><br><span class="line"> <span class="keyword">if</span> (client.<span class="property">readyState</span> === <span class="title class_">WebSocket</span>.<span class="property">OPEN</span>) {</span><br><span class="line"> client.<span class="title function_">send</span>(<span class="title function_">createMessage</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, client.<span class="property">user</span>, <span class="title class_">JSON</span>.<span class="title function_">stringify</span>(<span class="title class_">Array</span>.<span class="title function_">from</span>(wss.<span class="property">clients</span>).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =></span> item.<span class="property">user</span>))))</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h5 id="3-socket-io模块"><a href="#3-socket-io模块" class="headerlink" title="3.socket.io模块"></a>3.socket.io模块</h5><p>服务端:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> io = <span class="built_in">require</span>(<span class="string">'socket.io'</span>)(server);</span><br><span class="line">io.<span class="title function_">on</span>(<span class="string">'connection'</span>, <span class="function">(<span class="params">socket</span>) =></span> {</span><br><span class="line"></span><br><span class="line"> <span class="keyword">const</span> payload = <span class="variable constant_">JWT</span>.<span class="title function_">verify</span>(socket.<span class="property">handshake</span>.<span class="property">query</span>.<span class="property">token</span>)</span><br><span class="line"> <span class="keyword">if</span> (payload) {</span><br><span class="line"> socket.<span class="property">user</span> = payload</span><br><span class="line"> socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, <span class="title function_">createMessage</span>(socket.<span class="property">user</span>, <span class="string">"欢迎来到聊天室"</span>))</span><br><span class="line"> <span class="title function_">sendBroadList</span>() <span class="comment">//发送好友列表</span></span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">Error</span>, <span class="title function_">createMessage</span>(<span class="literal">null</span>, <span class="string">"token过期"</span>))</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, <span class="function">() =></span> {</span><br><span class="line"> socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, <span class="title function_">createMessage</span>(<span class="literal">null</span>, <span class="title class_">Array</span>.<span class="title function_">from</span>(io.<span class="property">sockets</span>.<span class="property">sockets</span>).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =></span> item[<span class="number">1</span>].<span class="property">user</span>).<span class="title function_">filter</span>(<span class="function"><span class="params">item</span>=></span>item)));</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, <span class="function">(<span class="params">messageObj</span>) =></span> {</span><br><span class="line"> socket.<span class="property">broadcast</span>.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, <span class="title function_">createMessage</span>(socket.<span class="property">user</span>, messageObj.<span class="property">data</span>));</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>, <span class="function">(<span class="params">messageObj</span>) =></span> {</span><br><span class="line"> <span class="title class_">Array</span>.<span class="title function_">from</span>(io.<span class="property">sockets</span>.<span class="property">sockets</span>).<span class="title function_">forEach</span>(<span class="keyword">function</span> (<span class="params">socket</span>) {</span><br><span class="line"> <span class="keyword">if</span> (socket[<span class="number">1</span>].<span class="property">user</span>.<span class="property">username</span> === messageObj.<span class="property">to</span>) {</span><br><span class="line"> socket[<span class="number">1</span>].<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>, <span class="title function_">createMessage</span>(socket[<span class="number">1</span>].<span class="property">user</span>, messageObj.<span class="property">data</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"> socket.<span class="title function_">on</span>(<span class="string">'disconnect'</span>, <span class="function"><span class="params">reason</span> =></span> {</span><br><span class="line"> </span><br><span class="line"> <span class="title function_">sendBroadList</span>() <span class="comment">//发送好用列表</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="keyword">function</span> <span class="title function_">sendBroadList</span>(<span class="params"></span>) {</span><br><span class="line"> io.<span class="property">sockets</span>.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, <span class="title function_">createMessage</span>(<span class="literal">null</span>, <span class="title class_">Array</span>.<span class="title function_">from</span>(io.<span class="property">sockets</span>.<span class="property">sockets</span>).<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =></span> item[<span class="number">1</span>].<span class="property">user</span>).<span class="title function_">filter</span>(<span class="function"><span class="params">item</span>=></span>item)))</span><br><span class="line">}</span><br><span class="line"><span class="comment">//最后filter,是因为 有可能存在null的值</span></span><br><span class="line"></span><br></pre></td></tr></table></figure><p>客户端:</p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> <span class="title class_">WebSocketType</span> = {</span><br><span class="line"> <span class="title class_">Error</span>: <span class="number">0</span>, <span class="comment">//错误</span></span><br><span class="line"> <span class="title class_">GroupList</span>: <span class="number">1</span>, <span class="comment">//群列表</span></span><br><span class="line"> <span class="title class_">GroupChat</span>: <span class="number">2</span>, <span class="comment">//群聊</span></span><br><span class="line"> <span class="title class_">SingleChat</span>: <span class="number">3</span> <span class="comment">//私聊</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> socket = <span class="title function_">io</span>(<span class="string">`ws://localhost:3000?token=<span class="subst">${<span class="variable language_">localStorage</span>.getItem(<span class="string">"token"</span>)}</span>`</span>);</span><br><span class="line">socket.<span class="title function_">on</span>(<span class="string">"connect"</span>,<span class="function">()=></span>{</span><br><span class="line">socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>)</span><br><span class="line">})</span><br><span class="line">socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupList</span>, <span class="function">(<span class="params">messageObj</span>) =></span> {</span><br><span class="line"> select.<span class="property">innerHTML</span> = <span class="string">""</span></span><br><span class="line"> select.<span class="property">innerHTML</span> = <span class="string">`<option value="all">all</option>`</span> + messageObj.<span class="property">data</span>.<span class="title function_">map</span>(<span class="function"><span class="params">item</span> =></span> <span class="string">`</span></span><br><span class="line"><span class="string"> <option value="<span class="subst">${item.username}</span>"><span class="subst">${item.username}</span></option>`</span>).<span class="title function_">join</span>(<span class="string">""</span>)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>, <span class="function">(<span class="params">msg</span>) =></span> {</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(msg)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>, <span class="function">(<span class="params">msg</span>) =></span> {</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(msg)</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">socket.<span class="title function_">on</span>(<span class="title class_">WebSocketType</span>.<span class="property">Error</span>, <span class="function">(<span class="params">msg</span>) =></span> {</span><br><span class="line"> <span class="variable language_">localStorage</span>.<span class="title function_">removeItem</span>(<span class="string">"token"</span>)</span><br><span class="line"> location.<span class="property">href</span> = <span class="string">"/login"</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line">send.<span class="property">onclick</span> = <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">if</span> (select.<span class="property">value</span> === <span class="string">"all"</span>) {</span><br><span class="line"> socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">GroupChat</span>,{</span><br><span class="line"> <span class="attr">data</span>: text.<span class="property">value</span></span><br><span class="line"> })</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> socket.<span class="title function_">emit</span>(<span class="title class_">WebSocketType</span>.<span class="property">SingleChat</span>,{</span><br><span class="line"> <span class="attr">data</span>: text.<span class="property">value</span>,</span><br><span class="line"> <span class="attr">to</span>:select.<span class="property">value</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="十一、mocha"><a href="#十一、mocha" class="headerlink" title="十一、mocha"></a>十一、mocha</h4><p>单元测试是用来对一个模块、一个函数或者一个类来进行正确性检验的测试工作。</p><p>比如对函数abs(),我们可以编写出以下几个测试用例:</p><p>输入正数,比如1、1.2、0.99,期待返回值与输入相同;</p><p>输入负数,比如-1、-1.2、-0.99,期待返回值与输入相反;</p><p>输入0,期待返回0;</p><p>输入非数值类型,比如null、[]、{},期待抛出Error。</p><p>把上面的测试用例放到一个测试模块里,就是一个完整的单元测试。</p><p>如果单元测试通过,说明我们测试的这个函数能够正常工作。如果单元测试不通过,要么函数有bug,要么测试条件输入不正确,总之,需要修复使单元测试能够通过。</p><p>单元测试通过后有什么意义呢?如果我们对abs()函数代码做了修改,只需要再跑一遍单元测试,如果通过,说明我们的修改不会对abs()函数原有的行为造成影响,如果测试不通过,说明我们的修改与原有行为不一致,要么修改代码,要么修改测试。</p><p>这种以测试为驱动的开发模式最大的好处就是确保一个程序模块的行为符合我们设计的测试用例。在将来修改的时候,可以极大程度地保证该模块行为仍然是正确的。</p><p>mocha是JavaScript的一种单元测试框架,既可以在浏览器环境下运行,也可以在Node.js环境下运行。</p><p>使用mocha,我们就只需要专注于编写单元测试本身,然后,让mocha去自动运行所有的测试,并给出测试结果。</p><p>mocha的特点主要有:</p><ol><li>既可以测试简单的JavaScript函数,又可以测试异步代码,因为异步是JavaScript的特性之一;</li><li>可以自动运行所有测试,也可以只运行特定的测试;</li><li>可以支持before、after、beforeEach和afterEach来编写初始化代码。</li></ol><h5 id="1-编写测试"><a href="#1-编写测试" class="headerlink" title="1.编写测试"></a>1.编写测试</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> assert = <span class="built_in">require</span>(<span class="string">'assert'</span>);</span><br><span class="line"><span class="keyword">const</span> sum = <span class="built_in">require</span>(<span class="string">'../test'</span>);</span><br><span class="line"><span class="title function_">describe</span>(<span class="string">'#hello.js'</span>, <span class="function">() =></span> {</span><br><span class="line"></span><br><span class="line"> <span class="title function_">describe</span>(<span class="string">'#sum()'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'sum() should return 0'</span>, <span class="function">() =></span> {</span><br><span class="line"> assert.<span class="title function_">strictEqual</span>(<span class="title function_">sum</span>(), <span class="number">0</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'sum(1) should return 1'</span>, <span class="function">() =></span> {</span><br><span class="line"> assert.<span class="title function_">strictEqual</span>(<span class="title function_">sum</span>(<span class="number">1</span>), <span class="number">1</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'sum(1, 2) should return 3'</span>, <span class="function">() =></span> {</span><br><span class="line"> assert.<span class="title function_">strictEqual</span>(<span class="title function_">sum</span>(<span class="number">1</span>, <span class="number">2</span>), <span class="number">3</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'sum(1, 2, 3) should return 6'</span>, <span class="function">() =></span> {</span><br><span class="line"> assert.<span class="title function_">strictEqual</span>(<span class="title function_">sum</span>(<span class="number">1</span>, <span class="number">2</span>, <span class="number">3</span>), <span class="number">6</span>);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h5 id="2-chai断言库"><a href="#2-chai断言库" class="headerlink" title="2.chai断言库"></a>2.chai断言库</h5><p><img src="/posts/5063/image-20220505113605440.webp" alt="image-20220505113605440"></p><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> chai = <span class="built_in">require</span>(<span class="string">'chai'</span>)</span><br><span class="line"><span class="keyword">var</span> assert = chai.<span class="property">assert</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">'assert Demo'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'use assert lib'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> value = <span class="string">"hello"</span>;</span><br><span class="line"> assert.<span class="title function_">typeOf</span>(value, <span class="string">'string'</span>)</span><br><span class="line"> assert.<span class="title function_">equal</span>(value, <span class="string">'hello'</span>)</span><br><span class="line"> assert.<span class="title function_">lengthOf</span>(value, <span class="number">5</span>)</span><br><span class="line"> })</span><br><span class="line">})</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> chai = <span class="built_in">require</span>(<span class="string">'chai'</span>);</span><br><span class="line">chai.<span class="title function_">should</span>();</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">'should Demo'</span>, <span class="keyword">function</span>(<span class="params"></span>){</span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'use should lib'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> value = <span class="string">'hello'</span></span><br><span class="line"> value.<span class="property">should</span>.<span class="property">exist</span>.<span class="property">and</span>.<span class="title function_">equal</span>(<span class="string">'hello'</span>).<span class="property">and</span>.<span class="property">have</span>.<span class="title function_">length</span>(<span class="number">5</span>).<span class="property">and</span>.<span class="property">be</span>.<span class="title function_">a</span>(<span class="string">'string'</span>)</span><br><span class="line"> <span class="comment">// value.should.be.a('string')</span></span><br><span class="line"> <span class="comment">// value.should.equal('hello')</span></span><br><span class="line"> <span class="comment">// value.should.not.equal('hello2')</span></span><br><span class="line"> <span class="comment">// value.should.have.length(5);</span></span><br><span class="line"> })</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></table></figure><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> chai = <span class="built_in">require</span>(<span class="string">'chai'</span>);</span><br><span class="line"><span class="keyword">var</span> expect = chai.<span class="property">expect</span>;</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">'expect Demo'</span>, <span class="keyword">function</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'use expect lib'</span>, <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> value = <span class="string">'hello'</span></span><br><span class="line"> <span class="keyword">var</span> number = <span class="number">3</span></span><br><span class="line"></span><br><span class="line"> <span class="title function_">expect</span>(number).<span class="property">to</span>.<span class="property">be</span>.<span class="property">at</span>.<span class="title function_">most</span>(<span class="number">5</span>)</span><br><span class="line"> <span class="title function_">expect</span>(number).<span class="property">to</span>.<span class="property">be</span>.<span class="property">at</span>.<span class="title function_">least</span>(<span class="number">3</span>)</span><br><span class="line"> <span class="title function_">expect</span>(number).<span class="property">to</span>.<span class="property">be</span>.<span class="title function_">within</span>(<span class="number">1</span>, <span class="number">4</span>)</span><br><span class="line"></span><br><span class="line"> <span class="title function_">expect</span>(value).<span class="property">to</span>.<span class="property">exist</span></span><br><span class="line"> <span class="title function_">expect</span>(value).<span class="property">to</span>.<span class="property">be</span>.<span class="title function_">a</span>(<span class="string">'string'</span>)</span><br><span class="line"> <span class="title function_">expect</span>(value).<span class="property">to</span>.<span class="title function_">equal</span>(<span class="string">'hello'</span>)</span><br><span class="line"> <span class="title function_">expect</span>(value).<span class="property">to</span>.<span class="property">not</span>.<span class="title function_">equal</span>(<span class="string">'您好'</span>)</span><br><span class="line"> <span class="title function_">expect</span>(value).<span class="property">to</span>.<span class="property">have</span>.<span class="title function_">length</span>(<span class="number">5</span>)</span><br><span class="line"> })</span><br><span class="line">});</span><br><span class="line"></span><br></pre></td></tr></table></figure><h5 id="3-异步测试"><a href="#3-异步测试" class="headerlink" title="3.异步测试"></a>3.异步测试</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> fs =<span class="built_in">require</span>(<span class="string">"fs"</span>).<span class="property">promises</span></span><br><span class="line"><span class="keyword">var</span> chai = <span class="built_in">require</span>(<span class="string">'chai'</span>);</span><br><span class="line"><span class="keyword">var</span> expect = chai.<span class="property">expect</span>;</span><br><span class="line"><span class="title function_">it</span>(<span class="string">'test async function'</span>,<span class="keyword">async</span> <span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">const</span> data =<span class="keyword">await</span> fs.<span class="title function_">readFile</span>(<span class="string">'./1.txt'</span>,<span class="string">"utf8"</span>);</span><br><span class="line"> <span class="title function_">expect</span>(data).<span class="property">to</span>.<span class="title function_">equal</span>(<span class="string">'hello'</span>)</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h5 id="4-http测试"><a href="#4-http测试" class="headerlink" title="4.http测试"></a>4.http测试</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> request = <span class="built_in">require</span>(<span class="string">'supertest'</span>)</span><br><span class="line"><span class="keyword">const</span> app = <span class="built_in">require</span>(<span class="string">'../app'</span>);</span><br><span class="line"></span><br><span class="line"><span class="title function_">describe</span>(<span class="string">'#test koa app'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="keyword">let</span> server = app.<span class="title function_">listen</span>(<span class="number">3000</span>);</span><br><span class="line"> <span class="title function_">describe</span>(<span class="string">'#test server'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">it</span>(<span class="string">'#test GET /'</span>, <span class="keyword">async</span> () => {</span><br><span class="line"> <span class="keyword">await</span> <span class="title function_">request</span>(server)</span><br><span class="line"> .<span class="title function_">get</span>(<span class="string">'/'</span>)</span><br><span class="line"> .<span class="title function_">expect</span>(<span class="string">'Content-Type'</span>, <span class="regexp">/text\/html/</span>)</span><br><span class="line"> .<span class="title function_">expect</span>(<span class="number">200</span>, <span class="string">'<h1>hello world</h1>'</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">after</span>(<span class="keyword">function</span> (<span class="params"></span>) {</span><br><span class="line"> server.<span class="title function_">close</span>()</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure><h5 id="5-钩子函数"><a href="#5-钩子函数" class="headerlink" title="5.钩子函数"></a>5.钩子函数</h5><figure class="highlight js"><table><tr><td class="code"><pre><span class="line"><span class="title function_">describe</span>(<span class="string">'#hello.js'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">describe</span>(<span class="string">'#sum()'</span>, <span class="function">() =></span> {</span><br><span class="line"> <span class="title function_">before</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="string">'before:'</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">after</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="string">'after.'</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">beforeEach</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="string">' beforeEach:'</span>);</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="title function_">afterEach</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="string">' afterEach.'</span>);</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><p>文章转载自 <a href="http://www.qfedu.com/teachers/90465.html">Kerwin</a>,如有侵权,请联系作者,本站仅当学习使用。</p>
<h4 id="一、Node-js基础"><a href="#一、Node-js基础" </summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="Nodejs" scheme="https://www.qingyi1220.cn/tags/Nodejs/"/>
</entry>
<entry>
<title>hexo图片引入</title>
<link href="https://www.qingyi1220.cn/posts/22565.html"/>
<id>https://www.qingyi1220.cn/posts/22565.html</id>
<published>2022-11-11T16:00:00.000Z</published>
<updated>2022-11-25T12:21:28.736Z</updated>
<content type="html"><![CDATA[<p> <code>source</code> 文件夹中除了文章以外还可能需要引入其它文件,例如图片、CSS、JS 文件等。如果Hexo项目中只有少量图片,那最简单的方法就是将它们放在 <code>source/images</code> 文件夹中。然后通过类似于 <code>![](/images/image.jpg)</code> 的方法访问它们。</p><p>如果需要更有规律地提供图片和其他资源以及想要将资源分布在各个文章上,可以通过将 <code>config.yml</code> 文件中的 <code>post_asset_folder</code> 选项设为 <code>true</code> 来打开。</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">_config.ymlpost_asset_folder:</span> <span class="literal">true</span></span><br><span class="line"><span class="attr">marked:</span></span><br><span class="line"> <span class="attr">prependRoot:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">postAsset:</span> <span class="literal">true</span></span><br></pre></td></tr></table></figure><p>安装插件hexo图片管理插件:<code>npm i hexo-asset-image</code></p><p>启用后,资源图片将会被自动解析为其对应文章的路径。<br>例如: <code>image.jpg</code> 位置为 <code>/2022/09/01/Hello world/image.jpg</code> ,这表示它是 <code>/2022/09/01/Hello world/</code> 文章的一张资源图片, <code>![](image.jpg)</code> 将会被解析为 <code><img src="/2022/09/01/Hello world/image.jpg"></code> 。</p><p>当资源文件管理功能打开后,Hexo将会在每一次通过 <code>hexo new [layout] <title></code> 命令创建新文章时自动创建一个同名文件夹。将所有与当前文章有关的资源放在这个关联文件夹中之后,之后可以通过相对路径来引用它们,这样就可以同时在Markdown编辑器和文章页中预览所需图片。</p>]]></content>
<summary type="html"><p> <code>source</code> 文件夹中除了文章以外还可能需要引入其它文件,例如图片、CSS、JS 文件等。如果Hexo项目中只有少量图片,那最简单的方法就是将它们放在 <code>source/images</code> 文件夹中。然后通过类似于 <code>!</summary>
<category term="Hexo" scheme="https://www.qingyi1220.cn/categories/Hexo/"/>
<category term="Hexo优化" scheme="https://www.qingyi1220.cn/tags/Hexo%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>hexo提交报错</title>
<link href="https://www.qingyi1220.cn/posts/49461.html"/>
<id>https://www.qingyi1220.cn/posts/49461.html</id>
<published>2022-11-10T16:00:00.000Z</published>
<updated>2022-11-29T16:06:40.435Z</updated>
<content type="html"><![CDATA[<h1 id="解决hexo报错spwan-failed"><a href="#解决hexo报错spwan-failed" class="headerlink" title="解决hexo报错spwan failed"></a>解决hexo报错spwan failed</h1><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">FATAL Something<span class="string">'s wrong. Maybe you can find the solution here: https://hexo.io/docs/troubleshooting.html</span></span><br><span class="line"><span class="string">Error: Spawn failed</span></span><br><span class="line"><span class="string"> at ChildProcess.<anonymous> (D:\Blog\node_modules\hexo-util\lib\spawn.js:51:21)</span></span><br><span class="line"><span class="string"> at ChildProcess.emit (node:events:527:28)</span></span><br><span class="line"><span class="string"> at ChildProcess.cp.emit (D:\Blog\node_modules\cross-spawn\lib\enoent.js:34:29)</span></span><br><span class="line"><span class="string"> at Process.ChildProcess._handle.onexit (node:internal/child_process:291:12)</span></span><br></pre></td></tr></table></figure><h3 id="解决方案"><a href="#解决方案" class="headerlink" title="解决方案"></a>解决方案</h3><ul><li>进行以下处理</li></ul><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="comment">##进入博客根目录(以我的为例)</span></span><br><span class="line"><span class="built_in">cd</span> D:\Blog</span><br><span class="line"></span><br><span class="line"><span class="comment">##删除git提交文件夹</span></span><br><span class="line"><span class="built_in">rm</span> -rf .deploy_git/</span><br><span class="line"></span><br><span class="line">git config --global core.autocrlf <span class="literal">false</span></span><br></pre></td></tr></table></figure><ul><li>最后重新生成提交</li></ul><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hexo clean</span><br><span class="line">hexo g</span><br><span class="line">hexo d</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html"><h1 id="解决hexo报错spwan-failed"><a href="#解决hexo报错spwan-failed" class="headerlink" title="解决hexo报错spwan failed"></a>解决hexo报错spwan failed</h1><</summary>
<category term="Hexo" scheme="https://www.qingyi1220.cn/categories/Hexo/"/>
<category term="Hexo优化" scheme="https://www.qingyi1220.cn/tags/Hexo%E4%BC%98%E5%8C%96/"/>
</entry>
<entry>
<title>深拷贝和浅拷贝</title>
<link href="https://www.qingyi1220.cn/posts/16061.html"/>
<id>https://www.qingyi1220.cn/posts/16061.html</id>
<published>2022-10-11T16:00:00.000Z</published>
<updated>2023-03-19T10:47:56.809Z</updated>
<content type="html"><![CDATA[<h1 id="深拷贝和浅拷贝的区别"><a href="#深拷贝和浅拷贝的区别" class="headerlink" title="深拷贝和浅拷贝的区别"></a>深拷贝和浅拷贝的区别</h1><p>假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅拷贝,拿人手短,如果B没变,那就是深拷贝,自食其力。</p><ol><li>深拷贝,是拷贝对象各个层级的属性,拷贝对象的地址。<br>例如:<code>JSON.parse(JSON.stringify())</code>(特殊情况下无法使用,见文章末尾)</li><li>浅拷贝,指针指向堆中相同的地址,所有会改变原来的数据。<br>例如:<code>Object.assign()</code> <code>Array.prototype.slice()</code></li></ol><h1 id="对象的扩展运算符(…)深拷贝还是浅拷贝"><a href="#对象的扩展运算符(…)深拷贝还是浅拷贝" class="headerlink" title="对象的扩展运算符(…)深拷贝还是浅拷贝"></a>对象的扩展运算符(…)深拷贝还是浅拷贝</h1><ol><li><p>如果只是一层数组或是对象,其元素只是简单类型的元素,那么属于深拷贝(即只有一层拷贝)</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = {</span><br><span class="line"><span class="attr">age</span>: <span class="number">22</span>,</span><br><span class="line"><span class="attr">name</span>: <span class="string">'abc'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> b = {...a};</span><br><span class="line">b.<span class="property">age</span> = <span class="number">18</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a.<span class="property">age</span>); <span class="comment">// 22</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b.<span class="property">age</span>); <span class="comment">// 18</span></span><br></pre></td></tr></table></figure></li><li><p>如果数组或对象中的元素是引用类型的元素,那么就是浅拷贝</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">let</span> a = {</span><br><span class="line"><span class="attr">age</span>: <span class="number">22</span>,</span><br><span class="line"><span class="attr">name</span>: <span class="string">'abc'</span>,</span><br><span class="line"><span class="attr">address</span>: {</span><br><span class="line"><span class="attr">city</span>: <span class="string">'Anhui'</span></span><br><span class="line">}</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> b = {...a};</span><br><span class="line">b.<span class="property">address</span>.<span class="property">city</span> = <span class="string">'Shanghai'</span>;</span><br><span class="line"></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(a.<span class="property">address</span>.<span class="property">city</span>); <span class="comment">// Shanghai</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(b.<span class="property">address</span>.<span class="property">city</span>); <span class="comment">// Shanghai</span></span><br></pre></td></tr></table></figure></li><li><p>如何使得深拷贝</p> <figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">const</span> obj = {</span><br><span class="line"> <span class="attr">age</span>: <span class="number">20</span>,</span><br><span class="line"> <span class="attr">name</span>: <span class="string">'dyk'</span>,</span><br><span class="line"> <span class="attr">address</span>: {</span><br><span class="line"> <span class="attr">city</span>: <span class="string">'anqing'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="attr">arr</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><br><span class="line"><span class="comment">// const obj1 = JSON.parse(JSON.stringify(obj))</span></span><br><span class="line"><span class="keyword">const</span> obj1 = <span class="title function_">deepClone</span>(obj)</span><br><span class="line"></span><br><span class="line">obj1.<span class="property">address</span>.<span class="property">city</span> = <span class="string">'shanghai'</span></span><br><span class="line">obj1.<span class="property">arr</span>[<span class="number">1</span>] = <span class="number">666</span></span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(obj, obj1)</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">deepClone</span>(<span class="params">obj</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">typeof</span> obj !== <span class="string">'object'</span> || obj == <span class="literal">null</span>) {</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> result</span><br><span class="line"> <span class="keyword">if</span> (obj <span class="keyword">instanceof</span> <span class="title class_">Array</span>) {</span><br><span class="line"> result = []</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> result = {}</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> key <span class="keyword">in</span> obj) {</span><br><span class="line"> <span class="comment">//保证key不是原型的属性</span></span><br><span class="line"> <span class="keyword">if</span> (obj.<span class="title function_">hasOwnProperty</span>(key)) {</span><br><span class="line"> <span class="comment">//递归</span></span><br><span class="line"> result[key] = <span class="title function_">deepClone</span>(obj[key])</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> result</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p> 或者利用<code>JSON.parse(JSON.stringfy(xxx))</code>来实现深拷贝<br> 注意:<code>JSON.stringfy(xxx)</code>的方法<em>无法处理函数和 undefined</em>、<em>无法处理循环引用</em>、<em>无法处理某些特殊的 JavaScript 对象,如 RegExp、Error、Date 等,这些对象会被转换成空对象</em>、<em>对象的原型链上的属性会丢失,如果原始对象的某个属性是通过原型链继承的,那么使用该方法得到的新对象会丢失该属性</em>。</p></li></ol>]]></content>
<summary type="html"><h1 id="深拷贝和浅拷贝的区别"><a href="#深拷贝和浅拷贝的区别" class="headerlink" title="深拷贝和浅拷贝的区别"></a>深拷贝和浅拷贝的区别</h1><p>假设B复制了A,当修改A时,看B是否会发生变化,如果B也跟着变了,说明这是浅</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="JavaScript" scheme="https://www.qingyi1220.cn/tags/JavaScript/"/>
</entry>
<entry>
<title>动态星空Sky粒子特效背景</title>
<link href="https://www.qingyi1220.cn/posts/23413.html"/>
<id>https://www.qingyi1220.cn/posts/23413.html</id>
<published>2022-09-05T16:00:00.000Z</published>
<updated>2023-03-29T09:54:02.127Z</updated>
<content type="html"><![CDATA[<ol><li><p><code>[Blogroot]\_config.butterfly.yml</code> 找到 <code>index_img</code> 设置为空:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># The banner image of home page</span></span><br><span class="line"><span class="attr">index_img:</span> </span><br></pre></td></tr></table></figure></li><li><p><code>[Blogroot]\_config.butterfly.yml</code> 找到 <code>background</code> 设置渐变色:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="comment"># Website Background (設置網站背景)</span></span><br><span class="line"><span class="comment"># can set it to color or image (可設置圖片 或者 顔色)</span></span><br><span class="line"><span class="attr">background:</span> <span class="string">'linear-gradient(to right top, #009fff, #ec2f4b)'</span></span><br></pre></td></tr></table></figure></li><li><p><code>[Blogroot]\themes\butterfly\source\css\_global\index.styl</code> 找到 <code>#web_bg</code> 设置渐变动画:</p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="selector-id">#web_bg</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">z-index</span>: -<span class="number">999</span>;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">background</span>: $web-bg;</span><br><span class="line"> <span class="attribute">background-attachment</span>: local;</span><br><span class="line"> <span class="attribute">background-position</span>: center;</span><br><span class="line"> <span class="attribute">background-size</span>: cover;</span><br><span class="line"> <span class="attribute">background-repeat</span>: no-repeat;</span><br><span class="line"> <span class="attribute">animation</span>: bganimation <span class="number">10s</span> infinite;</span><br><span class="line"> <span class="attribute">background-size</span>: <span class="number">800%</span>;</span><br><span class="line"> }</span><br></pre></td></tr></table></figure></li><li><p>新建 <code>[Blogroot]\themes\butterfly\source\css\[_username]\index.styl</code> ,添加渐变动画 <code>CSS</code> 和 粒子元素的样式:</p><figure class="highlight css"><table><tr><td class="code"><pre><span class="line"><span class="comment">/* 樱花特效样式 */</span></span><br><span class="line"><span class="selector-class">.canvas-container</span> {</span><br><span class="line"> <span class="attribute">position</span>: fixed;</span><br><span class="line"> <span class="attribute">width</span>: <span class="number">100%</span>;</span><br><span class="line"> <span class="attribute">height</span>: <span class="number">100vh</span>;</span><br><span class="line"> <span class="attribute">margin</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">padding</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">top</span>: <span class="number">0</span>;</span><br><span class="line"> <span class="attribute">z-index</span>: -<span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">// 阅读模式下隐藏背景</span><br><span class="line"><span class="selector-class">.read-mode</span> <span class="selector-class">.canvas-container</span> {</span><br><span class="line"> <span class="attribute">display</span>: none;</span><br><span class="line">}</span><br><span class="line">// 首页背景渐变动画</span><br><span class="line"><span class="keyword">@keyframes</span> bganimation {</span><br><span class="line"> <span class="number">0%</span> {</span><br><span class="line"> <span class="attribute">background-position</span>: <span class="number">0%</span> <span class="number">50%</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="number">50%</span> {</span><br><span class="line"> <span class="attribute">background-position</span>: <span class="number">100%</span> <span class="number">50%</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="number">100%</span> {</span><br><span class="line"> <span class="attribute">background-position</span>: <span class="number">0%</span> <span class="number">50%</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>记得在 <code>[Blogroot]\themes\butterfly\source\css\index.styl</code> 引入,以后所有的 <code>css</code> 都可以写在这个里面</p><figure class="highlight diff"><table><tr><td class="code"><pre><span class="line">if (hexo-config('css_prefix')) {</span><br><span class="line">@import 'nib';</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">@import '_third-party/normalize.min.css';</span><br><span class="line">// project</span><br><span class="line">@import 'var';</span><br><span class="line">@import '_global/*';</span><br><span class="line">@import '_highlight/highlight';</span><br><span class="line">@import '_page/*';</span><br><span class="line">@import '_layout/*';</span><br><span class="line">@import '_tags/*';</span><br><span class="line">@import '_mode/*';</span><br><span class="line"><span class="addition">+ @import '_TsingYi/*';</span></span><br><span class="line"></span><br><span class="line">// search</span><br><span class="line">if (hexo-config('algolia_search.enable')) {</span><br><span class="line">@import '_search/index';</span><br><span class="line">@import '_search/algolia';</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">if (hexo-config('local_search') && hexo-config('local_search.enable')) {</span><br><span class="line">@import '_search/index';</span><br><span class="line">@import '_search/local-search';</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><code>[Blogroot]\themes\butterfly\layout\includes\layout.pug</code> 添加元素:</p><figure class="highlight diff"><table><tr><td class="code"><pre><span class="line">if page.type !== '404'</span><br><span class="line"><span class="addition">+ #canvas.canvas-container</span></span><br><span class="line"> #body-wrap(class=pageType)</span><br><span class="line"> include ./header/index.pug</span><br><span class="line"></span><br><span class="line"> main#content-inner.layout(class=hideAside)</span><br><span class="line"> if body</span><br><span class="line"> div!= body</span><br><span class="line"> else</span><br><span class="line"> block content</span><br><span class="line"> if theme.aside.enable && page.aside !== false</span><br><span class="line"> include widget/index.pug</span><br></pre></td></tr></table></figure></li><li><p>新建 <code>[Blogroot]\themes\butterfly\source\js\sky.js</code></p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> scene,</span><br><span class="line"> camera,</span><br><span class="line"> renderer,</span><br><span class="line"> container,</span><br><span class="line"> aspect,</span><br><span class="line"> fov,</span><br><span class="line"> plane,</span><br><span class="line"> far,</span><br><span class="line"> mouseX,</span><br><span class="line"> mouseY,</span><br><span class="line"> windowHalfX,</span><br><span class="line"> windowHalfY,</span><br><span class="line"> geometry,</span><br><span class="line"> starStuff,</span><br><span class="line"> materialOptions,</span><br><span class="line"> stars;</span><br><span class="line"></span><br><span class="line"><span class="title function_">init</span>();</span><br><span class="line"><span class="title function_">animate</span>();</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">init</span>(<span class="params"></span>) {</span><br><span class="line"> container = <span class="variable language_">document</span>.<span class="title function_">getElementById</span>(<span class="string">`canvas`</span>);</span><br><span class="line"></span><br><span class="line"> mouseX = <span class="number">0</span>;</span><br><span class="line"> mouseY = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> aspect = <span class="variable language_">window</span>.<span class="property">innerWidth</span> / <span class="variable language_">window</span>.<span class="property">innerHeight</span>;</span><br><span class="line"> fov = <span class="number">40</span>;</span><br><span class="line"> plane = <span class="number">1</span>;</span><br><span class="line"> far = <span class="number">800</span>;</span><br><span class="line"></span><br><span class="line"> windowHalfX = <span class="variable language_">window</span>.<span class="property">innerWidth</span> / <span class="number">2</span>;</span><br><span class="line"> windowHalfY = <span class="variable language_">window</span>.<span class="property">innerHeight</span> / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> camera = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">PerspectiveCamera</span>(</span><br><span class="line"> fov,</span><br><span class="line"> aspect,</span><br><span class="line"> plane,</span><br><span class="line"> far</span><br><span class="line"> );</span><br><span class="line"></span><br><span class="line"> camera.<span class="property">position</span>.<span class="property">z</span> = far / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> scene = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Scene</span>({ <span class="attr">antialias</span>: <span class="literal">true</span> });</span><br><span class="line"> scene.<span class="property">fog</span> = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">FogExp2</span>(<span class="number">0x1b1b1b</span>, <span class="number">0.0001</span>);</span><br><span class="line"></span><br><span class="line"> <span class="title function_">starForge</span>();</span><br><span class="line"></span><br><span class="line"> renderer = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">WebGLRenderer</span>({ <span class="attr">antialias</span>: <span class="literal">true</span>, <span class="attr">alpha</span>: <span class="literal">true</span> });</span><br><span class="line"> renderer.<span class="title function_">setPixelRatio</span>((<span class="variable language_">window</span>.<span class="property">devicePixelRatio</span>) ? <span class="variable language_">window</span>.<span class="property">devicePixelRatio</span> : <span class="number">1</span>);</span><br><span class="line"> renderer.<span class="title function_">setSize</span>(<span class="variable language_">window</span>.<span class="property">innerWidth</span>, <span class="variable language_">window</span>.<span class="property">innerHeight</span>);</span><br><span class="line"> renderer.<span class="property">autoClear</span> = <span class="literal">false</span>;</span><br><span class="line"> renderer.<span class="title function_">setClearColor</span>(<span class="number">0x000000</span>, <span class="number">0.0</span>);</span><br><span class="line"> container.<span class="title function_">appendChild</span>(renderer.<span class="property">domElement</span>);</span><br><span class="line"></span><br><span class="line"> <span class="variable language_">document</span>.<span class="title function_">addEventListener</span>(<span class="string">'mousemove'</span>, onMouseMove, <span class="literal">false</span>);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">animate</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="title function_">requestAnimationFrame</span>(animate);</span><br><span class="line"> <span class="title function_">render</span>();</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">render</span>(<span class="params"></span>) {</span><br><span class="line"> camera.<span class="property">position</span>.<span class="property">x</span> += (mouseX - camera.<span class="property">position</span>.<span class="property">x</span>) * <span class="number">0.005</span>;</span><br><span class="line"> camera.<span class="property">position</span>.<span class="property">y</span> += (-mouseY - camera.<span class="property">position</span>.<span class="property">y</span>) * <span class="number">0.005</span>;</span><br><span class="line"> camera.<span class="title function_">lookAt</span>(scene.<span class="property">position</span>);</span><br><span class="line"> renderer.<span class="title function_">render</span>(scene, camera);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">starForge</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="keyword">var</span> amount = <span class="number">45000</span>;</span><br><span class="line"> geometry = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">SphereGeometry</span>(<span class="number">1000</span>, <span class="number">100</span>, <span class="number">50</span>);</span><br><span class="line"></span><br><span class="line"> materialOptions = {</span><br><span class="line"> <span class="attr">color</span>: <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Color</span>(<span class="number">0xffffff</span>),</span><br><span class="line"> <span class="attr">size</span>: <span class="number">1.1</span>,</span><br><span class="line"> <span class="attr">transparency</span>: <span class="literal">true</span>,</span><br><span class="line"> <span class="attr">opacity</span>: <span class="number">0.8</span></span><br><span class="line"> };</span><br><span class="line"></span><br><span class="line"> starStuff = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">PointsMaterial</span>(materialOptions);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < amount; i++) {</span><br><span class="line"> <span class="keyword">var</span> item = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">Vector3</span>();</span><br><span class="line"> item.<span class="property">x</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">2000</span> - <span class="number">1000</span>;</span><br><span class="line"> item.<span class="property">y</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">2000</span> - <span class="number">1000</span>;</span><br><span class="line"> item.<span class="property">z</span> = <span class="title class_">Math</span>.<span class="title function_">random</span>() * <span class="number">2000</span> - <span class="number">1000</span>;</span><br><span class="line"></span><br><span class="line"> geometry.<span class="property">vertices</span>.<span class="title function_">push</span>(item);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> stars = <span class="keyword">new</span> <span class="variable constant_">THREE</span>.<span class="title class_">PointCloud</span>(geometry, starStuff);</span><br><span class="line"> scene.<span class="title function_">add</span>(stars);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">function</span> <span class="title function_">onMouseMove</span>(<span class="params">e</span>) {</span><br><span class="line"> mouseX = e.<span class="property">clientX</span> - windowHalfX;</span><br><span class="line"> mouseY = e.<span class="property">clientY</span> - windowHalfY;</span><br><span class="line">}</span><br></pre></td></tr></table></figure></li><li><p><code>[Blogroot]\_config.butterfly.yml</code> 找到 <code>inject</code>,引入 <code>js</code>,由于星空动画是基于 <code>three.js</code>,所以一定要先引入 <code>three.js</code>:</p><figure class="highlight yaml"><table><tr><td class="code"><pre><span class="line"><span class="attr">inject:</span></span><br><span class="line"> <span class="attr">head:</span> <span class="string"><script</span> <span class="string">src="https://cdn.bootcdn.net/ajax/libs/three.js/0.146.0/three.min.js"></script></span></span><br><span class="line"> <span class="attr">bottom:</span> <span class="string"><script</span> <span class="string">async</span> <span class="string">data-pjax</span> <span class="string">src="/js/sky.js"></script></span></span><br></pre></td></tr></table></figure></li><li><p>最后执行 <code>hexo cl,hexo g,hexo s</code> 三连即可看到效果。</p></li></ol>]]></content>
<summary type="html"><ol>
<li><p><code>[Blogroot]\_config.butterfly.yml</code> 找到 <code>index_img</code> 设置为空:</p>
<figure class="highlight yaml"><table><tr><td </summary>
<category term="Hexo" scheme="https://www.qingyi1220.cn/categories/Hexo/"/>
<category term="Butterfly美化" scheme="https://www.qingyi1220.cn/tags/Butterfly%E7%BE%8E%E5%8C%96/"/>
</entry>
<entry>
<title>命名规范</title>
<link href="https://www.qingyi1220.cn/posts/17900.html"/>
<id>https://www.qingyi1220.cn/posts/17900.html</id>
<published>2022-09-04T16:00:00.000Z</published>
<updated>2022-11-24T14:21:02.996Z</updated>
<content type="html"><![CDATA[<h2 id="一、导航类"><a href="#一、导航类" class="headerlink" title="一、导航类"></a>一、导航类</h2><p>导航:nav 主导航:mainbav 子导航:subnav 顶导航:topnav</p><p>边导航:sidebar 左导航:leftsidebar 右导航:rightsidebar 菜单:menu</p><p>子菜单:submenu 标题:title 摘要:summary</p><h2 id="二、页面结构"><a href="#二、页面结构" class="headerlink" title="二、页面结构"></a>二、页面结构</h2><p>容器:container 页头:header 内容:content/container 页面主体:main</p><p>页尾:footer 导航:nav 侧栏:sidebar 栏目:column</p><p>页面外围控制整体布局宽度:wrapper 左右中:left right center</p><h2 id="三、功能"><a href="#三、功能" class="headerlink" title="三、功能"></a>三、功能</h2><p>标志:logo 滚动:scroll 广告:banner 标签页:tab</p><p>登录:login 文章列表:list 登录条:loginbar 提示信息:msg</p><p>注册:regsiter 当前的:current 搜索:search 小技巧:tips</p><p>功能区:shop 图标:icon 标题:title 注释:note</p><p>加入:joinus 指南:guild 状态:status 服务:service</p><p>按钮:btn 热点:hot 合作伙伴:partner 新闻:news</p><p>友情链接:friendlink 下载:download 版权:copyright 投票:vote</p><h2 id="四、div英文释译"><a href="#四、div英文释译" class="headerlink" title="四、div英文释译"></a>四、div英文释译</h2><p>标题 title 字体 font 身体 body 大小 size</p><p>列表 list 文本 text 样式 style 对齐 align</p><p>图像 image 修饰 decoration 资源 source 线 line</p><p>宽度 width 穿过 through 高度 height 缩进 indent</p><p>行 line 斜体 italic 链接 link 加粗 bold</p><p>斜体 italic 加重 weight 加粗 bold 宽 width</p><p>输入 input 高 height 下面的 under 背景 background</p><p>顶部 top 主体 main</p><h2 id="五、css英文释译"><a href="#五、css英文释译" class="headerlink" title="五、css英文释译"></a>五、css英文释译</h2><p>重复 repeat 填充 padding 位置 position 正常 normal</p><p>显示类型 display 父级 parent 显示可见 visibility 子级 children</p><p>隐藏 hidden 顶部导航topnav显示 visible 溢出 overflow</p><p>列表 list 样式 style 边框 border 导航 nav</p><p>广告图片 banner 行高line-height边界 margin 页眉 header</p><h2 id="六、样式文件命名"><a href="#六、样式文件命名" class="headerlink" title="六、样式文件命名"></a>六、样式文件命名</h2><p>主要的master.css 模块module.css 基本共用base.css 布局,版面layout.css</p><p>主题themes.css 专栏columns.css 文字font.css 表单forms.css</p><p>补丁mend.css 打印print.css</p><h2 id="七、颜色释译"><a href="#七、颜色释译" class="headerlink" title="七、颜色释译"></a>七、颜色释译</h2><p>绿 green 红 red 蓝 blue 黑 black</p><p>灰 gray 黄 yellowa 紫 purple 白 white</p><p>棕 brown 卡其色 khaki</p><h2 id="六、命名参考"><a href="#六、命名参考" class="headerlink" title="六、命名参考"></a>六、命名参考</h2><p>登录条loginBar 标志:logo 侧边栏:sideBar 广告:banner</p><p>导航:nav 子导航:navBar 菜单:menu 子菜单:subMenu</p><p>搜索:search 滚动:scroll 页面主体:main 内容:content</p><p>标签页:tab 文章列表:list 提示信息:msg 小技巧:tips</p><p>栏目标题:title 加入:joins 指南:guild 服务:service</p><p>热点:hot 新闻:news 按钮:btn 投票:vote</p><p>状态:status 合作伙伴:partner 外套:wrap 商标:label</p><p>顶导航:topnav 左导航:leftSideBar 右导航:rightSideBar菜单内容:menuContent</p><p>菜单容器:menuContainer注释:note 边导航图标:sidebarIcon版权:copyright</p><p>友情链接:friendLink容器:container 页脚:footer 当前:current</p><p>激活:active 购物车:shop 登录:login 注册:regsiter</p><p>下载:download 面包屑:breadCrumb</p><h2 id="其它"><a href="#其它" class="headerlink" title="其它"></a>其它</h2><p>1)文件编码统一使用 UTF-8 编码;<br>2)命名时以符合语义为主要参考指标,CSS属性书写规范,采用统一风格及命名方法;<br>3)结构清晰,层级关系明朗,以不超过三级为标准;<br>4)同一表现形式的样式要求可重复利用,模块组件类的样式要求可整体外部移植;<br>5)编写 CSS 的时候,应当避免使用CSS Hack,能不用则不用。<br>6)所有页面默认都针对Firefox 等最接近标准的浏览器进行设计,然后使用 IE 特有条件注释功能进行针对性修正。<br>7)请使用英文进行命名,尽量避免使用拼音。命名要求具有可读性,尽量避免使用缩写。命名虽然允许数字,但应尽量避免使用数字命名。<br>2、命名方法 要求采用统一的命名方法。常用命名方法有:<br>1)连写式命名法,如:helloworld<br>2)中线命名法,如:hello-world(目前采用此方法的较多,建议采用)<br>3)下划线命名法,如:hello_world<br>4)骆驼命名法,如:helloWorld<br>5)帕斯卡命名法,如:HelloWorld<br>6)其他方法。<br>3、样式文件命名规范 如:<br>全局的:global.css;<br>主要的:master.css;<br>布局、版面:layout.css;<br>专栏:columns.css;<br>文字:font.css;<br>打印样式:print.css;<br>主题:themes.css;<br>补丁:pacth.css;<br>格式化浏览器:base.css等。<br>4、ID及Class命名常用名称<br>页头:header<br>登录条:login-bar<br>标志:logo<br>侧栏:side-bar<br>广告:banner<br>导航:nav<br>子导航:sub-nav<br>菜单:menu<br>子菜单:sub-menu<br>搜索:search<br>滚动:scroll<br>页面主体:main<br>内容:content<br>标签页:tab<br>文章列表:list<br>提示信息:msg<br>小技巧:tips<br>栏目标题:title<br>加入:joinus<br>指南:guild<br>服务:service<br>热点:hot<br>新闻:news<br>下载:download<br>注册:regsiter<br>状态:status<br>按钮:btn<br>投票:vote<br>合作伙伴:partner<br>友情链接:friendlink<br>版权:copyright<br>外套:wrap<br>页脚:footer<br>标题:title<br>顶导航:top-nav(mini-nav)</p>]]></content>
<summary type="html"><h2 id="一、导航类"><a href="#一、导航类" class="headerlink" title="一、导航类"></a>一、导航类</h2><p>导航:nav 主导航:mainbav 子导航:subnav 顶导航:top</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="命名规范" scheme="https://www.qingyi1220.cn/tags/%E5%91%BD%E5%90%8D%E8%A7%84%E8%8C%83/"/>
</entry>
<entry>
<title>Git经验总结</title>
<link href="https://www.qingyi1220.cn/posts/10553.html"/>
<id>https://www.qingyi1220.cn/posts/10553.html</id>
<published>2022-09-03T16:00:00.000Z</published>
<updated>2022-11-24T14:21:17.797Z</updated>
<content type="html"><![CDATA[<h1 id="git常用命令"><a href="#git常用命令" class="headerlink" title="git常用命令"></a>git常用命令</h1><ol><li><p><code>git init</code><strong>初始化本地仓库</strong></p></li><li><p><code>git add .</code><strong>添加/删除(所有)文件到暂存区</strong></p></li><li><p><code>git reset HEAD .</code><strong>重置暂存区的文件与上一次的提交(commit)保持一致,工作区文件内容保持不变。</strong></p></li><li><p><code>git status</code><strong>查看git此时的状态</strong></p></li><li><p><code>git commit -m 'message'</code><strong>提交到本地仓库</strong></p></li><li><p><code>git remote add origin Address</code><strong>绑定远程仓库</strong></p></li><li><p><code>git pull origin main/master</code><strong>拉取同步远程服务器代码,无冲突直接合并,有冲突需要手动合并</strong></p></li><li><p><code>git push origin main/master</code><strong>推送至远程仓库</strong></p></li><li><p><code>git log</code><strong>查看提交记录</strong></p></li><li><p><code>git reflog</code><strong>操作记录(查看版本号)</strong></p></li><li><p><code>git reset --hard HEAD^/版本号</code><strong>回退上一个版本/某个版本(HEAD表示当前版本 ^表示上一个)</strong></p></li></ol><h1 id="git多人协作"><a href="#git多人协作" class="headerlink" title="git多人协作"></a>git多人协作</h1><ol><li><code>git branch -a</code><strong>查看所有分支</strong></li><li><code>git checkout -b aaa</code><strong>创建新的分支(-b)aaa</strong></li><li><code>git checkout aaa</code><strong>切换到aaa分支</strong></li><li><code>git merge aaa</code><strong>(master)分支aaa合并到master</strong></li><li><code>git push origin aaa</code><strong>推送aaa分支到远程仓库aaa分支</strong></li><li><code>git push origin master:aaa</code><strong>推送master到远程的aaa分支</strong></li><li><code>git branch -d aaa</code><strong>删除aaa分支</strong></li></ol><h1 id="git远程仓库更换名称,本地修改"><a href="#git远程仓库更换名称,本地修改" class="headerlink" title="git远程仓库更换名称,本地修改"></a>git远程仓库更换名称,本地修改</h1><p>这种情况适用于本地和远程的代码没得任何问题,就是远程仓库改了个名称,直接在本地修改远程仓库地址即可:</p><p><code>git remote set-url origin newAddress</code></p><p>另外还可以先删除,然后添加地址:</p><p><code>git remote rm origin</code></p><p><code>git remote add origin newAddress</code></p><h1 id="绑定GitHub"><a href="#绑定GitHub" class="headerlink" title="绑定GitHub"></a>绑定GitHub</h1><p>打开 Git Bash,输入 ssh-keygen -t rsa 命令,表示我们指定 RSA 算法生成密钥,然后敲四次回车键,之后就就会生成两个文件,分别为秘钥 id_rsa 和公钥 id_rsa.pub.<em><strong>(注意:git中的复制粘贴不是 Ctrl+C 和 Ctrl+V,而是 Ctrl+insert 和 Shift+insert.)</strong></em>文件的位置在 Git Bash 上面都有显示,默认生成在以下目录:</p><ul><li>Linux 系统:~/.ssh</li><li>Mac 系统:~/.ssh</li><li>Windows 10 :C:/Users/ASUS/.ssh</li></ul><p>把公钥 id_rsa.pub 的内容记事本打开复制添加到 GitHub。</p><img src="/posts/10553/git1.jpg" style="zoom: 67%;"><h1 id="同时使用GitHub和Gitee"><a href="#同时使用GitHub和Gitee" class="headerlink" title="同时使用GitHub和Gitee"></a>同时使用GitHub和Gitee</h1><h3 id="1-删掉全局配置"><a href="#1-删掉全局配置" class="headerlink" title="1. 删掉全局配置"></a>1. 删掉全局配置</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">git config --global --list</span><br><span class="line">$ git config --global --<span class="built_in">unset</span> user.name <span class="string">"你的名字"</span></span><br><span class="line">$ git config --global --<span class="built_in">unset</span> user.email <span class="string">"你的邮箱"</span></span><br></pre></td></tr></table></figure><h3 id="2-为不同账户配置ssh秘钥"><a href="#2-为不同账户配置ssh秘钥" class="headerlink" title="2. 为不同账户配置ssh秘钥"></a>2. 为不同账户配置ssh秘钥</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cd</span> ~/.ssh <span class="comment"># cd到当前用户的.ssh文件夹</span></span><br><span class="line">ssh-keygen -t rsa -f ~/.ssh/id_rsa.gitee -C <span class="string">"注册gitee邮箱"</span><span class="comment">#为不同秘钥指定名称</span></span><br><span class="line">ssh-keygen -t rsa -f ~/.ssh/id_rsa.github -C <span class="string">"注册github邮箱"</span></span><br></pre></td></tr></table></figure><p>完成后会在~/.ssh / 目录下生成以下文件:</p><ul><li>id_rsa.github</li><li>id_rsa.github.pub</li><li>id_rsa.gitee</li><li>id_rsa.gitee.pub</li></ul><p>复制公钥分别在github和gitee中设置</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">cat</span> id_rsa.github.pub</span><br><span class="line"><span class="built_in">cat</span> id_rsa.gitee.pub</span><br></pre></td></tr></table></figure><p>添加新的私钥</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ ssh-agent bash</span><br><span class="line">$ ssh-add ~/.ssh/id_rsa.github</span><br><span class="line">$ ssh-add ~/.ssh/id_rsa.gitee</span><br></pre></td></tr></table></figure><h3 id="3-进行全局配置"><a href="#3-进行全局配置" class="headerlink" title="3. 进行全局配置"></a>3. 进行全局配置</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line"><span class="built_in">touch</span> ~/.ssh/config </span><br><span class="line"></span><br><span class="line"><span class="comment"># gitee</span></span><br><span class="line">Host gitee.com</span><br><span class="line">HostName gitee.com</span><br><span class="line">PreferredAuthentications publickey</span><br><span class="line">IdentityFile ~/.ssh/id_rsa.gitee</span><br><span class="line"></span><br><span class="line"><span class="comment"># github</span></span><br><span class="line">Host github.com</span><br><span class="line">HostName github.com</span><br><span class="line">PreferredAuthentications publickey</span><br><span class="line">IdentityFile ~/.ssh/id_rsa.github</span><br></pre></td></tr></table></figure><p>Host 它涵盖了下面一个段的配置,我们可以通过他来替代将要连接的服务器地址。 这里可以使用任意字段或通配符。 当ssh的时候如果服务器地址能匹配上这里Host指定的值,则Host下面指定的HostName将被作为最终的服务器地址使用,并且将使用该Host字段下面配置的所有自定义配置来覆盖默认的/etc/ssh/ssh_config配置信息。</p><p>Port 自定义的端口。默认为22,可不配置</p><p>User 自定义的用户名,默认为git,可不配置</p><p>HostName 真正连接的服务器地址</p><p>PreferredAuthentications 指定优先使用哪种方式验证,支持密码和秘钥验证方式</p><p>IdentityFile 指定本次连接使用的密钥文件</p><h3 id="4-测试连接"><a href="#4-测试连接" class="headerlink" title="4. 测试连接"></a>4. 测试连接</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">ssh -T [email protected]</span><br><span class="line">Hi yourname! You<span class="string">'ve successfully authenticated, but GitHub does not provide shell access.</span></span><br><span class="line"><span class="string">ssh -T [email protected]</span></span><br><span class="line"><span class="string">Hi yourname! You'</span>ve successfully authenticated, but GITEE.COM does not provide shell access.</span><br></pre></td></tr></table></figure><h3 id="5-hexo博客仓库"><a href="#5-hexo博客仓库" class="headerlink" title="5. hexo博客仓库"></a>5. hexo博客仓库</h3><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">vi .depoly_git/.git/config 增加</span><br><span class="line"></span><br><span class="line">[user]</span><br><span class="line">name = username</span><br><span class="line">email = email</span><br></pre></td></tr></table></figure><h3 id="6-针对不同的项目仓库"><a href="#6-针对不同的项目仓库" class="headerlink" title="6. 针对不同的项目仓库"></a>6. 针对不同的项目仓库</h3><p>增加本地配置,在每个仓库的<code>.git/config</code>中进行配置不同的用户,以及其他的配置信息</p><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">$ git config --<span class="built_in">local</span> user.name <span class="string">'github/gitee账号名'</span></span><br><span class="line">$ git config --<span class="built_in">local</span> user.email <span class="string">'github/gitee账号邮箱'</span></span><br></pre></td></tr></table></figure><p>–global是在全局配置文件中设置</p><p>–local 是针对当前仓库的项目进行设置</p><h2 id="更新hexo-blog"><a href="#更新hexo-blog" class="headerlink" title="更新hexo blog"></a>更新hexo blog</h2><figure class="highlight bash"><table><tr><td class="code"><pre><span class="line">hexo clean <span class="comment">#清除缓存文件 db.json 和已生成的静态文件 public</span></span><br><span class="line">hexo g <span class="comment">#生成网站静态文件到默认设置的 public 文件夹(hexo generate 的缩写)</span></span><br><span class="line">hexo s <span class="comment">#启动本地服务器预览网页</span></span><br><span class="line">hexo d <span class="comment">#自动生成网站静态文件,并部署到设定的仓库(hexo deploy 的缩写)</span></span><br></pre></td></tr></table></figure><h2 id="项目打包dist文件commit出错"><a href="#项目打包dist文件commit出错" class="headerlink" title="项目打包dist文件commit出错"></a>项目打包dist文件commit出错</h2><p><strong>检查后发现格式不符合标准,git不让上传,为了避免格式检查用</strong><br><code>git commit -m "dist1" --no-verify</code></p>]]></content>
<summary type="html"><h1 id="git常用命令"><a href="#git常用命令" class="headerlink" title="git常用命令"></a>git常用命令</h1><ol>
<li><p><code>git init</code><strong>初始化本地仓库</str</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="Git" scheme="https://www.qingyi1220.cn/tags/Git/"/>
</entry>
<entry>
<title>vue-router钩子函数与执行流程</title>
<link href="https://www.qingyi1220.cn/posts/4511.html"/>
<id>https://www.qingyi1220.cn/posts/4511.html</id>
<published>2022-09-03T16:00:00.000Z</published>
<updated>2022-11-25T12:20:31.576Z</updated>
<content type="html"><![CDATA[<h2 id="全局守卫"><a href="#全局守卫" class="headerlink" title="全局守卫"></a>全局守卫</h2><h3 id="前置守卫-beforeEach"><a href="#前置守卫-beforeEach" class="headerlink" title="前置守卫 beforeEach"></a>前置守卫 <code>beforeEach</code></h3><p>全局前置守卫,在路由跳转前触发,它在 <code>每次导航</code> 时都会触发。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">router.<span class="title function_">beforeEach</span>(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'to:'</span>, to);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'from:'</span>, <span class="keyword">from</span>);</span><br><span class="line"> <span class="title function_">next</span>();</span><br><span class="line"> })</span><br></pre></td></tr></table></figure><p><code>beforeEach</code> 全局前置守卫接收三个参数:</p><ol><li><code>to: Route:</code> 即将要进入的目标路由对象</li><li><code>from: Route:</code> 当前导航正要离开的路由对象</li><li><code>next: Function:</code> 一定要调用该方法不然会阻塞路由</li></ol><div class="note warning flat"><p><strong><code>next</code> 参数可以不添加,但是一旦添加,则必须调用一次,否则路由跳转等会停止。</strong></p></div><p><code>next()</code>方法的几种情况</p><ol><li><code>next()</code>: 进行管道中的下一个钩子。</li><li><code>next(false)</code>: 中断当前的导航。回到 <code>from</code> 路由对应的地址。</li><li><code>next('/')</code> 或者 <code>next({ path: '/' })</code>: 跳转到一个不同的地址,可传递的参数与 <code>router.push</code> 中选项一致。</li><li><code>next(error)</code>: 导航终止,且该错误会被传递给 <code>router.onError()</code> 注册过的回调。</li></ol><p>返回值:</p><ul><li><code>false</code>:取消当前的导航。</li><li><code>null,undefined,true</code>或者直接<code>return</code>:调用下一个导航守卫。</li></ul><h3 id="解析守卫-beforeResolve"><a href="#解析守卫-beforeResolve" class="headerlink" title="解析守卫 beforeResolve"></a>解析守卫 <code>beforeResolve</code></h3><p>全局解析守卫,在路由跳转前,所有 <code>组件内守卫</code> 和 <code>异步路由组件</code> 被解析之后触发,它同样在 <code>每次导航</code> 时都会触发。</p><p>通过 <code>router.beforeResolve</code> 注册一个全局解析守卫:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">router.<span class="title function_">beforeResolve</span>(<span class="function">(<span class="params">to, <span class="keyword">from</span>, next</span>) =></span> {</span><br><span class="line"> <span class="title function_">next</span>();</span><br><span class="line">})</span><br></pre></td></tr></table></figure><p>回调参数,返回值和 <code>beforeEach</code> 一样。也可以定义多个全局解析守卫。</p><h3 id="后置守卫-afterEach"><a href="#后置守卫-afterEach" class="headerlink" title="后置守卫 afterEach"></a>后置守卫 afterEach</h3><p>全局后置钩子,它发生在路由跳转完成后,<code>beforeEach</code> 和 <code>beforeResolve</code> 之后,<code>beforeRouteEnter</code>(组件内守卫)之前。它同样在 <code>每次导航</code> 时都会触发。</p><p>通过 <code>router.afterEach</code> 注册一个全局后置钩子:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">router.<span class="title function_">afterEach</span>(<span class="function">(<span class="params">to, <span class="keyword">from</span></span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'to:'</span>, to);</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'from:'</span>, <span class="keyword">from</span>);</span><br><span class="line">})</span><br></pre></td></tr></table></figure><h2 id="路由独享守卫-beforeEnter"><a href="#路由独享守卫-beforeEnter" class="headerlink" title="路由独享守卫 beforeEnter"></a>路由独享守卫 <code>beforeEnter</code></h2><p>路由守卫只有一个,就是 <code>beforeEnter</code>,<code>beforeEnter</code> 守卫 只在进入路由时触发,不会在 <code>params</code>、<code>query</code> 或 <code>hash</code> 改变时触发。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">{</span><br><span class="line"> <span class="attr">path</span>: <span class="string">'/a'</span>,</span><br><span class="line"> <span class="attr">component</span>: <span class="function">() =></span> <span class="title function_">import</span>(<span class="string">'../components/A.vue'</span>),</span><br><span class="line"> <span class="attr">beforeEnter</span>: <span class="function">(<span class="params">to, <span class="keyword">from</span></span>) =></span> {</span><br><span class="line"> <span class="variable language_">console</span>.<span class="title function_">log</span>(<span class="string">'beforeEnter '</span>);</span><br><span class="line"> },</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="组件守卫"><a href="#组件守卫" class="headerlink" title="组件守卫"></a>组件守卫</h2><h3 id="beforeRouteEnter"><a href="#beforeRouteEnter" class="headerlink" title="beforeRouteEnter"></a>beforeRouteEnter</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="title function_">beforeRouteEnter</span>(<span class="params">to, <span class="keyword">from</span></span>) {</span><br><span class="line"> <span class="comment">// 在渲染该组件的对应路由被验证前调用</span></span><br><span class="line"> <span class="comment">// 不能获取组件实例 `this` !</span></span><br><span class="line"> <span class="comment">// 因为当守卫执行时,组件实例还没被创建!</span></span><br><span class="line">},</span><br></pre></td></tr></table></figure><p><code>beforeRouteEnter</code> 守卫 不能 访问 <code>this</code>,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。</p><p>不过,你可以通过传一个回调给 <code>next</code> 来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"> beforeRouteEnter (to, <span class="keyword">from</span>, next) {</span><br><span class="line"> <span class="title function_">next</span>(<span class="function"><span class="params">vm</span> =></span> {</span><br><span class="line"> <span class="comment">// 通过 `vm` 访问组件实例</span></span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="beforeRouteUpdate"><a href="#beforeRouteUpdate" class="headerlink" title="beforeRouteUpdate"></a>beforeRouteUpdate</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">beforeRouteUpdate (to, <span class="keyword">from</span>) {</span><br><span class="line"> <span class="comment">// just use `this`</span></span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = to.<span class="property">params</span>.<span class="property">name</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="beforeRouteLeave"><a href="#beforeRouteLeave" class="headerlink" title="beforeRouteLeave"></a>beforeRouteLeave</h3><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line">beforeRouteLeave (to, <span class="keyword">from</span>) {</span><br><span class="line"> <span class="keyword">const</span> answer = <span class="variable language_">window</span>.<span class="title function_">confirm</span>(<span class="string">'Do you really want to leave? you have unsaved changes!'</span>)</span><br><span class="line"> <span class="keyword">if</span> (!answer) <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>beforeRouteEnter</code> 是支持给 <code>next</code> 传递回调的唯一守卫。对于 <code>beforeRouteUpdate</code> 和 <code>beforeRouteLeave</code> 来说,<code>this</code> 已经可用了,所以不支持 <code>传递回调</code>,因为没有必要了</p><h2 id="完整的导航解析流程"><a href="#完整的导航解析流程" class="headerlink" title="完整的导航解析流程"></a>完整的导航解析流程</h2><ol><li>导航被触发</li><li>在失活的组件里调用 <code>beforeRouteLeave</code> 守卫</li><li>调用全局的 <code>beforeEach</code> 守卫</li><li>在重用的组件里调用 <code>beforeRouteUpdate</code> 守卫</li><li>在路由配置里调用 <code>beforeEnter</code></li><li>解析异步路由组件</li><li>在被激活的组件里调用 <code>beforeRouteEnter</code> 守卫</li><li>调用全局的 <code>beforeResolve</code> 守卫</li><li>导航被确认</li><li>调用全局的 <code>afterEach</code> 守卫</li><li>触发 <code>DOM</code> 更新</li><li>调用 <code>beforeRouteEnter</code> 守卫中传给 <code>next</code> 的回调函数,创建好的组件实例会作为回调函数的参数传入</li></ol>]]></content>
<summary type="html"><h2 id="全局守卫"><a href="#全局守卫" class="headerlink" title="全局守卫"></a>全局守卫</h2><h3 id="前置守卫-beforeEach"><a href="#前置守卫-beforeEach" class="header</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="Vue" scheme="https://www.qingyi1220.cn/tags/Vue/"/>
<category term="Vue-router" scheme="https://www.qingyi1220.cn/tags/Vue-router/"/>
</entry>
<entry>
<title>this指向</title>
<link href="https://www.qingyi1220.cn/posts/36763.html"/>
<id>https://www.qingyi1220.cn/posts/36763.html</id>
<published>2022-09-02T16:00:00.000Z</published>
<updated>2022-11-24T14:22:05.199Z</updated>
<content type="html"><![CDATA[<h3 id="一、方法调用模式"><a href="#一、方法调用模式" class="headerlink" title="一、方法调用模式"></a>一、方法调用模式</h3><p> 当函数被保存为一个对象的属性时,它就可称为这个对象的方法。当一个方法被调用时,this被绑定到这个对象上。如果调用表达式包含一个提取属性的动作(. 或 []),那么它被称为方法调用。<br>例如:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"window"</span>;</span><br><span class="line"><span class="keyword">var</span> obj = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"TsingYi"</span>,</span><br><span class="line"> <span class="attr">sayName</span>: <span class="keyword">function</span>(<span class="params"></span>) { <span class="comment">// 如果是箭头函数this就会指向window</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><br><span class="line"> }</span><br><span class="line">};</span><br><span class="line">obj.<span class="title function_">sayName</span>(); <span class="comment">//TsingYi</span></span><br></pre></td></tr></table></figure><p>sayName函数作为对象obj的方法调用,所以函数体中的this就代表obj对象。</p><h3 id="二、函数调用模式"><a href="#二、函数调用模式" class="headerlink" title="二、函数调用模式"></a>二、函数调用模式</h3><p> 当一个函数并非一个对象的属性时,那么它就是被当做函数来调用的。在此种模式下,this被绑定为全局对象,在浏览器环境下就是window对象。<br>例如:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"window"</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sayName</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><br><span class="line">}</span><br><span class="line"><span class="title function_">sayName</span>();</span><br></pre></td></tr></table></figure><p>sayName以函数调用模式调用,所以函数体中的this代表window对象。</p><h3 id="三、构造函数模式"><a href="#三、构造函数模式" class="headerlink" title="三、构造函数模式"></a>三、构造函数模式</h3><p> 如果在一个函数前面加上new关键字来调用,那么就会创建一个连接到该函数的prototype成员的新对象,同时,this会被绑定到这个新对象上。这种情况下,这个函数就可以成为此对象的构造函数。<br>例如:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">function</span> <span class="title function_">Obj</span>(<span class="params"></span>) {</span><br><span class="line"> <span class="variable language_">this</span>.<span class="property">name</span> = <span class="string">"TsingYi"</span>;</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> person = <span class="keyword">new</span> <span class="title class_">Obj</span>();</span><br><span class="line"><span class="variable language_">console</span>.<span class="title function_">log</span>(person.<span class="property">name</span>); <span class="comment">//TsingYi</span></span><br></pre></td></tr></table></figure><p>Obj作为构造函数被调用,函数体内的this被绑定为新创建的对象person。</p><h3 id="四、apply调用模式"><a href="#四、apply调用模式" class="headerlink" title="四、apply调用模式"></a>四、apply调用模式</h3><p> 在JS中,函数也是对象,所有函数对象都有两个方法:apply和call,这两个方法可以让我们构建一个参数数组传递给调用函数,也允许我们改变this的值。<br>例如:</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"window"</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">"TsingYi"</span></span><br><span class="line">};</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">sayName</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><br><span class="line">}</span><br><span class="line"><span class="title function_">sayName</span>(); <span class="comment">//window</span></span><br><span class="line">sayName.<span class="title function_">apply</span>(person); <span class="comment">//TsingYi</span></span><br><span class="line">sayName.<span class="title function_">apply</span>(); <span class="comment">//window</span></span><br></pre></td></tr></table></figure><p> 当以函数调用模式调用sayName时,this代表window;当用apply模式调用sayName,并给它传入的第一个参数为person时,this被绑定到person对象上。如果不给apply传入任何参数,则this代表window。</p><figure class="highlight javascript"><table><tr><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">"window"</span>;</span><br><span class="line"><span class="keyword">function</span> <span class="title function_">showName</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><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> person1 = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"TsingYi"</span>,</span><br><span class="line"> <span class="attr">sayName</span>: showName</span><br><span class="line">}</span><br><span class="line"><span class="keyword">var</span> person2 = {</span><br><span class="line"> <span class="attr">name</span>: <span class="string">"Xiaoming"</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="keyword">var</span> fun = person1.<span class="property">sayName</span>;</span><br><span class="line"> <span class="title function_">fun</span>();</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line">person1.<span class="title function_">sayName</span>(); <span class="comment">//TsingYi</span></span><br><span class="line">person2.<span class="title function_">sayName</span>(); <span class="comment">//window</span></span><br></pre></td></tr></table></figure><p>person1.sayName(); 首先确定这是方法调用模式,对象为person1,再看sayName被赋值为全局函数对象showName,在showName执行时,this绑定的是person1,所以结果为”TsingYi”。<br>person2.sayName(); 这还是方法调用模式,对象为person2,调用的是它的sayName方法。再看sayName函数体,发现函数体最终执行的函数是fun,fun是用函数调用模式调用的。而fun最终也被赋值为showName函数,因为fun是用函数调用模式调用的,所以这里的this绑定为window,结果为”window“。</p>]]></content>
<summary type="html"><h3 id="一、方法调用模式"><a href="#一、方法调用模式" class="headerlink" title="一、方法调用模式"></a>一、方法调用模式</h3><p> 当函数被保存为一个对象的属性时,它就可称为这个对象的方法。当一个方法被调用时,th</summary>
<category term="Web前端" scheme="https://www.qingyi1220.cn/categories/Web%E5%89%8D%E7%AB%AF/"/>
<category term="JavaScript" scheme="https://www.qingyi1220.cn/tags/JavaScript/"/>
</entry>
</feed>