This repository has been archived by the owner on Nov 14, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
523 lines (305 loc) · 395 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>SpiritLing</title>
<subtitle>满船清梦压星河</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="https://blog.spiritling.cn/"/>
<updated>2021-01-19T08:07:07.000Z</updated>
<id>https://blog.spiritling.cn/</id>
<author>
<name>SpiritLing</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>Asp.Net Core 扩展 Linq,简化自定义</title>
<link href="https://blog.spiritling.cn/articles/d49d2f96/"/>
<id>https://blog.spiritling.cn/articles/d49d2f96/</id>
<published>2021-01-19T06:53:32.000Z</published>
<updated>2021-01-19T08:07:07.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Asp.Net Core 扩展 Linq<br>实现通过属性名称来排序和查询以及分页</p></blockquote><a id="more"></a><h2 id="前言-为什么需要扩展-Linq-方法"><a href="#前言-为什么需要扩展-Linq-方法" class="headerlink" title="前言 -为什么需要扩展 Linq 方法"></a>前言 -为什么需要扩展 <code>Linq</code> 方法</h2><blockquote><p>Linq 在 .net 中使用是比较多的,而微软开发的 linq 相关函数无法满足实际项目开发中的需求,我们需要自己来扩展一些方法。</p></blockquote><p><a href="/posts/e24afe6f/">c# 扩展方法</a></p><p>在 Asp.Net Core 开发中或者其他的后端开发中都会有一个需求(尤其对于中台或者后台管理),那就是展示数据列表;当然不是普普通通的数据列表展示,而是需要进行<strong>排序</strong>、<strong>分页</strong>、<strong>查询关键字</strong>来获取列表。</p><p>甚至在有些时候需要三个同时处理来更精确的筛选数据,而对于 Asp.Net Core 来说,用的语言是 C#,是一门强类型语言,在许多时候具有很大便利性,但是正因为这种原因,在某些时候却不太方便使用,需要进行额外方式来进行处理,才可以达到目标。</p><h2 id="普通查询"><a href="#普通查询" class="headerlink" title="普通查询"></a>普通查询</h2><p>对于 Linq 查询来说,<code>Where</code> 和 <code>OrderBy</code> 使用时需要直接点出来属于或者字段才行,如下所示:</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 数据结构</span></span><br><span class="line"><span class="keyword">public</span> <span class="keyword">class</span> <span class="title">ArticleTag</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> Id { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> Guid KeyId { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Name { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> BgColor { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> TextColor { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> UrlParam { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> Remark { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> DateTime CreatedTime { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> DateTime UpdatedTime { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Test</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> articleTags = <span class="keyword">new</span> List<ArticleTag>();</span><br><span class="line"> <span class="comment">// where 查找 Name中含有Admin的数据,orderby 通过 id 来进行排序</span></span><br><span class="line"> <span class="keyword">var</span> result = articleTags.Where(p => p.Name.Contains(<span class="string">"Admin"</span>)).OrderBy(p => p.Id);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>而所谓的一些限制,指的就是如上所示的,在进行 <code>where</code> 时,是通过 <code>.</code> 出来属性进行查询的,但是实际使用中,从前端传递过来的一般都是字符串 <code>"Name"</code>,而在后端进行查询时,以目前方式是无法将属性的<code>key</code>写到<code>where</code>函数中,也就无法执行查询通过<code>"Name"</code>来过滤数据;如果通过 <code>if...else</code> 来判断,那么将会是一个非常大的工程量,每个实体上面有 m 个属性,而一个项目中有 n 张表,那么几乎需要 <code>m*n</code>个判断进行处理,非常的差劲,不利于后续扩展和维护。</p><p>但是天无绝人之路,在 c#中拥有扩展方法、表达式目录树和反射,可以将上面的方式进行优化。</p><h2 id="查询条件参数公共类型"><a href="#查询条件参数公共类型" class="headerlink" title="查询条件参数公共类型"></a>查询条件参数公共类型</h2><p>先需要定义查询条件的公共参数,用于统一规范</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">BlogSite.CommonLib.CommonEntity</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 查询基本实体</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">CoditionEntity</span></span><br><span class="line"> {</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"paginationKeyValue"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> PaginationKeyValue PaginationKeyValue { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"orderByKeyValue"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> OrderByKeyValue OrderByKeyValue { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"></span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"searchKeyValue"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> SearchKeyValue SearchKeyValue { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 关键字搜索</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">SearchKeyValue</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">string</span> _propertyName;</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"enable"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">bool</span> Enable { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"propertyName"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> PropertyName</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">get</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 因为前端传递的是小写字母开头的,但是在后端都是大写字母开头的,我们需要在获取时将首字母大写</span></span><br><span class="line"> <span class="keyword">return</span> _propertyName.ToUpperFirstLetter();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">set</span></span><br><span class="line"> {</span><br><span class="line"> _propertyName = <span class="keyword">value</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"searchContent"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> SearchContent { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 排序字段格式</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">OrderByKeyValue</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">string</span> _propertyName;</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"enable"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">bool</span> Enable { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"propertyName"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">string</span> PropertyName {</span><br><span class="line"> <span class="keyword">get</span></span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> _propertyName.ToUpperFirstLetter();</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">set</span></span><br><span class="line"> {</span><br><span class="line"> _propertyName = <span class="keyword">value</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"orderDirection"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> OrderDirection OrderDirection { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 分页格式</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">class</span> <span class="title">PaginationKeyValue</span></span><br><span class="line"> {</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"enable"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">bool</span> Enable { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"count"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> Count { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"pageSize"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> PageSize { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> [<span class="meta">JsonPropertyName(<span class="meta-string">"pageNumber"</span>)</span>]</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">int</span> PageNumber { <span class="keyword">get</span>; <span class="keyword">set</span>; }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 排序类型</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">enum</span> OrderDirection</span><br><span class="line"> {</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 升序</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> [<span class="meta">Description(<span class="meta-string">"升序"</span>)</span>]</span><br><span class="line"> ASC = <span class="number">0</span>,</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 降序</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> [<span class="meta">Description(<span class="meta-string">"降序"</span>)</span>]</span><br><span class="line"> DESC = <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>建立了查询条件基本结构,然后需要基于这个结构来进行处理</p><h2 id="Linq-扩展方法"><a href="#Linq-扩展方法" class="headerlink" title="Linq 扩展方法"></a>Linq 扩展方法</h2><p>对于 Linq 扩展方法来说,需要使用到表达式目录树和反射等高级操作,本人目前对于此处理解不是太深,就越过这里了,直接说如何实现即可</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">LinqExtension</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Orders the by.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="source"></span>The source.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span>Name of the property.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="direction"></span>The direction.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IOrderedQueryable<T> OrderBy<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName, OrderDirection direction)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">switch</span> (direction)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> OrderDirection.ASC:</span><br><span class="line"> <span class="keyword">return</span> source.OrderBy(ToLambda<T>(propertyName));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> OrderDirection.DESC:</span><br><span class="line"> <span class="keyword">return</span> source.OrderByDescending(ToLambda<T>(propertyName));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> source.OrderBy(ToLambda<T>(propertyName));</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 class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Orders the by.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="source"></span>The source.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span>Name of the property.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="direction"></span>The direction.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IOrderedQueryable<T> OrderBy<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName, <span class="keyword">int</span> direction)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> dir = (OrderDirection)direction;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (dir)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">case</span> OrderDirection.ASC:</span><br><span class="line"> <span class="keyword">return</span> source.OrderBy(ToLambda<T>(propertyName));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> OrderDirection.DESC:</span><br><span class="line"> <span class="keyword">return</span> source.OrderByDescending(ToLambda<T>(propertyName));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">default</span>:</span><br><span class="line"> <span class="keyword">return</span> source.OrderBy(ToLambda<T>(propertyName));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Orders the by.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="source"></span>The source.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span>Name of the property.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IOrderedQueryable<T> OrderBy<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> source.OrderBy(ToLambda<T>(propertyName));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Orders the by descending.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="source"></span>The source.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span>Name of the property.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IOrderedQueryable<T> OrderByDescending<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> source.OrderByDescending(ToLambda<T>(propertyName));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IQueryable<T> Where<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName, <span class="keyword">string</span> content)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> source.Where(ToLambdaWhere<T>(propertyName, content));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> IQueryable<T> WhereList<T>(<span class="keyword">this</span> IQueryable<T> source, <span class="keyword">string</span> propertyName, List<<span class="keyword">string</span>> contentList)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> source.Where(ToLambdaWhere<T>(propertyName, contentList));</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Where Lambda 表示式</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="contentList"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Expression<Func<T, <span class="keyword">bool</span>>> ToLambdaWhere<T>(<span class="keyword">string</span> propertyName, List<<span class="keyword">string</span>> contentList)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 传递到表达式目录树的参数 x 和它的类型</span></span><br><span class="line"> ParameterExpression x = Expression.Parameter(<span class="keyword">typeof</span>(T));</span><br><span class="line"></span><br><span class="line"> MemberExpression property = Expression.Property(x, propertyName);</span><br><span class="line"> ConstantExpression constant = Expression.Constant(contentList, <span class="keyword">typeof</span>(List<<span class="keyword">string</span>>));</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> methodCall = Expression.Call(constant, <span class="string">"Contains"</span>, Type.EmptyTypes, property);</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> expression = Expression.Lambda<Func<T, <span class="keyword">bool</span>>>(methodCall, <span class="keyword">new</span> ParameterExpression[] { x });</span><br><span class="line"> <span class="keyword">return</span> expression;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Where Lambda 表示式</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="content"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Expression<Func<T, <span class="keyword">bool</span>>> ToLambdaWhere<T>(<span class="keyword">string</span> propertyName, <span class="keyword">string</span> content)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 传递到表达式目录树的参数 x 和它的类型</span></span><br><span class="line"> ParameterExpression x = Expression.Parameter(<span class="keyword">typeof</span>(T));</span><br><span class="line"> <span class="comment">// 获取类型的属性</span></span><br><span class="line"> PropertyInfo prop = <span class="keyword">typeof</span>(T).GetProperty(propertyName);</span><br><span class="line"> <span class="comment">// 设定常量值相当于Contains中传递的值</span></span><br><span class="line"> ConstantExpression constant = Expression.Constant(content, <span class="keyword">typeof</span>(<span class="keyword">string</span>));</span><br><span class="line"> <span class="comment">// 使用类型调用对应的属性</span></span><br><span class="line"> <span class="keyword">var</span> propExp = Expression.Property(x, prop);</span><br><span class="line"> <span class="comment">// 获取方法</span></span><br><span class="line"> MethodInfo contains = <span class="keyword">typeof</span>(<span class="keyword">string</span>).GetMethod(<span class="string">"Contains"</span>, <span class="keyword">new</span> Type[] { });</span><br><span class="line"> <span class="comment">// 给属性调用上面获取的方法,传递值</span></span><br><span class="line"> <span class="keyword">var</span> methodCall = Expression.Call(propExp, contains, <span class="keyword">new</span> Expression[] { constant });</span><br><span class="line"> <span class="comment">// 拼装成表达式目录树</span></span><br><span class="line"> Expression<Func<T, <span class="keyword">bool</span>>> expression = Expression.Lambda<Func<T, <span class="keyword">bool</span>>>(methodCall, <span class="keyword">new</span> ParameterExpression[] { x });</span><br><span class="line"> <span class="keyword">return</span> expression;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Converts to lambda.</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="propertyName"></span>Name of the property.<span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">private</span> <span class="keyword">static</span> Expression<Func<T, <span class="keyword">object</span>>> ToLambda<T>(<span class="keyword">string</span> propertyName)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 传递到表达式目录树的参数 x</span></span><br><span class="line"> ParameterExpression x = Expression.Parameter(<span class="keyword">typeof</span>(T));</span><br><span class="line"> <span class="comment">// 通过传递过来的属性字符串获取对应的属性</span></span><br><span class="line"> MemberExpression property = Expression.Property(x, propertyName);</span><br><span class="line"> <span class="comment">// 创建一个表示类型转换运算的 UnaryExpression。</span></span><br><span class="line"> <span class="comment">// 使用 property.Type 会在最后转换时出现错误</span></span><br><span class="line"> <span class="keyword">var</span> propAsObject = Expression.Convert(property, <span class="keyword">typeof</span>(<span class="keyword">object</span>));</span><br><span class="line"></span><br><span class="line"> Expression<Func<T, <span class="keyword">object</span>>> expression = Expression.Lambda<Func<T, <span class="keyword">object</span>>>(propAsObject, x);</span><br><span class="line"> <span class="keyword">return</span> expression;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>实际使用中可以直接使用上方的扩展方法,扩展方法做成后,只需要按照如下调用即可</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">Test</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> articleTags = <span class="keyword">new</span> List<ArticleTag>();</span><br><span class="line"> <span class="comment">// 将属性key字符串传递进来就可以实现查询和排序,方便使用,PropertyName可以随意更改,当PropertyName不属于查询的类型中时,只会抛出错误,需要进一步处理</span></span><br><span class="line"> <span class="keyword">var</span> result = articleTags.Where(<span class="string">"PropertyName"</span>,<span class="string">"SerarchKeyword"</span>).OrderBy(<span class="string">"PropertyName"</span>,<span class="number">1</span>);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="更进一步"><a href="#更进一步" class="headerlink" title="更进一步"></a>更进一步</h2><p>当然在上面我们也定义了通用的查询条件,那么我们直接也可以再进一步扩展,来达到更好的使用方式</p><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">LinqMethod</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 使用自定linq扩展执行排序,查询,分页功能 item1: 未分页结果,item2:分页后的结果</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><typeparam name="T"></span><span class="doctag"></typeparam></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="source"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="coditionEntity"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> Tuple<IQueryable<T>, IQueryable<T>> UseCoditionFind<T>(<span class="keyword">this</span> IQueryable<T> source, CoditionEntity coditionEntity)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> query = source;</span><br><span class="line"> <span class="keyword">var</span> resultQuery = source;</span><br><span class="line"> <span class="keyword">if</span> (coditionEntity.SearchKeyValue != <span class="literal">null</span> && coditionEntity.SearchKeyValue.Enable && !coditionEntity.SearchKeyValue.SearchContent.IsNullOrWhiteSpace())</span><br><span class="line"> {</span><br><span class="line"> query = query.Where(coditionEntity.SearchKeyValue.PropertyName, coditionEntity.SearchKeyValue.SearchContent);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (coditionEntity.OrderByKeyValue != <span class="literal">null</span> && coditionEntity.OrderByKeyValue.Enable)</span><br><span class="line"> {</span><br><span class="line"> query = query.OrderBy(coditionEntity.OrderByKeyValue.PropertyName, coditionEntity.OrderByKeyValue.OrderDirection);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (coditionEntity.PaginationKeyValue != <span class="literal">null</span> && coditionEntity.PaginationKeyValue.Enable)</span><br><span class="line"> {</span><br><span class="line"> resultQuery = query.Skip((coditionEntity.PaginationKeyValue.PageNumber - <span class="number">1</span>) * coditionEntity.PaginationKeyValue.PageSize)</span><br><span class="line"> .Take(coditionEntity.PaginationKeyValue.PageSize);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> Tuple<IQueryable<T>, IQueryable<T>>(query, resultQuery);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这样通过 linq 就可以直接调用 <code>UseCoditionFind</code> 然后获取返回的 query,然后再 <code>ToList</code> 获取数据,返回即可。方便使用,规范查询数据</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Asp.Net Core 扩展 Linq<br>实现通过属性名称来排序和查询以及分页</p></blockquote>
</summary>
<category term="dotnet" scheme="https://blog.spiritling.cn/categories/dotnet/"/>
<category term="dotNet" scheme="https://blog.spiritling.cn/tags/dotNet/"/>
</entry>
<entry>
<title>C# 扩展静态方法</title>
<link href="https://blog.spiritling.cn/articles/e24afe6f/"/>
<id>https://blog.spiritling.cn/articles/e24afe6f/</id>
<published>2021-01-19T05:13:03.000Z</published>
<updated>2021-01-19T06:53:32.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>c# 扩展静态方法</p></blockquote><a id="more"></a><h2 id="扩展方法"><a href="#扩展方法" class="headerlink" title="扩展方法"></a>扩展方法</h2><blockquote><p>扩展方法使你能够向现有类型“添加”方法,而无需创建新的派生类型、重新编译或以其他方式修改原始类型。 扩展方法是一种静态方法,但可以像扩展类型上的实例方法一样进行调用。</p><p>– <a href="https://docs.microsoft.com/zh-cn/dotnet/csharp/programming-guide/classes-and-structs/extension-methods" rel="external nofollow noopener noreferrer" target="_blank">《Microsoft 官方文档》</a></p></blockquote><h2 id="实现一个静态扩展方法"><a href="#实现一个静态扩展方法" class="headerlink" title="实现一个静态扩展方法"></a>实现一个静态扩展方法</h2><p>现在有个需求,需要对密码进行 SHA512 加密,才可以存放进入数据库中,但是每次通过其他方法调用比较麻烦,有没有一种可以直接连续点出来的方法呢?</p><p>连续点的方法一般都是静态方法,而且官方提供了静态方法扩展的可操作性,所以我们可以实现一个自定义的扩展方法。</p><p>而加密一般都为字符串,所以只需要在字符串上进行扩展即可。</p><p>扩展方法被定义为静态方法,但它们是通过实例方法语法进行调用的。 它们的第一个参数指定方法操作的类型。</p><p>参数前面是此修饰符。 仅当你使用 using 指令将命名空间显式导入到源代码中之后,扩展方法才位于范围中。</p><h3 id="将类型放入到静态方法中"><a href="#将类型放入到静态方法中" class="headerlink" title="将类型放入到静态方法中"></a>将类型放入到静态方法中</h3><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">StringExtension</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ConvertSHA512</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> currentStr</span>)</span>{</span><br><span class="line"> <span class="comment">// 现在在函数参数中的 `currentStr` 就是这个函数的字符串</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p><code>"name".SHA512()</code> 时,上面的 <code>currentStr</code> 就是 <code>"name"</code>这个字符串。</p><p>而所谓的静态类型并没有什么用,只是方便你用来区分扩展方法是那个范围的。</p><p>当然你在使用扩展方法时,需要引用当前静扩展函数所在的命名空间才行。</p><p>现在你在这个函数中已经拿到了当前执行函数的字符串,那么就可以对这个字符串进行一些操作了。</p><h3 id="实现-SHA512-加密"><a href="#实现-SHA512-加密" class="headerlink" title="实现 SHA512 加密"></a>实现 SHA512 加密</h3><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">StringExtension</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ConvertSHA512</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> currentStr</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">var</span> bytes = Encoding.UTF8.GetBytes(currentStr);</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> hash = SHA512.Create())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> hashedInputBytes = hash.ComputeHash(bytes);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Convert to text</span></span><br><span class="line"> <span class="comment">// StringBuilder Capacity is 128, because 512 bits / 8 bits in byte * 2 symbols for byte</span></span><br><span class="line"> <span class="keyword">var</span> hashedInputStringBuilder = <span class="keyword">new</span> StringBuilder(<span class="number">128</span>);</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> b <span class="keyword">in</span> hashedInputBytes)</span><br><span class="line"> hashedInputStringBuilder.Append(b.ToString(<span class="string">"X2"</span>));</span><br><span class="line"> <span class="keyword">return</span> hashedInputStringBuilder.ToString();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在静态扩展方法中对传递进来的字符串进行加密,然后返回回去。当然你可以返回任何类型的数据,但是一旦返回的类型变化了,那么你在连点调用加密函数后,只能继续点返回的类型的方法,而不能在继续使用字符串的方法了。</p><h2 id="一些扩展方法的例子"><a href="#一些扩展方法的例子" class="headerlink" title="一些扩展方法的例子"></a>一些扩展方法的例子</h2><h3 id="字符串相关的扩展方法"><a href="#字符串相关的扩展方法" class="headerlink" title="字符串相关的扩展方法"></a>字符串相关的扩展方法</h3><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">BlogSite.CommonLib</span></span><br><span class="line">{</span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">StringExtension</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 将字符串进行SHA512加密</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><param name="s"></span><span class="doctag"></param></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span><span class="doctag"></returns></span></span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ConvertSHA512</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> s</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">var</span> bytes = Encoding.UTF8.GetBytes(s);</span><br><span class="line"> <span class="keyword">using</span> (<span class="keyword">var</span> hash = SHA512.Create())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> hashedInputBytes = hash.ComputeHash(bytes);</span><br><span class="line"></span><br><span class="line"> <span class="comment">// Convert to text</span></span><br><span class="line"> <span class="comment">// StringBuilder Capacity is 128, because 512 bits / 8 bits in byte * 2 symbols for byte</span></span><br><span class="line"> <span class="keyword">var</span> hashedInputStringBuilder = <span class="keyword">new</span> StringBuilder(<span class="number">128</span>);</span><br><span class="line"> <span class="keyword">foreach</span> (<span class="keyword">var</span> b <span class="keyword">in</span> hashedInputBytes)</span><br><span class="line"> hashedInputStringBuilder.Append(b.ToString(<span class="string">"X2"</span>));</span><br><span class="line"> <span class="keyword">return</span> hashedInputStringBuilder.ToString();</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">IsNullOrWhiteSpace</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> s</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.IsNullOrWhiteSpace(s);</span><br><span class="line"> }</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">bool</span> <span class="title">IsNullOrEmpty</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> s</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.IsNullOrEmpty(s);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 首字母大写</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ToUpperFirstLetter</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> <span class="keyword">value</span></span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">value</span>.IsNullOrWhiteSpace())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.Empty;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span>[] letters = <span class="keyword">value</span>.ToCharArray();</span><br><span class="line"> letters[<span class="number">0</span>] = <span class="keyword">char</span>.ToUpper(letters[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">string</span>(letters);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 首字母小写</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ToLowerFirstLetter</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> <span class="keyword">value</span></span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">value</span>.IsNullOrWhiteSpace())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.Empty;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span>[] letters = <span class="keyword">value</span>.ToCharArray();</span><br><span class="line"> letters[<span class="number">0</span>] = <span class="keyword">char</span>.ToLower(letters[<span class="number">0</span>]);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">string</span>(letters);</span><br><span class="line"> }</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 字符串转大写</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">ToLower</span>(<span class="params"><span class="keyword">this</span> <span class="keyword">string</span> <span class="keyword">value</span></span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">value</span>.IsNullOrWhiteSpace())</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.Empty;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">char</span>[] letters = <span class="keyword">value</span>.ToCharArray();</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">0</span>; i < letters.Length; i++)</span><br><span class="line"> {</span><br><span class="line"> letters[i] = <span class="keyword">char</span>.ToLower(letters[i]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">new</span> <span class="keyword">string</span>(letters);</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="Enum-枚举扩展方法"><a href="#Enum-枚举扩展方法" class="headerlink" title="Enum 枚举扩展方法"></a><code>Enum</code> 枚举扩展方法</h3><figure class="highlight c#"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">namespace</span> <span class="title">BlogSite.CommonLib</span></span><br><span class="line">{</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> Enum 静态扩展方法</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">class</span> <span class="title">EnumExtension</span></span><br><span class="line"> {</span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> 获取枚举值上的Description特性的说明</span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"></summary></span></span></span><br><span class="line"> <span class="comment"><span class="doctag">///</span> <span class="doctag"><returns></span>特性的说明<span class="doctag"></returns></span></span></span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">string</span> <span class="title">GetDescription</span>(<span class="params"><span class="keyword">this</span> Enum en</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">var</span> type = en.GetType();</span><br><span class="line"> FieldInfo field = type.GetField(Enum.GetName(type, en));</span><br><span class="line"> <span class="keyword">if</span> (!(Attribute.GetCustomAttribute(field, <span class="keyword">typeof</span>(DescriptionAttribute)) <span class="keyword">is</span> DescriptionAttribute descAttr))</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">string</span>.Empty;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> descAttr.Description;</span><br><span class="line"> }</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><p>当然扩展方法在实际中很是常用的,扩展方法比较好写,但是比较难的在于函数内部的一些实现,所以这些需要多写写,多练练。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>c# 扩展静态方法</p></blockquote>
</summary>
<category term="c#" scheme="https://blog.spiritling.cn/categories/csharp/"/>
<category term="c#" scheme="https://blog.spiritling.cn/tags/csharp/"/>
</entry>
<entry>
<title>windows 广告定位与解决</title>
<link href="https://blog.spiritling.cn/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/"/>
<id>https://blog.spiritling.cn/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/</id>
<published>2020-10-22T03:34:55.000Z</published>
<updated>2020-10-22T04:34:55.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>windows 各种弹出广告,影响使用和美观。如何解决呢?</p><p>如何定位广告是由于那个软件弹出来的?</p></blockquote><a id="more"></a><h2 id="前言-神出鬼没的广告"><a href="#前言-神出鬼没的广告" class="headerlink" title="前言-神出鬼没的广告"></a>前言-神出鬼没的广告</h2><p>最近也快到双十一了,各种广告满天飞,这不一不小心就中招了。</p><p>今天突然发现桌面出现了一个广告,如下所示</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_10-25-53.png" title="广告图"><p>最简单的便是点击叉号即可关闭,但是这也是治标不治本啊,过上一段时间,也是还会出现的,而且想把显示广告的软件删除掉,都比较难啊,一看程序那么多,都不知道是哪一个隐藏起来整出来的。所以想卸载也无法准确定位到。</p><p>但是“魔高一尺,道高一丈”,下面就说下如何彻底解决掉烦人的广告。</p><h2 id="安装官网进程管理工具"><a href="#安装官网进程管理工具" class="headerlink" title="安装官网进程管理工具"></a>安装官网进程管理工具</h2><p>首先需要安装 windows 官方给的进程管理工具 <a href="https://docs.microsoft.com/en-us/sysinternals/downloads/process-explorer" rel="external nofollow noopener noreferrer" target="_blank">Process Explorer</a>。</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/processexplorer.jpg" title="ProcessExplorer图"><p>安装完毕后直接打开即可,上面显示的界面图是官方网站里面的图。</p><h2 id="定位广告"><a href="#定位广告" class="headerlink" title="定位广告"></a>定位广告</h2><p>点击软件后,在主界面会有一个按钮,如下所示:</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_10-18-27.png" title="主界面按钮"><p>点击红框处的按钮,并拖动到广告上去。</p><p>一定要<strong>按下并拖动</strong></p><p>然后在界面进程中会自动定位到该广告发出的进程。</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_10-27-23.png" title="定位进程"><p>鼠标放置停留几秒后,会显示详细的信息。</p><h2 id="定位软件位置"><a href="#定位软件位置" class="headerlink" title="定位软件位置"></a>定位软件位置</h2><p>如果你看到这个进程比较熟悉,或者知道是那个程序,那么这里就不需要看了,直接卸载软件,一了百了。</p><p>但是如果你不熟悉软件名称,不知道是哪一个,那么还是需要继续往下看。</p><p>嗯(⊙﹏⊙),因为自己有点手快,就给把进程删除了,所以查找程序地址我就使用了其他的进程作为例子 <code>Everything</code>。</p><p>先右键进程,在列表中选择 <code>Properties...</code>。</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_11-26-59.png" title="右键进程"><p>点击属性进入详细后,选择 Threads 下面的 Module</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_11-27-11.png" title="详细属性"><p>点击后可以查看到运行程序位置,当然如果出现错误,可以使用上面的 Image 选项卡,里面也含有运行程序的地址。</p><img src="/articles/f5a82147-b24e-4bd6-8d3c-a878bde87a2c/2020-10-22_11-28-30.png" title="程序信息"><p>定位到了软件信息,那么就是删除软件了。</p><p>大功告成 O(∩_∩)O。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>windows 各种弹出广告,影响使用和美观。如何解决呢?</p><p>如何定位广告是由于那个软件弹出来的?</p></blockquote>
</summary>
<category term="windows" scheme="https://blog.spiritling.cn/categories/windows/"/>
<category term="windows" scheme="https://blog.spiritling.cn/tags/windows/"/>
</entry>
<entry>
<title>Leetcode 链表 算法练习</title>
<link href="https://blog.spiritling.cn/articles/4970029a/"/>
<id>https://blog.spiritling.cn/articles/4970029a/</id>
<published>2020-07-21T08:23:59.000Z</published>
<updated>2020-09-24T05:12:45.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Leetcode 链表 算法练习</p></blockquote><a id="more"></a><blockquote><p>算法与数据结构学习仓库地址:<a href="https://spiritling.coding.net/public/public/AaDS-docs-code/git/files" rel="external nofollow noopener noreferrer" target="_blank">https://spiritling.coding.net/public/public/AaDS-docs-code/git/files</a><br>下面短网址失效的可以使用这个原始地址</p></blockquote><h2 id="237-删除链表中的节点"><a href="#237-删除链表中的节点" class="headerlink" title="237 删除链表中的节点"></a>237 删除链表中的节点</h2><p>地址:<a href="https://leetcode-cn.com/problems/delete-node-in-a-linked-list/" rel="external nofollow noopener noreferrer" target="_blank">https://leetcode-cn.com/problems/delete-node-in-a-linked-list/</a></p><p>代码:<a href="http://suo.im/6diowW" rel="external nofollow noopener noreferrer" target="_blank">http://suo.im/6diowW</a></p><h3 id="题目"><a href="#题目" class="headerlink" title="题目"></a>题目</h3><p>请编写一个函数,使其可以删除某个链表中给定的(非末尾)节点,你将只被给定要求被删除的节点。</p><h3 id="解题思路"><a href="#解题思路" class="headerlink" title="解题思路"></a>解题思路</h3><p>首先这个题目刚开始有点影响人的判断。</p><p>我以为函数中传入的是整个链表结构。(⊙﹏⊙)</p><p>然后就错了。。。</p><p>再次看题时,发现传入的是删除的节点,那么就简单了一部分</p><p>首先,要删除这个节点,常规来说就是通过改变上一节点的指向来删除节点。</p><p>但是很不幸的是,这个节点结构中是不存在 <code>prev</code> 指向,那么也就无法通过改变上一节点来删除。</p><p>既然这个节点存在,有值,有指向。</p><p>那么我们可不可以将这个节点的值改为下个节点的值,相应的这个节点的值就被删除了,其实是覆盖了。</p><p>改变了值,那么同样的需要改变 <code>next</code> 的指向,将其指向到下一个节点的下一个。</p><p>实际就是将下个节点复制到删除的节点这里,覆盖掉需要删除的节点。</p><h3 id="代码"><a href="#代码" class="headerlink" title="代码"></a>代码</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode {</span></span><br><span class="line"><span class="comment"> * public int val;</span></span><br><span class="line"><span class="comment"> * public ListNode next;</span></span><br><span class="line"><span class="comment"> * public ListNode(int x) { val = x; }</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">public</span> <span class="keyword">class</span> <span class="title">Solution</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">DeleteNode</span>(<span class="params">ListNode node</span>)</span> {</span><br><span class="line"> node.val=node.next.val;</span><br><span class="line"> node.next=node.next.next;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="1290-二进制链表转整数"><a href="#1290-二进制链表转整数" class="headerlink" title="1290. 二进制链表转整数"></a>1290. 二进制链表转整数</h2><p>地址:<a href="https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer/" rel="external nofollow noopener noreferrer" target="_blank">https://leetcode-cn.com/problems/convert-binary-number-in-a-linked-list-to-integer/</a></p><p>代码:<a href="http://suo.im/65MbOl" rel="external nofollow noopener noreferrer" target="_blank">http://suo.im/65MbOl</a></p><h3 id="题目-1"><a href="#题目-1" class="headerlink" title="题目"></a>题目</h3><p>给你一个单链表的引用结点 head。链表中每个结点的值不是 0 就是 1。已知此链表是一个整数数字的二进制表示形式。</p><p>请你返回该链表所表示数字的 十进制值 。</p><h3 id="解题思路-1"><a href="#解题思路-1" class="headerlink" title="解题思路"></a>解题思路</h3><h4 id="第一版:"><a href="#第一版:" class="headerlink" title="第一版:"></a>第一版:</h4><p>链表不是 1 就是 0,所以我想的是,直接将所有值拼接成字符串。</p><p>然后通过 c#自带的进制转换,将其二进制字符串转为十进制。</p><p>但是相应的,这个也有缺点,感觉没有对最前面为 0 的额外处理。</p><h4 id="第二版:"><a href="#第二版:" class="headerlink" title="第二版:"></a>第二版:</h4><p>利用进制转换算法,可以直接将二进制转为十进制,而不需要去调用其他方法。</p><p>并且直接使用传入进来的参数来进行赋值和操作。</p><h3 id="代码-1"><a href="#代码-1" class="headerlink" title="代码"></a>代码</h3><h4 id="第一版:-1"><a href="#第一版:-1" class="headerlink" title="第一版:"></a>第一版:</h4><p>时间复杂度分析:O(n)</p><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode {</span></span><br><span class="line"><span class="comment"> * public int val;</span></span><br><span class="line"><span class="comment"> * public ListNode next;</span></span><br><span class="line"><span class="comment"> * public ListNode(int x) { val = x; }</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">public</span> <span class="keyword">class</span> <span class="title">Solution</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">GetDecimalValue</span>(<span class="params">ListNode head</span>)</span> {</span><br><span class="line"> <span class="comment">// 追加判断,题目中是链表不为空,所以其实并不需要,但是为了养成好的代码习惯,还是加上了</span></span><br><span class="line"> <span class="keyword">if</span>(head==<span class="literal">null</span>) <span class="keyword">return</span> <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">string</span> str = <span class="string">""</span>;</span><br><span class="line"> <span class="keyword">var</span> current = head;</span><br><span class="line"> <span class="keyword">while</span> (current != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> str += current.val.ToString();</span><br><span class="line"> current = current.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> Convert.ToInt32(str,<span class="number">2</span>);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="第二版:-1"><a href="#第二版:-1" class="headerlink" title="第二版:"></a>第二版:</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode {</span></span><br><span class="line"><span class="comment"> * public int val;</span></span><br><span class="line"><span class="comment"> * public ListNode next;</span></span><br><span class="line"><span class="comment"> * public ListNode(int x) { val = x; }</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">public</span> <span class="keyword">class</span> <span class="title">Solution</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">GetDecimalValue</span>(<span class="params">ListNode head</span>)</span> {</span><br><span class="line"> <span class="keyword">int</span> ans = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">while</span>(head!=<span class="literal">null</span>){</span><br><span class="line"> ans = ans * <span class="number">2</span> + head.val;</span><br><span class="line"> head = head.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> ans;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="206-反转链表"><a href="#206-反转链表" class="headerlink" title="206. 反转链表"></a>206. 反转链表</h2><p>地址:<a href="https://leetcode-cn.com/problems/reverse-linked-list/" rel="external nofollow noopener noreferrer" target="_blank">https://leetcode-cn.com/problems/reverse-linked-list/</a></p><p>代码:<a href="http://suo.im/6e32hZ" rel="external nofollow noopener noreferrer" target="_blank">http://suo.im/6e32hZ</a></p><h3 id="题目-2"><a href="#题目-2" class="headerlink" title="题目"></a>题目</h3><p>反转一个单链表</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">输入: 1->2->3->4->5->NULL</span><br><span class="line">输出: 5->4->3->2->1->NULL</span><br></pre></td></tr></table></figure><h3 id="解题思路-2"><a href="#解题思路-2" class="headerlink" title="解题思路"></a>解题思路</h3><h4 id="第一版:首先反应想到的"><a href="#第一版:首先反应想到的" class="headerlink" title="第一版:首先反应想到的"></a>第一版:首先反应想到的</h4><p>时间复杂度:<strong>O(n^2)</strong></p><p>看到题目第一时间想到的方式,首先将每个值拿出来,然后再将存放的数组反转下,在构建新的链表。</p><p>方式比较差劲,后续改进了下</p><h4 id="第二版:递归方式"><a href="#第二版:递归方式" class="headerlink" title="第二版:递归方式"></a>第二版:递归方式</h4><p>题目描述中,说可以使用迭代和递归方式进行。</p><p>创建个函数,处理每一个节点,将每个节点赋值新的节点,然后将传递进来的节点赋值到新的节点的 next 上,并且传递下个递归,层层下去</p><p>当按顺序传递进去的节点的 next 为 null 时,返回最后节点。</p><h4 id="第三版:迭代方式"><a href="#第三版:迭代方式" class="headerlink" title="第三版:迭代方式"></a>第三版:迭代方式</h4><p>这个方式跑了两遍,第一遍 120 毫秒左右,第二遍跑了 104 毫秒 O(∩_∩)O 😄 。</p><p>迭代是一种不断用变量的旧值递推出新值的解决问题的方法。</p><p>所以在 while 循环链表时,将当前旧值赋值给新的节点,并且将新的节点 next 指向 prev</p><p>最后在反向赋值下,将临时的赋值给 prev</p><p>将 head 赋值为 head.next,继续循环,直到为 null</p><h3 id="代码-2"><a href="#代码-2" class="headerlink" title="代码"></a>代码</h3><h4 id="第一版:首先想到的,比较差"><a href="#第一版:首先想到的,比较差" class="headerlink" title="第一版:首先想到的,比较差"></a>第一版:首先想到的,比较差</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ListNode <span class="title">ReverseList</span>(<span class="params">ListNode head</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// 普通方法</span></span><br><span class="line"> <span class="keyword">if</span> (head==<span class="literal">null</span> || head.next ==<span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">return</span> head;</span><br><span class="line"> }</span><br><span class="line"> List<<span class="keyword">int</span>> intList = <span class="keyword">new</span> List<<span class="keyword">int</span>>();</span><br><span class="line"> <span class="keyword">while</span> (head != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> intList.Add(head.val);</span><br><span class="line"> head = head.next;</span><br><span class="line"> }</span><br><span class="line"> intList.Reverse();</span><br><span class="line"> <span class="keyword">var</span> newListNode = <span class="keyword">new</span> ListNode(intList[<span class="number">0</span>]);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">int</span> i = <span class="number">1</span>; i < intList.Count; i++)</span><br><span class="line"> {</span><br><span class="line"> <span class="keyword">var</span> tempNode = <span class="keyword">new</span> ListNode(intList[i])</span><br><span class="line"> {</span><br><span class="line"> next = <span class="literal">null</span></span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">var</span> temp = newListNode;</span><br><span class="line"> <span class="keyword">while</span> (temp.next != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> temp = temp.next;</span><br><span class="line"> }</span><br><span class="line"> temp.next = tempNode;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> newListNode;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="第二版:递归方式-1"><a href="#第二版:递归方式-1" class="headerlink" title="第二版:递归方式"></a>第二版:递归方式</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ListNode <span class="title">ReverseList2</span>(<span class="params">ListNode head</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// 递归方式解决</span></span><br><span class="line"> <span class="keyword">return</span> GetReverse(head, <span class="literal">null</span>);</span><br><span class="line"> <span class="function">ListNode <span class="title">GetReverse</span>(<span class="params">ListNode current,ListNode res</span>)</span></span><br><span class="line"><span class="function"></span> {</span><br><span class="line"> <span class="keyword">if</span> (current == <span class="literal">null</span>) <span class="keyword">return</span> res;</span><br><span class="line"> ListNode newNode = <span class="keyword">new</span> ListNode(current.val);</span><br><span class="line"> newNode.next = res;</span><br><span class="line"> <span class="keyword">return</span> GetReverse(current.next, newNode);</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="第三版:迭代方式-1"><a href="#第三版:迭代方式-1" class="headerlink" title="第三版:迭代方式"></a>第三版:迭代方式</h4><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> ListNode <span class="title">ReverseList3</span>(<span class="params">ListNode head</span>)</span></span><br><span class="line"><span class="function"></span>{</span><br><span class="line"> <span class="comment">// 迭代方式解决</span></span><br><span class="line"> ListNode prev = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">while</span> (head != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> <span class="comment">// 将当前的赋值到temp</span></span><br><span class="line"> <span class="comment">// 将temp引用改为prev</span></span><br><span class="line"> <span class="keyword">var</span> temp = <span class="keyword">new</span> ListNode(head.val)</span><br><span class="line"> {</span><br><span class="line"> next = prev</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 再将temp赋值到prev,覆盖掉</span></span><br><span class="line"> prev = temp;</span><br><span class="line"> head = head.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> prev;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>O(∩_∩)O 😄 这个方式跑了两遍,第一遍 120 毫秒左右,第二遍跑了 104 毫秒。</p><h2 id="876-链表的中间结点"><a href="#876-链表的中间结点" class="headerlink" title="876. 链表的中间结点"></a>876. 链表的中间结点</h2><p>地址:<a href="https://leetcode-cn.com/problems/middle-of-the-linked-list/" rel="external nofollow noopener noreferrer" target="_blank">https://leetcode-cn.com/problems/middle-of-the-linked-list/</a></p><p>代码:<a href="http://suo.im/65XknP" rel="external nofollow noopener noreferrer" target="_blank">http://suo.im/65XknP</a></p><h3 id="解题思路-3"><a href="#解题思路-3" class="headerlink" title="解题思路"></a>解题思路</h3><p>找出中间值,一旦涉及到中间的处理,在链表中都可以使用快慢指针的方式</p><p>快指针每次两步</p><p>慢指针每次一步</p><p>当快指针为 null 或者它的下一个是 null</p><p>则证明到了末尾,然后同时返回慢指针的值(这个题好处就是如果是两个中间返回后一个),就是剩余的后半部分</p><p>具体末尾图看图</p><h3 id="代码-3"><a href="#代码-3" class="headerlink" title="代码"></a>代码</h3><figure class="highlight csharp"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * Definition for singly-linked list.</span></span><br><span class="line"><span class="comment"> * public class ListNode {</span></span><br><span class="line"><span class="comment"> * public int val;</span></span><br><span class="line"><span class="comment"> * public ListNode next;</span></span><br><span class="line"><span class="comment"> * public ListNode(int x) { val = x; }</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">public</span> <span class="keyword">class</span> <span class="title">Solution</span> {</span><br><span class="line"> <span class="function"><span class="keyword">public</span> ListNode <span class="title">MiddleNode</span>(<span class="params">ListNode head</span>)</span> {</span><br><span class="line"> <span class="keyword">if</span>(head.next == <span class="literal">null</span>) <span class="keyword">return</span> head;</span><br><span class="line"> <span class="comment">// 一次前进两格</span></span><br><span class="line"> ListNode quick = head;</span><br><span class="line"> <span class="keyword">while</span> (quick != <span class="literal">null</span> && quick.next != <span class="literal">null</span>)</span><br><span class="line"> {</span><br><span class="line"> quick = quick.next.next;</span><br><span class="line"> head = head.next;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> head;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="手画图"><a href="#手画图" class="headerlink" title="手画图"></a>手画图</h3><img src="/articles/4970029a/20200723165911.png" title="第一种"> <img src="/articles/4970029a/20200723170014.png" title="第二种">]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Leetcode 链表 算法练习</p></blockquote>
</summary>
<category term="算法" scheme="https://blog.spiritling.cn/categories/algorithms/"/>
<category term="算法" scheme="https://blog.spiritling.cn/tags/algorithms/"/>
<category term="leetcode" scheme="https://blog.spiritling.cn/tags/leetcode/"/>
</entry>
<entry>
<title>十大经典排序算法</title>
<link href="https://blog.spiritling.cn/articles/827ca9cd/"/>
<id>https://blog.spiritling.cn/articles/827ca9cd/</id>
<published>2020-07-20T08:25:11.000Z</published>
<updated>2020-09-22T11:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>十大经典排序算法</p></blockquote><a id="more"></a><blockquote><p>原文地址:<a href="https://www.cnblogs.com/onepixel/articles/7674659.html" rel="external nofollow noopener noreferrer" target="_blank">https://www.cnblogs.com/onepixel/articles/7674659.html</a></p></blockquote><h2 id="算法概述"><a href="#算法概述" class="headerlink" title="算法概述"></a>算法概述</h2><h3 id="算法分类"><a href="#算法分类" class="headerlink" title="算法分类"></a>算法分类</h3><p>十种常见排序算法可以分为两大类:</p><ul><li>比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O(nlogn),因此也称为非线性时间比较类排序。</li><li>非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。</li></ul><img src="/articles/827ca9cd/si-wei-dao-tu.png" title="分类"><h3 id="算法复杂度"><a href="#算法复杂度" class="headerlink" title="算法复杂度"></a>算法复杂度</h3><img src="/articles/827ca9cd/pai-xu-table.png" title="十大经典算法复杂度分析表"><h3 id="相关概念"><a href="#相关概念" class="headerlink" title="相关概念"></a>相关概念</h3><ul><li>稳定性:如果待排序的序列中存在值相等的元素,经过排序之后,相等元素之间原有的先后顺序不变。</li><li>时间复杂度:对排序数据的总的操作次数。反映当 n 变化时,操作次数呈现什么规律。</li><li>空间复杂度:是指算法在计算机内执行时所需存储空间的度量,它也是数据规模 n 的函数。</li></ul><h2 id="冒泡排序(Bubble-Sort)"><a href="#冒泡排序(Bubble-Sort)" class="headerlink" title="冒泡排序(Bubble Sort)"></a>冒泡排序(Bubble Sort)</h2><p>冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。</p><h3 id="算法描述"><a href="#算法描述" class="headerlink" title="算法描述"></a>算法描述</h3><ol><li>比较相邻的元素。如果第一个比第二个大,就交换它们两个;</li><li>对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;</li><li>针对所有的元素重复以上的步骤,除了最后一个;</li><li>重复步骤 1~3,直到排序完成。</li></ol><h3 id="动图演示"><a href="#动图演示" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/BubbleSort.gif" title="冒泡排序动图"><h3 id="代码实现"><a href="#代码实现" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bubbleSort</span>(<span class="params">numberList</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> len = numberList.length;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < len - <span class="number">1</span>; i++) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = <span class="number">0</span>; j < len - <span class="number">1</span> - i; j++) {</span><br><span class="line"> <span class="keyword">if</span> (numberList[j] > numberList[j + <span class="number">1</span>]) {</span><br><span class="line"> <span class="comment">// 相邻元素两两对比</span></span><br><span class="line"> <span class="keyword">const</span> tempSwapSpace = numberList[j + <span class="number">1</span>]; <span class="comment">// 元素交换</span></span><br><span class="line"> numberList[j + <span class="number">1</span>] = numberList[j];</span><br><span class="line"> numberList[j] = tempSwapSpace;</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> numberList;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="选择排序(Selection-Sort)"><a href="#选择排序(Selection-Sort)" class="headerlink" title="选择排序(Selection Sort)"></a>选择排序(Selection Sort)</h2><p>选择排序(Selection Sort)是一种简单直观的排序算法。它的工作原理:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。</p><h3 id="算法描述-1"><a href="#算法描述-1" class="headerlink" title="算法描述"></a>算法描述</h3><p>n 个记录的直接选择排序可经过 n-1 趟直接选择排序得到有序结果。具体算法描述如下:</p><ul><li>初始状态:无序区为 R[1..n],有序区为空;</li><li>第 i 趟排序(i=1,2,3…n-1)开始时,当前有序区和无序区分别为 R[1..i-1]和 R(i..n)。该趟排序从当前无序区中-选出关键字最小的记录 R[k],将它与无序区的第 1 个记录 R 交换,使 R[1..i]和 R[i+1..n)分别变为记录个数增加 1 个的新有序区和记录个数减少 1 个的新无序区;</li><li>n-1 趟结束,数组有序化了。</li></ul><h3 id="动图演示-1"><a href="#动图演示-1" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/SelectionSort.gif" title="选择排序动图"><h3 id="代码实现-1"><a href="#代码实现-1" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">selectionSort</span>(<span class="params">numberList</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> len = numberList.length;</span><br><span class="line"> <span class="keyword">let</span> minIndex, tempSwapSpace;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < len - <span class="number">1</span>; i++) {</span><br><span class="line"> minIndex = i;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> j = i + <span class="number">1</span>; j < len; j++) {</span><br><span class="line"> <span class="keyword">if</span> (numberList[j] < numberList[minIndex]) {</span><br><span class="line"> <span class="comment">// 寻找最小的数</span></span><br><span class="line"> minIndex = j; <span class="comment">// 将最小数的索引保存</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> tempSwapSpace = numberList[i];</span><br><span class="line"> numberList[i] = numberList[minIndex];</span><br><span class="line"> numberList[minIndex] = tempSwapSpace;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> numberList;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析"><a href="#算法分析" class="headerlink" title="算法分析"></a>算法分析</h3><p>表现最稳定的排序算法之一,因为无论什么数据进去都是 O(n<sup>2</sup>) 的时间复杂度,所以用到它的时候,数据规模越小越好。唯一的好处可能就是不占用额外的内存空间了吧。理论上讲,选择排序可能也是平时排序一般人想到的最多的排序方法了吧。</p><h2 id="插入排序(Insertion-Sort)"><a href="#插入排序(Insertion-Sort)" class="headerlink" title="插入排序(Insertion Sort)"></a>插入排序(Insertion Sort)</h2><p>插入排序(Insertion-Sort)的算法描述是一种简单直观的排序算法。它的工作原理是通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。</p><h3 id="算法描述-2"><a href="#算法描述-2" class="headerlink" title="算法描述"></a>算法描述</h3><p>一般来说,插入排序都采用 in-place 在数组上实现。具体算法描述如下:</p><ol><li>从第一个元素开始,该元素可以认为已经被排序;</li><li>取出下一个元素,在已经排序的元素序列中从后向前扫描;</li><li>如果该元素(已排序)大于新元素,将该元素移到下一位置;</li><li>重复步骤 3,直到找到已排序的元素小于或者等于新元素的位置;</li><li>将新元素插入到该位置后;</li><li>重复步骤 2~5。</li></ol><h3 id="动图演示-2"><a href="#动图演示-2" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/InsertionSort.gif" title="插入排序动图"><h3 id="代码实现-2"><a href="#代码实现-2" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">insertionSort</span>(<span class="params">numberList</span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> len = numberList.length;</span><br><span class="line"> <span class="keyword">let</span> preIndex, current;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">1</span>; i < len; i++) {</span><br><span class="line"> preIndex = i - <span class="number">1</span>;</span><br><span class="line"> current = numberList[i];</span><br><span class="line"> <span class="keyword">while</span> (preIndex >= <span class="number">0</span> && numberList[preIndex] > current) {</span><br><span class="line"> numberList[preIndex + <span class="number">1</span>] = numberList[preIndex];</span><br><span class="line"> preIndex--;</span><br><span class="line"> }</span><br><span class="line"> numberList[preIndex + <span class="number">1</span>] = current;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> numberList;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析-1"><a href="#算法分析-1" class="headerlink" title="算法分析"></a>算法分析</h3><p>插入排序在实现上,通常采用 in-place 排序(即只需用到 O(1)的额外空间的排序),因而在从后向前扫描过程中,需要反复把已排序元素逐步向后挪位,为最新元素提供插入空间。</p><h2 id="4、希尔排序(Shell-Sort)"><a href="#4、希尔排序(Shell-Sort)" class="headerlink" title="4、希尔排序(Shell Sort)"></a>4、希尔排序(Shell Sort)</h2><p>1959 年 Shell 发明,第一个突破 O(n2)的排序算法,是简单插入排序的改进版。它与插入排序的不同之处在于,它会优先比较距离较远的元素。希尔排序又叫缩小增量排序。</p><h3 id="算法描述-3"><a href="#算法描述-3" class="headerlink" title="算法描述"></a>算法描述</h3><p>先将整个待排序的记录序列分割成为若干子序列分别进行直接插入排序,具体算法描述:</p><p>选择一个增量序列 t1,t2,…,tk,其中 ti>tj,tk=1;<br>按增量序列个数 k,对序列进行 k 趟排序;<br>每趟排序,根据对应的增量 ti,将待排序列分割成若干长度为 m 的子序列,分别对各子表进行直接插入排序。仅增量因子为 1 时,整个序列作为一个表来处理,表长度即为整个序列的长度。</p><h3 id="动图演示-3"><a href="#动图演示-3" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/ShellSort.gif" title="希尔排序动图"><h3 id="代码实现-3"><a href="#代码实现-3" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">shellSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> gap = <span class="built_in">Math</span>.floor(len / <span class="number">2</span>); gap > <span class="number">0</span>; gap = <span class="built_in">Math</span>.floor(gap / <span class="number">2</span>)) {</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 = gap; i < len; i++) {</span><br><span class="line"> <span class="keyword">var</span> j = i;</span><br><span class="line"> <span class="keyword">var</span> current = arr[i];</span><br><span class="line"> <span class="keyword">while</span> (j - gap >= <span class="number">0</span> && current < arr[j - gap]) {</span><br><span class="line"> arr[j] = arr[j - gap];</span><br><span class="line"> j = j - gap;</span><br><span class="line"> }</span><br><span class="line"> arr[j] = current;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析-2"><a href="#算法分析-2" class="headerlink" title="算法分析"></a>算法分析</h3><p>希尔排序的核心在于间隔序列的设定。既可以提前设定好间隔序列,也可以动态的定义间隔序列。动态定义间隔序列的算法是《算法(第 4 版)》的合著者 Robert Sedgewick 提出的。</p><h2 id="归并排序(Merge-Sort)"><a href="#归并排序(Merge-Sort)" class="headerlink" title="归并排序(Merge Sort)"></a>归并排序(Merge Sort)</h2><p>归并排序是建立在归并操作上的一种有效的排序算法。该算法是采用分治法(Divide and Conquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为 2-路归并。</p><h3 id="算法描述-4"><a href="#算法描述-4" class="headerlink" title="算法描述"></a>算法描述</h3><p>把长度为 n 的输入序列分成两个长度为 n/2 的子序列;<br>对这两个子序列分别采用归并排序;<br>将两个排序好的子序列合并成一个最终的排序序列。</p><h3 id="动图演示-4"><a href="#动图演示-4" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/MergeSort.gif" title="归并排序动图"><h3 id="代码实现-4"><a href="#代码实现-4" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">mergeSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length;</span><br><span class="line"> <span class="keyword">if</span> (len < <span class="number">2</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> middle = <span class="built_in">Math</span>.floor(len / <span class="number">2</span>),</span><br><span class="line"> left = arr.slice(<span class="number">0</span>, middle),</span><br><span class="line"> right = arr.slice(middle);</span><br><span class="line"> <span class="keyword">return</span> merge(mergeSort(left), mergeSort(right));</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">merge</span>(<span class="params">left, right</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = [];</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (left.length > <span class="number">0</span> && right.length > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (left[<span class="number">0</span>] <= right[<span class="number">0</span>]) {</span><br><span class="line"> result.push(left.shift());</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> result.push(right.shift());</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (left.length) result.push(left.shift());</span><br><span class="line"></span><br><span class="line"> <span class="keyword">while</span> (right.length) result.push(right.shift());</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><h3 id="算法分析-3"><a href="#算法分析-3" class="headerlink" title="算法分析"></a>算法分析</h3><p>归并排序是一种稳定的排序方法。和选择排序一样,归并排序的性能不受输入数据的影响,但表现比选择排序好的多,因为始终都是 O(nlogn)的时间复杂度。代价是需要额外的内存空间。</p><h2 id="快速排序(Quick-Sort)"><a href="#快速排序(Quick-Sort)" class="headerlink" title="快速排序(Quick Sort)"></a>快速排序(Quick Sort)</h2><p>快速排序的基本思想:通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。</p><h3 id="算法描述-5"><a href="#算法描述-5" class="headerlink" title="算法描述"></a>算法描述</h3><p>快速排序使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:</p><p>从数列中挑出一个元素,称为 “基准”(pivot);<br>重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;<br>递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。</p><h3 id="动图演示-5"><a href="#动图演示-5" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/QuickSort.gif" title="快速排序动图"><h3 id="代码实现-5"><a href="#代码实现-5" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">quickSort</span>(<span class="params">arr, left, right</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> len = arr.length,</span><br><span class="line"> partitionIndex,</span><br><span class="line"> left = typeofleft != <span class="string">"number"</span> ? <span class="number">0</span> : left,</span><br><span class="line"> right = typeofright != <span class="string">"number"</span> ? len - <span class="number">1</span> : right;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (left < right) {</span><br><span class="line"> partitionIndex = partition(arr, left, right);</span><br><span class="line"> quickSort(arr, left, partitionIndex - <span class="number">1</span>);</span><br><span class="line"> quickSort(arr, partitionIndex + <span class="number">1</span>, right);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">partition</span>(<span class="params">arr, left, right</span>) </span>{</span><br><span class="line"> <span class="comment">// 分区操作</span></span><br><span class="line"> <span class="keyword">var</span> pivot = left, <span class="comment">// 设定基准值(pivot)</span></span><br><span class="line"> index = pivot + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = index; i <= right; i++) {</span><br><span class="line"> <span class="keyword">if</span> (arr[i] < arr[pivot]) {</span><br><span class="line"> swap(arr, i, index);</span><br><span class="line"> index++;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> swap(arr, pivot, index - <span class="number">1</span>);</span><br><span class="line"> <span class="keyword">return</span> index - <span class="number">1</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">swap</span>(<span class="params">arr, i, j</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> temp = arr[i];</span><br><span class="line"> arr[i] = arr[j];</span><br><span class="line"> arr[j] = temp;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="堆排序(Heap-Sort)"><a href="#堆排序(Heap-Sort)" class="headerlink" title="堆排序(Heap Sort)"></a>堆排序(Heap Sort)</h2><p>堆排序(Heapsort)是指利用堆这种数据结构所设计的一种排序算法。堆积是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点。</p><h3 id="算法描述-6"><a href="#算法描述-6" class="headerlink" title="算法描述"></a>算法描述</h3><p>将初始待排序关键字序列(R1,R2….Rn)构建成大顶堆,此堆为初始的无序区;<br>将堆顶元素 R[1]与最后一个元素 R[n]交换,此时得到新的无序区(R1,R2,……Rn-1)和新的有序区(Rn),且满足 R[1,2…n-1]<=R[n];<br>由于交换后新的堆顶 R[1]可能违反堆的性质,因此需要对当前无序区(R1,R2,……Rn-1)调整为新堆,然后再次将 R[1]与无序区最后一个元素交换,得到新的无序区(R1,R2….Rn-2)和新的有序区(Rn-1,Rn)。不断重复此过程直到有序区的元素个数为 n-1,则整个排序过程完成。</p><h3 id="动图演示-6"><a href="#动图演示-6" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/HeapSort.gif" title="堆排序动图"><h3 id="代码实现-6"><a href="#代码实现-6" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> len; <span class="comment">// 因为声明的多个函数都需要数据长度,所以把len设置成为全局变量</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildMaxHeap</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> <span class="comment">// 建立大顶堆</span></span><br><span class="line"> len = arr.length;</span><br><span class="line"> <span class="keyword">for</span> (vari = <span class="built_in">Math</span>.floor(len / <span class="number">2</span>); i >= <span class="number">0</span>; i--) {</span><br><span class="line"> heapify(arr, i);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapify</span>(<span class="params">arr, i</span>) </span>{</span><br><span class="line"> <span class="comment">// 堆调整</span></span><br><span class="line"> <span class="keyword">var</span> left = <span class="number">2</span> * i + <span class="number">1</span>,</span><br><span class="line"> right = <span class="number">2</span> * i + <span class="number">2</span>,</span><br><span class="line"> largest = i;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (left < len && arr[left] > arr[largest]) {</span><br><span class="line"> largest = left;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (right < len && arr[right] > arr[largest]) {</span><br><span class="line"> largest = right;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (largest != i) {</span><br><span class="line"> swap(arr, i, largest);</span><br><span class="line"> heapify(arr, largest);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">swap</span>(<span class="params">arr, i, j</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> temp = arr[i];</span><br><span class="line"> arr[i] = arr[j];</span><br><span class="line"> arr[j] = temp;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">heapSort</span>(<span class="params">arr</span>) </span>{</span><br><span class="line"> buildMaxHeap(arr);</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = arr.length - <span class="number">1</span>; i > <span class="number">0</span>; i--) {</span><br><span class="line"> swap(arr, <span class="number">0</span>, i);</span><br><span class="line"> len--;</span><br><span class="line"> heapify(arr, <span class="number">0</span>);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="计数排序(Counting-Sort)"><a href="#计数排序(Counting-Sort)" class="headerlink" title="计数排序(Counting Sort)"></a>计数排序(Counting Sort)</h2><p>计数排序不是基于比较的排序算法,其核心在于将输入的数据值转化为键存储在额外开辟的数组空间中。 作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。</p><h3 id="算法描述-7"><a href="#算法描述-7" class="headerlink" title="算法描述"></a>算法描述</h3><p>找出待排序的数组中最大和最小的元素;<br>统计数组中每个值为 i 的元素出现的次数,存入数组 C 的第 i 项;<br>对所有的计数累加(从 C 中的第一个元素开始,每一项和前一项相加);<br>反向填充目标数组:将每个元素 i 放在新数组的第 C(i)项,每放一个元素就将 C(i)减去 1。</p><h3 id="动图演示-7"><a href="#动图演示-7" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/CountingSort.gif" title="计数排序动图"><h3 id="代码实现-7"><a href="#代码实现-7" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">countingSort</span>(<span class="params">arr, maxValue</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> bucket = newArray(maxValue + <span class="number">1</span>),</span><br><span class="line"> sortedIndex = <span class="number">0</span>;</span><br><span class="line"> (arrLen = arr.length), (bucketLen = maxValue + <span class="number">1</span>);</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 < arrLen; i++) {</span><br><span class="line"> <span class="keyword">if</span> (!bucket[arr[i]]) {</span><br><span class="line"> bucket[arr[i]] = <span class="number">0</span>;</span><br><span class="line"> }</span><br><span class="line"> bucket[arr[i]]++;</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> j = <span class="number">0</span>; j < bucketLen; j++) {</span><br><span class="line"> <span class="keyword">while</span> (bucket[j] > <span class="number">0</span>) {</span><br><span class="line"> arr[sortedIndex++] = j;</span><br><span class="line"> bucket[j]--;</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> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析-4"><a href="#算法分析-4" class="headerlink" title="算法分析"></a>算法分析</h3><p>计数排序是一个稳定的排序算法。当输入的元素是 n 个 0 到 k 之间的整数时,时间复杂度是 O(n+k),空间复杂度也是 O(n+k),其排序速度快于任何比较排序算法。当 k 不是很大并且序列比较集中时,计数排序是一个很有效的排序算法。</p><h2 id="桶排序(Bucket-Sort)"><a href="#桶排序(Bucket-Sort)" class="headerlink" title="桶排序(Bucket Sort)"></a>桶排序(Bucket Sort)</h2><p>桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。桶排序 (Bucket sort)的工作的原理:假设输入数据服从均匀分布,将数据分到有限数量的桶里,每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排)。</p><h3 id="算法描述-8"><a href="#算法描述-8" class="headerlink" title="算法描述"></a>算法描述</h3><p>设置一个定量的数组当作空桶;<br>遍历输入数据,并且把数据一个一个放到对应的桶里去;<br>对每个不是空的桶进行排序;<br>从不是空的桶里把排好序的数据拼接起来。</p><h3 id="图片演示"><a href="#图片演示" class="headerlink" title="图片演示"></a>图片演示</h3><img src="/articles/827ca9cd/BucketSort.gif" title="桶排序动图"><h3 id="代码实现-8"><a href="#代码实现-8" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">bucketSort</span>(<span class="params">arr, bucketSize</span>) </span>{</span><br><span class="line"> <span class="keyword">if</span>(arr.length === <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">return</span> arr;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> i;</span><br><span class="line"> <span class="keyword">var</span> minValue = arr[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">var</span> maxValue = arr[<span class="number">0</span>];</span><br><span class="line"> <span class="keyword">for</span>(i = <span class="number">1</span>; i < arr.length; i++) {</span><br><span class="line"> <span class="keyword">if</span>(arr[i] < minValue) {</span><br><span class="line"> minValue = arr[i]; <span class="comment">// 输入数据的最小值</span></span><br><span class="line"> } elseif(arr[i] > maxValue) {</span><br><span class="line"> maxValue = arr[i]; <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="comment">// 桶的初始化</span></span><br><span class="line"> <span class="keyword">var</span> DEFAULT_BUCKET_SIZE = <span class="number">5</span>; <span class="comment">// 设置桶的默认数量为5</span></span><br><span class="line"> bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;</span><br><span class="line"> <span class="keyword">var</span> bucketCount = <span class="built_in">Math</span>.floor((maxValue - minValue) / bucketSize) + <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">var</span> buckets = newArray(bucketCount);</span><br><span class="line"> <span class="keyword">for</span>(i = <span class="number">0</span>; i < buckets.length; i++) {</span><br><span class="line"> buckets[i] = [];</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>(i = <span class="number">0</span>; i < arr.length; i++) {</span><br><span class="line"> buckets[<span class="built_in">Math</span>.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> arr.length = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span>(i = <span class="number">0</span>; i < buckets.length; i++) {</span><br><span class="line"> insertionSort(buckets[i]); <span class="comment">// 对每个桶进行排序,这里使用了插入排序</span></span><br><span class="line"> <span class="keyword">for</span>(<span class="keyword">var</span> j = <span class="number">0</span>; j < buckets[i].length; j++) {</span><br><span class="line"> arr.push(buckets[i][j]);</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> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析-5"><a href="#算法分析-5" class="headerlink" title="算法分析"></a>算法分析</h3><p>桶排序最好情况下使用线性时间 O(n),桶排序的时间复杂度,取决与对各个桶之间数据进行排序的时间复杂度,因为其它部分的时间复杂度都为 O(n)。很显然,桶划分的越小,各个桶之间的数据越少,排序所用的时间也会越少。但相应的空间消耗就会增大。</p><h2 id="基数排序(Radix-Sort)"><a href="#基数排序(Radix-Sort)" class="headerlink" title="基数排序(Radix Sort)"></a>基数排序(Radix Sort)</h2><p>基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序。最后的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。</p><h3 id="算法描述-9"><a href="#算法描述-9" class="headerlink" title="算法描述"></a>算法描述</h3><p>取得数组中的最大数,并取得位数;<br>arr 为原始数组,从最低位开始取每个位组成 radix 数组;<br>对 radix 进行计数排序(利用计数排序适用于小范围数的特点);</p><h3 id="动图演示-8"><a href="#动图演示-8" class="headerlink" title="动图演示"></a>动图演示</h3><img src="/articles/827ca9cd/RadixSort.gif" title="基数排序动图"><h3 id="代码实现-9"><a href="#代码实现-9" class="headerlink" title="代码实现"></a>代码实现</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> counter = [];</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">radixSort</span>(<span class="params">arr, maxDigit</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> mod = <span class="number">10</span>;</span><br><span class="line"> <span class="keyword">var</span> dev = <span class="number">1</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < maxDigit; i++, dev *= <span class="number">10</span>, mod *= <span class="number">10</span>) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j < arr.length; j++) {</span><br><span class="line"> <span class="keyword">var</span> bucket = <span class="built_in">parseInt</span>((arr[j] % mod) / dev);</span><br><span class="line"> <span class="keyword">if</span> (counter[bucket] == <span class="literal">null</span>) {</span><br><span class="line"> counter[bucket] = [];</span><br><span class="line"> }</span><br><span class="line"> counter[bucket].push(arr[j]);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">var</span> pos = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> j = <span class="number">0</span>; j < counter.length; j++) {</span><br><span class="line"> <span class="keyword">var</span> value = <span class="literal">null</span>;</span><br><span class="line"> <span class="keyword">if</span> (counter[j] != <span class="literal">null</span>) {</span><br><span class="line"> <span class="keyword">while</span> ((value = counter[j].shift()) != <span class="literal">null</span>) {</span><br><span class="line"> arr[pos++] = value;</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">return</span> arr;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="算法分析-6"><a href="#算法分析-6" class="headerlink" title="算法分析"></a>算法分析</h3><p>基数排序基于分别排序,分别收集,所以是稳定的。但基数排序的性能比桶排序要略差,每一次关键字的桶分配都需要 O(n)的时间复杂度,而且分配之后得到新的关键字序列又需要 O(n)的时间复杂度。假如待排数据可以分为 d 个关键字,则基数排序的时间复杂度将是 O(d*2n) ,当然 d 要远远小于 n,因此基本上还是线性级别的。</p><p>基数排序的空间复杂度为 O(n+k),其中 k 为桶的数量。一般来说 n>>k,因此额外空间需要大概 n 个左右。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>十大经典排序算法</p></blockquote>
</summary>
<category term="算法" scheme="https://blog.spiritling.cn/categories/algorithms/"/>
<category term="算法" scheme="https://blog.spiritling.cn/tags/algorithms/"/>
</entry>
<entry>
<title>js 函数柯里化(Currying)</title>
<link href="https://blog.spiritling.cn/articles/54432a21-88df-4a83-a02f-762296b72609/"/>
<id>https://blog.spiritling.cn/articles/54432a21-88df-4a83-a02f-762296b72609/</id>
<published>2020-07-15T08:44:12.000Z</published>
<updated>2020-07-22T07:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>JavaScript 函数柯里化(Currying)</p></blockquote><a id="more"></a><blockquote><p>在计算机科学中,柯里化(Currying),又译为卡瑞化或加里化,是把接受多个参数的函数变换成接受一个单一参数(最初函数的第一个参数)的函数,并且返回接受余下的参数而且返回结果的新函数的技术。</p></blockquote><h2 id="从一道面试题谈谈函数柯里化从一道面试题谈谈函数柯里化"><a href="#从一道面试题谈谈函数柯里化从一道面试题谈谈函数柯里化" class="headerlink" title="从一道面试题谈谈函数柯里化从一道面试题谈谈函数柯里化"></a>从一道面试题谈谈函数柯里化从一道面试题谈谈函数柯里化</h2><blockquote><p>题目:使用 js 实现 add(1)(2)(3)(4) 返回 10</p></blockquote><p>函数柯里化要求多个参数转为单一参数,所以相当于</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="built_in">Array</span>.from(<span class="built_in">arguments</span>);</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 调用:add(1, 2, 3, 4) => [1, 2, 3, 4]</span></span><br><span class="line"><span class="comment">// 转换为下面形式</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addCurrying</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="comment">// do something</span></span><br><span class="line">}</span><br><span class="line"><span class="comment">// 调用:addCurrying(1)(2)(3)(4) => [1, 2, 3, 4]</span></span><br></pre></td></tr></table></figure><h2 id="闭包拿参数"><a href="#闭包拿参数" class="headerlink" title="闭包拿参数"></a>闭包拿参数</h2><p>闭包:定义在一个函数内部的函数,静态保存所有了父级作用域的内部函数。所以每次的值可以保存住,到了最后可以全部访问到</p><p>所以,我们先来一个闭包来拿取到所有参数</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> add = <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> result = [];</span><br><span class="line"> result.push(value);</span><br><span class="line"> <span class="comment">// _add 函数就是一个闭包,拿取到父级作用域的值,因为_add函数已获取到父级数据,所以父级一直存在,没有释放</span></span><br><span class="line"> <span class="keyword">var</span> _add = <span class="function"><span class="keyword">function</span> (<span class="params">value2</span>) </span>{</span><br><span class="line"> result.push(value);</span><br><span class="line"> <span class="keyword">return</span> _add;</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 此方法调用返回数组</span></span><br><span class="line"> _add.getResult = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">return</span> _add;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>这样通过上面函数就可以实现基本的柯里化要求</p><p>执行:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// addCurrying(1)(2)(3)(4).getResult => [1, 2, 3, 4]</span></span><br><span class="line"><span class="comment">// addCurrying(1)(2)(3)(4)</span></span><br><span class="line"><span class="comment">// =></span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment">ƒ (value2) {</span></span><br><span class="line"><span class="comment"> result.push(value);</span></span><br><span class="line"><span class="comment"> return _add;</span></span><br><span class="line"><span class="comment">}</span></span><br><span class="line"><span class="comment">*/</span></span><br></pre></td></tr></table></figure><h2 id="重写内置函数返回结果"><a href="#重写内置函数返回结果" class="headerlink" title="重写内置函数返回结果"></a>重写内置函数返回结果</h2><p>上面的代码虽然已经获取到所有的参数,但是返回结果并没有自动返回,而是需要调用函数才返回。</p><p>并且由于不知道参数长度,也就无法通过参数数组长度来及时返回结果。</p><p>那么有什么方法可以解决呢?🤔</p><p>当然有了,在 js 中函数是有原型链的,所以每个函数都继承了基本的一些方法。</p><p>当你定义一个函数后,你如果打印时只输入函数名,并不执行,则函数内部信息就被打印出来。</p><p>那么我们可以重写这个方法,来在结束后执行。</p><p>函数的 <code>toString</code> 方法可以打印函数体,所以我们改造下,让 <code>toString</code> 返回结果</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//首先我们需要一个入口函数进行基本处理</span></span><br><span class="line"><span class="keyword">var</span> add = <span class="function"><span class="keyword">function</span> (<span class="params">value</span>) </span>{</span><br><span class="line"> <span class="comment">//定义一个保存所有传入的参数的数组</span></span><br><span class="line"> <span class="keyword">var</span> result = [];</span><br><span class="line"> <span class="comment">// 将第一个传入的参数放置进去</span></span><br><span class="line"> result.push(value);</span><br><span class="line"> <span class="comment">// 定义函数 _add 引用result变量,使其保持不被销毁掉</span></span><br><span class="line"> <span class="keyword">var</span> _add = <span class="function"><span class="keyword">function</span> (<span class="params">subValue</span>) </span>{</span><br><span class="line"> <span class="comment">// 将参数push进入result中去</span></span><br><span class="line"> result.push(subValue);</span><br><span class="line"> <span class="comment">// 返回函数 _add 进行下次一调用</span></span><br><span class="line"> <span class="keyword">return</span> _add;</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 将原生自带的toString 进行重写</span></span><br><span class="line"> _add.toString = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> result;</span><br><span class="line"> };</span><br><span class="line"> <span class="comment">// 返回 _add 函数进行下次调用</span></span><br><span class="line"> <span class="keyword">return</span> _add;</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>执行结果</p><ul><li><code>add(1)</code> => <code>[1]</code></li><li><code>add(1)(2)</code> => <code>[1,2]</code></li><li><code>add(1)(2)(3)</code> => <code>[1,2,3]</code></li></ul><img src="/articles/54432a21-88df-4a83-a02f-762296b72609/screenshot_1535697196480.png" title="函数执行流程图"><h2 id="结束"><a href="#结束" class="headerlink" title="结束"></a>结束</h2><p>通过上面的研究,解决一个函数柯里化问题。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>JavaScript 函数柯里化(Currying)</p></blockquote>
</summary>
<category term="JavaScript" scheme="https://blog.spiritling.cn/categories/JavaScript/"/>
<category term="JavaScript" scheme="https://blog.spiritling.cn/tags/JavaScript/"/>
<category term="Function" scheme="https://blog.spiritling.cn/tags/Function/"/>
<category term="Currying" scheme="https://blog.spiritling.cn/tags/Currying/"/>
</entry>
<entry>
<title>choco 安装 和 mkcert 本地https</title>
<link href="https://blog.spiritling.cn/articles/93535928-cc52-4039-9e8e-08904e381682/"/>
<id>https://blog.spiritling.cn/articles/93535928-cc52-4039-9e8e-08904e381682/</id>
<published>2020-06-18T01:45:30.000Z</published>
<updated>2020-06-20T10:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>choco 安装 和 mkcert 本地 https</p></blockquote><a id="more"></a><h2 id="Choco"><a href="#Choco" class="headerlink" title="Choco"></a>Choco</h2><h3 id="命令行安装"><a href="#命令行安装" class="headerlink" title="命令行安装"></a>命令行安装</h3><p>直接拷贝执行即可,注意需要<strong>管理员身份</strong>运行</p><p><code>cmd</code> 安装:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">@"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin"</span><br></pre></td></tr></table></figure><p><code>PowerShell</code> 安装:</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Set-ExecutionPolicy Bypass -Scope Process -Force; iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))</span><br></pre></td></tr></table></figure><h3 id="检查安装是否成功"><a href="#检查安装是否成功" class="headerlink" title="检查安装是否成功"></a>检查安装是否成功</h3><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">choco -v</span><br></pre></td></tr></table></figure><h3 id="设置安装路径"><a href="#设置安装路径" class="headerlink" title="设置安装路径"></a>设置安装路径</h3><img src="/articles/93535928-cc52-4039-9e8e-08904e381682/chocolatey-install-path-config.png" title="choco安装路径"><p>修改路径后,需要添加安装路径下的<code>bin</code>目录到<code>path</code>环境中。</p><p>有时安装完毕后,下载的软件无法使用,有可能是<code>bin</code>目录没有在<code>path</code>环境中。</p><h3 id="常用指令"><a href="#常用指令" class="headerlink" title="常用指令"></a>常用指令</h3><p><a href="https://chocolatey.org/docs/commands-reference" rel="external nofollow noopener noreferrer" target="_blank">Commands</a></p><ul><li>choco list -li 查看本地安装的软件</li><li>choco search nodejs 查找安装包</li><li>choco install sublimetext3 下载</li><li>choco uninstall sublimetext3 卸载</li><li>choco upgrade sublimetext3 更新(update)</li></ul><h2 id="mkcert"><a href="#mkcert" class="headerlink" title="mkcert"></a>mkcert</h2><p>mkcert 是一个使用 go 语言编写的生成本地自签证书的小程序,具有跨平台,使用简单,支持多域名,自动信任 CA 等一系列方便的特性可供本地开发时快速创建 https 环境使用。</p><h3 id="安装-mkcert"><a href="#安装-mkcert" class="headerlink" title="安装 mkcert"></a>安装 mkcert</h3><p>安装方式也非常简单,由于 go 语言的静态编译和跨平台的特性,官方提供各平台预编译的版本,直接下载到本地,给可执行权限(Linux/Unix 需要)就可以了。下载地址: <a href="https://github.com/FiloSottile/mkcert/releases/latest" rel="external nofollow noopener noreferrer" target="_blank">https://github.com/FiloSottile/mkcert/releases/latest</a></p><p>此外,mkcert 已经推送至 Homebrew, MacPorts, Linuxbrew, Chocolatey, Scoop 等包管理平台中,也可以直接借助对应的包管理平台安装。如:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">choco install mkcert</span><br></pre></td></tr></table></figure><p>安装成功后,应该可以使用 mkcert 命令了:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line">PS C:\Users\abcfy\projects> mkcert</span><br><span class="line">Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ✨</span><br><span class="line">Usage of mkcert:</span><br><span class="line"></span><br><span class="line"> $ mkcert -install</span><br><span class="line"> Install the local CA in the system trust store.</span><br><span class="line"></span><br><span class="line"> $ mkcert example.org</span><br><span class="line"> Generate "example.org.pem" and "example.org-key.pem".</span><br><span class="line"></span><br><span class="line"> $ mkcert example.com myapp.dev localhost 127.0.0.1 ::1</span><br><span class="line"> Generate "example.com+4.pem" and "example.com+4-key.pem".</span><br><span class="line"></span><br><span class="line"> $ mkcert "*.example.it"</span><br><span class="line"> Generate "_wildcard.example.it.pem" and "_wildcard.example.it-key.pem".</span><br><span class="line"></span><br><span class="line"> $ mkcert -uninstall</span><br><span class="line"> Uninstall the local CA (but do not delete it).</span><br><span class="line"></span><br><span class="line">For more options, run "mkcert -help".</span><br></pre></td></tr></table></figure><h3 id="mkcert-基本使用"><a href="#mkcert-基本使用" class="headerlink" title="mkcert 基本使用"></a>mkcert 基本使用</h3><p>从上面自带的帮助输出来看,mkcert 已经给出了一个基本的工作流,规避了繁杂的 openssl 命令,几个简单的参数就可以生成一个本地可信的 https 证书了。更详细的用法直接看官方文档就好。</p><h4 id="将-CA-证书加入本地可信-CA"><a href="#将-CA-证书加入本地可信-CA" class="headerlink" title="将 CA 证书加入本地可信 CA"></a>将 CA 证书加入本地可信 CA</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">$ mkcert -install</span><br><span class="line">Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ✨</span><br></pre></td></tr></table></figure><p>仅仅这么一条简单的命令,就帮助我们将 mkcert 使用的根证书加入了本地可信 CA 中,以后由该 CA 签发的证书在本地都是可信的。</p><p>在 Windows 的可信 CA 列表可以找到该证书</p><h4 id="生成自签证书"><a href="#生成自签证书" class="headerlink" title="生成自签证书"></a>生成自签证书</h4><p>生成自签证书的命令十分简单:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkcert domain1 [domain2 [...]]</span><br></pre></td></tr></table></figure><p>直接跟多个要签发的域名或 ip 就行了,比如签发一个仅本机访问的证书(可以通过 127.0.0.1 和 localhost,以及 ipv6 地址::1 访问)</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">mkcert localhost 127.0.0.1 ::1</span><br><span class="line">Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ✨</span><br><span class="line"></span><br><span class="line">Created a new certificate valid for the following names 📜</span><br><span class="line"> - "localhost"</span><br><span class="line"> - "127.0.0.1"</span><br><span class="line"> - "::1"</span><br><span class="line"></span><br><span class="line">The certificate is at "./localhost+2.pem" and the key at "./localhost+2-key.pem" ✅</span><br></pre></td></tr></table></figure><p>通过输出,我们可以看到成功生成了 localhost+2.pem 证书文件和 localhost+2-key.pem 私钥文件,只要在 web server 上使用这两个文件就可以了。</p><p>默认生成的证书格式为 PEM(Privacy Enhanced Mail)格式,任何支持 PEM 格式证书的程序都可以使用。比如常见的 Apache 或 Nginx 等,也可以使用参数指定生成的格式,比如 windows 的 pfx 格式证书</p><h4 id="高级参数"><a href="#高级参数" class="headerlink" title="高级参数"></a>高级参数</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line">-cert-file FILE, -key-file FILE, -p12-file FILE</span><br><span class="line">Customize the output paths.</span><br><span class="line"></span><br><span class="line">-client</span><br><span class="line">Generate a certificate for client authentication.</span><br><span class="line"></span><br><span class="line">-ecdsa</span><br><span class="line">Generate a certificate with an ECDSA key.</span><br><span class="line"></span><br><span class="line">-pkcs12</span><br><span class="line">Generate a ".p12" PKCS #12 file, also know as a ".pfx" file,</span><br><span class="line">containing certificate and key for legacy applications.</span><br><span class="line"></span><br><span class="line">-csr CSR</span><br><span class="line">Generate a certificate based on the supplied CSR. Conflicts with</span><br><span class="line">all other flags and arguments except -install and -cert-file.</span><br></pre></td></tr></table></figure><h5 id="pem-证书生成"><a href="#pem-证书生成" class="headerlink" title="pem 证书生成"></a>pem 证书生成</h5><p>不需要额外的任何操作,使用 <code>mkcert domain.com</code>,即可生成 <code>domain.com.pem</code> 和 <code>domain.com-key.pem</code></p><h5 id="pfx-证书生成"><a href="#pfx-证书生成" class="headerlink" title="pfx 证书生成"></a>pfx 证书生成</h5><p>密码:changeit</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">C:\Users\albertxiao>mkcert -pkcs12 *.example.com</span><br><span class="line">Using the local CA at "C:\Users\albertxiao\AppData\Local\mkcert" ✨</span><br><span class="line"></span><br><span class="line">Created a new certificate valid for the following names 📜</span><br><span class="line"> - "*.example.com"</span><br><span class="line"></span><br><span class="line">Reminder: X.509 wildcards only go one level deep, so this won't match a.b.example.com ℹ️</span><br><span class="line"></span><br><span class="line">The PKCS#12 bundle is at "./_wildcard.example.com.p12" ✅</span><br><span class="line"></span><br><span class="line">The legacy PKCS#12 encryption password is the often hardcoded default "changeit" ℹ️</span><br></pre></td></tr></table></figure><h5 id="crt-证书"><a href="#crt-证书" class="headerlink" title="crt 证书"></a>crt 证书</h5><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">mkcert -key-file localhost.key -cert-file localhost.crt localhost</span><br></pre></td></tr></table></figure><h2 id="局域网构建"><a href="#局域网构建" class="headerlink" title="局域网构建"></a>局域网构建</h2><p>有时候我们需要在局域网内测试 https 应用,这种环境可能不对外,因此也无法使用像 Let’s encrypt 这种免费证书的方案给局域网签发一个可信的证书,而且 Let’s encrypt 本身也不支持认证 Ip。</p><p>先来回忆一下证书可信的三个要素:</p><ul><li>由可信的 CA 机构签发</li><li>访问的地址跟证书认证地址相符</li><li>证书在有效期内</li></ul><p>如果期望我们自签证书在局域网内使用,以上三个条件都需要满足。很明显自签证书一定可以满足证书在有效期内,那么需要保证后两条。我们签发的证书必须匹配浏览器的地址栏,比如局域网的 ip 或者域名,此外还需要信任 CA。</p><p>我们先重新签发一下证书,加上本机的局域网 ip 认证:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">mkcert localhost 127.0.0.1 ::1 192.168.31.170</span><br><span class="line">Using the local CA at "C:\Users\abcfy\AppData\Local\mkcert" ✨</span><br><span class="line"></span><br><span class="line">Created a new certificate valid for the following names 📜</span><br><span class="line"> - "localhost"</span><br><span class="line"> - "127.0.0.1"</span><br><span class="line"> - "::1"</span><br><span class="line"> - "192.168.31.170"</span><br><span class="line"></span><br><span class="line">The certificate is at "./localhost+3.pem" and the key at "./localhost+3-key.pem" ✅</span><br></pre></td></tr></table></figure><p>再次验证发现使用<a href="https://192.168.31.170本机访问也是可信的。然后我们需要将**CA证书发放给局域网内其他的用户**。" rel="external nofollow noopener noreferrer" target="_blank">https://192.168.31.170本机访问也是可信的。然后我们需要将**CA证书发放给局域网内其他的用户**。</a></p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">mkcert -CAROOT</span><br><span class="line">C:\Users\abcfy\AppData\Local\mkcert</span><br></pre></td></tr></table></figure><p>使用 mkcert -CAROOT 命令可以列出 CA 证书的存放路径</p><p>可以看到 CA 路径下有两个文件 rootCA-key.pem 和 rootCA.pem 两个文件,用户需要信任 rootCA.pem 这个文件。将 rootCA.pem 拷贝一个副本,并命名为 rootCA.crt(因为 windows 并不识别 pem 扩展名,并且 Ubuntu 也不会将 pem 扩展名作为 CA 证书文件对待),将 rootCA.crt 文件分发给其他用户,手工导入。</p><p>windows 导入证书的方法是双击这个文件,在证书导入向导中将证书导入受信任的根证书颁发机构:</p><img src="/articles/93535928-cc52-4039-9e8e-08904e381682/window-import-mkcert-ca.png" title="导入证书"><p>Ubuntu 的做法可以将证书文件(必须是 crt 后缀)放入/usr/local/share/ca-certificates/,然后执行 sudo update-ca-certificates</p><p>Android 和 IOS 信任 CA 证书的做法参考<a href="https://github.com/FiloSottile/mkcert#mobile-devices" rel="external nofollow noopener noreferrer" target="_blank">官方文档</a>。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>choco 安装 和 mkcert 本地 https</p></blockquote>
</summary>
<category term="本地开发" scheme="https://blog.spiritling.cn/categories/local-development/"/>
<category term="choco" scheme="https://blog.spiritling.cn/tags/choco/"/>
<category term="mkcert" scheme="https://blog.spiritling.cn/tags/mkcert/"/>
<category term="本地https" scheme="https://blog.spiritling.cn/tags/local-https/"/>
</entry>
<entry>
<title>Jenkins 凭据使用</title>
<link href="https://blog.spiritling.cn/articles/53007723-2d22-4da5-a9c6-d372fb9171aa/"/>
<id>https://blog.spiritling.cn/articles/53007723-2d22-4da5-a9c6-d372fb9171aa/</id>
<published>2020-06-01T09:05:43.000Z</published>
<updated>2020-06-01T10:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Jenkins 凭据使用</p></blockquote><a id="more"></a><h2 id="环境变量"><a href="#环境变量" class="headerlink" title="环境变量"></a>环境变量</h2><h3 id="jenkinsfile-使用环境变量"><a href="#jenkinsfile-使用环境变量" class="headerlink" title="jenkinsfile 使用环境变量"></a>jenkinsfile 使用环境变量</h3><p>代码:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">pipeline {</span><br><span class="line"> agent {</span><br><span class="line"> docker {</span><br><span class="line"> image 'spiritling/node:10.15.3'</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> stages {</span><br><span class="line"> stage('get') {</span><br><span class="line"> environment {</span><br><span class="line"> VERSION = sh(script: 'node script/auto-versioning.js', , returnStdout: true)</span><br><span class="line"> }</span><br><span class="line"> steps {</span><br><span class="line"> sh 'echo "VERSION: "$VERSION'</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>将 auto-versioning.js 执行后返回的文本或数字存入到 <code>VERSION</code> 环境变量中去</p><p>在 <code>steps</code> 中使用 <code>$VERSION</code> 来获取环境变量</p><h2 id="凭据"><a href="#凭据" class="headerlink" title="凭据"></a>凭据</h2><h3 id="账号密码凭据管理"><a href="#账号密码凭据管理" class="headerlink" title="账号密码凭据管理"></a>账号密码凭据管理</h3><p>创建凭据,以下为例子:</p><p>类型:Username with password<br>范围:全局<br>用户名:root<br>密码:rootxxxx<br>ID:BIRRARY_ID<br>描述:随意填写</p><p>在 jenkinsfile 中使用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">pipeline {</span><br><span class="line"> agent {</span><br><span class="line"> docker {</span><br><span class="line"> image 'spiritling/node:10.15.3'</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> stages {</span><br><span class="line"> stage('get') {</span><br><span class="line"> steps {</span><br><span class="line"> withCredentials([usernamePassword(credentialsId: 'BIRRARY_ID', passwordVariable: 'password', usernameVariable: 'username')]) {</span><br><span class="line"> sh 'git remote set-url origin https://${username}:${password}@github.com/spiritling/blog.git'</span><br><span class="line"> }</span><br><span class="line"> sh 'echo "获取凭据"'</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以在 jenkinsfile 文件的构建过程中获取到 username 和 password 的凭据,并且可以在后续将其插入进去</p><h3 id="加密文本凭据管理"><a href="#加密文本凭据管理" class="headerlink" title="加密文本凭据管理"></a>加密文本凭据管理</h3><p>创建凭据,以下为例子:</p><p>类型:Secret text<br>范围:全局<br>Secret:rootxxxx<br>ID:BIRRARY_ID<br>描述:随意填写</p><p>在 jenkinsfile 中使用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line">pipeline {</span><br><span class="line"> agent {</span><br><span class="line"> docker {</span><br><span class="line"> image 'spiritling/node:10.15.3'</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"> stages {</span><br><span class="line"> stage('get') {</span><br><span class="line"> steps {</span><br><span class="line"> withCredentials([string(credentialsId: 'ID:BIRRARY_ID', variable: 'secret')]) { //set SECRET with the credential content</span><br><span class="line"> sh 'echo -e "registry=https://npmjs.org/spiritling/\n_auth = ${secret}\nemail = [email protected]\nalways-auth = true\n$PATH" > .npmrc'</span><br><span class="line"> }</span><br><span class="line"> sh 'echo "获取凭据"'</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以在 jenkinsfile 文件的构建过程中获取到 secret 的凭据,并且可以在后续将其插入进去</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Jenkins 凭据使用</p></blockquote>
</summary>
<category term="Jenkins" scheme="https://blog.spiritling.cn/categories/Jenkins/"/>
<category term="Jenkins" scheme="https://blog.spiritling.cn/tags/Jenkins/"/>
<category term="凭据" scheme="https://blog.spiritling.cn/tags/%E5%87%AD%E6%8D%AE/"/>
</entry>
<entry>
<title>Nodejs 一些细节</title>
<link href="https://blog.spiritling.cn/articles/8088a1a3-4e07-47b1-bbfa-6d0b741b5114/"/>
<id>https://blog.spiritling.cn/articles/8088a1a3-4e07-47b1-bbfa-6d0b741b5114/</id>
<published>2020-05-20T07:25:58.000Z</published>
<updated>2020-05-20T10:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Nodejs 一些细节</p></blockquote><a id="more"></a><h2 id="nodejs-路径以及-path-相关"><a href="#nodejs-路径以及-path-相关" class="headerlink" title="nodejs 路径以及 path 相关"></a>nodejs 路径以及 path 相关</h2><h3 id="process-cwd-、-dirname、-filename"><a href="#process-cwd-、-dirname、-filename" class="headerlink" title="process.cwd()、__dirname、__filename"></a><code>process.cwd()</code>、<code>__dirname</code>、<code>__filename</code></h3><p>三者的区别是</p><table><thead><tr><th>命令</th><th>说明</th></tr></thead><tbody><tr><td><code>process.cwd()</code></td><td>获得当前执行 node 命令时候的文件夹目录名</td></tr><tr><td><code>__dirname</code></td><td>获得当前执行文件所在目录的完整目录名</td></tr><tr><td><code>__filename</code></td><td>获得当前执行文件的带有完整绝对路径的文件名</td></tr></tbody></table><p>例子说明:<br>假设我们创建一个全局的 cli</p><p>它的项目路径在 <code>D:\MyProject\mycli</code> 文件夹中</p><p>而它的 <code>mycli init</code> 命令执行的函数在 <code>D:\MyProject\mycli\src\init.js</code> 中</p><p>在 <code>init.js</code> 存在以下代码</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">console</span>.log(process.cwd());</span><br><span class="line"><span class="built_in">console</span>.log(__dirname);</span><br><span class="line"><span class="built_in">console</span>.log(__filename);</span><br><span class="line"><span class="built_in">console</span>.log(path.join(<span class="string">"./index.html"</span>));</span><br><span class="line"><span class="built_in">console</span>.log(path.resolve(<span class="string">"./index.html"</span>));</span><br></pre></td></tr></table></figure><p>那么在 <code>D:\MyProject\mycli</code> 下执行命令输出</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">D:\MyProject\mycli</span><br><span class="line">D:\MyProject\mycli\src</span><br><span class="line">D:\MyProject\mycli\src\init.js</span><br><span class="line">index.html</span><br><span class="line">D:\MyProject\mycli\index.html</span><br></pre></td></tr></table></figure><p>如果在 <code>D:\otherDir</code> 中执行命令输出为</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">D:\otherDir</span><br><span class="line">D:\MyProject\mycli\src</span><br><span class="line">D:\MyProject\mycli\src\init.js</span><br><span class="line">index.html</span><br><span class="line">D:\otherDir\index.html</span><br></pre></td></tr></table></figure><p>主要区别就是 <code>process.cwd()</code> 和 <code>path</code> 连接命令</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Nodejs 一些细节</p></blockquote>
</summary>
<category term="NodeJS" scheme="https://blog.spiritling.cn/categories/NodeJS/"/>
<category term="NodeJS" scheme="https://blog.spiritling.cn/tags/NodeJS/"/>
</entry>
<entry>
<title>Git 常用命令</title>
<link href="https://blog.spiritling.cn/articles/a44fe4b7-9c07-4e87-b591-da4b2861d4e8/"/>
<id>https://blog.spiritling.cn/articles/a44fe4b7-9c07-4e87-b591-da4b2861d4e8/</id>
<published>2020-05-11T10:00:03.000Z</published>
<updated>2020-05-11T10:05:03.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Git 常用命令</p></blockquote><a id="more"></a><h3 id="配置操作"><a href="#配置操作" class="headerlink" title="配置操作"></a>配置操作</h3><h4 id="全局配置"><a href="#全局配置" class="headerlink" title="全局配置"></a>全局配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --global user.name '你的名字'</span><br><span class="line">git config --global user.email '你的邮箱'</span><br></pre></td></tr></table></figure><h4 id="当前仓库配置"><a href="#当前仓库配置" class="headerlink" title="当前仓库配置"></a>当前仓库配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git config --local user.name '你的名字'</span><br><span class="line">git config --local user.email '你的邮箱'</span><br></pre></td></tr></table></figure><h4 id="查看-global-配置"><a href="#查看-global-配置" class="headerlink" title="查看 global 配置"></a>查看 global 配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --global --list</span><br></pre></td></tr></table></figure><h4 id="查看当前仓库配置"><a href="#查看当前仓库配置" class="headerlink" title="查看当前仓库配置"></a>查看当前仓库配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --local --list复制代码</span><br></pre></td></tr></table></figure><h4 id="删除-global-配置"><a href="#删除-global-配置" class="headerlink" title="删除 global 配置"></a>删除 global 配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config -unset --global 要删除的配置项</span><br></pre></td></tr></table></figure><h4 id="删除当前仓库配置"><a href="#删除当前仓库配置" class="headerlink" title="删除当前仓库配置"></a>删除当前仓库配置</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git config --unset --local 要删除的配置项</span><br></pre></td></tr></table></figure><h3 id="本地操作"><a href="#本地操作" class="headerlink" title="本地操作"></a>本地操作</h3><h4 id="查看变更情况"><a href="#查看变更情况" class="headerlink" title="查看变更情况"></a>查看变更情况</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git status</span><br></pre></td></tr></table></figure><h4 id="将当前目录及其子目录下所有变更都加入到暂存区"><a href="#将当前目录及其子目录下所有变更都加入到暂存区" class="headerlink" title="将当前目录及其子目录下所有变更都加入到暂存区"></a>将当前目录及其子目录下所有变更都加入到暂存区</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add .</span><br></pre></td></tr></table></figure><h4 id="将仓库内所有变更都加入到暂存区"><a href="#将仓库内所有变更都加入到暂存区" class="headerlink" title="将仓库内所有变更都加入到暂存区"></a>将仓库内所有变更都加入到暂存区</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add -A</span><br></pre></td></tr></table></figure><h4 id="将指定文件添加到暂存区"><a href="#将指定文件添加到暂存区" class="headerlink" title="将指定文件添加到暂存区"></a>将指定文件添加到暂存区</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git add 文件1 文件2 文件3</span><br></pre></td></tr></table></figure><h4 id="比较工作区和暂存区的所有差异"><a href="#比较工作区和暂存区的所有差异" class="headerlink" title="比较工作区和暂存区的所有差异"></a>比较工作区和暂存区的所有差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff</span><br></pre></td></tr></table></figure><h4 id="比较某文件工作区和暂存区的差异"><a href="#比较某文件工作区和暂存区的差异" class="headerlink" title="比较某文件工作区和暂存区的差异"></a>比较某文件工作区和暂存区的差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff 文件</span><br></pre></td></tr></table></figure><h4 id="比较暂存区和-HEAD-的所有差异"><a href="#比较暂存区和-HEAD-的所有差异" class="headerlink" title="比较暂存区和 HEAD 的所有差异"></a>比较暂存区和 HEAD 的所有差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff --cached</span><br></pre></td></tr></table></figure><h4 id="比较某文件暂存区和-HEAD-的差异"><a href="#比较某文件暂存区和-HEAD-的差异" class="headerlink" title="比较某文件暂存区和 HEAD 的差异"></a>比较某文件暂存区和 HEAD 的差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff -cached 文件</span><br></pre></td></tr></table></figure><h4 id="比较某文件工作区和-HEAD-的差异"><a href="#比较某文件工作区和-HEAD-的差异" class="headerlink" title="比较某文件工作区和 HEAD 的差异"></a>比较某文件工作区和 HEAD 的差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git diff HEAD 文件</span><br></pre></td></tr></table></figure><h4 id="创建-commit"><a href="#创建-commit" class="headerlink" title="创建 commit"></a>创建 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git commit</span><br></pre></td></tr></table></figure><h4 id="将工作区指定文件恢复成和暂存区一致"><a href="#将工作区指定文件恢复成和暂存区一致" class="headerlink" title="将工作区指定文件恢复成和暂存区一致"></a>将工作区指定文件恢复成和暂存区一致</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout 文件1 文件2 文件3</span><br></pre></td></tr></table></figure><h4 id="将暂存区指定文件恢复成和-HEAD-一致"><a href="#将暂存区指定文件恢复成和-HEAD-一致" class="headerlink" title="将暂存区指定文件恢复成和 HEAD 一致"></a>将暂存区指定文件恢复成和 HEAD 一致</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset 文件1 文件2 文件3</span><br></pre></td></tr></table></figure><h4 id="将暂存区和工作区所有文件恢复成和-HEAD-一样"><a href="#将暂存区和工作区所有文件恢复成和-HEAD-一样" class="headerlink" title="将暂存区和工作区所有文件恢复成和 HEAD 一样"></a>将暂存区和工作区所有文件恢复成和 HEAD 一样</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git reset --hard</span><br></pre></td></tr></table></figure><h4 id="用-difftool-比较任意两个-commit-的差异"><a href="#用-difftool-比较任意两个-commit-的差异" class="headerlink" title="用 difftool 比较任意两个 commit 的差异"></a>用 difftool 比较任意两个 commit 的差异</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git difftool 提交1 提交2</span><br></pre></td></tr></table></figure><h4 id="查看哪些文件没被-Git-管控"><a href="#查看哪些文件没被-Git-管控" class="headerlink" title="查看哪些文件没被 Git 管控"></a>查看哪些文件没被 Git 管控</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git ls-files --others</span><br></pre></td></tr></table></figure><h4 id="将未处理完的变更先保存到-stash-中"><a href="#将未处理完的变更先保存到-stash-中" class="headerlink" title="将未处理完的变更先保存到 stash 中"></a>将未处理完的变更先保存到 stash 中</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash</span><br></pre></td></tr></table></figure><h4 id="临时任务处理完后继续之前的工作"><a href="#临时任务处理完后继续之前的工作" class="headerlink" title="临时任务处理完后继续之前的工作"></a>临时任务处理完后继续之前的工作</h4><ul><li><p>pop 不保留 stash</p></li><li><p>apply 保留 stash</p></li></ul><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash pop</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash apply</span><br></pre></td></tr></table></figure><h4 id="查看所有-stash"><a href="#查看所有-stash" class="headerlink" title="查看所有 stash"></a>查看所有 stash</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash list</span><br></pre></td></tr></table></figure><h4 id="取回某次-stash-的变更"><a href="#取回某次-stash-的变更" class="headerlink" title="取回某次 stash 的变更"></a>取回某次 stash 的变更</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git stash pop stash@{数字n}</span><br></pre></td></tr></table></figure><h4 id="优雅修改最后一次-commit"><a href="#优雅修改最后一次-commit" class="headerlink" title="优雅修改最后一次 commit"></a>优雅修改最后一次 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git add.</span><br><span class="line">git commit --amend</span><br></pre></td></tr></table></figure><h3 id="分支操作"><a href="#分支操作" class="headerlink" title="分支操作"></a>分支操作</h3><h4 id="查看当前工作分支及本地分支"><a href="#查看当前工作分支及本地分支" class="headerlink" title="查看当前工作分支及本地分支"></a>查看当前工作分支及本地分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -v</span><br></pre></td></tr></table></figure><h4 id="查看本地和远端分支"><a href="#查看本地和远端分支" class="headerlink" title="查看本地和远端分支"></a>查看本地和远端分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -av</span><br></pre></td></tr></table></figure><h4 id="查看远端分支"><a href="#查看远端分支" class="headerlink" title="查看远端分支"></a>查看远端分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -rv</span><br></pre></td></tr></table></figure><h4 id="切换到指定分支"><a href="#切换到指定分支" class="headerlink" title="切换到指定分支"></a>切换到指定分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout 指定分支</span><br></pre></td></tr></table></figure><h4 id="基于当前分支创建新分支"><a href="#基于当前分支创建新分支" class="headerlink" title="基于当前分支创建新分支"></a>基于当前分支创建新分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch 新分支</span><br></pre></td></tr></table></figure><h4 id="基于指定分支创建新分支"><a href="#基于指定分支创建新分支" class="headerlink" title="基于指定分支创建新分支"></a>基于指定分支创建新分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch 新分支 指定分支</span><br></pre></td></tr></table></figure><h4 id="基于某个-commit-创建分支"><a href="#基于某个-commit-创建分支" class="headerlink" title="基于某个 commit 创建分支"></a>基于某个 commit 创建分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch 新分支 某个 commit 的 id</span><br></pre></td></tr></table></figure><h4 id="创建并切换到该分支"><a href="#创建并切换到该分支" class="headerlink" title="创建并切换到该分支"></a>创建并切换到该分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git checkout -b 新分支</span><br></pre></td></tr></table></figure><h4 id="安全删除本地某分支"><a href="#安全删除本地某分支" class="headerlink" title="安全删除本地某分支"></a>安全删除本地某分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -d 要删除的分支</span><br></pre></td></tr></table></figure><h4 id="强行删除本地某分支"><a href="#强行删除本地某分支" class="headerlink" title="强行删除本地某分支"></a>强行删除本地某分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch -D 要删除的分支</span><br></pre></td></tr></table></figure><h4 id="删除已合并到-master-分支的所有本地分支"><a href="#删除已合并到-master-分支的所有本地分支" class="headerlink" title="删除已合并到 master 分支的所有本地分支"></a>删除已合并到 master 分支的所有本地分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git branch --merged master | grep -v '^\*\| master' | xargs -n 1 git branch -d</span><br></pre></td></tr></table></figure><h4 id="删除远端-origin-已不存在的所有本地分支"><a href="#删除远端-origin-已不存在的所有本地分支" class="headerlink" title="删除远端 origin 已不存在的所有本地分支"></a>删除远端 origin 已不存在的所有本地分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote prune orign</span><br></pre></td></tr></table></figure><h4 id="将-A-分支合入到当前分支中且为-merge-创建-commit"><a href="#将-A-分支合入到当前分支中且为-merge-创建-commit" class="headerlink" title="将 A 分支合入到当前分支中且为 merge 创建 commit"></a>将 A 分支合入到当前分支中且为 merge 创建 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge A分支</span><br></pre></td></tr></table></figure><h4 id="将-A-分支合入到-B-分支中且为-merge-创建-commit"><a href="#将-A-分支合入到-B-分支中且为-merge-创建-commit" class="headerlink" title="将 A 分支合入到 B 分支中且为 merge 创建 commit"></a>将 A 分支合入到 B 分支中且为 merge 创建 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git merge A分支 B分支</span><br></pre></td></tr></table></figure><h4 id="将当前分支基于-B-分支做-rebase,以便将B分支合入到当前分支"><a href="#将当前分支基于-B-分支做-rebase,以便将B分支合入到当前分支" class="headerlink" title="将当前分支基于 B 分支做 rebase,以便将B分支合入到当前分支"></a>将当前分支基于 B 分支做 rebase,以便将B分支合入到当前分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase B分支</span><br></pre></td></tr></table></figure><h4 id="将-A-分支基于-B-分支做-rebase,以便将-B-分支合入到-A-分支"><a href="#将-A-分支基于-B-分支做-rebase,以便将-B-分支合入到-A-分支" class="headerlink" title="将 A 分支基于 B 分支做 rebase,以便将 B 分支合入到 A 分支"></a>将 A 分支基于 B 分支做 rebase,以便将 B 分支合入到 A 分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git rebase B分支 A分支</span><br></pre></td></tr></table></figure><h3 id="变更历史"><a href="#变更历史" class="headerlink" title="变更历史"></a>变更历史</h3><h4 id="当前分支各个-commit-用一行显示"><a href="#当前分支各个-commit-用一行显示" class="headerlink" title="当前分支各个 commit 用一行显示"></a>当前分支各个 commit 用一行显示</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git log --oneline</span><br></pre></td></tr></table></figure><h4 id="显示就近的-n-个-commit"><a href="#显示就近的-n-个-commit" class="headerlink" title="显示就近的 n 个 commit"></a>显示就近的 n 个 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git log -n</span><br></pre></td></tr></table></figure><h4 id="用图示显示所有分支的历史"><a href="#用图示显示所有分支的历史" class="headerlink" title="用图示显示所有分支的历史"></a>用图示显示所有分支的历史</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git log --oneline --graph --all</span><br></pre></td></tr></table></figure><h4 id="查看涉及到某文件变更的所有-commit"><a href="#查看涉及到某文件变更的所有-commit" class="headerlink" title="查看涉及到某文件变更的所有 commit"></a>查看涉及到某文件变更的所有 commit</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git log 文件</span><br></pre></td></tr></table></figure><h4 id="某文件各行最后修改对应的-commit-以及作者"><a href="#某文件各行最后修改对应的-commit-以及作者" class="headerlink" title="某文件各行最后修改对应的 commit 以及作者"></a>某文件各行最后修改对应的 commit 以及作者</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git blame 文件</span><br></pre></td></tr></table></figure><h3 id="标签操作"><a href="#标签操作" class="headerlink" title="标签操作"></a>标签操作</h3><h4 id="查看已有标签"><a href="#查看已有标签" class="headerlink" title="查看已有标签"></a>查看已有标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag</span><br></pre></td></tr></table></figure><h4 id="新建标签"><a href="#新建标签" class="headerlink" title="新建标签"></a>新建标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag v1.0</span><br></pre></td></tr></table></figure><h4 id="新建带备注标签"><a href="#新建带备注标签" class="headerlink" title="新建带备注标签"></a>新建带备注标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -a v1.0 -m '前端食堂'</span><br></pre></td></tr></table></figure><h4 id="给指定的-commit-打标签"><a href="#给指定的-commit-打标签" class="headerlink" title="给指定的 commit 打标签"></a>给指定的 commit 打标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag v1.0 commitid</span><br></pre></td></tr></table></figure><h4 id="推送一个本地标签"><a href="#推送一个本地标签" class="headerlink" title="推送一个本地标签"></a>推送一个本地标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin v1.0</span><br></pre></td></tr></table></figure><h4 id="推送全部未推送过的本地标签"><a href="#推送全部未推送过的本地标签" class="headerlink" title="推送全部未推送过的本地标签"></a>推送全部未推送过的本地标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin --tags</span><br></pre></td></tr></table></figure><h4 id="删除一个本地标签"><a href="#删除一个本地标签" class="headerlink" title="删除一个本地标签"></a>删除一个本地标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -d v1.0</span><br></pre></td></tr></table></figure><h4 id="删除一个远端标签"><a href="#删除一个远端标签" class="headerlink" title="删除一个远端标签"></a>删除一个远端标签</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin :refs/tags/v1.0</span><br></pre></td></tr></table></figure><h3 id="远端交互"><a href="#远端交互" class="headerlink" title="远端交互"></a>远端交互</h3><h4 id="查看所有远端仓库"><a href="#查看所有远端仓库" class="headerlink" title="查看所有远端仓库"></a>查看所有远端仓库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote -v</span><br></pre></td></tr></table></figure><h4 id="添加远端仓库"><a href="#添加远端仓库" class="headerlink" title="添加远端仓库"></a>添加远端仓库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote add url</span><br></pre></td></tr></table></figure><h4 id="删除远端仓库"><a href="#删除远端仓库" class="headerlink" title="删除远端仓库"></a>删除远端仓库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote remove remote的名称</span><br></pre></td></tr></table></figure><h4 id="重命名远端仓库"><a href="#重命名远端仓库" class="headerlink" title="重命名远端仓库"></a>重命名远端仓库</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git remote rename 旧名称 新名称</span><br></pre></td></tr></table></figure><h4 id="将远端所有分支和标签的变更都拉到本地"><a href="#将远端所有分支和标签的变更都拉到本地" class="headerlink" title="将远端所有分支和标签的变更都拉到本地"></a>将远端所有分支和标签的变更都拉到本地</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git fetch remote</span><br></pre></td></tr></table></figure><h4 id="把远端分支的变更拉到本地,且-merge-到本地分支"><a href="#把远端分支的变更拉到本地,且-merge-到本地分支" class="headerlink" title="把远端分支的变更拉到本地,且 merge 到本地分支"></a>把远端分支的变更拉到本地,且 merge 到本地分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git pull origin 分支名</span><br></pre></td></tr></table></figure><h4 id="将本地分支-push-到远端"><a href="#将本地分支-push-到远端" class="headerlink" title="将本地分支 push 到远端"></a>将本地分支 push 到远端</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push origin 分支名</span><br></pre></td></tr></table></figure><h4 id="删除远端分支"><a href="#删除远端分支" class="headerlink" title="删除远端分支"></a>删除远端分支</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push remote --delete 远端分支名</span><br></pre></td></tr></table></figure><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push remote :远端分支名</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Git 常用命令</p></blockquote>
</summary>
<category term="Git" scheme="https://blog.spiritling.cn/categories/Git/"/>
<category term="Git" scheme="https://blog.spiritling.cn/tags/Git/"/>
</entry>
<entry>
<title>.Net Core + EF + mysql 从数据库生成实体</title>
<link href="https://blog.spiritling.cn/articles/1a400a2d-a8f7-49d1-a1e3-1a88eb19e86c/"/>
<id>https://blog.spiritling.cn/articles/1a400a2d-a8f7-49d1-a1e3-1a88eb19e86c/</id>
<published>2020-02-26T12:48:25.000Z</published>
<updated>2020-01-20T01:27:20.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>.Net Core + EF + mysql 从数据库生成实体</p></blockquote><a id="more"></a><blockquote><p>因为 mysql 官方给出 MySql.Data.EntityFrameworkCore 新版本 8.0.20 版本,所以这里有了新的生成方式,并且几乎没有各种问题</p></blockquote><h2 id="安装-NuGet-包"><a href="#安装-NuGet-包" class="headerlink" title="安装 NuGet 包"></a>安装 NuGet 包</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Install-Package Microsoft.EntityFrameworkCore</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Design</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Tools</span><br><span class="line">Install-Package MySql.Data.EntityFrameworkCore</span><br></pre></td></tr></table></figure><p>都要安装新版,支持.net core 3.1</p><p>版本分别是,按照我使用安装时的版本为最低:</p><ul><li>MySql.Data.EntityFrameworkCore 8.0.20 起步</li><li>其他为 3.1.6 起步</li></ul><blockquote><p>修改:因为已经出了.Net 5,所以版本已经升级到 5.x 了,如果你依旧使用的是.net core 3.x,则只需要安装到 3.x 最高版本即可,截至修改文章时版本号为 3.1.11</p></blockquote><p>对于 <code>MySql.Data.EntityFrameworkCore</code> 目前最高版本为 8.0.22(截至修改文章时),但是在这个最高版本会出现一些问题(<code>Could not find type mapping for column 'blogsite.users.updatedTime' with data type 'datetime'. Skipping column.</code>),但是在 8.0.20 版本不会出现,所以一般请按照下面的版本号来使用</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Install-Package Microsoft.EntityFrameworkCore -version 3.1.11</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Design -version 3.1.11</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Tools -version 3.1.11</span><br><span class="line">Install-Package MySql.Data.EntityFrameworkCore -version 8.0.20</span><br></pre></td></tr></table></figure><h2 id="运行命令"><a href="#运行命令" class="headerlink" title="运行命令"></a>运行命令</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Scaffold-DbContext "server=localhost;port=3306;user=root;password=123456789;database=sakila" MySql.Data.EntityFrameworkCore -OutputDir sakila -f</span><br></pre></td></tr></table></figure><p>来源查看 mysql 官方文档:<a href="https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework-core-scaffold-example.html" rel="external nofollow noopener noreferrer" target="_blank">https://dev.mysql.com/doc/connector-net/en/connector-net-entityframework-core-scaffold-example.html</a></p><h2 id="以下都是旧版"><a href="#以下都是旧版" class="headerlink" title="以下都是旧版"></a>以下都是旧版</h2><p>原文地址:<a href="https://www.cnblogs.com/yangjinwang/p/9516988.html" rel="external nofollow noopener noreferrer" target="_blank">https://www.cnblogs.com/yangjinwang/p/9516988.html</a></p><h2 id="安装-NuGet-包-1"><a href="#安装-NuGet-包-1" class="headerlink" title="安装 NuGet 包"></a>安装 NuGet 包</h2><p>点击 <code>工具</code> -> <code>NuGet包管理器</code> -> <code>程序包管理器控制台</code></p><p>分别安装以下几个包</p><p>Mysql 版本:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Install-Package MySql.Data.EntityFrameworkCore -Pre</span><br><span class="line">Install-Package Pomelo.EntityFrameworkCore.MySql</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Tools</span><br><span class="line">Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design</span><br></pre></td></tr></table></figure><p>Sql server 版本:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">Install-Package Microsoft.EntityFrameworkCore</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.SqlServer</span><br><span class="line">Install-Package Microsoft.EntityFrameworkCore.Tools</span><br><span class="line">Install-Package Microsoft.VisualStudio.Web.CodeGeneration.Design</span><br></pre></td></tr></table></figure><h2 id="运行命令生成实体"><a href="#运行命令生成实体" class="headerlink" title="运行命令生成实体"></a>运行命令生成实体</h2><p>Mysql 版本:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line">Scaffold-DbContext "server=.;userid=tech5_kj;pwd=xxx;port=3306;database=tech5_kj;sslmode=none;" Pomelo.EntityFrameworkCore.MySql -OutputDir Models -Force</span><br><span class="line"></span><br><span class="line">或者</span><br><span class="line"></span><br><span class="line">Scaffold-DbContext "server=.;userid=tech5_kj;pwd=xxx;port=3306;database=tech5_kj;sslmode=none;" Pomelo.EntityFrameworkCore.MySql -OutputDir Models -UseDatabaseNames -Force</span><br></pre></td></tr></table></figure><p>Sql server 版本:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Scaffold-DbContext "Data Source=.;Initial Catalog=EFCore_dbfirst;User ID=sa;Password=sa.123" Microsoft.EntityFrameworkCore.SqlServer -OutputDir Models -Force</span><br></pre></td></tr></table></figure><h2 id="参数说明"><a href="#参数说明" class="headerlink" title="参数说明"></a>参数说明</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">-OutputDir *** 实体文件所存放的文件目录</span><br><span class="line">-ContextDir *** DbContext文件存放的目录</span><br><span class="line">-Context *** DbContext文件名</span><br><span class="line">-Schemas *** 需要生成实体数据的数据表所在的模式</span><br><span class="line">-Tables *** 需要生成实体数据的数据表的集合</span><br><span class="line">-DataAnnotations</span><br><span class="line">-UseDatabaseNames 直接使用数据库中的表名和列名(某些版本不支持)</span><br><span class="line">-Force 强制执行,重写已经存在的实体文件</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>.Net Core + EF + mysql 从数据库生成实体</p></blockquote>
</summary>
<category term="dotnet" scheme="https://blog.spiritling.cn/categories/dotnet/"/>
<category term="dotNet" scheme="https://blog.spiritling.cn/tags/dotNet/"/>
<category term="EF" scheme="https://blog.spiritling.cn/tags/EF/"/>
<category term="mysql" scheme="https://blog.spiritling.cn/tags/mysql/"/>
</entry>
<entry>
<title>React 使用 Proxy 代理(create-react-app)</title>
<link href="https://blog.spiritling.cn/articles/a836f301-4b4a-4f5c-b501-a7631e3eaa53/"/>
<id>https://blog.spiritling.cn/articles/a836f301-4b4a-4f5c-b501-a7631e3eaa53/</id>
<published>2020-02-19T02:21:16.000Z</published>
<updated>2020-02-19T02:30:16.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>React 使用 Proxy 代理(create-react-app)</p></blockquote><a id="more"></a><h2 id="在create-react-app-中配置proxy代理"><a href="#在create-react-app-中配置proxy代理" class="headerlink" title="在create-react-app 中配置proxy代理"></a>在create-react-app 中配置proxy代理</h2><p>proxy,默认为NULL,类型为URL,一个为了发送http请求的代理<br>在平时开发时,尤其前后端分离时,需要假数据来进行模拟请求,这个时候就需要<code>proxy</code>代理来处理</p><h3 id="create-react-app-lt-2-0"><a href="#create-react-app-lt-2-0" class="headerlink" title="create-react-app < 2.0"></a>create-react-app < 2.0</h3><p>package.json 中配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"proxy"</span>:{</span><br><span class="line"> <span class="string">"/api/**"</span>:{</span><br><span class="line"> <span class="string">"target"</span>:<span class="string">"https://easymock.spiritling.pub/"</span>,</span><br><span class="line"> <span class="string">"changeOrigin"</span>: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="create-react-app-gt-2-0"><a href="#create-react-app-gt-2-0" class="headerlink" title="create-react-app > 2.0"></a>create-react-app > 2.0</h3><h4 id="package-json-中配置(不推荐)"><a href="#package-json-中配置(不推荐)" class="headerlink" title="package.json 中配置(不推荐)"></a>package.json 中配置(不推荐)</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"proxy"</span>: <span class="string">"https://easymock.spiritling.pub/"</span>,</span><br></pre></td></tr></table></figure><h4 id="配置文件-src-setupProxy-js-(推荐)"><a href="#配置文件-src-setupProxy-js-(推荐)" class="headerlink" title="配置文件 /src/setupProxy.js (推荐)"></a>配置文件 <code>/src/setupProxy.js</code> (推荐)</h4><p>将 <code>create-react-app</code> 解包后,可以在 <code>config</code> 文件夹下找到配置</p><p>在 <code>config/path.js</code> 中存在 <code>proxySetup: resolveApp('src/setupProxy.js'),</code></p><p>而 <code>proxySetup</code> 是只在 <code>webpackDevServer.config.js</code> 文件中使用,也就是说只在开发时使用</p><p>所以,可以在 /src/setupProxy.js 中配置</p><p>首先安装 <code>http-proxy-middleware</code></p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install http-proxy-middleware -D</span><br></pre></td></tr></table></figure><p>然后文件配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> proxy = <span class="built_in">require</span>(<span class="string">'http-proxy-middleware'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">app</span>) </span>{</span><br><span class="line"> app.use(</span><br><span class="line"> <span class="string">'/api/v1/'</span>,</span><br><span class="line"> proxy({</span><br><span class="line"> target : <span class="string">'https://easymock.spiritling.pub/'</span>,</span><br><span class="line"> changeOrigin : <span class="literal">true</span>, <span class="comment">// 设置跨域请求</span></span><br><span class="line"> PathRewrite : {</span><br><span class="line"> <span class="string">'^/api/v1'</span> : <span class="string">''</span> <span class="comment">// 将/api/v1 变为 ''</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><h3 id="使用例子"><a href="#使用例子" class="headerlink" title="使用例子"></a>使用例子</h3><h4 id="01"><a href="#01" class="headerlink" title="01"></a>01</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> proxy = <span class="built_in">require</span>(<span class="string">'http-proxy-middleware'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">app</span>) </span>{</span><br><span class="line"> app.use(</span><br><span class="line"> <span class="string">'/api/v1/'</span>,</span><br><span class="line"> proxy({</span><br><span class="line"> target : <span class="string">'https://easymock.spiritling.pub/'</span>,</span><br><span class="line"> changeOrigin : <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"> );</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>游览器中请求为 <code>https://example.com/api/v1/login</code></p><p>则经过代理后可以转为 <code>https://easymock.spiritling.pub/api/v1/login</code></p><h4 id="02"><a href="#02" class="headerlink" title="02"></a>02</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> proxy = <span class="built_in">require</span>(<span class="string">'http-proxy-middleware'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">app</span>) </span>{</span><br><span class="line"> app.use(</span><br><span class="line"> <span class="string">'/api/v1/'</span>,</span><br><span class="line"> proxy({</span><br><span class="line"> target : <span class="string">'https://easymock.spiritling.pub/'</span>,</span><br><span class="line"> changeOrigin : <span class="literal">true</span>,</span><br><span class="line"> PathRewrite : {</span><br><span class="line"> <span class="string">'^/api/v1'</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></pre></td></tr></table></figure><p>游览器中请求为 <code>https://example.com/api/v1/login</code></p><p>则经过代理后可以转为 <code>https://easymock.spiritling.pub/login</code></p><blockquote><p><a href="https://create-react-app.dev/docs/proxying-api-requests-in-development/" rel="external nofollow noopener noreferrer" target="_blank">create-react-app官方-Proxying API Requests in Development</a></p></blockquote><h2 id="http-proxy-middleware-新版本-≧-1-0-0"><a href="#http-proxy-middleware-新版本-≧-1-0-0" class="headerlink" title="http-proxy-middleware 新版本 ≧ 1.0.0"></a><code>http-proxy-middleware</code> 新版本 ≧ 1.0.0</h2><p>在新版本大于等于 1.0.0 时使用会出现如下问题</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">proxy is not a function</span><br></pre></td></tr></table></figure><p>也就是说 <code>http-proxy-middleware</code> 或者 <code>setupProxy</code> 会出现报错 <code>proxy is not a function</code></p><p>所以需要使用新版本的写法才可以</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> { createProxyMiddleware } = <span class="built_in">require</span>(<span class="string">'http-proxy-middleware'</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> (<span class="params">app</span>) </span>{</span><br><span class="line"> app.use(createProxyMiddleware(<span class="string">'/api/v1'</span>, {</span><br><span class="line"> target : <span class="string">'https://easymock.spiritling.pub'</span>,</span><br><span class="line"> changeOrigin : <span class="literal">true</span>,</span><br><span class="line"> ws: <span class="literal">true</span>,</span><br><span class="line"> pathRewrite : {</span><br><span class="line"> <span class="string">'^/api/v1'</span> : <span class="string">''</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">
<blockquote class="blockquote-center"><p>React 使用 Proxy 代理(create-react-app)</p></blockquote>
</summary>
<category term="react" scheme="https://blog.spiritling.cn/categories/react/"/>
<category term="react" scheme="https://blog.spiritling.cn/tags/react/"/>
<category term="proxy" scheme="https://blog.spiritling.cn/tags/proxy/"/>
</entry>
<entry>
<title>IIS进行反向代理和URL重写——实现https重定向,文件类型隐藏访问重写,nodejs等服务重写等等</title>
<link href="https://blog.spiritling.cn/articles/8555088a-f796-48fb-a8d6-e28305955ec4/"/>
<id>https://blog.spiritling.cn/articles/8555088a-f796-48fb-a8d6-e28305955ec4/</id>
<published>2020-01-14T09:53:46.000Z</published>
<updated>2020-01-20T09:31:04.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>IIS 进行 反向代理和 URL 重写——实现 https 重定向,文件类型隐藏访问重写,nodejs 等服务重写等等</p></blockquote><a id="more"></a><h2 id="Why"><a href="#Why" class="headerlink" title="Why?"></a>Why?</h2><h3 id="什么是-URL-重写"><a href="#什么是-URL-重写" class="headerlink" title="什么是 URL 重写"></a>什么是 URL 重写</h3><p>URL 重写和反向代理就是首先获得一个进入的 URL 请求然后把它重新写成网站可以处理的另一个 URL 的过程。</p><p>举个例子来说,如果通过浏览器进来的 URL 是“UserProfile.aspx?ID=1”那么它可以被重写成 “UserProfile/1.aspx”,这样的 URL,这样的网址可以更好的被网站所阅读。</p><p>根据不同的服务器就会有不同的 URL 重写规则,比如 iis apache nginx 这三种重写方式都是不同的,并非完全一样的。</p><p>今天只说 IIS 的重写</p><h3 id="为什么要使用"><a href="#为什么要使用" class="headerlink" title="为什么要使用"></a>为什么要使用</h3><p>首先有需求就有使用,所以,是我的需求诞生了我去使用重写功能</p><p>第一次接触的后端为 Nodejs,一个 80 或者 443 端口只能绑定一个程序。使用 windows 自带的 iis 服务器来使用,想要一个服务器上部署多个站点程序,则需要使用到反向代理才行。</p><p>不使用反向代理如下图所示:</p><p>比如我的网站是:<a href="https://demo.example.com:4436/posts/82f57ccc" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com:4436/posts/82f57ccc</a></p><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/20200119161522.png" title="URL重写之前NodeJS的URL"><p>明眼一看就是有点不够友好,正准备记下网址,这一瞅,直接放弃了,太麻烦了,还有端口</p><p>但是经过 URL 重写,也就是反向代理后就改为这样的:<a href="https://demo.example.com/4436/posts/82f57ccc" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com/4436/posts/82f57ccc</a> ,只需要记域名即可</p><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/20200119161557.png" title="URL重写之后"><p>只有你的服务器够大,通过反向代理和 URL 重写就可以代理无数个本地服务,哪怕本地无数个 nodejs 服务器从 10000-65535 监听,都可以的,只需要做好反向代理即可</p><h2 id="前置条件"><a href="#前置条件" class="headerlink" title="前置条件"></a>前置条件</h2><ol><li>首先需要有个 IIS 服务器</li><li>然后去官网下载 <a href="https://www.iis.net/downloads/microsoft/web-platform-installer" rel="external nofollow noopener noreferrer" target="_blank">web 平台安装工具</a></li><li>然后安装工具并打开</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412200431336-787173232.png" title="web平台安装工具"><ol start="4"><li>搜索 Application Request Routing 或者 应用程序请求路由 ,和 url 重写工具 或者 url rewrite;因为有可能是英文也有可能是中文,所以当一个搜索不到时,搜索另一个语言,一定要在产品全部中搜索</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412200848094-824241073.png" title="web平台安装工具"><ol start="5"><li><p>最近(2019 底 2020 初)不知道 web 平台安装工具出什么问题了,一直安装不了,有时候甚至会打不开,所以这里留个官网下载单模块的链接:<a href="https://www.iis.net/downloads/microsoft/url-rewrite" rel="external nofollow noopener noreferrer" target="_blank">url rewrite</a>,<a href="https://www.iis.net/downloads/microsoft/application-request-routing" rel="external nofollow noopener noreferrer" target="_blank">Application Request Routing</a>,<a href="https://www.iis.net/downloads" rel="external nofollow noopener noreferrer" target="_blank">All Modules Downloads</a>;拉到底下会有下载链接,url rewrite 有对应的中文版本。</p></li><li><p>下载安装即可</p></li></ol><h3 id="网盘链接"><a href="#网盘链接" class="headerlink" title="网盘链接"></a>网盘链接</h3><ol><li>百度网盘:</li></ol><ul><li>Application Request Routing:链接:<a href="https://pan.baidu.com/s/1Wg6u6IJVMqa-DeK5y8Ym6w" rel="external nofollow noopener noreferrer" target="_blank">https://pan.baidu.com/s/1Wg6u6IJVMqa-DeK5y8Ym6w</a> 提取码:rcic</li><li>url rewrite:链接:<a href="https://pan.baidu.com/s/1ePW6HOkqqyHPqNNWyaQ43w" rel="external nofollow noopener noreferrer" target="_blank">https://pan.baidu.com/s/1ePW6HOkqqyHPqNNWyaQ43w</a> 提取码:x27v</li></ul><ol start="2"><li>腾讯网盘:</li></ol><ul><li>Application Request Routing:链接:<a href="https://share.weiyun.com/5iI6q1W" rel="external nofollow noopener noreferrer" target="_blank">https://share.weiyun.com/5iI6q1W</a> 密码:u47bv6</li><li>url rewrite:链接:<a href="https://share.weiyun.com/52XIKfL" rel="external nofollow noopener noreferrer" target="_blank">https://share.weiyun.com/52XIKfL</a> 密码:57fkan</li></ul><h2 id="应用程序请求路由设置"><a href="#应用程序请求路由设置" class="headerlink" title="应用程序请求路由设置"></a>应用程序请求路由设置</h2><ol><li>打开 IIS 工具,选择上面安装的请求路由</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201157869-978989213.png" title="应用程序请求路由设置"><ol start="2"><li>选择 Server Proxy Settings</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201244421-675549952.png" title="应用程序请求路由设置"><ol start="3"><li>在中间区域,选择勾选 Enable proxy,不用修改内容,当然也可以根据需求自己修改</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201358722-1759010199.png" title="应用程序请求路由设置"><ol start="4"><li>点击应用即可,完成请求路由的设置</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201437572-1668674268.png" title="应用程序请求路由设置"><h2 id="url-重写设置:这里讲基本参数,后面才会是例子"><a href="#url-重写设置:这里讲基本参数,后面才会是例子" class="headerlink" title="url 重写设置:这里讲基本参数,后面才会是例子"></a>url 重写设置:这里讲基本参数,后面才会是例子</h2><ol><li>打开站点,选择需要 url 重写的站点</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201808214-1496956504.png" title="url重写设置"><ol start="2"><li>当安装完成 url 重写时,会出现 url 重写这个工具,选择工具,名字也有可能是英文</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412201916597-1448053593.png" title="url重写设置"><ol start="3"><li>打开工具,选在右侧栏第一行添加规则,打开对话框,选择空白规则</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412202043308-320482397.png" title="url重写设置"><ol start="4"><li>输入名称,随意,但是尽量能表示出重写目标的含义</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412203113345-943932106.png" title="url重写设置"><ol start="5"><li>匹配 url,请求的 url(字面意思):与模式匹配(推荐),与模式不匹配;使用(匹配使用的方式):正则表达式(推荐),通配符,完全匹配;</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412203336729-93686218.png" title="url重写设置"><ol start="6"><li>模式:比如说 host 代表主机域名,则在这里面写的就是 host 之后的正则匹配表达式,比如匹配在 host/blog/,之下的都转到 nodejs 搭建的服务上,则这里填写^blog/(.*);点击测试模式,我们填写下面图片内容进行测试,发现测试结果中有个{R:1},我们需要将其导向至 nodejs 搭建的服务,也就是重写目标</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412203903048-2138965636.png" title="url重写设置"><ol start="7"><li><p>忽略大小写,自然就是字面意思</p></li><li><p>下面便是条件,按照图片来进行填写,这里不具体细数条件类型和规则模式填写,后面有三个例子进行了基本讲述,其余的请查看官方文档</p></li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412204422154-761160361.png" title="url重写设置"><ol start="9"><li>服务器变量可以不写,操作类型可以因情况而定,一般使用 重定向或者重写(http 强制跳转 https 一般为重定向,其余大多数为重写)</li></ol><p>重写 URL:比如博客是在 nodejs 搭建的服务上,并且监听本地 3001 端口时,填写<a href="http://127.0.0.1:3001/{R:1},这个{R:1}就是上面模式匹配的字符串,其实访问www.example.com/blog/index.html,就是相当于访问http://127.0.0.1:3001/index.html,这样监听本地3001,不用公网3001,可以减少服务器危险" rel="external nofollow noopener noreferrer" target="_blank">http://127.0.0.1:3001/{R:1},这个{R:1}就是上面模式匹配的字符串,其实访问www.example.com/blog/index.html,就是相当于访问http://127.0.0.1:3001/index.html,这样监听本地3001,不用公网3001,可以减少服务器危险</a></p><p>停止后续规则,自然就是字面意思,一旦勾选后,匹配处理结束,将不再处理下面的其他规则</p><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/1076304-20180412204907092-626474199.png" title="url重写设置"><ol start="10"><li>点击右侧栏应用即可,当应用显示灰色时,证明有必填项没有填写,所以无法应用,到这里也就重写完毕。</li></ol><h2 id="反向代理例子"><a href="#反向代理例子" class="headerlink" title="反向代理例子"></a>反向代理例子</h2><p>目前共有四个,分别是:</p><ul><li>http 强制跳转 https 上(一般网站都会使用,全站 https 是趋势)</li><li>所有页面都返回同一个静态页(适合网站调整,整改,以及说明时)</li><li>隐藏其余端口访问,一个暴露的服务器后面隐藏多个监听本地的服务(代理多个内部服务)</li><li>隐藏访问的文件类型(*.php 文件可以隐藏或更改文件后缀)</li></ul><h3 id="http-强制跳转-https-上"><a href="#http-强制跳转-https-上" class="headerlink" title="http 强制跳转 https 上"></a>http 强制跳转 https 上</h3><ol><li>重写规则名称,以及请求的正则匹配</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/20200120135333.png" title="http强制跳转https"><ol start="2"><li>请求条件筛选</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/20200120135842.png" title="http强制跳转https"><p>域名和 https 状态一定要核对清除,并且匹配要选择全部匹配</p><ol start="3"><li>请求匹配,条件过滤后就是要重定向的地址</li></ol><img src="/articles/8555088a-f796-48fb-a8d6-e28305955ec4/20200120140954.png" title="http强制跳转https"><p>其中 {R:1} 就是指原请求中的路径,比如:<a href="http://demo.example.com/blog/posts/dec13d34" rel="external nofollow noopener noreferrer" target="_blank">http://demo.example.com/blog/posts/dec13d34</a> 它的请求路径就是 blog/posts/dec13d34,所以在重定向的域名后需要添加 <code>/</code></p><p>然后就可以将 http 请求强制跳转到 https 上</p><p>在最后会附有 <code>web.config</code> 配置文件</p><h3 id="所有页面都返回同一个静态页"><a href="#所有页面都返回同一个静态页" class="headerlink" title="所有页面都返回同一个静态页"></a>所有页面都返回同一个静态页</h3><p>在上面我们将每个 http 请求都重定向到 https 上,相当于一个 http 对应一个 https,通过 <code>{R:1}</code> 来动态赋值</p><p>但是如果深入一想,假设我们将 <code>{R:1}</code> 固定指向 <code>index.html</code>,那么每一个请求都会返回 <code>index.html</code> 内容,在将重定向改为重写,保持客户端 url 不变,但是内容已经被重写为 <code>index.html</code> 的内容了</p><p>重写域名可以使用 <code>http://localhost:port/</code> 或者 <code>http://127.0.0.1:port/</code></p><p>所以实现访问任何页面都返回同一个内容是很轻松的。</p><p>在最后会附有 <code>web.config</code> 配置文件</p><h3 id="隐藏其余端口访问,一个暴露的服务器后面隐藏多个监听本地的服务(代理多个内部服务)"><a href="#隐藏其余端口访问,一个暴露的服务器后面隐藏多个监听本地的服务(代理多个内部服务)" class="headerlink" title="隐藏其余端口访问,一个暴露的服务器后面隐藏多个监听本地的服务(代理多个内部服务)"></a>隐藏其余端口访问,一个暴露的服务器后面隐藏多个监听本地的服务(代理多个内部服务)</h3><p>在可以重写到指定的其他服务地址上去,那么同样也可以将本地监听非 80 443 端口的服务进行服务器层面的隐藏</p><p>比如原本访问 <a href="https://demo.example.com:4436/posts/82f57ccc" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com:4436/posts/82f57ccc</a> 内容,但是可以使用重写进行改变</p><p>重写请求模式,也就是正则匹配内容为:<code>^4436/(.*)</code></p><p>服务器条件,域名来源为:<code>^demo\.example\.com$</code></p><p>重写 url 可以为:<code>https://demo.example.com:4436/{R:1}</code></p><p>那么可以将明面上的请求 <a href="https://demo.example.com/4436/posts/82f57ccc" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com/4436/posts/82f57ccc</a> 进行 url 重写</p><p>先通过请求匹配 <code>^4436/(.*)</code> 获取到 <code>{R:1}</code> 为 <code>posts/82f57ccc</code></p><p>然后将 <code>{R:1}</code> 转发到 <code>https://demo.example.com:4436/</code> 上,就可以完成请求代理</p><h3 id="隐藏访问的文件类型(-php-文件可以隐藏或更改文件后缀)"><a href="#隐藏访问的文件类型(-php-文件可以隐藏或更改文件后缀)" class="headerlink" title="隐藏访问的文件类型(*.php 文件可以隐藏或更改文件后缀)"></a>隐藏访问的文件类型(*.php 文件可以隐藏或更改文件后缀)</h3><p>相应的,既然请求匹配可以匹配指定路径,那么也可以匹配指定文件,比如说:请求的为 <code>php</code> 文件</p><p>比如原本访问 <a href="https://demo.example.com/posts/82f57ca2phpf" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com/posts/82f57ca2phpf</a> 内容,但是可以使用重写进行改变</p><p>重写请求模式,也就是正则匹配内容为:<code>(.*)phpf$</code></p><p>服务器条件,域名来源为:<code>^demo\.example\.com$</code></p><p>重写 url 可以为:<code>https://demo.example.com/{R:1}.php</code></p><p>那么可以将明面上的请求 <a href="https://demo.example.com/posts/82f57ca2phpf" rel="external nofollow noopener noreferrer" target="_blank">https://demo.example.com/posts/82f57ca2phpf</a> 进行 url 重写</p><p>先通过请求匹配 <code>(.*)phpf$</code> 获取到 <code>{R:1}</code> 为 <code>posts/82f57ca2</code></p><p>然后将 <code>{R:1}</code> 转发到 <code>https://demo.example.com/{R:1}.php</code> 上,实际请求为:<code>https://demo.example.com/posts/82f57ca2.php</code></p><h2 id="web-config配置文件"><a href="#web-config配置文件" class="headerlink" title="web.config配置文件"></a><code>web.config</code>配置文件</h2><p><code>web.config</code> 配置文件放置在 iis 站点的根目录</p><p>可以根据自己需求进行更改</p><p>在线代码块:<a href="https://gist.github.com/SpiritLing/c0f12694158378594991a3084c433df3" rel="external nofollow noopener noreferrer" target="_blank">gist</a>,<a href="https://bitbucket.org/spiritling-team/workspace/snippets/Br4MdR" rel="external nofollow noopener noreferrer" target="_blank">bitbucket</a></p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta"><?xml version="1.0" encoding="UTF-8"?></span></span><br><span class="line"><span class="comment"><!--- Origin Release:https://blog.spiritling.cn/posts/82f57ccc/ --></span></span><br><span class="line"><span class="tag"><<span class="name">configuration</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">system.webServer</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">rewrite</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">rules</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">clear</span> /></span></span><br><span class="line"> <span class="comment"><!-- htpt => https --></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">name</span>=<span class="string">"demo-https"</span> <span class="attr">enabled</span>=<span class="string">"true"</span> <span class="attr">stopProcessing</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">match</span> <span class="attr">url</span>=<span class="string">"(.*)"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">conditions</span> <span class="attr">logicalGrouping</span>=<span class="string">"MatchAll"</span> <span class="attr">trackAllCaptures</span>=<span class="string">"false"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">add</span> <span class="attr">input</span>=<span class="string">"{HTTPS}"</span> <span class="attr">pattern</span>=<span class="string">"^OFF$"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">add</span> <span class="attr">input</span>=<span class="string">"{HTTP_HOST}"</span> <span class="attr">pattern</span>=<span class="string">"^demo\.example\.com$"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">conditions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">type</span>=<span class="string">"Redirect"</span> <span class="attr">url</span>=<span class="string">"https://demo.example.com/{R:1}"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">rule</span>></span></span><br><span class="line"> <span class="comment"><!-- https domain/4436 => https domain:4436 --></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">name</span>=<span class="string">"demo"</span> <span class="attr">enabled</span>=<span class="string">"true"</span> <span class="attr">stopProcessing</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">match</span> <span class="attr">url</span>=<span class="string">"^4436/(.*)"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">conditions</span> <span class="attr">logicalGrouping</span>=<span class="string">"MatchAll"</span> <span class="attr">trackAllCaptures</span>=<span class="string">"false"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">add</span> <span class="attr">input</span>=<span class="string">"{HTTPS}"</span> <span class="attr">pattern</span>=<span class="string">"^ON$"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">add</span> <span class="attr">input</span>=<span class="string">"{HTTP_HOST}"</span> <span class="attr">pattern</span>=<span class="string">"^demo\.example\.com$"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">conditions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">type</span>=<span class="string">"Rewrite"</span> <span class="attr">url</span>=<span class="string">"https://demo.example.com:4436/{R:1}"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">rule</span>></span></span><br><span class="line"> <span class="comment"><!-- domain => https://localhost --></span></span><br><span class="line"> <span class="tag"><<span class="name">rule</span> <span class="attr">name</span>=<span class="string">"index"</span> <span class="attr">stopProcessing</span>=<span class="string">"true"</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">match</span> <span class="attr">url</span>=<span class="string">"(.*)"</span> /></span></span><br><span class="line"> <span class="tag"><<span class="name">conditions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">add</span> <span class="attr">input</span>=<span class="string">"{HTTP_HOST}"</span> <span class="attr">pattern</span>=<span class="string">"^test\.example\.com$"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">conditions</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">action</span> <span class="attr">type</span>=<span class="string">"Rewrite"</span> <span class="attr">url</span>=<span class="string">"https://localhost"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">rule</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">rules</span>></span></span><br><span class="line"> <span class="tag"></<span class="name">rewrite</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">caching</span> <span class="attr">enabled</span>=<span class="string">"false"</span> <span class="attr">enableKernelCache</span>=<span class="string">"false"</span> /></span></span><br><span class="line"> <span class="tag"></<span class="name">system.webServer</span>></span></span><br><span class="line"><span class="tag"></<span class="name">configuration</span>></span></span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>IIS 进行 反向代理和 URL 重写——实现 https 重定向,文件类型隐藏访问重写,nodejs 等服务重写等等</p></blockquote>
</summary>
<category term="iis" scheme="https://blog.spiritling.cn/categories/iis/"/>
<category term="iis" scheme="https://blog.spiritling.cn/tags/iis/"/>
<category term="反向代理" scheme="https://blog.spiritling.cn/tags/reverse-proxy/"/>
<category term="url重写" scheme="https://blog.spiritling.cn/tags/url-rewrite-zh/"/>
<category term="url rewrite" scheme="https://blog.spiritling.cn/tags/url-rewrite/"/>
</entry>
<entry>
<title>koa2 相关的记录</title>
<link href="https://blog.spiritling.cn/articles/44cfd7d1-0ab5-40ad-9550-ce57460d1403/"/>
<id>https://blog.spiritling.cn/articles/44cfd7d1-0ab5-40ad-9550-ce57460d1403/</id>
<published>2020-01-03T08:23:28.000Z</published>
<updated>2020-01-03T11:17:02.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>koa2 相关的记录 第一节</p></blockquote><a id="more"></a><h2 id="koa-如何优雅的手动中断请求"><a href="#koa-如何优雅的手动中断请求" class="headerlink" title="koa 如何优雅的手动中断请求"></a>koa 如何优雅的手动中断请求</h2><h3 id="事件起因"><a href="#事件起因" class="headerlink" title="事件起因"></a>事件起因</h3><p>在开发自己的blog系统时,后端采用了koa2来处理,在使用过程中可以算是比较完美和舒服了。</p><p>但是在我进行一个最初的登录api时出现了一个问题,以下是这个api处理逻辑。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!username || !password) {</span><br><span class="line"> <span class="comment">// 判断不过,直接结束后续操作,不想使用 if else 就是因为嫌看起来太麻烦了</span></span><br><span class="line">}</span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">let</span> user = <span class="keyword">await</span> db.user.findOne({</span><br><span class="line"> where : {</span><br><span class="line"> username</span><br><span class="line"> }</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!user) {</span><br><span class="line"> <span class="comment">// 用户不存在,则也直接结束</span></span><br><span class="line"> }</span><br><span class="line"> <span class="comment">// 如果存在直接返回用户</span></span><br><span class="line"> ctx.body = user;</span><br><span class="line">} <span class="keyword">catch</span> (err) {</span><br><span class="line"> logger.error(err);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>我无法提前结束本次的请求,不论使用 <code>return;</code> <code>return next()</code> … 等等</p><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p>在我进行官网查询时,发现可以使用 nodejs 原生的 <code>res.end()</code> 来结束</p><p>在官方文档中 <a href="https://github.com/koajs/koa/blob/master/docs/api/context.md#ctxres" rel="external nofollow noopener noreferrer" target="_blank">ctx.res 是 Nodejs 的 response</a>,但是问题是并不支持 <code>res.end()</code>,也就是 <code>ctx.res.end()</code> 函数。</p><hr><blockquote class="blockquote-center"><p>官方文档</p></blockquote><p>Node’s <code>response</code> object.</p><p>Bypassing Koa’s response handling is <strong>not supported</strong>. Avoid using the following node properties:</p><ul><li><code>res.statusCode</code></li><li><code>res.writeHead()</code></li><li><code>res.write()</code></li><li><code>res.end()</code></li></ul><hr><p>koa 不建议直接结束,于是我继续查找资料,发现了 <a href="https://github.com/koajs/koa/blob/master/docs/api/context.md#ctxthrowstatus-msg-properties" rel="external nofollow noopener noreferrer" target="_blank"><code>koa.throw</code></a> 这个函数,</p><hr><blockquote class="blockquote-center"><p>官方文档</p></blockquote><p>ctx.throw([status], [msg], [properties])<br>Helper method to throw an error with a <code>.status</code> property<br>defaulting to <code>500</code> that will allow Koa to respond appropriately.<br>The following combinations are allowed:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">ctx.throw(<span class="number">400</span>);</span><br><span class="line">ctx.throw(<span class="number">400</span>, <span class="string">'name required'</span>);</span><br><span class="line">ctx.throw(<span class="number">400</span>, <span class="string">'name required'</span>, { <span class="attr">user</span>: user });</span><br></pre></td></tr></table></figure><p>For example <code>ctx.throw(400, 'name required')</code> is equivalent to:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> err = <span class="keyword">new</span> <span class="built_in">Error</span>(<span class="string">'name required'</span>);</span><br><span class="line">err.status = <span class="number">400</span>;</span><br><span class="line">err.expose = <span class="literal">true</span>;</span><br><span class="line"><span class="keyword">throw</span> err;</span><br></pre></td></tr></table></figure><p>Note that these are user-level errors and are flagged with<br><code>err.expose</code> meaning the messages are appropriate for<br>client responses, which is typically not the case for<br>error messages since you do not want to leak failure<br>details.</p><p>You may optionally pass a <code>properties</code> object which is merged into the error as-is, useful for decorating machine-friendly errors which are reported to the requester upstream.</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">ctx.throw(<span class="number">401</span>, <span class="string">'access_denied'</span>, { <span class="attr">user</span>: user });</span><br></pre></td></tr></table></figure><p>Koa uses <a href="https://github.com/jshttp/http-errors" rel="external nofollow noopener noreferrer" target="_blank">http-errors</a> to create errors. <code>status</code> should only be passed as the first parameter.</p><hr><p>这个函数可以抛出错误,并<strong>结束本次请求</strong>,而 <code>ctx,throw</code> 其实就是一个错误类型,基于 js 最原始的 Error 类型,所以我们也可以使用 <code>throw new Error</code> 来手动抛出自定义错误。</p><p>并且通过 koa 中的 next 来,可以将错误拦截住,并进行进一步处理</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">next().catch(<span class="function">(<span class="params">err</span>)=></span>{ </span><br><span class="line"> <span class="comment">// do something </span></span><br><span class="line">});</span><br></pre></td></tr></table></figure><p>所以,接下来开始尝试更改和定义</p><h3 id="修改代码"><a href="#修改代码" class="headerlink" title="修改代码"></a>修改代码</h3><p>首先实现自定义错误</p><p>将自定义错误类型继承到标准 Error 类下</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> util = <span class="built_in">require</span>(<span class="string">'util'</span>);</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">CustomError</span>(<span class="params">code, data = null</span>) </span>{</span><br><span class="line"> <span class="built_in">Error</span>.call(<span class="keyword">this</span>, <span class="string">''</span>);</span><br><span class="line"> <span class="keyword">this</span>.code = code;</span><br><span class="line"> <span class="keyword">this</span>.body = data;</span><br><span class="line"> <span class="keyword">this</span>.getInfo = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> code : <span class="keyword">this</span>.code,</span><br><span class="line"> body : <span class="keyword">this</span>.body,</span><br><span class="line"> };</span><br><span class="line"> };</span><br><span class="line">}</span><br><span class="line"><span class="comment">// inherits https://nodejs.org/docs/latest/api/util.html#util_util_inherits_constructor_superconstructor</span></span><br><span class="line">util.inherits(CustomError, <span class="built_in">Error</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = CustomError;</span><br></pre></td></tr></table></figure><p>可以使用 ES6 的类继承(<a href="http://es6.ruanyifeng.com/#docs/class-extends" rel="external nofollow noopener noreferrer" target="_blank">不懂的可以看阮一峰的es6教程</a>)</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">CustomError</span> <span class="keyword">extends</span> <span class="title">Error</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span> (code, data = null) {</span><br><span class="line"> <span class="keyword">super</span>();</span><br><span class="line"> <span class="keyword">this</span>.code = code;</span><br><span class="line"> <span class="keyword">this</span>.body = data;</span><br><span class="line"> }</span><br><span class="line"> getInfo(){</span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> code : <span class="keyword">this</span>.code,</span><br><span class="line"> body : <span class="keyword">this</span>.body,</span><br><span class="line"> };</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"><span class="built_in">module</span>.exports = CustomError;</span><br></pre></td></tr></table></figure><p>自定义错误函数,可以自己增删参数。</p><p>如果想方便使用,不用每次导入,可以在每次请求进入后在中间件中处理</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> CustomError = <span class="built_in">require</span>(<span class="string">'../utils/customError'</span>);</span><br><span class="line"><span class="built_in">module</span>.exports = <span class="keyword">async</span> (ctx, next) => {</span><br><span class="line"> ctx.custom = {</span><br><span class="line"> error : CustomError,</span><br><span class="line"> };</span><br><span class="line"> <span class="keyword">await</span> next();</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>然后在逻辑层可以这样使用</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (!username || !password) {</span><br><span class="line"> <span class="comment">// 想结束本次请求,可以直接抛出即可</span></span><br><span class="line"> <span class="keyword">throw</span> <span class="keyword">new</span> ctx.custom.error(<span class="number">200</span>, {<span class="attr">message</span> : <span class="string">'请输入用户名和密码'</span>});</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>既然请求都结束了,那么接下来得处理koa,因为koa一旦捕获错误后,会自动处理成 <code>4xx</code> 或者 <code>5xx</code> 状态码;</p><p>当你的网站出现很多的 <code>4xx</code> 和 <code>5xx</code>,对搜索引擎优化来说就会出现问题。</p><p>所以,那么接下来就我们自己处理 koa 的捕获。</p><p>使用 <code>next().catch()</code> 来捕获错误</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = <span class="keyword">async</span> (ctx, next) => {</span><br><span class="line"> <span class="keyword">return</span> next().catch(<span class="function">(<span class="params">err</span>)=></span>{</span><br><span class="line"> ctx.status = err.code;</span><br><span class="line"> ctx.body = err.body;</span><br><span class="line"> });</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>捕获后,我们将 <code>err</code> 中的 <code>code</code> 赋值给 <code>ctx.status</code> ,改变 koa 的响应状态,同理,将 <code>err</code> 中的需要返回的数据赋值给 koa 的返回值</p><p>然后,我们就可以在前端收到一个200的结果。</p><h3 id="完成"><a href="#完成" class="headerlink" title="完成"></a>完成</h3><p>这样就可以手动优雅的结束此次请求。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>koa2 相关的记录 第一节</p></blockquote>
</summary>
<category term="NodeJS" scheme="https://blog.spiritling.cn/categories/NodeJS/"/>
<category term="koa" scheme="https://blog.spiritling.cn/categories/koa/"/>
<category term="koa" scheme="https://blog.spiritling.cn/tags/koa/"/>
</entry>
<entry>
<title>Hexo 文章图片添加水印,不用云处理</title>
<link href="https://blog.spiritling.cn/articles/72dc68b0/"/>
<id>https://blog.spiritling.cn/articles/72dc68b0/</id>
<published>2019-12-20T08:16:02.000Z</published>
<updated>2019-12-20T08:17:02.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Hexo 文章图片添加水印,不用云处理,本地每次构建进行处理</p></blockquote><a id="more"></a><p>由于网上找到的都是借用第三方云处理添加水印,但是我不太想用,所以自己开发了一个插件</p><p><a href="https://github.com/SpiritLing/hexo-images-watermark" rel="external nofollow noopener noreferrer" target="_blank">Hexo 图片添加水印Github地址</a></p><p>目前插件可以直接在 <code>hexo</code> 官网上搜索到</p><p>下面内容都是在 <code>Github</code> 上复制过来的,我的正式版博客目前挂载在我的 <code>Github</code> 仓库 <code>blog</code></p><img src="/articles/72dc68b0/5917f3d3862c9.png" title="演示图片"><hr><p><a href="https://travis-ci.com/SpiritLing/hexo-images-watermark" rel="external nofollow noopener noreferrer" target="_blank"><img src="https://travis-ci.com/SpiritLing/hexo-images-watermark.svg?branch=master" alt="Build Status"></a><a href="https://www.npmjs.com/package/hexo-images-watermark" rel="external nofollow noopener noreferrer" target="_blank"><img src="https://img.shields.io/npm/v/hexo-images-watermark?label=npm%20version" alt="npm version"></a><a href="https://www.npmjs.com/package/hexo-images-watermark" rel="external nofollow noopener noreferrer" target="_blank"><img src="https://img.shields.io/npm/dm/hexo-images-watermark?label=npm%20downloads" alt="npm package download"></a><a href="https://spdx.org/licenses/GPL-3.0-only.html" rel="external nofollow noopener noreferrer" target="_blank"><img src="https://img.shields.io/npm/l/hexo-images-watermark" alt="NPM License"></a></p><p>一款用于 Hexo 静态博客网站生成时对图片添加水印。</p><p>不对原图产生任何影响,在网站静态页构建过程中将原图读取,输出添加了水印的图片。</p><p>在构建的静态网站中不会存在原图,只存在水印图片。</p><blockquote><p>原始文章图片</p></blockquote><p><a href="https://github.com/SpiritLing/hexo-images-watermark/blob/master/static/origin_image.png" rel="external nofollow noopener noreferrer" target="_blank"><img src="https://github.com/SpiritLing/hexo-images-watermark/blob/master/static/origin_image.png" alt="原图,如果图片不显示请前往Github查看"></a></p><blockquote><p>水印图片</p></blockquote><p><img src="./static/change_image.png" alt="水印图,如果图片不显示请前往Github查看"></p><blockquote><p><strong>一定要阅读最后的提示事项,包含现有版本的支持情况和即将添加的功能</strong></p></blockquote><p>使用 <code>npm</code> 安装插件</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-images-watermark --save</span><br></pre></td></tr></table></figure><p>在站点配置文件 <code>_config.yml</code> 中进行如下配置:</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">watermark:</span></span><br><span class="line"> <span class="attr">enable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">textEnable:</span> <span class="literal">true</span></span><br><span class="line"> <span class="attr">rotate:</span> <span class="number">-30</span></span><br><span class="line"> <span class="attr">gravity:</span> <span class="string">centre</span></span><br></pre></td></tr></table></figure><p>在 <code>hexo generate</code> 运行时会自动为你的 <code>_post</code> 目录下的图片添加水印,新的图片将会放到 <code>public</code> 目录中对应的位置。</p><h2 id="必备配置参数"><a href="#必备配置参数" class="headerlink" title="必备配置参数"></a>必备配置参数</h2><h3 id="enable"><a href="#enable" class="headerlink" title="enable"></a><code>enable</code></h3><p><strong>默认值</strong>:无<br><strong>说明</strong>:<code>true</code> 将会执行图片添加水印,<code>false</code> 将会不执行添加</p><h3 id="textEnable"><a href="#textEnable" class="headerlink" title="textEnable"></a><code>textEnable</code></h3><p><strong>默认值</strong>:false<br><strong>说明</strong>:是否使用文本来添加水印(❌警告:目前不支持文本和图片同时添加水印)</p><h3 id="imageEnable"><a href="#imageEnable" class="headerlink" title="imageEnable"></a><code>imageEnable</code></h3><p><strong>默认值</strong>:false<br><strong>说明</strong>:是否使用图片来添加水印(❌警告:目前不支持文本和图片同时添加水印)</p><h2 id="其他参数"><a href="#其他参数" class="headerlink" title="其他参数"></a>其他参数</h2><h3 id="text"><a href="#text" class="headerlink" title="text"></a><code>text</code></h3><p><strong>默认值</strong>:使用配置文件中的url,一旦url不存在直接显示作者名字(SpiritLing)<br><strong>说明</strong>:当你使用的文字过长时,一旦转为图片后大于待加水印的图片尺寸,则会出现错误。</p><h3 id="fontPath"><a href="#fontPath" class="headerlink" title="fontPath"></a><code>fontPath</code></h3><p><strong>默认值</strong>:undefined,使用 <code>text-to-svg</code> 自带字体<br><strong>说明</strong>:自己加载需要的字体,支持单个字体文件,不区分中英文;如果纯中文,请只加载中文字体,例:需要加载 <code>source/static/font/custom.ttf</code> 字体直接将其写入到配置文件中即可</p><h3 id="color"><a href="#color" class="headerlink" title="color"></a><code>color</code></h3><p><strong>默认值</strong>:rgb(169,169,167)<br><strong>说明</strong>:颜色可以使用 rgb,rgba,#xxxxxx以及red名字式的。⚠️只对text有效</p><h3 id="gravity"><a href="#gravity" class="headerlink" title="gravity"></a><code>gravity</code></h3><p><strong>默认值</strong>:southeast<br><strong>说明</strong>:设置水印位置处于什么方向,以 <code>上北下南左东右西</code> 来确定</p><p>参数可用值:</p><table><thead><tr><th>类型</th><th>说明</th><th>备注</th></tr></thead><tbody><tr><td>centre</td><td>中央</td><td>图片正中间</td></tr><tr><td>north</td><td>北</td><td>上方中间</td></tr><tr><td>west</td><td>东</td><td>左边中间</td></tr><tr><td>south</td><td>南</td><td>下边中间</td></tr><tr><td>east</td><td>西</td><td>右边中间</td></tr><tr><td>northwest</td><td>东北</td><td>左上角</td></tr><tr><td>southwest</td><td>东南</td><td>左下角</td></tr><tr><td>southeast</td><td>西南</td><td>右下角</td></tr><tr><td>northeast</td><td>西北</td><td>右上角</td></tr></tbody></table><h3 id="fontSize"><a href="#fontSize" class="headerlink" title="fontSize"></a><code>fontSize</code></h3><p><strong>默认值</strong>:18<br><strong>说明</strong>:文本字体大小,⚠️只对text有效</p><h3 id="watermarkImage"><a href="#watermarkImage" class="headerlink" title="watermarkImage"></a><code>watermarkImage</code></h3><p><strong>默认值</strong>:watermark.png<br><strong>说明</strong>:水印图片,放置在source文件根路径的图片名称;⚠️大小不要超过任何一张文章中的图片,否则会出错,可以搭配缩放进行使用</p><h3 id="width"><a href="#width" class="headerlink" title="width"></a><code>width</code></h3><p><strong>默认值</strong>:50<br><strong>说明</strong>:对图片进行缩放。⚠️只对image有效</p><h3 id="height"><a href="#height" class="headerlink" title="height"></a><code>height</code></h3><p><strong>默认值</strong>:50<br><strong>说明</strong>:对图片进行缩放。⚠️只对image有效</p><h3 id="rotate"><a href="#rotate" class="headerlink" title="rotate"></a><code>rotate</code></h3><p><strong>默认值</strong>:0<br><strong>说明</strong>:旋转角度,如<code>45</code>代表逆时针45度,<code>-45</code>代表顺时针45度</p><h3 id="background"><a href="#background" class="headerlink" title="background"></a><code>background</code></h3><p><strong>默认值</strong>:transparent<br><strong>说明</strong>:配合text和rotate使用,指的是文字转成的图片一旦旋转会出现多余空白,设置这些地方的颜色,一般透明色即可</p><h2 id="使用注意事项⚠️⚠️⚠️"><a href="#使用注意事项⚠️⚠️⚠️" class="headerlink" title="使用注意事项⚠️⚠️⚠️"></a>使用注意事项⚠️⚠️⚠️</h2><ol><li>文字不要过长过大,水印图片不要过大(太大可以使用缩放,⚠️但是缩放是全局性的),否则都会出现错误,终止生成静态页面</li><li>暂不支持图片和文字共同添加</li><li>目前只支持 <code>*.jpg</code>,<code>*.jpeg</code>,<code>*.png</code> 两种格式图片,并且只支持<code>source/_posts</code>文件夹下的图片,也就是文章本地图片;同时也不支持远程图片</li><li>水印图片也不支持远程和非soucre根路径下的文件,也是只支持 <code>*.jpg</code>,<code>*.jpeg</code>,<code>*.png</code> 两种格式图片</li><li>不支持循环满图添加水印</li><li>请使用 <code>1.1.x</code> 以上版本, <code>1.0.x</code> 是进行Hexo api相关测试时使用的,版本杂乱无章,使用 <code>1.0.x</code> 版本出现任何问题,概不理会</li></ol><h2 id="TODO-LIST"><a href="#TODO-LIST" class="headerlink" title="TODO LIST"></a>TODO LIST</h2><ul><li><input checked disabled type="checkbox"> 文字水印<ul><li><input checked disabled type="checkbox"> 自定义文字,颜色,大小</li><li><input checked disabled type="checkbox"> 自定义字体 - 2019-12-24 Done</li><li><input disabled type="checkbox"> 支持循环添加</li><li><input disabled type="checkbox"> 超限处理</li></ul></li><li><input checked disabled type="checkbox"> 图片水印<ul><li><input checked disabled type="checkbox"> 自定义水印图片</li><li><input disabled type="checkbox"> 远程水印图片</li><li><input disabled type="checkbox"> 支持循环添加</li><li><input disabled type="checkbox"> 超限处理</li></ul></li><li><input checked disabled type="checkbox"> 位置<ul><li><input checked disabled type="checkbox"> 固定位置:九个方位</li><li><input disabled type="checkbox"> 自定义 top,left</li></ul></li><li><input checked disabled type="checkbox"> 旋转</li><li><input checked disabled type="checkbox"> 缩放(仅限图片)</li><li><input disabled type="checkbox"> 其他格式支持</li></ul>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Hexo 文章图片添加水印,不用云处理,本地每次构建进行处理</p></blockquote>
</summary>
<category term="Hexo 系列" scheme="https://blog.spiritling.cn/categories/hexo-series/"/>
<category term="Hexo" scheme="https://blog.spiritling.cn/tags/Hexo/"/>
<category term="images" scheme="https://blog.spiritling.cn/tags/images/"/>
<category term="plugin" scheme="https://blog.spiritling.cn/tags/plugin/"/>
<category term="watermark" scheme="https://blog.spiritling.cn/tags/watermark/"/>
</entry>
<entry>
<title>Hexo 静态博客网站自动化构建和部署(二)自动构建和部署</title>
<link href="https://blog.spiritling.cn/articles/f35d4de6/"/>
<id>https://blog.spiritling.cn/articles/f35d4de6/</id>
<published>2019-12-18T06:01:33.000Z</published>
<updated>2019-12-18T13:05:45.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Hexo 静态博客网站自动化构建和部署(二)自动构建和部署</p></blockquote><a id="more"></a><p><a href="/posts/b90941ba/">Hexo 静态博客网站自动化构建和部署(一)Hexo 介绍</a><br><a href="/posts/f35d4de6/">Hexo 静态博客网站自动化构建和部署(二)自动构建和部署</a></p><p>想要自动化构建需要 <code>.travis.yml</code> 文件</p><p>想要保存版本,可以自动对其打标签,需要使用到 <code>source/scripts/auto-versioning.js</code> 文件来获取版本号</p><p>想自动构建 Docker 镜像,需要 <code>source/Dockerfile</code> 和 <code>source/config/nginx.conf</code> 文件来构建 Docker 镜像和 nginx 的配置</p><p>这四个主要文件代码片段都在以下地址:</p><ul><li><a href="https://gist.github.com/SpiritLing/306900cfe70f704343fefaef5bb26571" rel="external nofollow noopener noreferrer" target="_blank">gist 代码片段</a> 一般会被限制访问,原因你们懂得</li><li><a href="https://bitbucket.org/spiritling-team/workspace/snippets/Grxjdo" rel="external nofollow noopener noreferrer" target="_blank">bitbucket 代码片段</a> Bitbucket 代码片段,国内可以访问,但是稍微慢点</li></ul><h2 id="Travis-网站自动化构建"><a href="#Travis-网站自动化构建" class="headerlink" title="Travis 网站自动化构建"></a>Travis 网站自动化构建</h2><p><a href="https://travis-ci.com/" rel="external nofollow noopener noreferrer" target="_blank">Travis</a> 网站是一个对开源项目免费提供自动化构建的网站。</p><p>travis 目前只支持 Github 开源项目,其他暂不支持,在 travis 上使用 github 登录,可以直接获取你自己的仓库</p><p>在仓库列表中,每个仓库后面有一个 <code>Trigger a build</code> 按钮,点击这个按钮即可对这个仓库进行自动化构建,当然前提是你的仓库根路径有 <code>.travis.yml</code> 文件。</p><h3 id="travis-yml-文件分析"><a href="#travis-yml-文件分析" class="headerlink" title=".travis.yml 文件分析"></a>.travis.yml 文件分析</h3><h4 id="基本环境"><a href="#基本环境" class="headerlink" title="基本环境"></a>基本环境</h4><p>对于 <code>.travis.yml</code> 文件具体更细的讲解可以之间看<a href="https://docs.travis-ci.com/" rel="external nofollow noopener noreferrer" target="_blank">官方文档</a>,这里只说涉及到的一些问题</p><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">language:</span> <span class="string">node_js</span></span><br><span class="line"><span class="attr">services:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="string">docker</span></span><br><span class="line"><span class="attr">node_js:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="number">12</span></span><br></pre></td></tr></table></figure><p>对于上面部分代码来说:<br>language: 就是说指定什么语言来出来,这里选择 node_js,则会在构建的虚拟环境中使用 nodejs 环境。你可以直接使用 npm 和 node 命令。<br>services: 提供的服务,这里选择 docker,因为我们需要将编译后的静态网站文件打包成镜像,有了这个就可以直接使用 docker 命令<br>node_js: 指定 nodejs 版本</p><h4 id="构建工作"><a href="#构建工作" class="headerlink" title="构建工作"></a>构建工作</h4><figure class="highlight yml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="attr">jobs:</span></span><br><span class="line"> <span class="attr">include:</span></span><br><span class="line"> <span class="bullet">-</span> <span class="attr">stage:</span> <span class="string">xxx</span></span><br><span class="line"> <span class="attr">name:</span> <span class="string">"xxx"</span></span><br><span class="line"> <span class="attr">if:</span> <span class="string">branch</span> <span class="string">=</span> <span class="string">master</span></span><br></pre></td></tr></table></figure><p>建立一个工作,它包含以下阶段,可以分成很多阶段。比如:install,build,deploy 等等阶段<br>stage: 阶段<br>name: 你为这个阶段起的名字,可有可无<br>if: 是否指定那些的分支可以执行,.travis.yml 在不同分支使用同一个文件,所以进行各自分支走各自的构建阶段,共同阶段可以提出来,取消掉 if,目前我的没有提出来,原因在于每个阶段和每个阶段几乎没有相互联系,导致我的需求下无法提取使用</p><p>每种语言都有自己的默认 install 脚本,nodejs 为 <code>npm install</code> ,所以我们这里不执行 install,有构建默认自己执行</p><p>install 和 script 都是由 travis 提供的钩子,在 script 期间 nodejs 默认执行为<code>npm test</code>,但是我们这里写了新的脚本,所以相当于替换掉默认的执行脚本</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line">before_install:install 阶段之前执行</span><br><span class="line">before_script:script 阶段之前执行</span><br><span class="line">after_failure:script 阶段失败时执行</span><br><span class="line">after_success:script 阶段成功时执行</span><br><span class="line">before_deploy:deploy 步骤之前执行</span><br><span class="line">after_deploy:deploy 步骤之后执行</span><br><span class="line">after_script:script 阶段之后执行</span><br></pre></td></tr></table></figure><p>完整的生命周期为:</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">1. before_install</span><br><span class="line">2. install</span><br><span class="line">3. before_script</span><br><span class="line">4. script</span><br><span class="line">5. aftersuccess or afterfailure</span><br><span class="line">5. [OPTIONAL] before_deploy</span><br><span class="line">7. [OPTIONAL] deploy</span><br><span class="line">8. [OPTIONAL] after_deploy</span><br><span class="line">9. after_script</span><br></pre></td></tr></table></figure><h3 id="script-分析"><a href="#script-分析" class="headerlink" title="script 分析"></a>script 分析</h3><h4 id="版本号获取"><a href="#版本号获取" class="headerlink" title="版本号获取"></a>版本号获取</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">VERSION=$(node ./source/scripts/auto-versioning.js)</span><br></pre></td></tr></table></figure><p>熟悉 shell 的应该不陌生,这条命令就是将 node 运行 js 文件输出的内容赋值到临时变量 <code>VERSION</code> 上,实际查看这个脚本文件,其实就是获取 package.json 中 version 的值,所以你如果觉得这个阶段结束了,需要保存下,则在下阶段开始的第一次将 version 改为其他版本号,则就会生成新的 tag,如果一直不修改,则会每次构建都会覆盖掉远程仓库的旧 tag。</p><h4 id="自动打-Tag"><a href="#自动打-Tag" class="headerlink" title="自动打 Tag"></a>自动打 Tag</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git tag -a -f v$VERSION -m "Travis CI Auto Tag `date +"%Y-%m-%d %H:%M:%S"`"</span><br></pre></td></tr></table></figure><p>切记,标签一定要在添加静态文件,切换静态文件分支,push 静态文件之前。命令意思是强制打一个新 tag,无论是否存在,配置 message 信息。</p><h4 id="Tag-push-origin"><a href="#Tag-push-origin" class="headerlink" title="Tag push origin"></a>Tag push origin</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push -f https://${GITHUB_USERNAME}:${GITHUB_PASSWD}@github.com/SpiritLing/blog.git v$VERSION</span><br></pre></td></tr></table></figure><p>强制 push tag 时,远程仓库用户名和密码分别为:<code>GITHUB_USERNAME</code> 和 <code>GITHUB_PASSWD</code> ,这个在 travis 仓库构建设置中可以设置变量,有个选项,默认为加密状态,意思在构建日志中不显示,达到隐藏你的敏感信息,保护账户安全。通过这个方法,你可以在此仓库构建,但是可以推送到任何一个网站一个仓库中去,只需要应用密码也就是 token(每个网站叫法和获取都不相同)。</p><h4 id="Docker-build"><a href="#Docker-build" class="headerlink" title="Docker build"></a>Docker build</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">docker login -u ${DOCKER_USERNAME} --password ${DOCKER_PASSWD}</span><br><span class="line">docker build -t ${DOCKER_RPO}:latest .</span><br><span class="line">docker push ${DOCKER_RPO}</span><br></pre></td></tr></table></figure><p>目前我只在 dev 分支构建中直接使用 Docker 命令来进行,Master 分支我是通过 Docker 网站连接到 github 上,一旦 gh-pages 分支改变,Docker 官网会自动构建响应的镜像。</p><p>首先第一行是 docker 登录命令,一般保险起见,不支持直接使用 docker 网站密码,而是生成 token 来进行登录,所以我这里的 <code>DOCKER_PASSWD</code> 实际上是一个 token 值</p><p>第二行是构建镜像 -t 代表打的标签;<code>DOCKER_RPO</code> 代表标签名,一般为 <code>{Docker UserName}/{Docker Repository}</code> ,这样不用后续 push 时,再更改标签;latest 代表这个镜像版本,一般 Docker 默认都为 latest;<code>.</code> 代表构建当前目录</p><p>第三行是 push 镜像到 Docker 仓库中去,一般如果出现错误,请确认你的 <code>DOCKER_RPO</code> 是否等于 <code>{Docker UserName}/{Docker Repository}</code>,如果不是用户名加仓库名,将无法 push 上去。</p><p>其余脚本和上面类似,一般熟悉一点 linux 操作命令和 git 命令应该都没什么问题,具体参数可以查看官方文档,这里推荐一个<a href="https://man.linuxde.net/" rel="external nofollow noopener noreferrer" target="_blank">Linux 命令查询网站</a></p><h2 id="Docker-构建镜像"><a href="#Docker-构建镜像" class="headerlink" title="Docker 构建镜像"></a>Docker 构建镜像</h2><p>关于<a href="https://www.kancloud.cn/spirit-ling/docker-study" rel="external nofollow noopener noreferrer" target="_blank">Docker 学习</a></p><p>Docker 知识可以查看上面的文档,也可以直接点击站点概览中的 Docker 导航</p><h3 id="Dockerfile-分析"><a href="#Dockerfile-分析" class="headerlink" title="Dockerfile 分析"></a>Dockerfile 分析</h3><figure class="highlight dockerfile"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">FROM</span> nginx</span><br><span class="line"></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> ./config/nginx.conf /etc/nginx/conf.d/default.conf</span></span><br><span class="line"><span class="keyword">COPY</span><span class="bash"> . /usr/share/nginx/html/</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">EXPOSE</span> <span class="number">80</span> <span class="number">443</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">CMD</span><span class="bash"> [<span class="string">"nginx"</span>, <span class="string">"-g"</span>, <span class="string">"daemon off;"</span>]</span></span><br></pre></td></tr></table></figure><p>以上为 Dockerfile 文件内容</p><ul><li>FROM: 通过什么镜像构建,这里直接选择 nginx,毕竟要直接将静态文件当网站访问</li><li>COPY: 复制文件到什么地方去,这里首先将 <code>config/nginx.conf</code> 网站配置文件复制到 <code>Nginx</code> 服务配置目录,直接覆盖掉默认的配置文件 <code>default.conf</code></li><li>COPY: 第二个复制,将当前目录下的所有文件复制到 <code>Nginx</code> 静态网站目录下,当然静态网站目录也是可以在 <code>nginx.conf</code> 中配置的</li><li>EXPOSE 80 443: 对外放开的端口号,一般 <code>Nginx</code> 放通 80 和 443 端口</li><li>CMD [“nginx”, “-g”, “daemon off;”]: CMD 运行命令,一个 Dockerfile 中只允许出现一次,代表运行 nginx 服务</li></ul><h3 id="Docker-部署镜像"><a href="#Docker-部署镜像" class="headerlink" title="Docker 部署镜像"></a>Docker 部署镜像</h3><p>构建完毕并且推送到 Docker 镜像仓库后,可以使用下面命令来启动一个容器</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="meta">#</span><span class="bash"> dev-blog-pages镜像部署,不是开发版的博客网站</span></span><br><span class="line">docker run \</span><br><span class="line">-u root \</span><br><span class="line">--name=dev-blog-pages \</span><br><span class="line">-d \</span><br><span class="line">--restart=on-failure:10 \</span><br><span class="line">-p 80:80 \</span><br><span class="line"><span class="meta">$</span><span class="bash">{Docker UserName}/<span class="variable">${Docker Repository}</span></span></span><br></pre></td></tr></table></figure><p>-p 80:80 代表开放的端口,第一个代表对外放开的端口,也就是公网访问端口,第二个 80 端口指的是你的容器端口,相当于给外层系统和你的容器建立了一个端口映射。</p><p>其他具体命令意思可以直接看我上面的 Docker 文档。</p><h2 id="具体流程"><a href="#具体流程" class="headerlink" title="具体流程"></a>具体流程</h2><p>单分支:</p><ol><li>首先本地 master 分支开发,撰写博客,当前 version:0.0.3</li><li>提交代码到远程仓库</li><li>触发 Travis 自动构建</li><li>首先先强制 push 标签 v0.0.3</li><li>通过复制,移动,删除,将 public 移到最外层,全部添加,push 到 gh-pages 分支上</li><li>github 自动将 gh-pages 生成新的静态网站,Docker 通过 gh-pages 变化来构建新的镜像</li><li>本地撰写下一个阶段 版本改为 0.0.4,可以多次提交,最终此阶段满意为止,换新版本号,继续即可</li></ol><p>只需要本地写好文章,push 到仓库后,什么事情也不需要管,过个几分钟,你的博客网站将会出现新的版本。</p><p>多分支:</p><ul><li>master:发布版 域名:blog.example.com</li><li>dev:开发版 域名:dev.example.com</li></ul><ol><li>本地始终开发 dev 分支,撰写博客,当前版本 0.0.3</li><li>提交代码到远程仓库</li><li>触发 Travis 自动构建</li><li>首先先强制 push 标签 v0.0.3</li><li>通过复制,移动,删除,将 public 移到最外层,全部添加,push 到 dev-blog 分支上(这里我使用的是国内 coding 构建静态网站,自定义域名为:<a href="https://dev.blog.spiritling.pub" target="_blank" rel="noopener">dev.blog.spiritling.pub</a>)</li><li>coding 设置静态网站部署会自动将 dev-blog 生成新的静态网站</li><li>通过 docker 命令构建镜像并推送</li><li>这时候你想给博客网站添加个百度统计,然后修修改改,再次上传</li><li>继续前面的过程,一旦你觉得更改的差不多了,可以发布了</li><li>那么直接将 dev 合并至 master 分支,然后需要反向合并,因为我使用的是 <code>git merge -squash</code> 命令,所以需要反向一次,保证下次合并不会出现问题。</li><li>接下来的步骤就是自动构建自动发布。</li><li>开始新的时,将 version 改为新的,继续新一轮的使用</li></ol>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Hexo 静态博客网站自动化构建和部署(二)自动构建和部署</p></blockquote>
</summary>
<category term="Hexo 系列" scheme="https://blog.spiritling.cn/categories/hexo-series/"/>
<category term="Hexo静态网站自动化构建和部署系列" scheme="https://blog.spiritling.cn/categories/hexo-automated-build-and-deployment/"/>
<category term="Hexo" scheme="https://blog.spiritling.cn/tags/Hexo/"/>
<category term="Next" scheme="https://blog.spiritling.cn/tags/Next/"/>
<category term="Auto CI" scheme="https://blog.spiritling.cn/tags/auto-ci/"/>
<category term="Auto Deploy" scheme="https://blog.spiritling.cn/tags/auto-deploy/"/>
<category term="Github" scheme="https://blog.spiritling.cn/tags/Github/"/>
<category term="Pages" scheme="https://blog.spiritling.cn/tags/Pages/"/>
</entry>
<entry>
<title>Hexo 静态博客网站自动化构建和部署(一)Hexo 介绍</title>
<link href="https://blog.spiritling.cn/articles/b90941ba/"/>
<id>https://blog.spiritling.cn/articles/b90941ba/</id>
<published>2019-12-18T02:17:39.000Z</published>
<updated>2019-12-18T11:05:45.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Hexo 静态博客网站自动化构建和部署(一)Hexo 介绍</p></blockquote><a id="more"></a><p><a href="/posts/b90941ba/">Hexo 静态博客网站自动化构建和部署(一)Hexo 介绍</a><br><a href="/posts/f35d4de6/">Hexo 静态博客网站自动化构建和部署(二)自动构建和部署</a></p><h2 id="前提"><a href="#前提" class="headerlink" title="前提"></a>前提</h2><p>如果你只需要在一个分支开发,一个分支发布,则只需要本地写好后直接提交到远程仓库,然后等待几分钟后,直接看你的网站就会发现已经更新上去了。</p><p>如果你需要和我一样一个开发版一个发布版,那么在自动化构建中只需要多一个手动将 <code>dev</code> 合并至 <code>master</code> 的步骤操作,其余和单分支开发一样都是自动构建自动部署。</p><p>开发推荐使用 Github 来使用,因为接下来的自动化构建的网站一般对 Github 支持较好,还有就是国内国外都可以访问 Github,并且速度还算可以。</p><p>以下是我的博客相关网站,如果需要对源码加密,可以将 Maste源码 和 Master Pages 分支分别存放在私有仓库和公开仓库</p><table><thead><tr><th>名称</th><th>地址</th></tr></thead><tbody><tr><td>Master 源码</td><td><a href="https://github.com/SpiritLing/blog" rel="external nofollow noopener noreferrer" target="_blank">https://github.com/SpiritLing/blog</a></td></tr><tr><td>Dev 源码</td><td><a href="https://github.com/SpiritLing/blog/tree/dev" rel="external nofollow noopener noreferrer" target="_blank">https://github.com/SpiritLing/blog/tree/dev</a></td></tr><tr><td>Master Pages</td><td><a href="https://github.com/SpiritLing/blog/tree/gh-pages" rel="external nofollow noopener noreferrer" target="_blank">https://github.com/SpiritLing/blog/tree/gh-pages</a></td></tr><tr><td>Dev Pages</td><td><a href="https://spiritling.coding.net/p/dev/d/dev/git/tree/dev-blog" rel="external nofollow noopener noreferrer" target="_blank">https://spiritling.coding.net/p/dev/d/dev/git/tree/dev-blog</a></td></tr></tbody></table><h2 id="开始前的工作"><a href="#开始前的工作" class="headerlink" title="开始前的工作"></a>开始前的工作</h2><h3 id="环境和账号"><a href="#环境和账号" class="headerlink" title="环境和账号"></a>环境和账号</h3><p>首先需要准备环境</p><ul><li>Nodejs</li><li>一款编辑器-Vscode</li><li>Github 账号</li><li>Coding 账号(单分支可以不用,如果博客主要面对的是国内,可以使用这个来当静态网站站点)</li></ul><h3 id="本地开发安装"><a href="#本地开发安装" class="headerlink" title="本地开发安装"></a>本地开发安装</h3><p>首先全局安装 Hexo</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install hexo-cli -g</span><br></pre></td></tr></table></figure><p>使用 Hexo 创建一个项目</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">$ hexo init hexo-blog</span><br><span class="line">$ <span class="built_in">cd</span> <folder></span><br><span class="line">$ npm install</span><br></pre></td></tr></table></figure><p>之后大概目录如下所示</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">.</span><br><span class="line">├── _config.yml</span><br><span class="line">├── package.json</span><br><span class="line">├── scaffolds</span><br><span class="line">├── source</span><br><span class="line">| ├── _drafts</span><br><span class="line">| └── _posts</span><br><span class="line">└── themes</span><br></pre></td></tr></table></figure><h2 id="关联-Github-仓库"><a href="#关联-Github-仓库" class="headerlink" title="关联 Github 仓库"></a>关联 Github 仓库</h2><p>创建仓库时,不要选择初始化文件,如果选了,就需要将仓库克隆下来,在将 Hexo 生成的目录内的内容迁移过去</p><p>不选择初始化文件时,在仓库首页会有提示如何添加已有的本地仓库。</p><p>首先进入<code>hexo-blog</code>目录下<br>逐步运行下面命令</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">git init</span><br><span class="line">git add -A</span><br><span class="line">git commit -m "first commit"</span><br></pre></td></tr></table></figure><p>添加完已修改的文件后(切记不要把node_modules加入进去),一般情况下 hexo 创建的初始化项目都含有 <code>.gitignore</code> 文件,其中自动包含 <code>node_modules</code></p><p>接下来需要添加远程仓库地址,有两种协议,一种http一种git</p><p>使用http协议会弹出一个窗口,需要进行 GitHub 账号登录,以后使用 http 就不会再次弹出</p><p>使用git协议需要先在账户中添加本地私钥,才能够下载下来</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">git remote add origin [email protected]:{Your Name}/{Your repository}.git</span><br><span class="line">git remote add origin https://github.com/{Your Name}/{Your repository}.git</span><br></pre></td></tr></table></figure><p>接下来将其推送到远程仓库,并和远程仓库保持连接(-u)</p><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">git push -u origin master</span><br></pre></td></tr></table></figure><p>推送成功后,你的本地和远程就会构建起关联。</p><h2 id="Hexo-文件说明"><a href="#Hexo-文件说明" class="headerlink" title="Hexo 文件说明"></a>Hexo 文件说明</h2><h3 id="config-yml"><a href="#config-yml" class="headerlink" title="_config.yml"></a>_config.yml</h3><p>网站的 <a href="https://hexo.io/zh-cn/docs/configuration" rel="external nofollow noopener noreferrer" target="_blank">官方配置</a> 信息,您可以在此配置大部分的参数。</p><h3 id="package-json"><a href="#package-json" class="headerlink" title="package.json"></a>package.json</h3><p>做过nodejs项目和前端构建的工作的,应该对其不陌生</p><h3 id="scaffolds-文件夹"><a href="#scaffolds-文件夹" class="headerlink" title="scaffolds 文件夹"></a>scaffolds 文件夹</h3><p><a href="https://hexo.io/zh-cn/docs/writing" rel="external nofollow noopener noreferrer" target="_blank">官方模版</a> 文件夹。当您新建文章时,Hexo 会根据 scaffold 来建立文件。</p><p>Hexo的模板是指在新建的文章文件中默认填充的内容。例如,如果您修改scaffold/post.md中的Front-matter内容,那么每次新建一篇文章时都会包含这个修改。</p><h3 id="source-文件夹"><a href="#source-文件夹" class="headerlink" title="source 文件夹"></a>source 文件夹</h3><p>资源文件夹是存放用户资源的地方。除 _posts 文件夹之外,开头命名为 _ (下划线)的文件 / 文件夹和隐藏的文件将会被忽略。Markdown 和 HTML 文件会被解析并放到 public 文件夹,而其他文件会被拷贝过去。</p><h3 id="themes"><a href="#themes" class="headerlink" title="themes"></a>themes</h3><p><a href="https://hexo.io/zh-cn/docs/themes" rel="external nofollow noopener noreferrer" target="_blank">主题</a> 文件夹。Hexo 会根据主题来生成静态页面。</p><h2 id="生成静态网站和其他"><a href="#生成静态网站和其他" class="headerlink" title="生成静态网站和其他"></a>生成静态网站和其他</h2><p>使用Hexo内置的就可以生成静态网站,静态网站放置在 public 文件夹下,其他配置,比如 Next主题,google分析,百度通知,gitalk评论,版权声明等等,在此次不在进一步详细说明。</p><h2 id="自动化构建网站"><a href="#自动化构建网站" class="headerlink" title="自动化构建网站"></a>自动化构建网站</h2><h3 id="Travis-网站自动化构建"><a href="#Travis-网站自动化构建" class="headerlink" title="Travis 网站自动化构建"></a>Travis 网站自动化构建</h3><p><a href="https://travis-ci.com/" rel="external nofollow noopener noreferrer" target="_blank">Travis</a> 网站是一个对开源项目免费提供自动化构建的网站。</p><p>travis 目前只支持 Github 开源项目,其他暂不支持,在 travis 上使用github登录,可以直接获取你自己的仓库</p><p>在仓库列表中,每个仓库后面有一个 <code>Trigger a build</code> 按钮,点击这个按钮即可对这个仓库进行自动化构建,当然前提是你的仓库根路径有 <code>.travis.yml</code> 文件。</p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Hexo 静态博客网站自动化构建和部署(一)Hexo 介绍</p></blockquote>
</summary>
<category term="Hexo 系列" scheme="https://blog.spiritling.cn/categories/hexo-series/"/>
<category term="Hexo静态网站自动化构建和部署系列" scheme="https://blog.spiritling.cn/categories/hexo-automated-build-and-deployment/"/>
<category term="Hexo" scheme="https://blog.spiritling.cn/tags/Hexo/"/>
<category term="Next" scheme="https://blog.spiritling.cn/tags/Next/"/>
<category term="Auto CI" scheme="https://blog.spiritling.cn/tags/auto-ci/"/>
<category term="Auto Deploy" scheme="https://blog.spiritling.cn/tags/auto-deploy/"/>
<category term="Github" scheme="https://blog.spiritling.cn/tags/Github/"/>
<category term="Pages" scheme="https://blog.spiritling.cn/tags/Pages/"/>
</entry>
<entry>
<title>webpack 4.x 初级学习记录</title>
<link href="https://blog.spiritling.cn/articles/83824dfb-5385-43cf-adef-eee12f203749/"/>
<id>https://blog.spiritling.cn/articles/83824dfb-5385-43cf-adef-eee12f203749/</id>
<published>2019-11-28T03:18:09.000Z</published>
<updated>2019-11-28T12:05:45.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>webpack 4.x 初级学习记录</p></blockquote><a id="more"></a><h2 id="webpack-4-x-安装"><a href="#webpack-4-x-安装" class="headerlink" title="webpack 4.x 安装"></a>webpack 4.x 安装</h2><ol><li>首先需要在全局中安装</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install webpack -g</span><br><span class="line">npm install webpack-cli -g // 与webpack 3.x 的区别</span><br></pre></td></tr></table></figure><ol start="2"><li>接下来打开新的文件夹,创建package.json</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm init</span><br></pre></td></tr></table></figure><p>初始化 <code>package.json</code> 文件。</p><ol start="3"><li>局部安装</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">npm install webpack --save</span><br><span class="line">npm install webpack-cli --save</span><br></pre></td></tr></table></figure><h3 id="webpack-4-x-基本打包编译"><a href="#webpack-4-x-基本打包编译" class="headerlink" title="webpack 4.x 基本打包编译"></a>webpack 4.x 基本打包编译</h3><ol><li>webpack 3.x 编译</li></ol><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">webpack a.js b.js</span><br></pre></td></tr></table></figure><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># {extry file}出填写入口文件的路径,本文中就是上述main.js的路径,</span></span><br><span class="line"><span class="comment"># {destination for bundled file}处填写打包文件的存放路径</span></span><br><span class="line"><span class="comment"># 填写路径的时候不用添加{}</span></span><br><span class="line">webpack {entry file} {destination <span class="keyword">for</span> bundled file}</span><br></pre></td></tr></table></figure><p>以上就是4版本之前的使用方式,但是这种方式在4版本中就不能使用了,4版本有自己的新的方式</p><ol start="2"><li>webpack 4.x 默认打包编译</li></ol><p>为什么上面要写默认打包编译,是因为webpack可以自定义打包编译配置,我们首先说下默认的打包编译。</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">entry: "/src/index.js" // 默认入口文件</span><br><span class="line">output: "/dist/main.js" // 默认输入文件</span><br></pre></td></tr></table></figure><p>上面路径及文件中,<code>src</code> 和 <code>index.js</code> 需要我们手动去创建,在 <code>index.js</code> 中写好js代码即可,其余的 <code>dist</code> 和 <code>main.js</code> 都是由系统自动生成的,并且当你再一次编译时,会自动的在 <code>dist</code> 中覆盖同名文件。</p><p>而webpack 4.x 的编译命令也发生变化了,如下所示,分为开发环境和生产环境的命令</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">webpack --mode development</span><br><span class="line">webpack --mode production</span><br></pre></td></tr></table></figure><p>使用命令后,会自动生成文件。</p><p>配置 <code>package.json</code> 文件</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="string">"dev"</span>: <span class="string">"webpack --mode development"</span>,</span><br><span class="line"> <span class="string">"build"</span>: <span class="string">"webpack --mode production"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>可以使用 <code>npm ruin dev</code> 和 <code>npm run build</code> 进行执行命令</p><h2 id="webpack-配置"><a href="#webpack-配置" class="headerlink" title="webpack 配置"></a>webpack 配置</h2><h3 id="概念"><a href="#概念" class="headerlink" title="概念"></a>概念</h3><blockquote><p>本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(static module bundler)。在 webpack 处理应用程序时,它会在内部创建一个依赖图(dependency graph),用于映射到项目需要的每个模块,然后将所有这些依赖生成到一个或多个bundle。</p></blockquote><p>从 webpack 4.0.0 版本开始,可以不用通过引入一个配置文件打包项目。然而,webpack 仍然还是 高度可配置的,并且能够很好的满足需求。</p><p>webpack 的核心概念:</p><ol><li>入口(entry)</li><li>输出(output)</li><li>loader</li><li>插件(plugins)</li></ol><p>我们需要在根目录下创建一个 <code>webpack.config.js</code> 的文件,使用 Commonjs 规范来进行书写。</p><h4 id="入口(entry)"><a href="#入口(entry)" class="headerlink" title="入口(entry)"></a>入口(entry)</h4><p>入口起点(entry point)指示 webpack 应该使用哪个模块,来作为构建其内部依赖图的开始。进入入口起点后,webpack 会找出有哪些模块和库是入口起点(直接和间接)依赖的。</p><p>可以通过在 webpack 配置中配置 <code>entry</code> 属性,来指定一个入口起点(或多个入口起点)。默认值为 <code>./src</code> 。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: <span class="string">"./src/index.js"</span>,</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p><code>entry</code> 属性的单个入口语法,是下面的简写:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: {</span><br><span class="line"> main: <span class="string">"./src/index.js"</span>,</span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p>当你向 <code>entry</code> 传入一个数组时会发生什么?向 <code>entry</code> 属性传入「文件路径(file path)数组」将创建“多个主入口(multi-main entry)”。在你想要多个依赖文件一起注入,并且将它们的依赖导向(graph)到一个“chunk”时,传入数组的方式就很有用。</p></blockquote><p>多个入口文件处理</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: {</span><br><span class="line"> main: <span class="string">"./src/index.js"</span>,</span><br><span class="line"> app: <span class="string">'./src/app.js'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p>根据经验:每个 HTML 文档只使用一个入口起点。 当然也可以使用多个,但是推荐一个使用一个</p></blockquote><p><a href="https://www.webpackjs.com/concepts/entry-points/" rel="external nofollow noopener noreferrer" target="_blank">了解更多</a></p><h4 id="输出(output)"><a href="#输出(output)" class="headerlink" title="输出(output)"></a>输出(output)</h4><p><code>output</code> 属性告诉 <code>webpack</code> 在哪里输出它所创建的 <code>bundles</code>,以及如何命名这些文件,默认值为 <code>./dist</code>。基本上,整个应用程序结构,都会被编译到你指定的输出路径的文件夹中。你可以通过在配置中指定一个 <code>output</code> 字段,来配置这些处理过程:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><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><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> entry: <span class="string">"./src/index.js"</span></span><br><span class="line"> output: {</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line"> filename: <span class="string">'bundle.js'</span></span><br><span class="line"> }</span><br><span class="line">};</span><br></pre></td></tr></table></figure><p>当然上面的位置文件名都是可以改变的,可以自定义配置。</p><p>在上面的示例中,我们通过 <code>output.filename</code> 和 <code>output.path</code> 属性,来告诉 <code>webpack bundle</code> 的名称,以及我们想要 bundle 生成(emit)到哪里</p><p><a href="https://www.webpackjs.com/concepts/output/" rel="external nofollow noopener noreferrer" target="_blank">了解更多</a></p><h4 id="loader"><a href="#loader" class="headerlink" title="loader"></a>loader</h4><p>‘<br>loader 让 webpack 能够去处理那些非 JavaScript 文件(webpack 自身只理解 JavaScript)。loader 可以将所有类型的文件转换为 webpack 能够处理的有效模块,然后你就可以利用 webpack 的打包能力,对它们进行处理。</p><p>本质上,webpack loader 将所有类型的文件,转换为应用程序的依赖图(和最终的 bundle)可以直接引用的模块。</p><blockquote><p>注意,loader 能够 <code>import</code> 导入任何类型的模块(例如 .css 文件),这是 webpack 特有的功能,其他打包程序或任务执行器的可能并不支持。我们认为这种语言扩展是有很必要的,因为这可以使开发人员创建出更准确的依赖关系图。</p></blockquote><p>在更高层面,在 webpack 的配置中 loader 有两个目标:</p><ol><li><code>test</code> 属性,用于标识出应该被对应的 loader 进行转换的某个或某些文件。</li><li><code>use</code> 属性,表示进行转换时,应该使用哪个 loader。</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><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><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: <span class="string">"./src/index.js"</span></span><br><span class="line"> output: {</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>),</span><br><span class="line"> filename: <span class="string">'bundle.js'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> { <span class="attr">test</span>: <span class="regexp">/\.txt$/</span>, <span class="attr">use</span>: <span class="string">'raw-loader'</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="built_in">module</span>.exports = config;</span><br></pre></td></tr></table></figure><p>以上配置中,对一个单独的 module 对象定义了 rules 属性,里面包含两个必须属性:test 和 use。这告诉 webpack 编译器(compiler) 如下信息:</p><blockquote><p>“嘿,webpack 编译器,当你碰到「在 require()/import 语句中被解析为 ‘.txt’ 的路径」时,在你对它打包之前,先使用 raw-loader 转换一下。”</p></blockquote><p><a href="https://www.webpackjs.com/concepts/loaders/" rel="external nofollow noopener noreferrer" target="_blank">了解更多</a></p><h4 id="插件(plugins)"><a href="#插件(plugins)" class="headerlink" title="插件(plugins)"></a>插件(plugins)</h4><p>loader 被用于转换某些类型的模块,而插件则可以用于执行范围更广的任务。插件的范围包括,从打包优化和压缩,一直到重新定义环境中的变量。插件接口功能极其强大,可以用来处理各种各样的任务。</p><p>想要使用一个插件,你只需要 <code>require()</code> 它,然后把它添加到 <code>plugins</code> 数组中。多数插件可以通过选项(option)自定义。你也可以在一个配置文件中因为不同目的而多次使用同一个插件,这时需要通过使用 <code>new</code> 操作符来创建它的一个实例。</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>); <span class="comment">// 通过 npm 安装</span></span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>); <span class="comment">// 用于访问内置插件</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> <span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> { <span class="attr">test</span>: <span class="regexp">/\.txt$/</span>, <span class="attr">use</span>: <span class="string">'raw-loader'</span> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> webpack.optimize.UglifyJsPlugin(),</span><br><span class="line"> <span class="keyword">new</span> HtmlWebpackPlugin({<span class="attr">template</span>: <span class="string">'./src/index.html'</span>})</span><br><span class="line"> ]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config;</span><br></pre></td></tr></table></figure><p>webpack 提供许多开箱可用的插件!查阅<a href="https://www.webpackjs.com/plugins/" rel="external nofollow noopener noreferrer" target="_blank">插件列表</a>获取更多信息。</p><p>在 webpack 配置中使用插件是简单直接的,然而也有很多值得我们进一步探讨的用例。</p><p><a href="https://www.webpackjs.com/concepts/plugins/" rel="external nofollow noopener noreferrer" target="_blank">了解更多</a></p><h2 id="webpack-dev-server"><a href="#webpack-dev-server" class="headerlink" title="webpack-dev-server"></a>webpack-dev-server</h2><p>本地服务器</p><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install webpack-dev-server -S</span><br></pre></td></tr></table></figure><h3 id="基本概念"><a href="#基本概念" class="headerlink" title="基本概念"></a>基本概念</h3><p>可以构建一个本地服务器进行启动测试</p><h3 id="配置webpack-config-js"><a href="#配置webpack-config-js" class="headerlink" title="配置webpack.config.js"></a>配置webpack.config.js</h3><p>webpack.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">devServer: {</span><br><span class="line"> contentBase: path.join(__dirname, <span class="string">"/dist"</span>), <span class="comment">//启动路径</span></span><br><span class="line"> port: <span class="number">9001</span>, <span class="comment">// 端口号</span></span><br><span class="line"> hot: <span class="literal">true</span>, <span class="comment">// 热更新</span></span><br><span class="line"> inline:<span class="literal">true</span> <span class="comment">// 内联模式</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>当然在使用上面 <code>hot</code> 热更新时需要开启一个插件 <code>HotModuleReplacementPlugin</code> 此插件属于内置插件,可以直接使用 <code>new webpack.HotModuleReplacementPlugin()</code> 来进行启用</p><p>以上使 <code>webpack-dev-server</code> 的基本参数用法,具体的可以查看<a href="https://www.webpackjs.com/configuration/dev-server/" rel="external nofollow noopener noreferrer" target="_blank">此处</a></p><h3 id="配置package-json"><a href="#配置package-json" class="headerlink" title="配置package.json"></a>配置package.json</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="string">"scripts"</span>: {</span><br><span class="line"> <span class="string">"start"</span>: <span class="string">"webpack-dev-server --open"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>使用 <code>npm start</code> 开启启动命令</p><h2 id="webpack-loader处理"><a href="#webpack-loader处理" class="headerlink" title="webpack loader处理"></a>webpack loader处理</h2><p>loader : 加载程序</p><p>loader 用于对模块的源代码进行转换。loader 可以使你在 import 或”加载”模块时预处理文件。因此,loader 类似于其他构建工具中“任务(task)”,并提供了处理前端构建步骤的强大方法。loader 可以将文件从不同的语言(如 TypeScript)转换为 JavaScript,或将内联图像转换为 data URL。loader 甚至允许你直接在 JavaScript 模块中 import CSS文件!</p><h3 id="安装-1"><a href="#安装-1" class="headerlink" title="安装"></a>安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install css-loader style-loader -S</span><br></pre></td></tr></table></figure><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><p>当新建文件 <code>*.css</code> 文件时进行css文件处理</p><p>webpack.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>:{</span><br><span class="line"> rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.css$/</span>,</span><br><span class="line"> use:[<span class="string">'style-loader'</span>,<span class="string">'css-loader'</span>]</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>在其中需要注意的就是 <code>style-loader</code> 在 <code>css-loader</code> 之前。</p><p>当 css 有 <code>background-image: url('./1.jpg')</code> 有图片插入进来时,需要使用 <code>file-loader</code> 来进行处理</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>:{</span><br><span class="line"> rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.css$/</span>,</span><br><span class="line"> use:[<span class="string">'style-loader'</span>,<span class="string">'css-loader'</span>]</span><br><span class="line"> },</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.(jpg|png|jpeg)$/</span>,</span><br><span class="line"> use:[<span class="string">'file-loader'</span>]</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>设置图片保存地方及是否使用base64进行处理</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> test:<span class="regexp">/\.(jpg|png|jpeg)$/</span>,</span><br><span class="line"> use:<span class="string">'file-loader?limit=1024&name=./images/[hash:8].[name].[ext]'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="HTML的img标记处理"><a href="#HTML的img标记处理" class="headerlink" title="HTML的img标记处理"></a>HTML的img标记处理</h3><h4 id="安装-2"><a href="#安装-2" class="headerlink" title="安装"></a>安装</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install html-withimg-loader -S</span><br></pre></td></tr></table></figure><h4 id="配置-1"><a href="#配置-1" class="headerlink" title="配置"></a>配置</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> test:/\.html$/,</span><br><span class="line"> use:["html-withimg-loader"]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="CSS-打包分离"><a href="#CSS-打包分离" class="headerlink" title="CSS 打包分离"></a>CSS 打包分离</h3><h4 id="安装-3"><a href="#安装-3" class="headerlink" title="安装"></a>安装</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install extract-text-webpack-plugin@next -S</span><br></pre></td></tr></table></figure><h4 id="配置-2"><a href="#配置-2" class="headerlink" title="配置"></a>配置</h4><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> ExtractTextPlugin=<span class="built_in">require</span>(<span class="string">'extract-text-webpack-plugin'</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">//插件</span></span><br><span class="line"><span class="keyword">new</span> ExtractTextPlugin(<span class="string">'./css/[name].css'</span>)</span><br><span class="line"></span><br><span class="line"><span class="comment">// rules</span></span><br><span class="line">{</span><br><span class="line"> test:<span class="regexp">/\.css$/</span>,</span><br><span class="line"> use:ExtractTextPlugin.extract({</span><br><span class="line"> fallback:<span class="string">"style-loader"</span>,</span><br><span class="line"> use:[{</span><br><span class="line"> loader:<span class="string">"css-loader"</span>,</span><br><span class="line"> options:{</span><br><span class="line"> <span class="comment">// 压缩</span></span><br><span class="line"> minimize:<span class="literal">true</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"> publicPath:<span class="string">"../"</span></span><br><span class="line"> })</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="webpack-插件"><a href="#webpack-插件" class="headerlink" title="webpack 插件"></a>webpack 插件</h2><p>插件是 webpack 的支柱功能。webpack 自身也是构建于,你在 webpack 配置中用到的相同的插件系统之上!</p><p>插件目的在于解决 loader 无法实现的其他事。</p><h3 id="剖析"><a href="#剖析" class="headerlink" title="剖析"></a>剖析</h3><p>webpack 插件是一个具有 apply 属性的 JavaScript 对象。apply 属性会被 webpack compiler 调用,并且 compiler 对象可在整个编译生命周期访问。</p><p><strong>ConsoleLogOnBuildWebpackPlugin.js</strong></p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> pluginName = <span class="string">'ConsoleLogOnBuildWebpackPlugin'</span>;</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ConsoleLogOnBuildWebpackPlugin</span> </span>{</span><br><span class="line"> apply(compiler) {</span><br><span class="line"> compiler.hooks.run.tap(pluginName, compilation => {</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="string">"webpack 构建过程开始!"</span>);</span><br><span class="line"> });</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>compiler hook 的 tap 方法的第一个参数,应该是驼峰式命名的插件名称。建议为此使用一个常量,以便它可以在所有 hook 中复用。</p><h3 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h3><p>由于插件可以携带参数/选项,你必须在 webpack 配置中,向 <code>plugins</code> 属性传入 <code>new</code> 实例。</p><p>根据你的 webpack 用法,这里有多种方式使用插件。</p><h3 id="配置-3"><a href="#配置-3" class="headerlink" title="配置"></a>配置</h3><p>webpack.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> HtmlWebpackPlugin = <span class="built_in">require</span>(<span class="string">'html-webpack-plugin'</span>); <span class="comment">//通过 npm 安装</span></span><br><span class="line"><span class="keyword">const</span> webpack = <span class="built_in">require</span>(<span class="string">'webpack'</span>); <span class="comment">//访问内置的插件</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><br><span class="line"><span class="keyword">const</span> config = {</span><br><span class="line"> entry: <span class="string">'./path/to/my/entry/file.js'</span>,</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'my-first-webpack.bundle.js'</span>,</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>)</span><br><span class="line"> },</span><br><span class="line"> <span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.(js|jsx)$/</span>,</span><br><span class="line"> use: <span class="string">'babel-loader'</span></span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> webpack.optimize.UglifyJsPlugin(),</span><br><span class="line"> <span class="keyword">new</span> HtmlWebpackPlugin({<span class="attr">template</span>: <span class="string">'./src/index.html'</span>})</span><br><span class="line"> ]</span><br><span class="line">};</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = config;</span><br></pre></td></tr></table></figure><p>当然上面的 <code>HtmlWebpackPlugin</code> 插件只使用了基本的功能,更多的参数可以去github上查看。</p><h2 id="webpack-babel"><a href="#webpack-babel" class="headerlink" title="webpack babel"></a>webpack babel</h2><h3 id="安装-4"><a href="#安装-4" class="headerlink" title="安装"></a>安装</h3><p>核心<br>babel-core</p><p>功能<br>babel-loader<br>babel-preset-env<br>babel-preset-react</p><h4 id="babel-loader-7-x-版本安装"><a href="#babel-loader-7-x-版本安装" class="headerlink" title="babel-loader 7.x 版本安装"></a>babel-loader 7.x 版本安装</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install babel-core babel-loader@7 babel-preset-env babel-preset-react --save</span><br></pre></td></tr></table></figure><p>安装的 <code>babel-loader</code> 是7.x版本,8.x版本目前会出现报错,具体如何解决还没有了解清楚,所以安装 <code>babel-loader</code> 时需要写成这样的 <code>babel-loader@7</code></p><h4 id="babel-loader-8-x-版本安装"><a href="#babel-loader-8-x-版本安装" class="headerlink" title="babel-loader 8.x 版本安装"></a>babel-loader 8.x 版本安装</h4><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install -D babel-loader @babel/core @babel/preset-env @babel/preset-react -S</span><br></pre></td></tr></table></figure><p>上面为 <code>babel-loader 8.x</code> 版本安装,需要匹配下面的 8.x 配置</p><h3 id="配置-4"><a href="#配置-4" class="headerlink" title="配置"></a>配置</h3><h4 id="babel-loader-7-x-版本配置"><a href="#babel-loader-7-x-版本配置" class="headerlink" title="babel-loader 7.x 版本配置"></a>babel-loader 7.x 版本配置</h4><p>第一种 全在 <code>webpack.config.js</code> 中配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.(jsx|js)$/</span>,</span><br><span class="line"> use:{</span><br><span class="line"> loader:<span class="string">'babel-loader'</span>,</span><br><span class="line"> options:{</span><br><span class="line"> presets:[<span class="string">'env'</span>,<span class="string">'react'</span>]</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 排除node_modules 文件</span></span><br><span class="line"> exclude:<span class="regexp">/node_modules/</span></span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>第二种 新建 <code>.babelrc</code> 文件 (推荐使用第二种)</p><p>webpack.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.(jsx|js)$/</span>,</span><br><span class="line"> use:{</span><br><span class="line"> loader:<span class="string">'babel-loader'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 排除node_,modules 文件</span></span><br><span class="line"> exclude:<span class="regexp">/node_modules/</span></span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>.baelrc</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "presets": [</span><br><span class="line"> "env",</span><br><span class="line"> "react"</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h4 id="babel-loader-8-x-版本配置"><a href="#babel-loader-8-x-版本配置" class="headerlink" title="babel-loader 8.x 版本配置"></a>babel-loader 8.x 版本配置</h4><p>第一种 全在 <code>webpack.config.js</code> 中配置</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line">rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.(jsx|js)$/</span>,</span><br><span class="line"> use:{</span><br><span class="line"> loader:<span class="string">'babel-loader'</span>,</span><br><span class="line"> options:{</span><br><span class="line"> presets:[<span class="string">'"@babel/preset-env'</span>,<span class="string">'"@babel/preset-react'</span>]</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 排除node_modules 文件</span></span><br><span class="line"> exclude:<span class="regexp">/node_modules/</span></span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>第二种 新建 <code>.babelrc</code> 文件 (推荐使用第二种)</p><p>webpack.config.js</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">rules:[</span><br><span class="line"> {</span><br><span class="line"> test:<span class="regexp">/\.(jsx|js)$/</span>,</span><br><span class="line"> use:{</span><br><span class="line"> loader:<span class="string">'babel-loader'</span></span><br><span class="line"> },</span><br><span class="line"> <span class="comment">// 排除node_,modules 文件</span></span><br><span class="line"> exclude:<span class="regexp">/node_modules/</span></span><br><span class="line"> }</span><br><span class="line">]</span><br></pre></td></tr></table></figure><p>.baelrc</p><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">{</span><br><span class="line"> "presets": [</span><br><span class="line"> ""@babel/preset-env",</span><br><span class="line"> ""@babel/preset-react"</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h2 id="webpack-引入第三方库"><a href="#webpack-引入第三方库" class="headerlink" title="webpack 引入第三方库"></a>webpack 引入第三方库</h2><h3 id="安装-5"><a href="#安装-5" class="headerlink" title="安装"></a>安装</h3><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">cnpm install jquery -S</span><br></pre></td></tr></table></figure><h3 id="使用"><a href="#使用" class="headerlink" title="使用"></a>使用</h3><p>在webpack 3.x 中需要大量配置,但是在webpack中则少了很多</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> $ = <span class="built_in">require</span>(<span class="string">"jquery"</span>);</span><br><span class="line"></span><br><span class="line">$(<span class="string">"body"</span>).html(<span class="string">"<p>我是由JQuery写出来的</p>"</span>)</span><br></pre></td></tr></table></figure><p>欢迎到 <a href="https://www.kancloud.cn/spirit-ling/blog" rel="external nofollow noopener noreferrer" target="_blank">看云阅读</a></p>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>webpack 4.x 初级学习记录</p></blockquote>
</summary>
<category term="Webpack" scheme="https://blog.spiritling.cn/categories/Webpack/"/>
<category term="Webpack" scheme="https://blog.spiritling.cn/tags/Webpack/"/>
</entry>
<entry>
<title>JavaScript 创建对象和继承</title>
<link href="https://blog.spiritling.cn/articles/7da74427-1de3-488e-8ac2-ccb817b293fa/"/>
<id>https://blog.spiritling.cn/articles/7da74427-1de3-488e-8ac2-ccb817b293fa/</id>
<published>2019-11-28T02:39:07.000Z</published>
<updated>2019-11-28T10:07:26.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>JavaScript 创建对象和继承</p></blockquote><a id="more"></a><h2 id="创建对象"><a href="#创建对象" class="headerlink" title="创建对象"></a>创建对象</h2><p>JS中可以有许多设计模式,在这些中推荐使用组合构造函数和原型模式;<br>因为不太善于写文章,所以下面简化文字叙述,直接看代码</p><h3 id="工厂模式"><a href="#工厂模式" class="headerlink" title="工厂模式"></a>工厂模式</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">person</span>(<span class="params">name,age</span>)</span>{</span><br><span class="line"> <span class="keyword">var</span> obj={}</span><br><span class="line"> obj.name=name;</span><br><span class="line"> obj.age=age;</span><br><span class="line"> obj.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> obj;</span><br><span class="line">}</span><br></pre></td></tr></table></figure><h3 id="构造函数模式"><a href="#构造函数模式" class="headerlink" title="构造函数模式"></a>构造函数模式</h3><p>正常情况下我们将函数名起为大写字母开头</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name,age</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name=name;</span><br><span class="line"> <span class="keyword">this</span>.age=age;</span><br><span class="line"> <span class="keyword">this</span>.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> a=<span class="keyword">new</span> Person(<span class="string">"张三"</span>,<span class="number">29</span>);</span><br><span class="line"><span class="keyword">var</span> b=<span class="keyword">new</span> Person(<span class="string">"李四"</span>,<span class="number">22</span>);</span><br><span class="line"></span><br><span class="line">a.sayName();</span><br><span class="line">b.sayName();</span><br></pre></td></tr></table></figure><p>构造函数可以通过 <code>new</code> 关键字来进行处理,使其实例化,每个都有独有数据,也就是私有变量</p><h3 id="原型模式"><a href="#原型模式" class="headerlink" title="原型模式"></a>原型模式</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params"></span>)</span>{</span><br><span class="line"> </span><br><span class="line">}</span><br><span class="line">Person.prototype.name = <span class="string">"Li Si"</span>;</span><br><span class="line">Person.prototype.age = <span class="number">21</span>;</span><br><span class="line">Person.prototype.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person1=<span class="keyword">new</span> Person();</span><br><span class="line">person1.sayName(); <span class="comment">//"Li Si"</span></span><br><span class="line"><span class="keyword">var</span> person2=<span class="keyword">new</span> Person();</span><br><span class="line">person2.sayName(); <span class="comment">//"Li Si"</span></span><br></pre></td></tr></table></figure><p>原型模式所有的都共用同一个数据,相当于公有变量</p><h3 id="组合模式:构造函数和原型模式结合"><a href="#组合模式:构造函数和原型模式结合" class="headerlink" title="组合模式:构造函数和原型模式结合"></a>组合模式:构造函数和原型模式结合</h3><p>通过上面两个例子,两个都有独有的特性,所以我们可以组合两者了来进行处理,一般推荐使用组合模式</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Person</span>(<span class="params">name,age</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name=name;</span><br><span class="line"> <span class="keyword">this</span>.age=age;</span><br><span class="line">}</span><br><span class="line">Person.prototype.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> person1=<span class="keyword">new</span> Person(<span class="string">"张三"</span>);</span><br><span class="line">person1.sayName(); <span class="comment">//"张三"</span></span><br><span class="line"><span class="keyword">var</span> person2=<span class="keyword">new</span> Person(<span class="string">"李四"</span>);</span><br><span class="line">person2.sayName(); <span class="comment">//"李四"</span></span><br></pre></td></tr></table></figure><p>这样各自经过实例化后,都有自己的独有数据,但是却有着公共方法;</p><h2 id="继承"><a href="#继承" class="headerlink" title="继承"></a>继承</h2><p>ES5 继承有许多中方式,我们这里只说普通常用的继承方式</p><h3 id="原型链赋值继承"><a href="#原型链赋值继承" class="headerlink" title="原型链赋值继承"></a>原型链赋值继承</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">father</span>(<span class="params">name</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name=name;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">father.prototype.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sub</span>(<span class="params">age</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.age=age;</span><br><span class="line">}</span><br><span class="line">sub.prototype=<span class="keyword">new</span> father();</span><br><span class="line">sub.prototype.sayAge=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.age);</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>上面就是典型的原型链赋值继承,但是这个继承是有缺点的,在继承时需要 <code>new</code> 关键字进行处理。</p><h3 id="构造函数继承"><a href="#构造函数继承" class="headerlink" title="构造函数继承"></a>构造函数继承</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">father</span>(<span class="params">name</span>)</span>{</span><br><span class="line"> <span class="keyword">this</span>.name=name;</span><br><span class="line"> <span class="keyword">this</span>.age=<span class="number">22</span>;</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line">father.prototype.sayName=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name);</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">sub</span>(<span class="params"></span>)</span>{</span><br><span class="line"> father.apply(<span class="keyword">this</span>,<span class="built_in">arguments</span>)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> s=<span class="keyword">new</span> sub(<span class="string">"a"</span>)</span><br><span class="line">> s</span><br><span class="line">> sub {<span class="attr">name</span>: <span class="string">"a"</span>, <span class="attr">age</span>: <span class="number">22</span>}</span><br><span class="line"> age: <span class="number">22</span></span><br><span class="line"> name: <span class="string">"a"</span></span><br><span class="line"> __proto__:</span><br><span class="line"> <span class="keyword">constructor</span>: ƒ sub()</span><br><span class="line"> __proto__: Object</span><br></pre></td></tr></table></figure><p>构造函数继承最后在输出中会发现并没有 父级的方法 ,但是可以将数据传到父级,与原型链继承互补,所以衍生出组合继承的方式</p><h3 id="组合继承"><a href="#组合继承" class="headerlink" title="组合继承"></a>组合继承</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Father</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.className = <span class="string">"Father"</span></span><br><span class="line">}</span><br><span class="line">Father.prototype.sayName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Sub</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> Father.apply(<span class="keyword">this</span>, <span class="built_in">arguments</span>)</span><br><span class="line">}</span><br><span class="line"><span class="comment">//继承原型</span></span><br><span class="line">Sub.prototype = <span class="keyword">new</span> Father();</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> s=<span class="keyword">new</span> Sub(<span class="string">"张三"</span>,<span class="number">12</span>);</span><br></pre></td></tr></table></figure><p>不仅会继承构造函数中的属性,也会复制父类原型链中的属性</p><p>但是在 <code>Sub.prototype = new Father();</code> 之后</p><p>Sub 的原型变成这样的了</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">> Sub.prototype</span><br><span class="line">> {<span class="attr">name</span>: <span class="literal">undefined</span>, <span class="attr">className</span>: <span class="string">"person"</span>}</span><br></pre></td></tr></table></figure><p>也就是说Sub的原型中已经有了一个name属性,而之后创建 s 时传给构造的函数的name则是通过this重新定义了一个name属性,相当于只是覆盖掉了原型的name属性(原型中的name依然还在),这样很不优雅。</p><h3 id="寄生组合继承"><a href="#寄生组合继承" class="headerlink" title="寄生组合继承"></a>寄生组合继承</h3><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Father</span>(<span class="params">name</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.name = name;</span><br><span class="line"> <span class="keyword">this</span>.className = <span class="string">"Father"</span></span><br><span class="line">}</span><br><span class="line">Father.prototype.sayName = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.name)</span><br><span class="line">}</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Sub</span>(<span class="params">name,age</span>) </span>{</span><br><span class="line"> <span class="keyword">this</span>.age=age;</span><br><span class="line"> Father.call(<span class="keyword">this</span>, name)</span><br><span class="line">}</span><br><span class="line"><span class="comment">// 注意此处</span></span><br><span class="line">Sub.prototype=<span class="built_in">Object</span>.create(Father.prototype)</span><br><span class="line"></span><br><span class="line">Sub.prototype.sayAge=<span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{</span><br><span class="line"> <span class="built_in">console</span>.log(<span class="keyword">this</span>.age)</span><br><span class="line">}</span><br></pre></td></tr></table></figure><p>这里用到了 <code>Object.creat(obj)</code> 方法,该方法会对传入的obj对象进行浅拷贝。和上面组合继承的主要区别就是:将父类的原型复制给了子类原型。这种做法很清晰:</p><ol><li>构造函数中继承父类属性/方法,并初始化父类。</li><li>子类原型和父类原型建立联系。</li></ol><p>还有一个问题,就是constructor属性,我们来看一下:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">> Father.prototype.constructor</span><br><span class="line">< Father(name){</span><br><span class="line"> <span class="keyword">this</span>.name=name;</span><br><span class="line"> <span class="keyword">this</span>.className=<span class="string">"Father"</span> </span><br><span class="line"> }</span><br><span class="line">> Sub.prototype.constructor</span><br><span class="line">< Father(name){</span><br><span class="line"> <span class="keyword">this</span>.name=name; </span><br><span class="line"> <span class="keyword">this</span>.className=<span class="string">"Father"</span> </span><br><span class="line"> }</span><br></pre></td></tr></table></figure><p>constructor是类的构造函数,我们发现,Father和Sub实例的constructor指向都是Father,当然,这并不会改变instanceof的结果,但是对于需要用到construcor的场景,就会有问题。所以一般我们会加上这么一句:</p><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">Sub.prototype.constructor = Sub</span><br></pre></td></tr></table></figure>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>JavaScript 创建对象和继承</p></blockquote>
</summary>
<category term="JavaScript" scheme="https://blog.spiritling.cn/categories/JavaScript/"/>
<category term="JavaScript" scheme="https://blog.spiritling.cn/tags/JavaScript/"/>
<category term="Object" scheme="https://blog.spiritling.cn/tags/Object/"/>
<category term="Inheritance" scheme="https://blog.spiritling.cn/tags/Inheritance/"/>
</entry>
<entry>
<title>Node-RSA 非对称加密使用</title>
<link href="https://blog.spiritling.cn/articles/e09a8fce-63c0-4fb3-aa37-89aa888ff870/"/>
<id>https://blog.spiritling.cn/articles/e09a8fce-63c0-4fb3-aa37-89aa888ff870/</id>
<published>2019-10-16T04:26:23.000Z</published>
<updated>2019-11-07T08:07:26.000Z</updated>
<content type="html"><![CDATA[<blockquote class="blockquote-center"><p>Node-RSA 非对称加密使用</p></blockquote><a id="more"></a><h2 id="关于非对称加密算法"><a href="#关于非对称加密算法" class="headerlink" title="关于非对称加密算法"></a>关于非对称加密算法</h2><p><a href="https://www.zhihu.com/question/33645891" rel="external nofollow noopener noreferrer" target="_blank">如何用通俗易懂的话来解释非对称加密?</a></p><h2 id="Node-服务端使用非对称加密算法"><a href="#Node-服务端使用非对称加密算法" class="headerlink" title="Node 服务端使用非对称加密算法"></a>Node 服务端使用非对称加密算法</h2><ol><li>首先引入 <code>node-rsa</code> 包</li></ol><figure class="highlight shell"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">npm install node-rsa --save</span><br></pre></td></tr></table></figure><ol start="2"><li>生成获取私钥和公钥</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> key = <span class="keyword">new</span> NodeRSA({ <span class="attr">b</span>: <span class="number">512</span> });</span><br><span class="line"><span class="comment">// 一般情况下使用时建议将下面指定格式,可以在后面到导入时有效确认格式</span></span><br><span class="line"><span class="keyword">const</span> publicKey = key.exportKey(<span class="string">"pkcs8-public-pem"</span>);</span><br><span class="line"><span class="keyword">const</span> privateKey = key.exportKey(<span class="string">"pkcs8-private-pem"</span>);</span><br></pre></td></tr></table></figure><ol start="3"><li>导入私钥和公钥</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> key2 = <span class="keyword">new</span> NodeRSA();</span><br><span class="line"><span class="comment">// 需要与生成的核对一致,否则会出现错误</span></span><br><span class="line">key2.importKey(publicKey, <span class="string">"pkcs8-public-pem"</span>);</span><br><span class="line">key2.importKey(privateKey, <span class="string">"pkcs8-private-pem"</span>);</span><br></pre></td></tr></table></figure><ol start="4"><li>公钥加密私钥解密 — 用于加解密</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> encryptPublic = key.encrypt(data, <span class="string">"base64"</span>, <span class="string">"utf8"</span>);</span><br><span class="line"><span class="keyword">const</span> decryptPrivate = key.decrypt(encryptPublic, <span class="string">"json"</span>);</span><br></pre></td></tr></table></figure><ul><li><code>data</code> 就是需要加密的明文数据,可以是 <code>string</code>, <code>Buffer</code>,也可以是 <code>Array/Object</code>,<code>Array/Object</code> 首先会自动编码成 <code>json</code> 字符串</li><li><code>base64</code> 就是需要加密的结果以什么格式显示,这里选择以<code>base64</code>显示,默认为:buffer</li><li><code>utf8</code> 数据源是什么编码格式</li><li><code>json</code> 解密之后以什么格式出现,这里选择<code>json</code>格式</li></ul><ol start="5"><li>私钥加密公钥解密 — 用于签名</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> encryptPrivate = key.encryptPrivate(data, <span class="string">"base64"</span>, <span class="string">"utf8"</span>);</span><br><span class="line"><span class="keyword">const</span> decryptPublic = key.decryptPublic(encryptPrivate, <span class="string">"json"</span>);</span><br></pre></td></tr></table></figure><ul><li><code>data</code> 就是需要加密的明文数据,可以是 <code>string</code>, <code>Buffer</code>,也可以是 <code>Array/Object</code>,<code>Array/Object</code> 首先会自动编码成 <code>json</code> 字符串</li><li><code>base64</code> 就是需要加密的结果以什么格式显示,这里选择以<code>base64</code>显示,默认为:buffer</li><li><code>utf8</code> 数据源是什么编码格式</li><li><code>json</code> 解密之后以什么格式出现,这里选择<code>json</code>格式</li></ul><ol start="6"><li>返回结果</li></ol><figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line">ctx.body = {</span><br><span class="line"> privateKey,</span><br><span class="line"> publicKey,</span><br><span class="line"> publicData: data,</span><br><span class="line"> encryptPublic,</span><br><span class="line"> decryptPrivate,</span><br><span class="line"> encryptPrivate,</span><br><span class="line"> decryptPublic,</span><br><span class="line">};</span><br></pre></td></tr></table></figure><blockquote><p>如果下面不存在代码块时,请检查网络是否可以访问 github 的 <a href="https://gist.github.com/" rel="external nofollow noopener noreferrer" target="_blank">gist</a> ,无法访问 gist 时,可以点击此处访问 <a href="https://bitbucket.org/spiritling-team/workspace/snippets/dLGAn9" rel="external nofollow noopener noreferrer" target="_blank">bitbucket</a> 的代码块</p></blockquote><script src="https://gist.github.com/SpiritLing/3503dc24df084d8d007d470973c4d721.js"></script>]]></content>
<summary type="html">
<blockquote class="blockquote-center"><p>Node-RSA 非对称加密使用</p></blockquote>
</summary>
<category term="NodeJS" scheme="https://blog.spiritling.cn/categories/NodeJS/"/>
<category term="RSA" scheme="https://blog.spiritling.cn/categories/RSA/"/>
<category term="RSA" scheme="https://blog.spiritling.cn/tags/RSA/"/>
<category term="NodeJS" scheme="https://blog.spiritling.cn/tags/NodeJS/"/>
</entry>
</feed>