-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathlocal-search.xml
869 lines (412 loc) · 514 KB
/
local-search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
<?xml version="1.0" encoding="utf-8"?>
<search>
<entry>
<title>小知识补给包:AST抽象语法树基础</title>
<link href="/2022/03/26/ast/"/>
<url>/2022/03/26/ast/</url>
<content type="html"><![CDATA[<h2 id="抽象语法树"><a href="#抽象语法树" class="headerlink" title="抽象语法树"></a>抽象语法树</h2><p>抽象语法树AST是什么?看维基百科中的说明</p><blockquote><p>在<a href="https://zh.wikipedia.org/wiki/计算机科学">计算机科学</a>中,<strong>抽象语法树</strong>(<strong>A</strong>bstract <strong>S</strong>yntax <strong>T</strong>ree,AST),或简称<strong>语法树</strong>(Syntax tree),是<a href="https://zh.wikipedia.org/wiki/源代码">源代码</a><a href="https://zh.wikipedia.org/wiki/语法学">语法</a>结构的一种抽象表示。它以<a href="https://zh.wikipedia.org/wiki/树_(图论">树状</a>)的形式表现<a href="https://zh.wikipedia.org/wiki/编程语言">编程语言</a>的语法结构,树上的每个节点都表示源代码中的一种结构。之所以说语法是“抽象”的,是因为这里的语法并不会表示出真实语法中出现的每个细节</p></blockquote><p>简单的来说就是一种标准的树形结构数据来代表一段代码函数,通过特定的工具能够实现源码code 与 ast的互相转换。</p><h2 id="AST有什么用?"><a href="#AST有什么用?" class="headerlink" title="AST有什么用?"></a>AST有什么用?</h2><p>在前端领域,想必大家都了解<strong>babel</strong>。通过babel,能够将大家书写的ES 高级特性的语法转历史版本里浏览器能够兼容运行的ES5代码。其中babel就是通过不同的转换器,将新特性中的高级语法做了一层转换。通常我们在编写代码中都不需要了解这些,只需要配置下,就能生效起作用。在爬虫领域中javascript代码的混淆对抗离不开AST,了解AST的一些基本知识内容,无论是对“大神”编写的反混淆脚本还是自己编码脚本都是非常有帮助的。今天我们来探究下怎么“玩”AST。</p><h2 id="Babel工具库"><a href="#Babel工具库" class="headerlink" title="Babel工具库"></a>Babel工具库</h2><p>把code转化成ast的库很多,这里就不一一列举。这里说的都是babel的周边库</p><ul><li>@babel/parser 解析器</li><li>@babel/traverse 遍历器</li><li>@babel/types 类型判断、ast结构构造</li><li>@babel/generator code生成器</li></ul><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs js">mkdir test-ast<br>cd test-ast<br>npm install -D @babel/parser @babel/traverse @babel/types @babel/generator<br></code></pre></td></tr></table></figure><p>通过上面指令,创建文件夹安装babel库,尝试自己编码简单的脚本</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><code class="hljs js"><span class="hljs-comment">// test-code.js</span><br><span class="hljs-keyword">const</span> a = <span class="hljs-number">1</span> + <span class="hljs-number">2</span>;<br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// test.js</span><br><span class="hljs-keyword">const</span> parser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/parser"</span>);<br><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);<br><span class="hljs-keyword">const</span> traverse = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/traverse"</span>).default;<br><span class="hljs-keyword">const</span> t = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/types"</span>);<br><span class="hljs-keyword">const</span> generator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/generator"</span>).default;<br><br><br><span class="hljs-keyword">var</span> jscode = fs.readFileSync(<span class="hljs-string">'./code-test.js'</span>, {<br><span class="hljs-attr">encoding</span>: <span class="hljs-string">"utf-8"</span><br>});<br><br><span class="hljs-keyword">const</span> ast = parser.parse(jscode)<br><span class="hljs-keyword">const</span> BinaryExpressionEx = <span class="hljs-function">(<span class="hljs-params">path</span>) =></span> {<br><span class="hljs-keyword">const</span>{confident, value} = path.evaluate();<br>path.replaceInline(t.valueToNode(value))<br>}<br>traverse(<br>ast,<br>{<br><span class="hljs-attr">BinaryExpression</span>: {<br><span class="hljs-attr">exit</span>: [BinaryExpressionEx]<br>},<br>}<br>)<br><span class="hljs-keyword">var</span> { code } = generator(ast, {<br><span class="hljs-attr">jsescOption</span>: {<br><span class="hljs-comment">// 自动转义</span><br><span class="hljs-attr">minimal</span>: <span class="hljs-literal">true</span>,<br>}<br>});<br><br><span class="hljs-built_in">console</span>.log(code)<br><span class="hljs-comment">// const a = 3;</span><br></code></pre></td></tr></table></figure><p>编写 test-code.js【源码文件】test.js【脚本文件】</p><p>然后node test.js 执行脚本文件,最后发现源码里<code>const a = 1+2</code> 变成 <code>const a = 3</code></p><p>这是一个简单的例子,但是如果没有对每个库的作用和AST的基本内容了解,想要编写也是有点难。看过了简单里,我们先来了解AST基础知识。</p><h2 id="AST基础知识"><a href="#AST基础知识" class="headerlink" title="AST基础知识"></a>AST基础知识</h2><p>这里介绍几个实用网站</p><ul><li><a href="https://astexplorer.net/">在线转化AST </a> 可以在线看到行级别的代码对应的AST树的结构</li><li><a href="https://github.com/jamiebuilds/babel-handbook/blob/master/translations/zh-Hans/plugin-handbook.md#toc-scopes">babel使用手册</a> 对上面4个工具库有比较完整的介绍</li></ul><p>Babel 的三个主要处理步骤分别是: <strong>解析(parse)</strong>,<strong>转换(transform)</strong>,<strong>生成(generate)</strong>。.</p><p>通过解析后就得到AST抽象语法树,像下图所示一层一层的。每一个层级是一个「<strong>Node</strong>」节点</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220327213824762.png" alt="抽象语法树"></p><p>想要对AST操作的话,就需要对AST树进行递归遍历,在babel工具库 traverse 封装好遍历器,只需要编码对应的函数即可以实现遍历节点并修改。</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><code class="hljs js"><span class="hljs-keyword">const</span> MyVisitor = {<br> <span class="hljs-comment">// 变量遍历</span><br> <span class="hljs-function"><span class="hljs-title">Identifier</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"Called!"</span>);<br> },<br> <span class="hljs-comment">// 表达式遍历 </span><br> <span class="hljs-function"><span class="hljs-title">BinaryExpression</span>(<span class="hljs-params"></span>)</span> {<br>}<br> <span class="hljs-comment">// ....</span><br>};<br></code></pre></td></tr></table></figure><p>所有的遍历都是有进入和出去2个过程,可针对不同的场景在不同时机处理</p><p><strong>概念 Path</strong>:刚刚所说的遍历器,遍历的并非节点,而是path。</p><blockquote><p>AST 通常会有许多节点,那么节点直接如何相互关联呢? 我们可以使用一个可操作和访问的巨大可变对象表示节点之间的关联关系,或者也可以用<strong>Paths</strong>(路径)来简化这件事情。.<strong>Path</strong> 是表示两个节点之间连接的对象。</p></blockquote><p>个人对path理解就是链表的数据结构,通过path能够找到关联的parent 和其他相关的信息,从而找到该节点的位置信息,进行判断和修改</p><p><strong>概念 Scopes(作用域)</strong></p><p>在之前的文章中也讲解过一些JavaScript作用域相关的知识内容,而在AST中,也有对应的概念<strong>Scope</strong>。</p><p>这是AST中scope的数据结构,包含了关联的父级节点路径、当前节点路径与所有引用的“绑定”</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><code class="hljs js">{<br> <span class="hljs-attr">path</span>: path, <span class="hljs-comment">// 当前路径</span><br> <span class="hljs-attr">block</span>: path.node, <span class="hljs-comment">// 当前节点</span><br> <span class="hljs-attr">parentBlock</span>: path.parent, <span class="hljs-comment">// 父级路径</span><br> <span class="hljs-attr">parent</span>: parentScope, <span class="hljs-comment">// 父级作用域</span><br> <span class="hljs-attr">bindings</span>: [...] <span class="hljs-comment">// 所有引用</span><br>}<br></code></pre></td></tr></table></figure><p>作用域这块内容相对于Node、Path、遍历器来说是比较复杂的一块,后续可以通过更多的例子了解如何处理作用域。</p><h4 id="一个简单的对象合并例子"><a href="#一个简单的对象合并例子" class="headerlink" title="一个简单的对象合并例子"></a>一个简单的对象合并例子</h4><p>这个是从反混淆脚本<code>ob-decrypt.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><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">const</span> parser = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/parser"</span>);<br><span class="hljs-keyword">const</span> fs = <span class="hljs-built_in">require</span>(<span class="hljs-string">'fs'</span>);<br><span class="hljs-keyword">const</span> traverse = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/traverse"</span>).default;<br><span class="hljs-keyword">const</span> t = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/types"</span>);<br><span class="hljs-keyword">const</span> generator = <span class="hljs-built_in">require</span>(<span class="hljs-string">"@babel/generator"</span>).default;<br><br><br><span class="hljs-keyword">var</span> jscode = fs.readFileSync(<span class="hljs-string">'./code.js'</span>, {<br><span class="hljs-attr">encoding</span>: <span class="hljs-string">"utf-8"</span><br>});<br><br><br><span class="hljs-keyword">const</span> ast = parser.parse(jscode)<br>traverse(<br>ast, <br>{<br><span class="hljs-attr">VariableDeclarator</span>: {<br><span class="hljs-attr">exit</span>: [merge_obj]<br>},<br>}<br>);<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">merge_obj</span>(<span class="hljs-params">path</span>) </span>{<br><span class="hljs-comment">// 将拆分的对象重新合并</span><br><span class="hljs-keyword">const</span> {id, init} = path.node;<br><span class="hljs-comment">// 判断是否为对象</span><br><span class="hljs-keyword">if</span> (!t.isObjectExpression(init))<br><span class="hljs-keyword">return</span>;<br><br><span class="hljs-keyword">let</span> name = id.name;<br><span class="hljs-comment">// 对象的属性</span><br><span class="hljs-keyword">let</span> properties = init.properties;<br><br><span class="hljs-comment">// 拿到当前路径的作用域</span><br><span class="hljs-keyword">let</span> scope = path.scope;<br><span class="hljs-keyword">let</span> binding = scope.getBinding(name);<br><span class="hljs-keyword">if</span> (!binding || binding.constantViolations.length > <span class="hljs-number">0</span>) {<br><span class="hljs-keyword">return</span>;<br>}<br><span class="hljs-comment">// scope.block 对应的是当前节点的node</span><br>scope.traverse(scope.block, {<br><span class="hljs-comment">// AssignmentExpression 是赋值表达式遍历</span><br><span class="hljs-attr">AssignmentExpression</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">_path</span>) </span>{<br><span class="hljs-keyword">const</span> left = _path.get(<span class="hljs-string">"left"</span>);<br><span class="hljs-keyword">const</span> right = _path.get(<span class="hljs-string">"right"</span>);<br><span class="hljs-comment">// 判断是否是对象成员表达</span><br><span class="hljs-keyword">if</span> (!left.isMemberExpression())<br><span class="hljs-keyword">return</span>;<br><span class="hljs-keyword">const</span> object = left.get(<span class="hljs-string">"object"</span>);<br><span class="hljs-keyword">const</span> property = left.get(<span class="hljs-string">"property"</span>);<br><span class="hljs-comment">// object.isIdentifier({name: name}) 判断对象是否为当前遍历的对象</span><br><span class="hljs-comment">// property.isStringLiteral() 判断是否为字符串类型的赋值 例如 obj['a']这种赋值方式</span><br><span class="hljs-comment">// property.isIdentifier() 则判断是否 obj.a 这种方式</span><br><span class="hljs-comment">// 重新将作用域内的这些赋值表达在初始对象的时候赋予 properties</span><br><span class="hljs-comment">// _path.remove() 移除当前节点 避免重复赋值</span><br><span class="hljs-keyword">if</span> (object.isIdentifier({<span class="hljs-attr">name</span>: name}) && property.isStringLiteral() && _path.scope == scope) {<br>properties.push(t.ObjectProperty(t.valueToNode(property.node.value), right.node));<br>_path.remove();<br>}<br><span class="hljs-keyword">if</span> (object.isIdentifier({<span class="hljs-attr">name</span>: name}) && property.isIdentifier() && _path.scope == scope) {<br>properties.push(t.ObjectProperty(t.valueToNode(property.node.name), right.node));<br>_path.remove();<br>}<br>}<br>})<br>}<br><br><span class="hljs-keyword">var</span> { code } = generator(ast, {<br><span class="hljs-attr">jsescOption</span>: {<br><span class="hljs-comment">// 自动转义</span><br><span class="hljs-attr">minimal</span>: <span class="hljs-literal">true</span>,<br>}<br>});<br><br><span class="hljs-built_in">console</span>.log(code)<br><br><br></code></pre></td></tr></table></figure><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220327220202630.png" alt="执行脚本结果"></p><p>发现确实可以将对象赋值进行合并简化。这个是对babel工具库的简单应用。通过这个例子也让我们了解到如何去利用作用域找到所有关联的信息并一一修改,这个例子中简单概括就是</p><ul><li>遍历对象定义</li><li>找到对象定义的作用域内所有添加属性的地方</li><li>将添加属性值的“行为”放在初始化对象的时候</li><li>然后删除该属性定义的节点</li><li>重复执行至到最后</li></ul><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>AST的水还是比较深,因为JavaScript编码的过程就比较灵活,所以babel脚本通常需要不断的编码修复各种异常现象。通过了解AST基础知识,想必大家也学会了一些小技巧。留个课后作业题给大家尝试编码,关注后回复公众号 “AST1答案“ ,返回答案给你哦。</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><code class="hljs js"><span class="hljs-comment">// 简单混淆</span><br><span class="hljs-comment">// decode为还原函数</span><br>decode = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">number</span>)</span>{<br><span class="hljs-keyword">return</span><span class="hljs-built_in">String</span>.fromCharCode(number)<br><br>}<br><span class="hljs-keyword">const</span> getToken = <span class="hljs-function">(<span class="hljs-params">hash</span>) =></span> {<br><span class="hljs-keyword">const</span> obj = {}<br>obj[decode(<span class="hljs-number">97</span>)] = <span class="hljs-number">6</span><br>obj[decode(<span class="hljs-number">115</span>)] = <span class="hljs-number">6</span><br>obj[decode(<span class="hljs-number">116</span>)] = <span class="hljs-number">6</span><br>}<br></code></pre></td></tr></table></figure>]]></content>
</entry>
<entry>
<title>「svelte」是前端排名前三的框架,你知道吗?</title>
<link href="/2022/03/18/svelte/"/>
<url>/2022/03/18/svelte/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>可能大家对svelte这个框还比较陌生,甚至名字都没有听说过。先带大家看下官方的说法</p><blockquote><p>Svelte 是一种全新的构建用户界面的方法。传统框架如 React 和 Vue 在<em>浏览器</em>中需要做大量的工作,而 Svelte 将这些工作放到构建应用程序的<em>编译阶段</em>来处理。与使用虚拟(virtual)DOM 差异对比不同。Svelte 编写的代码在应用程序的状态更改时就能像做外科手术一样更新 DOM。</p></blockquote><p>大概讲的就是让代码具备响应式处理的阶段不一样,像React、vue 都有核心core去做一些事情,让数据具备响应式。可能这样讲,还比较抽象,接下来以教程的方式让大家了解svelte怎么写。本篇又名为《svelte》入门教程</p><h2 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h2><p>按<a href="https://svelte.dev/">官方文档</a>的指令直接敲</p><figure class="highlight applescript"><table><tr><td class="gutter"><div class="code-wrapper"><pre><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></div></td><td class="code"><pre><code class="hljs applescript">npx degit sveltejs/template <span class="hljs-keyword">my</span>-svelte-project<br>cd <span class="hljs-keyword">my</span>-svelte-project<br>npm install<br>npm <span class="hljs-built_in">run</span> dev<br></code></pre></td></tr></table></figure><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220318225633124.png" alt="简单hello world"></p><p>简单的hello world界面就出来了!</p><h4 id="简单的计数器"><a href="#简单的计数器" class="headerlink" title="简单的计数器"></a>简单的计数器</h4><p>这个代码有点像vue,先敲一个Button按钮出来</p><figure class="highlight plaintext"><figcaption><span>let name;```定义了props传入值name,在后面模板中只需要括号输出即可(大多数模板输出变量都是这样的语法)</span></figcaption><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs export"><br>```on:click```定义了所有事件都这样传递<br><br>```vue<br><script><br> // Button.svelte<br>export let name;<br></script><br><br><div<br>class="button"<br>on:click<br>><br>{<br>name<br>}<br></div><br><br><style><br>.button{<br>display: inline-block;<br>padding: 10px;<br>border: 1px solid #ddd;<br>cursor: pointer;<br>}<br></style><br></code></pre></td></tr></table></figure><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs vue"><script><br>import Button from './Button.svelte'<br>let count = 0;<br></script><br><br><main><br><Button<br>on:click={() => {<br>console.log(1)<br>count+=1<br>}}<br>name={`计数器${count}`}<br>><br></Button><br></main><br><br><style><br></style><br></code></pre></td></tr></table></figure><p>最终效果就是完成一个简单的计数器</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220318231401083.png" alt="计数器"></p><p>点击按钮计数会进行增加。整体的编码还是很流畅的,并没有react中的状态管理,也没有vue里的data初始化管理,超级简单的模板语法,大家也可以尝试编码一下,学习曲线很低。</p><h4 id="通信"><a href="#通信" class="headerlink" title="通信"></a>通信</h4><p>1、<strong>「父子通信props」</strong>上面的例子中也体现了这个点,name传值进去就能展示</p><p>2、「Events」事件机制实现跨组件通信 <a href="https://www.sveltejs.cn/examples#event-forwarding">参考例子</a></p><p>实际上是利用了createEventDispatcher 这个方法进行事件的广播</p><p>3、writable store 也能实现跨组件通信,<a href="https://www.sveltejs.cn/tutorial/auto-subscriptions">参考例子</a></p><p>简单概括下实现的方式就是通过引入同一个store里的值例如count,然后对count进行update方法或者set的执行</p><p>就可以达到变更值,与读取值的变化。这里有个语法糖,通过$符号可以实现compute 自动计算的效果。</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220318233800019.png" alt="$count"></p><p>4、具体例子看<a href="https://www.sveltejs.cn/tutorial/context-api">context</a>,通过getContext与setContext 达到通信的效果</p><blockquote><p>Contexts and stores seem similar. They differ in that stores are available to <em>any</em> part of an app, while a context is only available to <em>a component and its descendants</em>. This can be helpful if you want to use several instances of a component without the state of one interfering with the state of the others.</p></blockquote><p>Contexts 和 stores 非常的像,区别的地方就是当使用单例组件的时候如果用store会互相干扰,用Contexts则没有这个问题</p><p>当然很多场景可能是Contexts 与 stores会一起使用到。</p><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>svelte在2021年的欢迎程度已经远超了angular,目前在国外非常流行,因为具备简单的语法快速上手而且体积小,响应快速对于简单的页面来说,使用svelte无疑是一把利刃。对比框架的使用当然没有想象中的简单,需要对比实际的使用场景,结合团队的实际情况才能考虑是否落地推动。当然对svelte的学习并不止于此,后续还会深入svelte的原理,探讨下为什么svelte能够在国外火起来呢!</p><p>感谢观看~</p><p>觉得有帮助的朋友可以关注一波噢!</p><p>「程序与浪漫」持续更新前端/爬虫/安全相关的知识</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/qrcode.jpg" alt="公众号"></p>]]></content>
<tags>
<tag>前端、框架</tag>
</tags>
</entry>
<entry>
<title>JS基础知识补给包</title>
<link href="/2022/03/06/js-base/"/>
<url>/2022/03/06/js-base/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在逆向一些网站的时候,扣出来JS代码,能运行但是不知道为啥能这样跑。相信很多小伙伴有这样的疑惑,本篇文章主要讲解JavaScript里几个基础的知识点,帮助大家更好理解js代码。<br>下面是<strong>主要内容</strong>,已经了解的同学可以不用看啦~</p><ul><li>JavaScript原型链</li><li>函数作用域</li></ul><h2 id="JavaScript-原型链"><a href="#JavaScript-原型链" class="headerlink" title="JavaScript 原型链"></a>JavaScript 原型链</h2><p>直接讲述概念可能会特别的抽象,这里举个例子JavaScript原型的使用场景</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Person</span>(<span class="hljs-params">name, age</span>)</span>{ <br> <span class="hljs-built_in">this</span>.name = name;<br> <span class="hljs-built_in">this</span>.age = age;<br>}<br><br>Person.prototype.skill = <span class="hljs-string">"呼吸"</span><br><br><span class="hljs-keyword">let</span> xiaoming = <span class="hljs-keyword">new</span> Person(<span class="hljs-string">"小明"</span>, <span class="hljs-number">18</span>)<br><br><span class="hljs-built_in">console</span>.log(xiaoming)<br></code></pre></td></tr></table></figure><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220306112021201.png" alt="简单原型示意图"></p><p>可以看到我们创建了一个Person的函数(类),然后内部有2个属性<code>name</code>,<code>age</code>,后面再给这个Person的原型上添加<code>skill=呼吸</code> ,最后通过Person创建出来的对象“小明”,除了具备初始传参进去的“名字“和“年龄“,还有一个通用的技能 “呼吸”。假设没有原型,如果要给每个通过Person创建出来的对象添加“呼吸”这个技能就会特别麻烦。不过通过console输出这个对象,会发现 skill这个属性是挂在 <code>__proto__</code>上的,这也是javascript 原型链的特点,</p><blockquote><p>JavaScript 对象有一个指向一个原型对象的链。当试图访问一个对象的属性时,它不仅仅在该对象上搜寻,还会搜寻该对象的原型,以及该对象的原型的原型,依次层层向上搜索,直到找到一个名字匹配的属性或到达原型链的末尾。</p></blockquote><p>通过原型这一特点,也让JavaScript实现了“类”的继承、封装。</p><p>JavaScript原型链还有几个比较重要的概念 (比较容易混淆)</p><p><code>__proto__</code>: 属性<strong>proto</strong>是一个对象,它有两个属性,constructor和<strong>proto</strong></p><p><code>prototype</code>,<code>constructor</code> 原型对象prototype有一个默认的constructor属性,用于记录实例是由哪个构造函数创建</p><p>这个是原型链比较著名的一张图</p><p>比较特殊的是 <strong>Object</strong>、<strong>Funtion</strong> 这2个原生函数</p><p><code>Object.__proto__.__proto</code>__ 指向null</p><p><code>Funtion.__proto__</code> 指向本身的原型Funtion.prototype</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/1183435-20170915105226032-1488063174.jpg" alt="javascript原型、原型链神图"></p><h2 id="作用域"><a href="#作用域" class="headerlink" title="作用域"></a>作用域</h2><p>要理解JavaScript作用域,先得理解什么是<strong>作用域</strong></p><blockquote><p>作用域产生于程序源代码中定义变量的区域,在程序编码阶段就确定了。JavaScript 中分为全局作用域(Global context: <code>window</code>/<code>global</code> )和局部作用域(Local Scope , 又称为函数作用域 Function context)。简单讲作用域就是当前函数的<code>生成环境</code>或者<code>上下文</code>,包含了当前函数内定义的变量以及对外层作用域的引用。</p></blockquote><p>JavaScript里作用域的类型可分为几种</p><ul><li>window/global Scope 全局作用域</li><li>function Scope 函数作用域</li><li>Block Scope 块级作用域</li><li>eval Scope eval作用域</li></ul><p><strong>全局作用域</strong></p><p>在代码中任何地方都能访问到的对象拥有全局作用域,一般来说以下几种情形拥有全局作用域</p><ul><li>最外层函数和在最外层函数外面定义的变量拥有全局作用域</li><li>所有末定义直接赋值的变量自动声明为拥有全局作用域</li><li>所有 window 对象的属性拥有全局作用域</li></ul><p><strong>函数作用域</strong></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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">var</span> val = <span class="hljs-number">1</span> <span class="hljs-comment">// 全局变量</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">init</span>(<span class="hljs-params"></span>) </span>{<br> val2 = <span class="hljs-number">222</span> <span class="hljs-comment">// 未定义的变量直接复制默认是全局变量</span><br> <span class="hljs-keyword">var</span> val3 = <span class="hljs-number">333</span><br>}<br>init()<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>) </span>{<br><span class="hljs-built_in">console</span>.log(val) <span class="hljs-comment">// 1</span><br> <span class="hljs-built_in">console</span>.log(val2) <span class="hljs-comment">// 2</span><br> <span class="hljs-comment">// console.log(val3) // val3属于init函数的内部变量,如果这里使用val3 运行会提示 ReferenceError: val3 is not defined</span><br> <span class="hljs-comment">// test函数的函数作用域内没有val3,顺着作用域链寻找val3也找不到该变量的定义</span><br>}<br>test()<br></code></pre></td></tr></table></figure><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220306124038993.png" alt="demo"></p><h4 id="闭包"><a href="#闭包" class="headerlink" title="闭包"></a>闭包</h4><blockquote><p>一个函数和对其周围状态(<strong>lexical environment,词法环境</strong>)的引用捆绑在一起(或者说函数被引用包围),这样的组合就是<strong>闭包</strong>(<strong>closure</strong>)。也就是说,闭包让你可以在一个内层函数中访问到其外层函数的作用域。在 JavaScript 中,每当创建一个函数,闭包就会在函数创建的同时被创建出来。</p></blockquote><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">makeAdder</span>(<span class="hljs-params">x</span>) </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">y</span>) </span>{<br> <span class="hljs-keyword">return</span> x + y;<br> };<br>}<br><br><span class="hljs-keyword">var</span> add5 = makeAdder(<span class="hljs-number">5</span>);<br><span class="hljs-keyword">var</span> add10 = makeAdder(<span class="hljs-number">10</span>);<br><br><span class="hljs-built_in">console</span>.log(add5(<span class="hljs-number">2</span>)); <span class="hljs-comment">// 7</span><br><span class="hljs-built_in">console</span>.log(add10(<span class="hljs-number">2</span>)); <span class="hljs-comment">// 12</span><br></code></pre></td></tr></table></figure><p>看下这个例子,就可以发现add5,add10 这2个函数中的x不一样,是由函数创建时候的x的值确定的。</p><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><code class="hljs js"><span class="hljs-comment">// 简单的例子</span><br><span class="hljs-function"><span class="hljs-title">init</span>(<span class="hljs-params">key</span>)</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encode</span>(<span class="hljs-params">word</span>) </span>{<br> <span class="hljs-comment">// key 是闭包中的变量</span><br> <span class="hljs-keyword">return</span> key + word<br>}<br>}<br><span class="hljs-keyword">var</span> oneEncodeFn = init(<span class="hljs-string">'hello'</span>)<br><br></code></pre></td></tr></table></figure><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>JavaScript基础知识补给包送到大家手中, 后面一篇将会讲浏览器环境与Node环境的区别,再带大家一起实践下如何补环境。</p>]]></content>
<tags>
<tag>js逆向</tag>
<tag>爬虫</tag>
</tags>
</entry>
<entry>
<title>工欲善其事必先利其器——js逆向基础知识</title>
<link href="/2022/02/28/js-hook/"/>
<url>/2022/02/28/js-hook/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>这几年随着爬虫与反爬的对抗强提升,web端的js被加强了防护。想对js解密也没有以前那么简单,控制台打开,参数一看就可以开始请求接口。<br>所以对爬虫也有一定的技术要求。这里将整理js逆向的一些方法论和技术。</p><h2 id="总览"><a href="#总览" class="headerlink" title="总览"></a>总览</h2><p>自己的一些观点: 对于web端的反爬只要抓住请求是最后的“出口“就能顺着执行思路往回逆向。接口请求是客户端与server的交互,通过一定的参数让服务器“鉴权”通过,认可这个是一个“合法”客户端发出的请求,同时返回信息。反爬的侧重点就到了对接口请求过程的防护了。</p><ul><li>对参数加密</li><li>对协议的特殊处理</li><li>对返回的加密<br>其中加密的手段又通过“混淆”,“重新编码”,“jsvmp”等方式隐藏真正的加密方式<br>payload里也会附加 设备指纹、行为轨迹、等等的信息<br>对于“爬虫”来说,就需要抽丝剥茧,把这些信息获取到进而伪装成正常的用户发起请求<br>那对围绕这个“抽丝剥茧”的事情,就有很多工具方法,通过学习这些基础的知识,可以有效提升逆向效率</li></ul><h2 id="JS-hook"><a href="#JS-hook" class="headerlink" title="JS hook"></a>JS hook</h2><p>JS hook,js里经常用到的技术之一,通过js hook我们可以监听到一个数据的变化和使用的过程。<br>JS hook主要利用了浏览的API<br>1、<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Object/defineProperty">Object.defineProperty()</a> 不兼容IE8以下<br>2、<a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Proxy">Proxy</a> IE 不兼容<br>本质上都是对变量进行监听,可以对对象添加2个方法,get、set方法,get方法在对象使用的时候就会触发,set 是在对对象继续赋值的时候会触发。<br>使用场景之一:document.cookie的监听<br>有一些网站就是加密参数写入cookie中,然后随着请求一起发送到服务器进行校验<br>对于cookie的变更,js hook可以很快找到赋值的位置,再从堆栈中找到参数加密的位置<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br><span class="hljs-meta"> 'use strict'</span>;<br> <span class="hljs-keyword">var</span> cookieTemp = <span class="hljs-string">''</span>;<br> <span class="hljs-built_in">Object</span>.defineProperty(<span class="hljs-built_in">window</span>.byted_acrawler, <span class="hljs-string">'sign'</span>, {<br> <span class="hljs-attr">set</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">val</span>) </span>{<br> <span class="hljs-comment">// 通过判断含有特定的cookie 再断点</span><br> <span class="hljs-comment">// if (val.indexOf('name') != -1) {</span><br><span class="hljs-comment">// }</span><br><span class="hljs-keyword">debugger</span>;<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'Hook捕获到cookie设置->'</span>, val);<br> cookieTemp = val;<br> <span class="hljs-keyword">return</span> val;<br> },<br> <span class="hljs-attr">get</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">return</span> cookieTemp;<br> },<br> });<br>})();<br></code></pre></td></tr></table></figure><br>利用Proxy监听数据的改变也一样<br>下面是一个基础的proxy函数</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><code class="hljs js"><span class="hljs-keyword">const</span> proxy = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">obj</span>) </span>{<br><span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Proxy</span>(obj, {<br><span class="hljs-attr">set</span>: <span class="hljs-function">(<span class="hljs-params">target, prop, val</span>) =></span> {<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"SET>>>>"</span>, prop, prop, val)<br><span class="hljs-keyword">return</span> <span class="hljs-built_in">Reflect</span>.set(...arguments);<br>},<br><span class="hljs-attr">get</span>: <span class="hljs-function">(<span class="hljs-params">target, prop, r</span>) =></span> {<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"GET>>>>"</span>, prop, prop, target[prop])<br><span class="hljs-keyword">return</span> target[prop]<br>},<br>})<br>} <br><span class="hljs-comment">// 假设要对window进行监听</span><br><span class="hljs-built_in">window</span> = proxy(<span class="hljs-built_in">window</span>)<br></code></pre></td></tr></table></figure><h2 id="利用Chrome开发者工具与vscode(IDE)"><a href="#利用Chrome开发者工具与vscode(IDE)" class="headerlink" title="利用Chrome开发者工具与vscode(IDE)"></a>利用Chrome开发者工具与vscode(IDE)</h2><p>利用浏览器开发者工具或者vscode调试界面对js代码调试,能够方便我们理解加密逻辑<br>这里介绍几个方法和技巧</p><h4 id="Chrome浏览器断点"><a href="#Chrome浏览器断点" class="headerlink" title="Chrome浏览器断点"></a>Chrome浏览器断点</h4><p>先配置Chrome浏览器的开发者工具的语言环境</p><p>F12打开控制台,最上面的工具栏右侧有个齿轮,点击可以找到preference=> language => 设置中文</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/%E6%88%AA%E5%B1%8F2022-03-01%20%E4%B8%8B%E5%8D%8810.31.24.png" alt="截屏2022-03-01 下午10.31.24"></p><p>这时候就变成中文的控制面板,对于不熟悉Chrome 开发中工具且英文不好的同学来说是比较好的帮助</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220301223526150.png" alt="image-20220301223526150"></p><h5 id="Chrome浏览打断点的几个技巧"><a href="#Chrome浏览打断点的几个技巧" class="headerlink" title="Chrome浏览打断点的几个技巧"></a>Chrome浏览打断点的几个技巧</h5><p><strong>添加断点</strong> </p><p>顾名思义就是加断点,这个没有什么好讲的</p><p><strong>添加条件断点</strong> </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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">if</span>(condition) {<br><span class="hljs-keyword">debugger</span><br>}<br></code></pre></td></tr></table></figure><p><strong>添加日志点</strong></p><p>添加日志输出点则是每运行到该行进行输出信息,用的场景比较多是研究加密算法运算过程某些值的变化规律</p><p><strong>一律不在此处断点</strong></p><p>这个功能则是对一些简单的反爬debugger进行忽略</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/%E6%88%AA%E5%B1%8F2022-03-01%20%E4%B8%8B%E5%8D%8810.36.12.png" alt="截屏2022-03-01 下午10.36.12"></p><h5 id="Chrome浏览器的network"><a href="#Chrome浏览器的network" class="headerlink" title="Chrome浏览器的network"></a>Chrome浏览器的network</h5><p>可以观察到的几个点</p><ul><li>请求的顺序</li><li>请求的协议以及请求体、响应体</li><li>调用的堆栈信息</li></ul><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220301224342082.png" alt="image-20220301224342082"></p><p><strong>network</strong>给出的关键信息也能辅助我们快速定位到接口发起的地方,从而逆向参数生成的位置</p><h5 id="Chrome-油猴脚本插件"><a href="#Chrome-油猴脚本插件" class="headerlink" title="Chrome 油猴脚本插件"></a>Chrome 油猴脚本插件</h5><p>油猴脚本注入的js能够页面加载之前进去,所以js逻辑生效的时机也能够特别早</p><p>对于cookie的hook,可以通过油猴脚本的方式注入,第一时间能够观察到cookie的变化</p><h2 id="抓包"><a href="#抓包" class="headerlink" title="抓包"></a>抓包</h2><p>Win、Mac 都有抓包的工具<br>这里主要讲下Mac里使用的抓包工具 Charles<br>通过抓包工具可以看到一些跳页的请求,network里有时候难以看到的请求,还能通过Charles 代理某些地址到本地文件<br>比如首页替换、js替换等等</p><p><strong>Tool -> Map local Settings -> add -> Edit Mapping</strong></p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220302224039568.png" alt="Map local设置"></p><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>2022年03月02日 暂时先补充这么多方法和技巧,后续学习中还有别的了解会继续添加在上面</p>]]></content>
<tags>
<tag>js逆向</tag>
<tag>爬虫</tag>
</tags>
</entry>
<entry>
<title>GO 语言初体验</title>
<link href="/2022/02/06/2022-02-06-go/"/>
<url>/2022/02/06/2022-02-06-go/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>笔者是做前端开发工作的,对Js、Nodejs比较熟悉。对其他技术不熟悉但是非常愿意接触,前端构建的工具链中也逐步有其他语言的接入,比如esbuild(GO语言), swc(RUST)。率性就学习下GO语言,拓展自己的技术宽度。</p><h2 id="GO-语言"><a href="#GO-语言" class="headerlink" title="GO 语言"></a>GO 语言</h2><p>相信大家网上随便搜一下都能找到介绍GO语言的,这里我就不重复说了。</p><blockquote><p>Go 语言具有很强的表达能力,它简洁、清晰而高效。得益于其并发机制, 用它编写的程序能够非常有效地利用多核与联网的计算机,其新颖的类型系统则使程序结构变得灵活而模块化。 Go 代码编译成机器码不仅非常迅速,还具有方便的垃圾收集机制和强大的运行时反射机制。 它是一个快速的、静态类型的编译型语言,感觉却像动态类型的解释型语言。</p></blockquote><p>简单总结一下就是:<strong>易学习</strong>、<strong>高性能</strong>、<strong>跨平台</strong></p><h2 id="GO-语言之旅"><a href="#GO-语言之旅" class="headerlink" title="GO 语言之旅"></a>GO 语言之旅</h2><p>笔者是通过 <a href="https://tour.go-zh.org/welcome/1">《GO语言之旅》</a>这个教程初步学习的,大家也可以通过这个简单的线上课程去了解GO语言的一些基本特性和使用方式。</p><h3 id="包"><a href="#包" class="headerlink" title="包"></a>包</h3><p>go 语言和javascript相似也是有包管理的机制,不过与npm包不一样,不需要发布,直接通过线上的地址导入<br>举个例子,下面的例子就是使用第三方包log包去打印日志。 <code>github.com/sirupsen/logrus</code> 则是导入的地址<br><figure class="highlight go"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> log <span class="hljs-string">"github.com/sirupsen/logrus"</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br>log.Infof(<span class="hljs-string">"Hello world!"</span>)<br>}<br></code></pre></td></tr></table></figure><br>现在笔者接触的go包管理一般用的是go module管理,go.mod配置文件是下面这样的结构。<br>这两行和 npm package.json 的 name 字段的功能很类似。定义了模块名称</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><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash">// go.mod<br>module github.com/sirupsen/logrus<br><br>go 1.12<br></code></pre></td></tr></table></figure><p>go 模块管理在使用的时候因为不熟悉还踩了不少的坑,比如不能互相导入会导致编译器报错。如果想了解更多的go module的学习可以阅读<a href="https://colobu.com/2021/06/28/dive-into-go-module-1/">深入Go Module之go.mod文件解析</a></p><h3 id="练习题"><a href="#练习题" class="headerlink" title="练习题"></a>练习题</h3><p>直接通过练习题的讲解,可以更快速的让大家了解语言特性,其他想要学习了解GO语言的同学也可以自己先学习下GO语言课程里做一遍练习题,没有思路再通过笔者的注释学习。</p><h4 id="练习:循环与函数"><a href="#练习:循环与函数" class="headerlink" title="练习:循环与函数"></a>练习:循环与函数</h4><p>题目(一)<a href="https://tour.go-zh.org/flowcontrol/8">练习:循环与函数</a><br>利用循环计算出近似值</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br> <span class="hljs-string">"fmt"</span><br> <span class="hljs-string">"math"</span><br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Sqrt</span><span class="hljs-params">(x <span class="hljs-keyword">float64</span>)</span> <span class="hljs-title">float64</span></span> {<br> z := <span class="hljs-number">1.0</span><span class="hljs-comment">// 定义一个初始值并对它初始化</span><br> temp := <span class="hljs-number">0.0</span><span class="hljs-comment">// 临时变量,作为记录z 上次的值</span><br> <span class="hljs-comment">// for 循环</span><br> <span class="hljs-keyword">for</span> { <br> z = z - (z*z-x)/(<span class="hljs-number">2</span>*z)<span class="hljs-comment">// 计算出最新的z值</span><br> fmt.Println(z)<br> <span class="hljs-keyword">if</span> math.Abs(z-temp) < <span class="hljs-number">0.000000000000001</span> {<br> <span class="hljs-keyword">break</span><span class="hljs-comment">// 当值停止改变(或改变非常小)的时候退出循环</span><br> } <span class="hljs-keyword">else</span> {<br> temp = z<span class="hljs-comment">// 赋值最终的结果</span><br> }<br> }<br> <span class="hljs-keyword">return</span> z<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> fmt.Println(<span class="hljs-string">"猜测:"</span>, Sqrt(<span class="hljs-number">2</span>))<br> fmt.Println(<span class="hljs-string">"math.Sqrt(2):"</span>, math.Sqrt(<span class="hljs-number">2</span>))<br>}<br></code></pre></td></tr></table></figure><h4 id="练习:切片"><a href="#练习:切片" class="headerlink" title="练习:切片"></a>练习:切片</h4><p>题目(二)<a href="https://tour.go-zh.org/moretypes/18">练习:切片</a><br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> <span class="hljs-string">"golang.org/x/tour/pic"</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Pic</span><span class="hljs-params">(dx, dy <span class="hljs-keyword">int</span>)</span> [][]<span class="hljs-title">uint8</span></span> {<br> a := <span class="hljs-built_in">make</span>([][]<span class="hljs-keyword">uint8</span>,dy) <span class="hljs-comment">//外层切片</span><br> <span class="hljs-keyword">for</span> x := <span class="hljs-keyword">range</span> a{<br> b := <span class="hljs-built_in">make</span>([]<span class="hljs-keyword">uint8</span>,dx) <span class="hljs-comment">//里层切片</span><br> <span class="hljs-keyword">for</span> y := <span class="hljs-keyword">range</span> b{<br> b[y] = <span class="hljs-keyword">uint8</span>(x*y - <span class="hljs-number">1</span>) <span class="hljs-comment">//给里层切片里的每一个元素赋值。其中x*y可以替换成别的函数 形成不同图形</span><br> <span class="hljs-comment">// b[y] = uint8(x^y) </span><br> }<br> a[x] = b <span class="hljs-comment">//给外层切片里的每一个元素赋值</span><br> }<br> <span class="hljs-keyword">return</span> a<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> pic.Show(Pic)<br>}<br></code></pre></td></tr></table></figure></p><h4 id="练习:映射"><a href="#练习:映射" class="headerlink" title="练习:映射"></a>练习:映射</h4><p>题目(三)<a href="https://tour.go-zh.org/moretypes/23">练习:映射</a></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br> <span class="hljs-string">"golang.org/x/tour/wc"</span><br> <span class="hljs-string">"strings"</span><br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">WordCount</span><span class="hljs-params">(s <span class="hljs-keyword">string</span>)</span> <span class="hljs-title">map</span>[<span class="hljs-title">string</span>]<span class="hljs-title">int</span></span> {<br> m := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>) <span class="hljs-comment">// 创建映射</span><br> c := strings.Fields(s) <span class="hljs-comment">// 以[]string形式返回</span><br> <span class="hljs-keyword">for</span> _, v := <span class="hljs-keyword">range</span> c { <span class="hljs-comment">//每出现相同的单词(字符串)</span><br> m[v] += <span class="hljs-number">1</span> <span class="hljs-comment">//出现次数就 + 1 </span><br> }<br> <span class="hljs-keyword">return</span> m<br>}<br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> wc.Test(WordCount)<br>}<br></code></pre></td></tr></table></figure><h4 id="练习:斐波纳契闭包"><a href="#练习:斐波纳契闭包" class="headerlink" title="练习:斐波纳契闭包"></a>练习:斐波纳契闭包</h4><p>题目(四)<a href="https://tour.go-zh.org/moretypes/26">练习:斐波纳契闭包</a></p><p>和js有点相似,函数使用外部的变量,使变量变成必成函数闭包变量, 变量拥有“临时存储”功能<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span><br><br><span class="hljs-comment">// 返回一个“返回int的函数”</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">fibonacci</span><span class="hljs-params">()</span> <span class="hljs-title">func</span><span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {<br>a, b:= <span class="hljs-number">0</span>,<span class="hljs-number">1</span><br><span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">()</span> <span class="hljs-title">int</span></span> {<br>temp := a<br>a, b = b, (a+b)<br><span class="hljs-keyword">return</span> temp<br>}<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br>f := fibonacci()<br><span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < <span class="hljs-number">10</span>; i++ {<br>fmt.Println(f())<br>}<br>}<br><br></code></pre></td></tr></table></figure></p><h4 id="练习:Stringer"><a href="#练习:Stringer" class="headerlink" title="练习:Stringer"></a>练习:Stringer</h4><p>题目(五)<a href="https://tour.go-zh.org/methods/18">练习:Stringer</a></p><p>对于fmt的Printf方法会调用类型关联的string方法,在前面类型的学习中可以知道 对于入参参数只有1个且为该类型的函数可以通过 typeName.xxx 去调用函数</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><span class="hljs-keyword">import</span> <span class="hljs-string">"fmt"</span><br><br><span class="hljs-keyword">type</span> IPAddr [<span class="hljs-number">4</span>]<span class="hljs-keyword">byte</span><br><br><span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Add a "String() string" method to IPAddr.</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(v IPAddr)</span> <span class="hljs-title">String</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span>{ <br> <span class="hljs-keyword">return</span> fmt.Sprintf(<span class="hljs-string">"%v.%v.%v.%v"</span>, v[<span class="hljs-number">0</span>],v[<span class="hljs-number">1</span>],v[<span class="hljs-number">2</span>],v[<span class="hljs-number">3</span>]) <br>} <br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> hosts := <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]IPAddr{<br> <span class="hljs-string">"loopback"</span>: {<span class="hljs-number">127</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>, <span class="hljs-number">1</span>},<br> <span class="hljs-string">"googleDNS"</span>: {<span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>, <span class="hljs-number">8</span>},<br> }<br> <span class="hljs-keyword">for</span> name, ip := <span class="hljs-keyword">range</span> hosts {<br> fmt.Printf(<span class="hljs-string">"%v: %v\n"</span>, name, ip)<br> }<br>}<br></code></pre></td></tr></table></figure><h4 id="练习:错误"><a href="#练习:错误" class="headerlink" title="练习:错误"></a>练习:错误</h4><p>题目(六)<a href="https://tour.go-zh.org/methods/20">练习:错误</a><br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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><code class="hljs go"><br><span class="hljs-comment">// 待补充 为啥fmt.Sprintf 中e 直接写入会无限死循环</span><br><br><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">"fmt"</span><br><span class="hljs-string">"math"</span><br>)<br><span class="hljs-comment">// 定义类型</span><br><span class="hljs-keyword">type</span> ErrNegativeSqrt <span class="hljs-keyword">float64</span><br><br><span class="hljs-comment">// 重写Error()</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(e ErrNegativeSqrt)</span> <span class="hljs-title">Error</span><span class="hljs-params">()</span> <span class="hljs-title">string</span></span> {<br><span class="hljs-keyword">return</span> fmt.Sprintf(<span class="hljs-string">"cannot Sqrt negative number: %v"</span>, <span class="hljs-keyword">float64</span>(e))<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Sqrt</span><span class="hljs-params">(x <span class="hljs-keyword">float64</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">float64</span>, error)</span></span> {<br><span class="hljs-keyword">if</span> x < <span class="hljs-number">0</span> {<br><span class="hljs-keyword">return</span> <span class="hljs-number">0</span>, ErrNegativeSqrt(x)<br>}<br><span class="hljs-keyword">return</span> math.Sqrt(x), <span class="hljs-literal">nil</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br><span class="hljs-comment">//这里只是简单的打印</span><br>fmt.Println(Sqrt(<span class="hljs-number">2</span>))<br>fmt.Println(Sqrt(<span class="hljs-number">-2</span>))<br>}<br><br></code></pre></td></tr></table></figure></p><h4 id="练习:Reader"><a href="#练习:Reader" class="headerlink" title="练习:Reader"></a>练习:Reader</h4><p>题目(七)<a href="https://tour.go-zh.org/methods/22">练习:Reader</a><br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br> <span class="hljs-string">"golang.org/x/tour/reader"</span><br><br>)<br><br><span class="hljs-keyword">type</span> MyReader <span class="hljs-keyword">struct</span>{}<br><br><span class="hljs-comment">// <span class="hljs-doctag">TODO:</span> Add a Read([]byte) (int, error) method to MyReader.</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(r MyReader)</span> <span class="hljs-title">Read</span><span class="hljs-params">(b []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {<br> <span class="hljs-comment">// 赋值并返回</span><br> b[<span class="hljs-number">0</span>] = <span class="hljs-string">'A'</span><br> <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>, <span class="hljs-literal">nil</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> reader.Validate(MyReader{})<br>}<br><br></code></pre></td></tr></table></figure></p><h4 id="练习:rot13Reader"><a href="#练习:rot13Reader" class="headerlink" title="练习:rot13Reader"></a>练习:rot13Reader</h4><p>题目(八)<a href="https://tour.go-zh.org/methods/23">练习:rot13Reader</a></p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br> <span class="hljs-string">"io"</span><br> <span class="hljs-string">"os"</span><br> <span class="hljs-string">"strings"</span><br>)<br><br><span class="hljs-keyword">type</span> rot13Reader <span class="hljs-keyword">struct</span> {<br> r io.Reader<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(rot rot13Reader)</span> <span class="hljs-title">Read</span><span class="hljs-params">(p []<span class="hljs-keyword">byte</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">int</span>, error)</span></span> {<br> <span class="hljs-comment">// 读取字符串数组</span><br> n, err := rot.r.Read(p)<br> <span class="hljs-keyword">for</span> i := <span class="hljs-number">0</span>; i < n; i++ {<br> p[i] = rot13(p[i])<br> }<br> <span class="hljs-keyword">return</span> n, err<br><br>}<br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">rot13</span><span class="hljs-params">(b <span class="hljs-keyword">byte</span>)</span> <span class="hljs-title">byte</span></span> {<br> <span class="hljs-keyword">if</span> (b >= <span class="hljs-string">'A'</span> && b <= <span class="hljs-string">'M'</span>) || (b >= <span class="hljs-string">'a'</span> && b <= <span class="hljs-string">'m'</span>) {<br> b += <span class="hljs-number">13</span><br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (b >= <span class="hljs-string">'N'</span> && b <= <span class="hljs-string">'Z'</span>) || (b >= <span class="hljs-string">'n'</span> && b <= <span class="hljs-string">'z'</span>) {<br> b -= <span class="hljs-number">13</span><br> }<br> <span class="hljs-keyword">return</span> b<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> s := strings.NewReader(<span class="hljs-string">"Lbh penpxrq gur pbqr!"</span>)<br> r := rot13Reader{s}<br> io.Copy(os.Stdout, &r)<br>}<br></code></pre></td></tr></table></figure><h4 id="练习:图像"><a href="#练习:图像" class="headerlink" title="练习:图像"></a>练习:图像</h4><p>题目(九)<a href="https://tour.go-zh.org/methods/25">练习:图像</a></p><p>对类型定义不同的方法,这里有点像类的成员函数<br><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br> <span class="hljs-string">"golang.org/x/tour/pic"</span><br> <span class="hljs-string">"image/color"</span><br> <span class="hljs-string">"image"</span><br>)<br><br><span class="hljs-keyword">type</span> Image <span class="hljs-keyword">struct</span>{} <span class="hljs-comment">//新建一个Image结构体</span><br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(i Image)</span> <span class="hljs-title">ColorModel</span><span class="hljs-params">()</span> <span class="hljs-title">color</span>.<span class="hljs-title">Model</span></span>{ <span class="hljs-comment">//实现Image包中颜色模式的方法</span><br> <span class="hljs-keyword">return</span> color.RGBAModel<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(i Image)</span> <span class="hljs-title">Bounds</span><span class="hljs-params">()</span> <span class="hljs-title">image</span>.<span class="hljs-title">Rectangle</span></span>{ <span class="hljs-comment">//实现Image包中生成图片边界的方法</span><br> <span class="hljs-keyword">return</span> image.Rect(<span class="hljs-number">0</span>,<span class="hljs-number">0</span>,<span class="hljs-number">200</span>,<span class="hljs-number">200</span>)<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(i Image)</span> <span class="hljs-title">At</span><span class="hljs-params">(x,y <span class="hljs-keyword">int</span>)</span> <span class="hljs-title">color</span>.<span class="hljs-title">Color</span></span>{ <span class="hljs-comment">//实现Image包中生成图像某个点的方法</span><br> <span class="hljs-keyword">return</span> color.RGBA{<span class="hljs-keyword">uint8</span>(x),<span class="hljs-keyword">uint8</span>(y),<span class="hljs-keyword">uint8</span>(<span class="hljs-number">255</span>),<span class="hljs-keyword">uint8</span>(<span class="hljs-number">255</span>)}<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> m := Image{}<br> pic.ShowImage(m) <span class="hljs-comment">//调用</span><br>}<br><br></code></pre></td></tr></table></figure></p><h4 id="练习:等价二叉查找树"><a href="#练习:等价二叉查找树" class="headerlink" title="练习:等价二叉查找树"></a>练习:等价二叉查找树</h4><p>题目(九)<a href="https://tour.go-zh.org/concurrency/8">练习:等价二叉查找树</a></p><p>这个练习题主要让我们了解如何使用信道实现函数运行时的通信问题</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">"golang.org/x/tour/tree"</span><br><span class="hljs-string">"fmt"</span><br>)<br><span class="hljs-comment">// 发送value,结束后关闭channel</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Walk</span><span class="hljs-params">(t *tree.Tree, ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span>{<br> sendValue(t,ch)<br> <span class="hljs-built_in">close</span>(ch)<br>}<br><span class="hljs-comment">// 递归向channel传值</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">sendValue</span><span class="hljs-params">(t *tree.Tree, ch <span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)</span></span>{<br> <span class="hljs-keyword">if</span> t != <span class="hljs-literal">nil</span> {<br> sendValue(t.Left, ch)<br> ch <- t.Value<br> sendValue(t.Right, ch)<br> }<br>}<br><br><span class="hljs-comment">// 使用写好的Walk函数来确定两个tree对象 是否一样 原理还是判断value值</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Same</span><span class="hljs-params">(t1, t2 *tree.Tree)</span> <span class="hljs-title">bool</span></span> {<br> ch1 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)<br> ch2 := <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)<br> <span class="hljs-keyword">go</span> Walk(t1,ch1)<br> <span class="hljs-keyword">go</span> Walk(t2,ch2)<br> <span class="hljs-keyword">for</span> i := <span class="hljs-keyword">range</span> ch1 { <span class="hljs-comment">// ch1 关闭后 for循环自动跳出</span><br> <span class="hljs-keyword">if</span> i != <- ch2 {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br> <br> <span class="hljs-comment">// 打印 tree.New(1)的值</span><br> <span class="hljs-keyword">var</span> ch = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">chan</span> <span class="hljs-keyword">int</span>)<br> <span class="hljs-keyword">go</span> Walk(tree.New(<span class="hljs-number">1</span>),ch)<br> <span class="hljs-keyword">for</span> v := <span class="hljs-keyword">range</span> ch {<br> fmt.Println(v)<br> }<br> <br> <span class="hljs-comment">// 比较两个tree的value值是否相等</span><br> fmt.Println(Same(tree.New(<span class="hljs-number">1</span>), tree.New(<span class="hljs-number">1</span>)))<br> fmt.Println(Same(tree.New(<span class="hljs-number">1</span>), tree.New(<span class="hljs-number">2</span>)))<br>}<br></code></pre></td></tr></table></figure><h4 id="练习:Web-爬虫"><a href="#练习:Web-爬虫" class="headerlink" title="练习:Web 爬虫"></a>练习:Web 爬虫</h4><p>题目(十)<a href="https://tour.go-zh.org/concurrency/10">练习:Web 爬虫</a><br>这个练习题是练习对信道和锁的使用</p><figure class="highlight go"><table><tr><td class="gutter"><pre><span class="line">1</span><br><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></pre></td><td class="code"><pre><code class="hljs go"><span class="hljs-keyword">package</span> main<br><br><br><span class="hljs-keyword">import</span> (<br><span class="hljs-string">"fmt"</span><br><span class="hljs-string">"sync"</span><br>)<br><br><span class="hljs-keyword">type</span> Fetcher <span class="hljs-keyword">interface</span> {<br><span class="hljs-comment">// Fetch 返回 URL 的 body 内容,并且将在这个页面上找到的 URL 放到一个 slice 中。</span><br>Fetch(url <span class="hljs-keyword">string</span>) (body <span class="hljs-keyword">string</span>, urls []<span class="hljs-keyword">string</span>, err error)<br>}<br><br><span class="hljs-keyword">var</span> (<br><span class="hljs-comment">// map 存放爬取的url</span><br>m = <span class="hljs-built_in">make</span>(<span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]<span class="hljs-keyword">int</span>)<br><span class="hljs-comment">// 互斥锁</span><br>l sync.Mutex<br><span class="hljs-comment">// 群组等待 当添加的任务没有完成时(done()), wait() 会一直等待 三个方法 Add() Done() Wait()</span><br>i sync.WaitGroup<br>)<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">main</span><span class="hljs-params">()</span></span> {<br>i.Add(<span class="hljs-number">1</span>)<br>Crawl(<span class="hljs-string">"http://golang.org/"</span>, <span class="hljs-number">4</span>, fetcher)<br><br>i.Wait()<span class="hljs-comment">// 会一直等待直到子线程任务结束</span><br><br><span class="hljs-keyword">for</span> k, _ := <span class="hljs-keyword">range</span> m {<br>fmt.Println(k)<br>}<br>fmt.Println(<span class="hljs-string">"over"</span>)<br>}<br><br><span class="hljs-comment">// Crawl 使用 fetcher 从某个 URL 开始递归的爬取页面,直到达到最大深度。</span><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-title">Crawl</span><span class="hljs-params">(url <span class="hljs-keyword">string</span>, depth <span class="hljs-keyword">int</span>, fetcher Fetcher)</span></span> {<br><br><span class="hljs-keyword">defer</span> i.Done() <span class="hljs-comment">// 和add相对应</span><br><span class="hljs-keyword">if</span> depth <= <span class="hljs-number">0</span> {<br><span class="hljs-keyword">return</span><br>}<br>_, urls, err := fetcher.Fetch(url)<br><span class="hljs-keyword">if</span> err != <span class="hljs-literal">nil</span> {<br><span class="hljs-comment">//fmt.Println(err)</span><br><span class="hljs-keyword">return</span><br>}<br><span class="hljs-comment">// 存入数据 需要同步锁 因为这是在子线程中</span><br>l.Lock()<br><span class="hljs-keyword">if</span> m[url] == <span class="hljs-number">0</span> { <span class="hljs-comment">// 还未爬取过</span><br>m[url]++ <span class="hljs-comment">// 存入爬取的url 改变对应的标示</span><br>depth--<br><span class="hljs-comment">//fmt.Printf("found: %s %q\n", url, body)</span><br><span class="hljs-keyword">for</span> _, u := <span class="hljs-keyword">range</span> urls {<br>i.Add(<span class="hljs-number">1</span>)<br><span class="hljs-keyword">go</span> Crawl(u, depth, fetcher) <span class="hljs-comment">// 继续爬取</span><br>}<br>}<br>l.Unlock()<br><br>}<br><br><span class="hljs-comment">// fakeFetcher 是返回若干结果的 Fetcher。</span><br><span class="hljs-keyword">type</span> fakeFetcher <span class="hljs-keyword">map</span>[<span class="hljs-keyword">string</span>]*fakeResult<br><br><span class="hljs-keyword">type</span> fakeResult <span class="hljs-keyword">struct</span> {<br>body <span class="hljs-keyword">string</span><br>urls []<span class="hljs-keyword">string</span><br>}<br><br><span class="hljs-function"><span class="hljs-keyword">func</span> <span class="hljs-params">(f fakeFetcher)</span> <span class="hljs-title">Fetch</span><span class="hljs-params">(url <span class="hljs-keyword">string</span>)</span> <span class="hljs-params">(<span class="hljs-keyword">string</span>, []<span class="hljs-keyword">string</span>, error)</span></span> {<br><span class="hljs-keyword">if</span> res, ok := f[url]; ok {<br><span class="hljs-keyword">return</span> res.body, res.urls, <span class="hljs-literal">nil</span><br>}<br><span class="hljs-keyword">return</span> <span class="hljs-string">""</span>, <span class="hljs-literal">nil</span>, fmt.Errorf(<span class="hljs-string">"not found: %s"</span>, url)<br>}<br><br><span class="hljs-comment">// fetcher 是填充后的 fakeFetcher。</span><br><span class="hljs-keyword">var</span> fetcher = fakeFetcher{<br><span class="hljs-string">"http://golang.org/"</span>: &fakeResult{<br><span class="hljs-string">"The Go Programming Language"</span>,<br>[]<span class="hljs-keyword">string</span>{<br><span class="hljs-string">"http://golang.org/pkg/"</span>,<br><span class="hljs-string">"http://golang.org/cmd/"</span>,<br>},<br>},<br><span class="hljs-string">"http://golang.org/pkg/"</span>: &fakeResult{<br><span class="hljs-string">"Packages"</span>,<br>[]<span class="hljs-keyword">string</span>{<br><span class="hljs-string">"http://golang.org/"</span>,<br><span class="hljs-string">"http://golang.org/cmd/"</span>,<br><span class="hljs-string">"http://golang.org/pkg/fmt/"</span>,<br><span class="hljs-string">"http://golang.org/pkg/os/"</span>,<br>},<br>},<br><span class="hljs-string">"http://golang.org/pkg/fmt/"</span>: &fakeResult{<br><span class="hljs-string">"Package fmt"</span>,<br>[]<span class="hljs-keyword">string</span>{<br><span class="hljs-string">"http://golang.org/"</span>,<br><span class="hljs-string">"http://golang.org/pkg/"</span>,<br>},<br>},<br><span class="hljs-string">"http://golang.org/pkg/os/"</span>: &fakeResult{<br><span class="hljs-string">"Package os"</span>,<br>[]<span class="hljs-keyword">string</span>{<br><span class="hljs-string">"http://golang.org/"</span>,<br><span class="hljs-string">"http://golang.org/pkg/"</span>,<br>},<br>},<br>}<br><br></code></pre></td></tr></table></figure><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>通过该课程学习和这些练习题,想必大家都能过了解GO语言怎么写了。在学习GO语言的过程,和前端js编码最大的不同是在编码之前对自己想要的东西必须认知比较的清晰,在后续使用都是类型推断辅助编码。还有一个特定就是信道、go程、锁的了解知道了为啥go语言能够拥有高性能。最后自己也尝试编写一个开源项目去锻炼自己的go语言编程能力</p>]]></content>
<tags>
<tag>go</tag>
<tag>编程</tag>
</tags>
</entry>
<entry>
<title>Rollup原理分析(三)如何编写一个插件</title>
<link href="/2022/01/11/rollup-plugin/"/>
<url>/2022/01/11/rollup-plugin/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>当前分析的Rollup版本:<code>2.63.0</code></p><p>经过Rollup源码分析(一)、(二)篇幅的学习,大家对 Rollup 基础都有一定的了解。是否对 Rollup 插件想要更深入的了解呢?比如一个插件究竟怎么写出来的,一个插件的编写思路是怎样的。今天就让我带大家来学习一下,如何编写一个 Rollup 插件。</p><h2 id="常用插件功能"><a href="#常用插件功能" class="headerlink" title="常用插件功能"></a>常用插件功能</h2><p>对 Rollup插件编写需要对插件各个环节的钩子有所了解,才能比较好的实现插件。但是如果一上来就看钩子介绍源码,大家肯定受不了,看不下去,很难理论和实际结合去思考。所以我们先去了解下常用的插件功能是什么,怎么实现的。</p><p>让我们看看官方插件列表:<a href="https://github.com/rollup/plugins"> The one-stop shop for official Rollup plugins</a></p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220111215001972.png" alt="插件列表"></p><h4 id="url-插件"><a href="#url-插件" class="headerlink" title="url 插件"></a>url 插件</h4><p>代码仓库地址:<a href="https://github.com/rollup/plugins/tree/master/packages/url">https://github.com/rollup/plugins/tree/master/packages/url</a></p><p>用途:用base64 编码数据或者ES module的方式导入文件 <em>(<strong>Import files as data-URIs or ES Modules</strong>)</em></p><p>先看下<strong><code>目录结构</code></strong>,目录结构还比较整齐的。包含 <code>readme</code>、<code>变更日志</code>、<code>类型</code>、<code>测试用例</code>、<code>源码</code></p><p>后面我们自己去写一个插件也应该包含这些文件,才比较符合规范。</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220111215757356.png" alt="目录结构"></p><p>再看下源码,分 2 个钩子处理</p><p><strong>load</strong>: 加载钩子,在文件加载的时候判断先判断是否符合,不符合退出,符合则进行下一步。符合大小限制则用 data-uri 的方式内联文件,不是的话则返回<strong>ESmodule</strong> 的方式导入</p><p><strong>generateBundle</strong> 阶段: 生成 Bundle 触发的钩子,主要对文件的一个 copy 处理,移动到 publicPath 目录下</p><p>这样看就比较清晰了,这个插件运用了 2 个钩子就能够达到对 文件 url 的处理。</p><figure class="highlight typescript"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></pre></div></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-comment">// ...</span><br><span class="hljs-keyword">const</span> defaultInclude = [<span class="hljs-string">'**/*.svg'</span>, <span class="hljs-string">'**/*.png'</span>, <span class="hljs-string">'**/*.jp(e)?g'</span>, <span class="hljs-string">'**/*.gif'</span>, <span class="hljs-string">'**/*.webp'</span>];<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">url</span>(<span class="hljs-params">options = {}</span>) </span>{<br> <span class="hljs-keyword">const</span> {<br> limit = <span class="hljs-number">14</span> * <span class="hljs-number">1024</span>,<br> include = defaultInclude,<br> exclude,<br> publicPath = <span class="hljs-string">''</span>,<br> emitFiles = <span class="hljs-literal">true</span>,<br> fileName = <span class="hljs-string">'[hash][extname]'</span><br> } = options;<br> <span class="hljs-keyword">const</span> filter = createFilter(include, exclude);<br><br> <span class="hljs-keyword">const</span> copies = <span class="hljs-built_in">Object</span>.create(<span class="hljs-literal">null</span>);<br><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'url'</span>,<br> <span class="hljs-comment">// 加载文件钩子事件</span><br> <span class="hljs-function"><span class="hljs-title">load</span>(<span class="hljs-params">id</span>)</span> {<br> <span class="hljs-keyword">if</span> (!filter(id)) {<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">Promise</span>.all([fsStatPromise(id), fsReadFilePromise(id)]).then(<span class="hljs-function">(<span class="hljs-params">[stats, buffer]</span>) =></span> {<br> <span class="hljs-keyword">let</span> data;<br> <span class="hljs-comment">// 如果文件大小限制大于 limit 或者limit 为0</span><br> <span class="hljs-comment">// 采用 copy 的方式处理</span><br> <span class="hljs-keyword">if</span> ((limit && stats.size > limit) || limit === <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">const</span> hash = crypto.createHash(<span class="hljs-string">'sha1'</span>).update(buffer).digest(<span class="hljs-string">'hex'</span>).substr(<span class="hljs-number">0</span>, <span class="hljs-number">16</span>);<br> <span class="hljs-keyword">const</span> ext = path.extname(id);<br> <span class="hljs-keyword">const</span> name = path.basename(id, ext);<br> <span class="hljs-comment">// Determine the directory name of the file based</span><br> <span class="hljs-comment">// on either the relative path provided in options,</span><br> <span class="hljs-comment">// or the parent directory</span><br> <span class="hljs-keyword">const</span> relativeDir = options.sourceDir<br> ? path.relative(options.sourceDir, path.dirname(id))<br> : path.dirname(id).split(sep).pop();<br><br> <span class="hljs-comment">// Generate the output file name based on some string</span><br> <span class="hljs-comment">// replacement parameters</span><br> <span class="hljs-keyword">const</span> outputFileName = fileName<br> .replace(<span class="hljs-regexp">/\[hash\]/g</span>, hash)<br> .replace(<span class="hljs-regexp">/\[extname\]/g</span>, ext)<br> <span class="hljs-comment">// use `sep` for windows environments</span><br> .replace(<span class="hljs-regexp">/\[dirname\]/g</span>, relativeDir === <span class="hljs-string">''</span> ? <span class="hljs-string">''</span> : <span class="hljs-string">`<span class="hljs-subst">${relativeDir}</span><span class="hljs-subst">${sep}</span>`</span>)<br> .replace(<span class="hljs-regexp">/\[name\]/g</span>, name);<br> <span class="hljs-comment">// Windows fix - exports must be in unix format</span><br> data = <span class="hljs-string">`<span class="hljs-subst">${publicPath}</span><span class="hljs-subst">${outputFileName.split(sep).join(posix.sep)}</span>`</span>;<br> copies[id] = outputFileName;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 匹配不同格式的文件</span><br> <span class="hljs-comment">// 如果是 svg 用 svg 的格式处理</span><br> <span class="hljs-comment">// 如果是其他图片则用 base64 转化</span><br> <span class="hljs-keyword">const</span> mimetype = mime.getType(id);<br> <span class="hljs-keyword">const</span> isSVG = mimetype === <span class="hljs-string">'image/svg+xml'</span>;<br> data = isSVG ? encodeSVG(buffer) : buffer.toString(<span class="hljs-string">'base64'</span>);<br> <span class="hljs-keyword">const</span> encoding = isSVG ? <span class="hljs-string">''</span> : <span class="hljs-string">';base64'</span>;<br> data = <span class="hljs-string">`data:<span class="hljs-subst">${mimetype}</span><span class="hljs-subst">${encoding}</span>,<span class="hljs-subst">${data}</span>`</span>;<br> }<br> <span class="hljs-comment">// 导出文件</span><br> <span class="hljs-keyword">return</span> <span class="hljs-string">`export default "<span class="hljs-subst">${data}</span>"`</span>;<br> });<br> },<br> <span class="hljs-attr">generateBundle</span>: <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">write</span>(<span class="hljs-params">outputOptions</span>) </span>{<br> <span class="hljs-comment">// Allow skipping saving files for server side builds.</span><br> <span class="hljs-keyword">if</span> (!emitFiles) <span class="hljs-keyword">return</span>;<br><br> <span class="hljs-keyword">const</span> base = options.destDir || outputOptions.dir || path.dirname(outputOptions.file);<br><br> <span class="hljs-keyword">await</span> makeDir(base);<br><br> <span class="hljs-keyword">await</span> <span class="hljs-built_in">Promise</span>.all(<br> <span class="hljs-built_in">Object</span>.keys(copies).map(<span class="hljs-keyword">async</span> (name) => {<br> <span class="hljs-keyword">const</span> output = copies[name];<br> <span class="hljs-comment">// Create a nested directory if the fileName pattern contains</span><br> <span class="hljs-comment">// a directory structure</span><br> <span class="hljs-keyword">const</span> outputDirectory = path.join(base, path.dirname(output));<br> <span class="hljs-keyword">await</span> makeDir(outputDirectory);<br> <span class="hljs-keyword">return</span> copy(name, path.join(base, output));<br> })<br> );<br> }<br> };<br>}<br><br><span class="hljs-comment">// 文件拷贝</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">copy</span>(<span class="hljs-params">src, dest</span>) </span>{<br> <span class="hljs-comment">// ... </span><br>}<br><br><span class="hljs-comment">// svg 转uri数据格式</span><br><span class="hljs-comment">// https://github.com/filamentgroup/directory-encoder/blob/master/lib/svg-uri-encoder.js</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">encodeSVG</span>(<span class="hljs-params">buffer</span>) </span>{<br> <span class="hljs-keyword">return</span> (<br> <span class="hljs-built_in">encodeURIComponent</span>(<br> <span class="hljs-comment">// ..</span><br> );<br>}<br></code></pre></td></tr></table></figure><h4 id="json"><a href="#json" class="headerlink" title="json"></a>json</h4><p>官方插件目录结构都一致,就不讲目录结构了,直接看 json 插件是干嘛的。</p><p>插件描述:<strong>A Rollup plugin which Converts .json files to ES6 modules.</strong> (转化 json 文件为 ES6 modules)</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">import</span> { createFilter, dataToEsm } <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/pluginutils'</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">json</span>(<span class="hljs-params">options = {}</span>) </span>{<br> <span class="hljs-keyword">const</span> filter = createFilter(options.include, options.exclude);<br> <span class="hljs-keyword">const</span> indent = <span class="hljs-string">'indent'</span> <span class="hljs-keyword">in</span> options ? options.indent : <span class="hljs-string">'\t'</span>;<br><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'json'</span>,<br><br> <span class="hljs-comment">// eslint-disable-next-line no-shadow</span><br> <span class="hljs-function"><span class="hljs-title">transform</span>(<span class="hljs-params">json, id</span>)</span> {<br> <span class="hljs-comment">// transform 钩子中执行匹配如果不是 JSON 则跳过</span><br> <span class="hljs-keyword">if</span> (id.slice(-<span class="hljs-number">5</span>) !== <span class="hljs-string">'.json'</span> || !filter(id)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br><span class="hljs-comment">// try catch 方式处理 json 文件格式异常</span><br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">const</span> parsed = <span class="hljs-built_in">JSON</span>.parse(json);<br> <span class="hljs-comment">// 将 json 数据直接转化成 es6 module,Rollup 打包用的过程数据格式 code</span><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">code</span>: dataToEsm(parsed, {<br> <span class="hljs-attr">preferConst</span>: options.preferConst,<br> <span class="hljs-attr">compact</span>: options.compact,<br> <span class="hljs-attr">namedExports</span>: options.namedExports,<br> indent<br> }),<br> <span class="hljs-attr">map</span>: { <span class="hljs-attr">mappings</span>: <span class="hljs-string">''</span> }<br> };<br> } <span class="hljs-keyword">catch</span> (err) {<br> <span class="hljs-keyword">const</span> message = <span class="hljs-string">'Could not parse JSON file'</span>;<br> <span class="hljs-keyword">const</span> position = <span class="hljs-built_in">parseInt</span>(<span class="hljs-regexp">/[\d]/</span>.exec(err.message)[<span class="hljs-number">0</span>], <span class="hljs-number">10</span>);<br> <span class="hljs-built_in">this</span>.warn({ message, id, position });<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br> }<br> }<br> };<br>}<br></code></pre></td></tr></table></figure><h4 id="Yaml"><a href="#Yaml" class="headerlink" title="Yaml"></a>Yaml</h4><p>这个是对<code>Yaml</code> 文件进行读取处理,也是一种比较常用的插件</p><p>也是利用 <code>transform</code>钩子在针对 <code>Yaml</code> 类型的文件做解析后转化成为 js 能够识别的 <code>module</code>。</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">import</span> YAML <span class="hljs-keyword">from</span> <span class="hljs-string">'js-yaml'</span>;<br><span class="hljs-keyword">import</span> toSource <span class="hljs-keyword">from</span> <span class="hljs-string">'tosource'</span>;<br><span class="hljs-keyword">import</span> { createFilter, makeLegalIdentifier } <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/pluginutils'</span>;<br><br><span class="hljs-keyword">const</span> defaults = {<br> <span class="hljs-attr">documentMode</span>: <span class="hljs-string">'single'</span>,<br> <span class="hljs-attr">safe</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">transform</span>: <span class="hljs-literal">null</span><br>};<br><span class="hljs-keyword">const</span> ext = <span class="hljs-regexp">/\.ya?ml$/</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">yaml</span>(<span class="hljs-params">opts = {}</span>) </span>{<br> <span class="hljs-keyword">const</span> options = <span class="hljs-built_in">Object</span>.assign({}, defaults, opts);<br> <span class="hljs-keyword">const</span> { documentMode, safe } = options;<br> <span class="hljs-keyword">const</span> filter = createFilter(options.include, options.exclude);<br> <span class="hljs-keyword">let</span> loadMethod = <span class="hljs-literal">null</span>;<br><br> <span class="hljs-keyword">if</span> (documentMode === <span class="hljs-string">'single'</span>) {<br> loadMethod = safe ? YAML.load : YAML.safeLoad;<br> } <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (documentMode === <span class="hljs-string">'multi'</span>) {<br> loadMethod = safe ? YAML.loadAll : YAML.safeLoadAll;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-built_in">this</span>.error(<br> <span class="hljs-string">`plugin-yaml → documentMode: '<span class="hljs-subst">${documentMode}</span>' is not a valid value. Please choose 'single' or 'multi'`</span><br> );<br> }<br><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">name</span>: <span class="hljs-string">'yaml'</span>,<br><span class="hljs-comment">// transform 钩子</span><br> <span class="hljs-function"><span class="hljs-title">transform</span>(<span class="hljs-params">content, id</span>)</span> {<br> <span class="hljs-comment">// 文件后缀检测</span><br> <span class="hljs-keyword">if</span> (!ext.test(id)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br> <span class="hljs-comment">// 过滤函数检测</span><br> <span class="hljs-keyword">if</span> (!filter(id)) <span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br><span class="hljs-comment">// js-yaml 库对 yaml 文件进行处理</span><br> <span class="hljs-keyword">let</span> data = loadMethod(content);<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> options.transform === <span class="hljs-string">'function'</span>) {<br> <span class="hljs-keyword">const</span> result = options.transform(data, id);<br> <span class="hljs-comment">// eslint-disable-next-line no-undefined</span><br> <span class="hljs-keyword">if</span> (result !== <span class="hljs-literal">undefined</span>) {<br> data = result;<br> }<br> }<br><br> <span class="hljs-keyword">const</span> keys = <span class="hljs-built_in">Object</span>.keys(data).filter(<span class="hljs-function">(<span class="hljs-params">key</span>) =></span> key === makeLegalIdentifier(key));<br> <span class="hljs-keyword">const</span> code = <span class="hljs-string">`var data = <span class="hljs-subst">${toSource(data)}</span>;\n\n`</span>;<br> <span class="hljs-keyword">const</span> <span class="hljs-built_in">exports</span> = [<span class="hljs-string">'export default data;'</span>]<br> .concat(keys.map(<span class="hljs-function">(<span class="hljs-params">key</span>) =></span> <span class="hljs-string">`export var <span class="hljs-subst">${key}</span> = data.<span class="hljs-subst">${key}</span>;`</span>))<br> .join(<span class="hljs-string">'\n'</span>);<br><br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">code</span>: code + <span class="hljs-built_in">exports</span>,<br> <span class="hljs-attr">map</span>: { <span class="hljs-attr">mappings</span>: <span class="hljs-string">''</span> }<br> };<br> }<br> };<br>}<br><br></code></pre></td></tr></table></figure><p><strong>总结一下</strong>:以上三个插件总共用了 <code>load</code>、<code>generateBundle</code>、<code>transform</code> 三钩子。在文件转化方面是用了 transform 这个在转换环节有点类似 webpack 中的 loader 的效果,做模块的转换。在文件加载过程特异化的处理,则用 load 钩子,修改文件导入的方式与方法,<code>generateBundle</code> 则是在 <code>bundle.generate()</code> 后,在<code>bundle.write()</code>前触发。简单来说就是生成文件前做的一些处理。</p><p>看<code>generateBundle</code>钩子官方的描述</p><blockquote><p>Called at the end of <code>bundle.generate()</code> or immediately before the files are written in <code>bundle.write()</code>. To modify the files after they have been written, use the <a href="https://rollupjs.org/guide/en/#writebundle"><code>writeBundle</code></a> hook. <code>bundle</code> provides the full list of files being written or generated along with their details:</p></blockquote><h2 id="钩子流程图"><a href="#钩子流程图" class="headerlink" title="钩子流程图"></a>钩子流程图</h2><p>了解了几个钩子,让我们知道可以通过插件在打包的环节中插手做自己想做的事情。接下来我们将看官方的 2 张钩子流程图</p><p>第一个:<strong>构建流程</strong>的钩子函数与它所处于的位置</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220111230010708.png" alt="Build 流程"></p><p>第二个图:<strong>输出流程 </strong>各个钩子所在的位置</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220111230114657.png" alt="文件输出流程"></p><h2 id="钩子类型"><a href="#钩子类型" class="headerlink" title="钩子类型"></a>钩子类型</h2><p>钩子类型不外乎这 4 种</p><ul><li><code>async</code>: The hook may also return a promise resolving to the same type of value; otherwise, the hook is marked as <code>sync</code>.(同步、异步函数)</li><li><code>first</code>: If several plugins implement this hook, the hooks are run sequentially until a hook returns a value other than <code>null</code> or <code>undefined</code>.(frist 类型的话,使用相同的钩子,最先执行成功返回后面的钩子不执行)</li><li><code>sequential</code>: If several plugins implement this hook, all of them will be run in the specified plugin order. If a hook is async, subsequent hooks of this kind will wait until the current hook is resolved.( 如果多个插件实现了相同的钩子函数,那么会串式执行,按照使用插件的顺序从头到尾执行,如果是异步的,会等待之前处理完毕,在执行下一个插件。)</li><li><code>parallel</code>: If several plugins implement this hook, all of them will be run in the specified plugin order. If a hook is async, subsequent hooks of this kind will be run in parallel and not wait for the current hook.(同上,不过如果某个插件是异步的,其后的插件不会等待,而是并行执行,这个也就是我们在 rollup.rollup() 阶段看到的处理方式。)</li></ul><p>仔细看图里的标注: 分别对几种类型有颜色区分,对同步和异步的钩子有边框颜色的区分,让读者更好能够在图上就识别这个钩子的类型特征。</p><p>通过源码里的 d.ts我们也能够快速分分辨出来钩子类型,我这边也总结一下不同类型的钩子有哪些</p><h4 id="FirstPluginHooks"><a href="#FirstPluginHooks" class="headerlink" title="FirstPluginHooks"></a><em>FirstPluginHooks</em></h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> FirstPluginHooks =<br>| <span class="hljs-string">'load'</span> <span class="hljs-comment">// 加载</span><br>| <span class="hljs-string">'renderDynamicImport'</span> <br>| <span class="hljs-string">'resolveAssetUrl'</span> <span class="hljs-comment">// 已废弃</span><br>| <span class="hljs-string">'resolveDynamicImport'</span><br>| <span class="hljs-string">'resolveFileUrl'</span><br>| <span class="hljs-string">'resolveId'</span><br>| <span class="hljs-string">'resolveImportMeta'</span>;<br></code></pre></td></tr></table></figure><p><strong>load</strong>、<strong>resolveId</strong>、<strong>resolveDynamicImport</strong> 都是在 build 阶段触发的钩子函数、处理文件加载时候处理</p><p><strong>renderDynamicImport</strong>、<strong>resolveFileUrl</strong>、<strong>resolveImportMeta</strong>、则是在输出阶段对输出内容的处理</p><h4 id="SequentialPluginHooks"><a href="#SequentialPluginHooks" class="headerlink" title="SequentialPluginHooks"></a><em>SequentialPluginHooks</em></h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> SequentialPluginHooks =<br>| <span class="hljs-string">'augmentChunkHash'</span><br>| <span class="hljs-string">'closeWatcher'</span><br>| <span class="hljs-string">'generateBundle'</span><br>| <span class="hljs-string">'options'</span><br>| <span class="hljs-string">'outputOptions'</span><br>| <span class="hljs-string">'renderChunk'</span><br>| <span class="hljs-string">'transform'</span><br>| <span class="hljs-string">'watchChange'</span>;<br></code></pre></td></tr></table></figure><h4 id="ParallelPluginHooks"><a href="#ParallelPluginHooks" class="headerlink" title="ParallelPluginHooks"></a><em>ParallelPluginHooks</em></h4><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">type</span> ParallelPluginHooks =<br>| <span class="hljs-string">'banner'</span><br>| <span class="hljs-string">'buildEnd'</span><br>| <span class="hljs-string">'buildStart'</span><br>| <span class="hljs-string">'footer'</span><br>| <span class="hljs-string">'intro'</span><br>| <span class="hljs-string">'moduleParsed'</span><br>| <span class="hljs-string">'outro'</span><br>| <span class="hljs-string">'renderError'</span><br>| <span class="hljs-string">'renderStart'</span><br>| <span class="hljs-string">'writeBundle'</span><br>| <span class="hljs-string">'closeBundle'</span>;<br></code></pre></td></tr></table></figure><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>通过对插件钩子类型与插件钩子流程图的理解,大家也对插件钩子函数有大概的理解,实际上想要对每个钩子做到了如指掌还是比较困难的。笔者也只能看着官网的一堆英文发蒙。后面我们将会用实战的方式去运用这些钩子函数,只有我们想要利用插件去完成一件事情,才会去思考到在构建的哪个钩子可以做到,才会比较深入的了解钩子函数的作用。这样的思维训练次数多了,就知道每个钩子具体要做什么,这样 Rollup 插件的编写的也越得心应手,再也不是只会用,不懂原理的开发了。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://rollupjs.org/guide/en/#build-hooks">官方钩子</a></li><li><a href="https://juejin.cn/post/6844904126136991758">原来rollup这么简单之插件篇</a></li><li><a href="https://github.com/rollup/plugins">官方插件列表</a></li></ul>]]></content>
<tags>
<tag>rollup</tag>
<tag>web打包工具系列</tag>
</tags>
</entry>
<entry>
<title>Rollup原理分析(二)插件使用</title>
<link href="/2022/01/10/rollup2/"/>
<url>/2022/01/10/rollup2/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>当前分析的Rollup版本:<code>2.63.0</code></p><p>对于js的打包,在面对不同的业务场景时候,是需要不同的打包”姿势“。那怎么使用不同姿势呢?webpack有loader、plugin在打包的过程中处理各种各样的场景。Rollup说:我万变不离其宗,插件就能够做到相同的效果。今天这篇文章,主要讲的就是怎么使用Rollup插件。</p><h2 id="Rollup插件的简单使用"><a href="#Rollup插件的简单使用" class="headerlink" title="Rollup插件的简单使用"></a>Rollup插件的简单使用</h2><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs JS"><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>)<br><span class="hljs-keyword">const</span> pkg = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./package.json'</span>)<br><br><span class="hljs-keyword">import</span> resolveNode <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-node-resolve'</span>;<br><span class="hljs-keyword">import</span> commonjs <span class="hljs-keyword">from</span> <span class="hljs-string">'@rollup/plugin-commonjs'</span>;<br><br><span class="hljs-keyword">const</span> resolve = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>)</span>{<br><span class="hljs-keyword">return</span> path.resolve(__dirname, ...args)<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {<br> <span class="hljs-attr">input</span>: resolve(<span class="hljs-string">'./src/index.js'</span>),<br> <span class="hljs-attr">format</span>: <span class="hljs-string">'cjs'</span>,<br><span class="hljs-attr">external</span>: [<br><span class="hljs-string">'ms'</span><br>],<br> <span class="hljs-attr">output</span>: {<br><span class="hljs-attr">file</span>: resolve(<span class="hljs-string">'./'</span>, pkg.main),<br><span class="hljs-attr">format</span>: <span class="hljs-string">'umd'</span>,<br><span class="hljs-attr">name</span>: <span class="hljs-string">'test'</span>,<br><span class="hljs-attr">globals</span>: {<br><span class="hljs-attr">ms</span>: <span class="hljs-string">"ms"</span><br>},<br><span class="hljs-attr">exports</span>: <span class="hljs-string">'named'</span>,<br>},<br><span class="hljs-attr">name</span>: <span class="hljs-string">'tool'</span>,<br><span class="hljs-attr">Plugin</span>: [<br>resolveNode(),<br>commonjs()<br>]<br>}<br></code></pre></td></tr></table></figure><p>用上篇文章的例子来说,这里使用了2个插件 <code>resolveNode</code>,<code>commonjs</code>插件</p><p>第一个插件:<code>@rollup/plugin-node-resolve</code>是用来告诉 Rollup 如何查找外部模块</p><p>第二个插件:<code>@rollup/plugin-commonjs</code>就是用来将 CommonJS 转换成 ES2015 模块的。npm 中的大多数包都是以 CommonJS 模块的形式出现的,所以这个插件使用率也是非常高。</p><p>Rollup的插件用法都是特别简单,先导入插件,然后在<code>Plugin</code>字段执行放入插件数组当中。</p><h2 id="Rollup源码的插件分析"><a href="#Rollup源码的插件分析" class="headerlink" title="Rollup源码的插件分析"></a>Rollup源码的插件分析</h2><p>既然我们了解了插件<code>如何使用</code>,那我们就来探究下不同的插件究竟做了什么事情。</p><p>我们的目标是研究Rollup源码框架,那就从框架本身的插件入手,先看看Rollup自己使用了什么插件呢!</p><p>文件地址 <a href="https://github.com/CrewS/rollup/blob/master/rollup.config.ts">rollup.config.ts</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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// ...</span><br><span class="hljs-keyword">import</span> addCliEntry <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/add-cli-entry'</span>;<br><span class="hljs-keyword">import</span> conditionalFsEventsImport <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/conditional-fsevents-import'</span>;<br><span class="hljs-keyword">import</span> emitModulePackageFile <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/emit-module-package-file'</span>;<br><span class="hljs-keyword">import</span> esmDynamicImport <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/esm-dynamic-import'</span>;<br><span class="hljs-keyword">import</span> getLicenseHandler <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/generate-license-file'</span>;<br><span class="hljs-keyword">import</span> replaceBrowserModules <span class="hljs-keyword">from</span> <span class="hljs-string">'./build-plugins/replace-browser-modules'</span>;<br><span class="hljs-comment">// ...</span><br></code></pre></td></tr></table></figure><p>这几个都是自身core 源码中的插件包逐个分析</p><h4 id="addCliEntry"><a href="#addCliEntry" class="headerlink" title="addCliEntry"></a>addCliEntry</h4><p>在最开始看框架源码的时候,就一直没有找到编译 cli 的配置入口,想了半天怎么也想不通<code>bin/rollup</code> 这个文件是怎么编译出来的。最后看到这个插件,原来是它的作用。</p><p>直接看代码、发现就是在构建前通过 <code>this.emitFile</code> 增加一个入口配置,这样就能够打包出来一个 <code>bin/rollup</code> 的 <code>bin</code> 文件提供用户执行 rollup 这样的指令操作。<code>renderChunk</code> 则是给 <code>bin</code> 文件增加文件头部的配置信息。大家也可以学这一招,生成 bin后再增加这行信息,就可以避免不知道如何在 js, ts 文件顶部增加 <code>#!/usr/bin/env node</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><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> MagicString <span class="hljs-keyword">from</span> <span class="hljs-string">'magic-string'</span>;<br><span class="hljs-keyword">import</span> { Plugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup'</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addCliEntry</span>(<span class="hljs-params"></span>): <span class="hljs-title">Plugin</span> </span>{<br><span class="hljs-keyword">return</span> {<br> <span class="hljs-comment">// 构建前</span><br><span class="hljs-function"><span class="hljs-title">buildStart</span>(<span class="hljs-params"></span>)</span> {<br><span class="hljs-built_in">this</span>.emitFile({<br><span class="hljs-attr">fileName</span>: <span class="hljs-string">'bin/rollup'</span>,<br><span class="hljs-attr">id</span>: <span class="hljs-string">'cli/cli.ts'</span>,<br><span class="hljs-attr">preserveSignature</span>: <span class="hljs-literal">false</span>,<br><span class="hljs-attr">type</span>: <span class="hljs-string">'chunk'</span><br>});<br>},<br><span class="hljs-attr">name</span>: <span class="hljs-string">'add-cli-entry'</span>,<br> <span class="hljs-comment">// 渲染 chunk 变代码块</span><br><span class="hljs-function"><span class="hljs-title">renderChunk</span>(<span class="hljs-params">code, chunkInfo</span>)</span> {<br><span class="hljs-keyword">if</span> (chunkInfo.fileName === <span class="hljs-string">'bin/rollup'</span>) {<br><span class="hljs-keyword">const</span> magicString = <span class="hljs-keyword">new</span> MagicString(code);<br>magicString.prepend(<span class="hljs-string">'#!/usr/bin/env node\n\n'</span>);<br><span class="hljs-keyword">return</span> { <span class="hljs-attr">code</span>: magicString.toString(), <span class="hljs-attr">map</span>: magicString.generateMap({ <span class="hljs-attr">hires</span>: <span class="hljs-literal">true</span> }) };<br>}<br><span class="hljs-keyword">return</span> <span class="hljs-literal">null</span>;<br>}<br>};<br>}<br></code></pre></td></tr></table></figure><h4 id="conditionalFsEventsImport"><a href="#conditionalFsEventsImport" class="headerlink" title="conditionalFsEventsImport"></a>conditionalFsEventsImport</h4><p>这个插件是 2 年前的 PR 合并的代码,功能主要是替换 chokidar 里依赖<code>fsevents</code>使用的 <code>fsevents-handler.js</code> ,在使用这个库的时候替换成自定义的一段 code</p><p>通过当年的 <a href="https://github.com/rollup/rollup/pull/3331">PR </a>看看,作者说当时最大的两个依赖 <code>chokidar</code>和 <code>micromatch</code>解耦出来作为 watch 功能的加载和使用,<code>chokidar</code>本身是无痛使用的,但是<code>chokidar</code> 依赖 <code>fsevents</code> 的部分功能依赖了原生代码,这里做了这个 Plugin 主要也是解决<code>fsevents</code> 如果不能正常安装,也是不会影响运行的。</p><blockquote><p>so that chokidar will now behave the same no matter if fsevents is present and valid or missing.</p></blockquote><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220110222309904.png" alt="PR 文字"></p><p>搞懂这个插件,让我了解了 <code>fsevents</code>, <code>chokidar</code> 这几个包的使用流程</p><p>加载 chokidar 的时候会将<code>fsevents-handler.js</code>替换成自己的包</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/conditional-fsevents-import.ts.jpg" alt="conditional-fsevents-import流程图"></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">let</span> fsEvents: unknown;<br><span class="hljs-keyword">let</span> fsEventsImportError: <span class="hljs-built_in">Error</span> | <span class="hljs-literal">undefined</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">loadFsEvents</span>(<span class="hljs-params"></span>): <span class="hljs-title">Promise</span><<span class="hljs-title">void</span>> </span>{<br><span class="hljs-keyword">const</span> moduleName = <span class="hljs-string">'fsevents'</span>;<br><br><span class="hljs-keyword">try</span> {<br>({ <span class="hljs-attr">default</span>: fsEvents } = <span class="hljs-keyword">await</span> <span class="hljs-keyword">import</span>(moduleName));<br>} <span class="hljs-keyword">catch</span> (err: <span class="hljs-built_in">any</span>) {<br>fsEventsImportError = err;<br>}<br>}<br><span class="hljs-comment">// 动态加载 fsevents 如果异常情况则做兼容处理</span><br><span class="hljs-comment">// A call to this function will be injected into the chokidar code</span><br><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getFsEvents</span>(<span class="hljs-params"></span>): <span class="hljs-title">unknown</span> </span>{<br><span class="hljs-keyword">if</span> (fsEventsImportError) <span class="hljs-keyword">throw</span> fsEventsImportError;<br><span class="hljs-keyword">return</span> fsEvents;<br>}<br><br></code></pre></td></tr></table></figure><p>这种<code>优雅降级</code> 降级的处理方式,又让我学习到了!</p><h4 id="getLicenseHandler"><a href="#getLicenseHandler" class="headerlink" title="getLicenseHandler"></a>getLicenseHandler</h4><p>动态生成两个插件<code>collectLicenses</code>, <code>writeLicense</code>、是用来给自己打包的库文件头插入 license,如果你也有做开源工作,可以参考这种做法哦</p><p>插件式的添加 license!这里依赖另一个插件<code>rollup-plugin-license</code>,今天就不作更详细的讲解了</p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-comment">// ...</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">getLicenseHandler</span>(<span class="hljs-params"></span>): </span>{<br><span class="hljs-attr">collectLicenses</span>: PluginImpl;<br>writeLicense: PluginImpl;<br>} {<br><span class="hljs-keyword">const</span> licenses = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Map</span>();<br><span class="hljs-keyword">return</span> {<br><span class="hljs-function"><span class="hljs-title">collectLicenses</span>(<span class="hljs-params"></span>)</span> {<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">addLicenses</span>(<span class="hljs-params">dependencies: Dependency[]</span>) </span>{<br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> dependency <span class="hljs-keyword">of</span> dependencies) {<br>licenses.set(dependency.name, dependency);<br>}<br>}<br><br><span class="hljs-keyword">return</span> license({ <span class="hljs-attr">thirdParty</span>: addLicenses });<br>},<br><span class="hljs-function"><span class="hljs-title">writeLicense</span>(<span class="hljs-params"></span>)</span> {<br><span class="hljs-keyword">return</span> {<br><span class="hljs-attr">name</span>: <span class="hljs-string">'write-license'</span>,<br><span class="hljs-function"><span class="hljs-title">writeBundle</span>(<span class="hljs-params"></span>)</span> {<br>generateLicenseFile(<span class="hljs-built_in">Array</span>.from(licenses.values()));<br>}<br>};<br>}<br>};<br>}<br></code></pre></td></tr></table></figure><h4 id="replaceBrowserModules"><a href="#replaceBrowserModules" class="headerlink" title="replaceBrowserModules"></a>replaceBrowserModules</h4><p>这个插件看起来就比较简单一些了,对特定的库用浏览器模块替换模块,用作生成</p><p><code>dist/rollup.browser.js</code> 和 <code>dist/es/rollup.browser.js</code></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">import</span> path <span class="hljs-keyword">from</span> <span class="hljs-string">'path'</span>;<br><span class="hljs-keyword">import</span> { Plugin } <span class="hljs-keyword">from</span> <span class="hljs-string">'rollup'</span>;<br><br><span class="hljs-keyword">const</span> ID_CRYPTO = path.resolve(<span class="hljs-string">'src/utils/crypto'</span>);<br><span class="hljs-keyword">const</span> ID_FS = path.resolve(<span class="hljs-string">'src/utils/fs'</span>);<br><span class="hljs-keyword">const</span> ID_HOOKACTIONS = path.resolve(<span class="hljs-string">'src/utils/hookActions'</span>);<br><span class="hljs-keyword">const</span> ID_PATH = path.resolve(<span class="hljs-string">'src/utils/path'</span>);<br><span class="hljs-keyword">const</span> ID_RESOLVEID = path.resolve(<span class="hljs-string">'src/utils/resolveId'</span>);<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">replaceBrowserModules</span>(<span class="hljs-params"></span>): <span class="hljs-title">Plugin</span> </span>{<br><span class="hljs-keyword">return</span> {<br><span class="hljs-attr">name</span>: <span class="hljs-string">'replace-browser-modules'</span>,<br><span class="hljs-attr">resolveId</span>: <span class="hljs-function">(<span class="hljs-params">source, importee</span>) =></span> {<br><span class="hljs-keyword">if</span> (importee && source[<span class="hljs-number">0</span>] === <span class="hljs-string">'.'</span>) {<br><span class="hljs-keyword">const</span> resolved = path.join(path.dirname(importee), source);<br><span class="hljs-keyword">switch</span> (resolved) {<br><span class="hljs-keyword">case</span> ID_CRYPTO:<br><span class="hljs-keyword">return</span> path.resolve(<span class="hljs-string">'browser/crypto.ts'</span>);<br><span class="hljs-keyword">case</span> ID_FS:<br><span class="hljs-keyword">return</span> path.resolve(<span class="hljs-string">'browser/fs.ts'</span>);<br><span class="hljs-keyword">case</span> ID_HOOKACTIONS:<br><span class="hljs-keyword">return</span> path.resolve(<span class="hljs-string">'browser/hookActions.ts'</span>);<br><span class="hljs-keyword">case</span> ID_PATH:<br><span class="hljs-keyword">return</span> path.resolve(<span class="hljs-string">'browser/path.ts'</span>);<br><span class="hljs-keyword">case</span> ID_RESOLVEID:<br><span class="hljs-keyword">return</span> path.resolve(<span class="hljs-string">'browser/resolveId.ts'</span>);<br>}<br>}<br>}<br>};<br>}<br></code></pre></td></tr></table></figure><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>经过这篇博客的讲解,大家是否懂如何去使用插件了呢?不得不说官方 core 使用的插件还是挺有意思的,下次再和大家聊聊如何编写一个插件,插件的几个关键钩子是处于怎么构建环境的什么流程。等我们把插件的机制了解,最后再整体去看 Rollup 打包过程,可能大家学习起来的难度就没有这么大了。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/paulmillr/chokidar">chokidar 仓库代码</a></li><li><a href="https://www.rollupjs.com/guide/big-list-of-options">Rollup watch option 配置</a></li></ul>]]></content>
<tags>
<tag>rollup</tag>
<tag>web打包工具系列</tag>
</tags>
</entry>
<entry>
<title>Rollup原理分析(一)基础使用</title>
<link href="/2022/01/09/rollup/"/>
<url>/2022/01/09/rollup/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>当前分析的Rollup版本:<code>2.63.0</code></p><p>打包工具作为前端最基础的工具之一,想必大家或多或少都会对其实现原理感兴趣。但是由于网上的资料参差不齐,学习起来非常困难,容易劝退。<br>笔者想要通过<code>web打包工具分析</code>系列文章,揭秘web前端打包工具的实现原理,降低大家学习框架源码的门槛。</p><p>Rollup源码分析系列将会拆分成以下几部分</p><ul><li><p>[x] Rollup的简单使用</p></li><li><p>[ ] Rollup的插件使用</p></li><li><p>[ ] Rollup的打包过程</p></li><li><p>[ ] Rollup插件机制</p></li><li><p>[ ] 如何编写一个插件(实战)</p></li><li><p>[ ] 分析几个主流Rollup插件源码</p></li></ul><h2 id="Rollup是什么?"><a href="#Rollup是什么?" class="headerlink" title="Rollup是什么?"></a>Rollup是什么?</h2><p>首先了解下Rollup是什么东西,Rollup是和webpack齐名的打包工具。只不过与webpack不同的是,Rollup主打的是打包过程,插件机制也与webpack的实现思路不一样。用官方的描述总结一句话:Rollup是 JS模块打包器。</p><blockquote><p>Rollup 是一个 JavaScript 模块打包器,可以将小块代码编译成大块复杂的代码,例如 library 或应用程序。Rollup 对代码模块使用新的标准化格式,这些标准都包含在 JavaScript 的 ES6 版本中,而不是以前的特殊解决方案,如 CommonJS 和 AMD。ES6 模块可以使你自由、无缝地使用你最喜爱的 library 中那些最有用独立函数,而你的项目不必携带其他未使用的代码。ES6 模块最终还是要由浏览器原生实现,但当前 Rollup 可以使你提前体验。</p></blockquote><p>实现最简单的Rollup使用 <a href="https://github.com/CrewS/rollupCase">demo</a></p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs JS"><span class="hljs-comment">// src/js</span><br><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logA</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'function logA called'</span>)<br>}<br><br><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">logB</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'function logB called'</span>)<br>}<br></code></pre></td></tr></table></figure><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><code class="hljs JS"><span class="hljs-comment">// rollup.config.js</span><br><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">'path'</span>)<br><span class="hljs-keyword">const</span> pkg = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./package.json'</span>)<br><br><span class="hljs-keyword">const</span> resolve = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">...args</span>)</span>{<br><span class="hljs-keyword">return</span> path.resolve(__dirname, ...args)<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {<br> <span class="hljs-attr">input</span>: resolve(<span class="hljs-string">'./src/index.js'</span>),<br> <span class="hljs-attr">format</span>: <span class="hljs-string">'cjs'</span>,<br> <span class="hljs-attr">output</span>: {<br><span class="hljs-attr">file</span>: resolve(<span class="hljs-string">'./'</span>, pkg.main),<br><span class="hljs-attr">format</span>: <span class="hljs-string">'umd'</span>,<br><span class="hljs-attr">name</span>: <span class="hljs-string">'test'</span><br>}<br>}<br></code></pre></td></tr></table></figure><p>可以看到最终打包产物</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/image-20220109145058756.png" alt="打包产物"></p><p>源码文件 + 配置文件 = 打包后的文件 (source + option => output)</p><h2 id="Rollup-Config-配置"><a href="#Rollup-Config-配置" class="headerlink" title="Rollup Config 配置"></a>Rollup Config 配置</h2><p>更详情配置可看<a href="https://rollupjs.org/guide/en/#configuration-files">官方文档</a>,也可以通过源码<a href="https://github.com/CrewS/rollup/blob/master/src/rollup/types.d.ts">type.d.ts</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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> {<br> input, <span class="hljs-comment">// 入口文件 字符串或者是数组</span><br> <span class="hljs-attr">output</span>: {<span class="hljs-comment">// 文件输出配置</span><br> file,<span class="hljs-comment">//输出的文件名, 对于单个文件打包可以使用该选项指定打包内容写入带路径的文件</span><br> <span class="hljs-comment">// InternalModuleFormat = 'amd' | 'cjs' | 'es' | 'iife' | 'system' | 'umd';</span><br> format, <span class="hljs-comment">// 输出的打包格式一般使用 umd, es, cjs这几种</span><br> dir, <span class="hljs-comment">// 配置文件打包后统一输出的基本目录,适用于多文件打包,单文件打包也可以用 file 选项代替</span><br> }, <br> <span class="hljs-attr">plugins</span>: [] <span class="hljs-comment">// 插件配置</span><br> <span class="hljs-attr">external</span>: [] <span class="hljs-comment">// 指定第三方包不参与打包 现在Rollup默认逻辑绝对路径的包都不会参与打包</span><br>}<br></code></pre></td></tr></table></figure><h2 id="Rollup-配置加载过程"><a href="#Rollup-配置加载过程" class="headerlink" title="Rollup 配置加载过程"></a>Rollup 配置加载过程</h2><p><strong>为什么输出指令 <code>rollup -c</code> ,就能够执行打包呢?</strong></p><p>先到源码入口文件 cli.ts <code>cli/cli.ts</code></p><figure class="highlight typescript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs typescript"><span class="hljs-keyword">import</span> help <span class="hljs-keyword">from</span> <span class="hljs-string">'help.md'</span>;<br><span class="hljs-keyword">import</span> { version } <span class="hljs-keyword">from</span> <span class="hljs-string">'package.json'</span>;<br><span class="hljs-keyword">import</span> argParser <span class="hljs-keyword">from</span> <span class="hljs-string">'yargs-parser'</span>;<br><span class="hljs-keyword">import</span> { commandAliases } <span class="hljs-keyword">from</span> <span class="hljs-string">'../src/utils/options/mergeOptions'</span>;<br><span class="hljs-keyword">import</span> run <span class="hljs-keyword">from</span> <span class="hljs-string">'./run/index'</span>;<br><br><span class="hljs-comment">// 格式化command</span><br><span class="hljs-comment">// commandAliases 是别名配置</span><br><span class="hljs-keyword">const</span> command = argParser(process.argv.slice(<span class="hljs-number">2</span>), {<br><span class="hljs-attr">alias</span>: commandAliases,<br><span class="hljs-attr">configuration</span>: { <span class="hljs-string">'camel-case-expansion'</span>: <span class="hljs-literal">false</span> }<br>});<br><br><span class="hljs-comment">// 如果是help指令 则输出提示</span><br><br><span class="hljs-keyword">if</span> (command.help || (process.argv.length <= <span class="hljs-number">2</span> && process.stdin.isTTY)) {<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">`\n<span class="hljs-subst">${help.replace(<span class="hljs-string">'__VERSION__'</span>, version)}</span>\n`</span>);<br>} <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> (command.version) {<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">`rollup v<span class="hljs-subst">${version}</span>`</span>);<br>} <span class="hljs-keyword">else</span> {<br><span class="hljs-keyword">try</span> {<br><span class="hljs-built_in">require</span>(<span class="hljs-string">'source-map-support'</span>).install();<br>} <span class="hljs-keyword">catch</span> {<br><span class="hljs-comment">// do nothing</span><br>}<br><span class="hljs-comment">// run 函数执行command</span><br>run(command);<br>}<br><br></code></pre></td></tr></table></figure><p><strong>Run 函数</strong> <code>cli/run/index.ts</code></p><p>第一步:解析command input输入</p><p>第二步:解析<code>command</code>里的<code>environment</code>环境变量配置</p><p>第三步:判断是否是 <code>watch</code>模式,如果是则走<code>watch</code>模式,不是走到第四步</p><p>第四步:通过<code>getConfigs</code> 获取配置项 如果<code>command</code> 有 <code>config</code>则取配置文件、没有则走默认的<code>loadConfigFromCommand</code> 函数获取配置</p><p>第五步:执行<strong>Rollup build</strong>函数 完成构建</p><h3 id="执行流程图"><a href="#执行流程图" class="headerlink" title="执行流程图"></a>执行流程图</h3><p>通过下面的流程图大概理解整个的执行过程</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/%E6%8C%87%E4%BB%A4%E8%A7%A3%E6%9E%90%E6%89%A7%E8%A1%8C%E8%BF%87%E7%A8%8B.jpg" alt="指令解析执行过程"></p><h2 id="后续"><a href="#后续" class="headerlink" title="后续"></a>后续</h2><p>通过本篇大家都能够初步的掌握如何去使用Rollup、怎么<strong>简单</strong>的配置完成代码的打包过程。经过初步的学习使用,大概的了解Rollup的工作原理,再针对Rollup工作流程的每一个小步骤深入深究,带着问题去阅读源码,这样就能够做到<strong>庖丁解牛</strong>一般,将源码模块一个个拆解出来,再慢慢品尝吸收。下一篇将会针对Rollup的插件使用进行讲解,敬请期待。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://blog.cjw.design/blog/old/rollup">Rollup 配置详解</a></li><li><a href="https://rollupjs.org/guide/en/#outputinlinedynamicimports">Rollup官方文档</a></li></ul>]]></content>
<tags>
<tag>rollup</tag>
<tag>web打包工具系列</tag>
</tags>
</entry>
<entry>
<title>js逆向之旅(一)</title>
<link href="/2021/06/01/js%E9%80%86%E5%90%91%E4%B9%8B%E6%97%85/"/>
<url>/2021/06/01/js%E9%80%86%E5%90%91%E4%B9%8B%E6%97%85/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>之前有尝试过破解极验的<code>w</code>值,其中有涉及到反混淆、加解密等知识,一步一步的得到最后的结果非常的有意思。最近有幸了解到《猿人学-爬虫刷题平台》网站,有许多有趣的js逆向题目。以下就是js逆向之旅(刷题之旅)。</p><h2 id="js-混淆-源码乱码"><a href="#js-混淆-源码乱码" class="headerlink" title="js 混淆 - 源码乱码"></a>js 混淆 - 源码乱码</h2><p>环境<br>操作系统: mac<br>语言: node<br>编辑器:vscode</p><p>题目地址:<a href="http://match.yuanrenxue.com/match/1">http://match.yuanrenxue.com/match/1</a><br>概述:看了下题目大概就是请求5个接口然后拿返回的结果做下统计求平均值</p><h4 id="第一步-反调试"><a href="#第一步-反调试" class="headerlink" title="第一步 反调试"></a>第一步 反调试</h4><p>打开浏览器控制台看下请求,就开始报错,找到对应的js:<code>uyt.js</code> <code>uzt.js</code><br>右键添加断点条件设置为false后面就不会弹窗报错了。<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002723.png" alt="image.png"><br>清空Netwrok,看到对应的请求接口<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002757.png" alt="image.png"><br>可以看到每次请求都会携带一个m值的传参,这个就是我们要解密的m值。</p><h4 id="第二步-格式化代码"><a href="#第二步-格式化代码" class="headerlink" title="第二步 格式化代码"></a>第二步 格式化代码</h4><p>通常我会将html的源代码放在vscode编辑器里,然后格式化一下,得到一个比较好看的结果。<br>然后把最底下的script标签全部拷贝到新的js文件中,去掉标签再格式化一次,如下图所示。<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002832.png" alt="image.png"><br>发起的请求一般是<strong>$.ajax</strong>,搜索关键字 <strong>$.ajax</strong><br>找到参数m,发现是由<strong>oo0O0</strong>函数生成的<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002852.png" alt="image.png"></p><h4 id="第三步-解密"><a href="#第三步-解密" class="headerlink" title="第三步 解密"></a>第三步 解密</h4><p>接下来研究<strong>oo0O0</strong>函数即可。<br>阅读了一下代码发现需要window、和document对象 重新看html源码找到<br>把需要的变量都收集起来<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002914.png" alt="image.png"><br>发下c需要计算,其实可以直接在控制台输出c然后拿到值赋值就好。<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002936.png" alt="image.png"><br>重新整理顺序,然后执行:这里是用node的环境 <code>node run.js</code><br>发现报错了!提示 ReferenceError: atob is not defined<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002956.png" alt="image.png"><br>瞬间想到这个是node的环境并不是浏览器环境,没有atob这个函数。so,直接实现一个node base64解码<br><figure class="highlight javascript"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">const</span> atob = <span class="hljs-function">(<span class="hljs-params">str</span>) =></span> {<br> <span class="hljs-keyword">return</span> (<span class="hljs-keyword">new</span> Buffer.from(str, <span class="hljs-string">'base64'</span>)).toString()<br>}<br></code></pre></td></tr></table></figure><br>重新执行一次 输出结果! m值GET<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307003121.png" alt="image.png"></p><h4 id="第四步-构造请求"><a href="#第四步-构造请求" class="headerlink" title="第四步 构造请求"></a>第四步 构造请求</h4><p>接下来就是构造5个请求把获取回来的值算下平均值,然后得到答案!</p><h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>1、学习到反调试技巧<br>2、学习到格式化代码与信息量收集<br>3、了解node环境与浏览器环境差异,一些api的缺少后续逆向还是会遇到。</p>]]></content>
<tags>
<tag>js逆向</tag>
</tags>
</entry>
<entry>
<title>docker统一构建</title>
<link href="/2019/12/09/docker-front/"/>
<url>/2019/12/09/docker-front/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>最近工作中会遇到这样的一个问题,当有个依赖包更新,或者某些配置需要更新的时候,需要在目前处理的所有系统中一起更新,这样的工作量其实是挺繁琐的。(多个系统配置几乎一致)。所以就想着能够有方法统一这个构建环境,包依赖和webpack配置能够统一处理。</p><h3 id="实践过程"><a href="#实践过程" class="headerlink" title="实践过程"></a>实践过程</h3><ul><li>第一步:#抽取公共配置作为配置仓库 docker-front</li><li>第二步:编写Dockerfile,以node镜像为基础,拉取配置仓库为内容的镜像</li><li>第三步:编写docker-compose.yml。定义port,共享文件夹卷,定义启动后的指令操作</li><li>第四步:整理业务系统代码。项目目录剩下 src目录(处理业务),config目录 (处理配置)</li><li>第五步:docker-compose up启动项目</li></ul><h4 id="抽取公共配置作为仓库docker-front"><a href="#抽取公共配置作为仓库docker-front" class="headerlink" title="抽取公共配置作为仓库docker-front"></a>抽取公共配置作为仓库docker-front</h4><p>一般来说公共配置如下</p><ul><li>webpack.base.config.js</li><li>webpack.dev.config.js</li><li>webpack.pro.config.js</li><li>package.json</li><li>index.ejs</li></ul><p>还有一些可能有公共的常量配置等等,可以继续往上添加</p><h4 id="编写Dokcerfile"><a href="#编写Dokcerfile" class="headerlink" title="编写Dokcerfile"></a>编写Dokcerfile</h4><p>这里docker主要是提供了一个环境,拉取配置仓库代码</p><figure class="highlight dockerfile"><table><tr><td class="gutter"><div class="code-wrapper"><pre><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></div></td><td class="code"><pre><code class="hljs dockerfile"><span class="hljs-keyword">FROM</span> node:<span class="hljs-number">8.9</span>.<span class="hljs-number">3</span><br><span class="hljs-keyword">RUN</span><span class="bash"> git <span class="hljs-built_in">clone</span> http://username:[email protected]/docker-front.git</span><br><span class="hljs-keyword">WORKDIR</span><span class="bash"> /docker-front</span><br><br></code></pre></td></tr></table></figure><h4 id="编写docker-compose-yml"><a href="#编写docker-compose-yml" class="headerlink" title="编写docker-compose.yml"></a>编写docker-compose.yml</h4><p>yourapp 定义docker-compose名字</p><p>prots 定义暴露的端口</p><p>volumes 定义了共享的目录</p><p>command 定义启动时候执行的指令</p><figure class="highlight yaml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs yaml"><span class="hljs-attr">version:</span> <span class="hljs-string">"3"</span><br><br><span class="hljs-attr">services:</span><br> <span class="hljs-attr">yourapp:</span><br> <span class="hljs-attr">build:</span> <span class="hljs-string">.</span><br> <span class="hljs-attr">ports:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-number">5000</span><span class="hljs-string">:5000</span><br> <span class="hljs-comment"># stdin_open: true</span><br> <span class="hljs-attr">volumes:</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">"./src:/docker-front/src"</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">"./config:/docker-front/config"</span><br> <span class="hljs-comment"># 这段指令是每次启动都会拉取最新的代码执行npm i再启动</span><br> <span class="hljs-attr">command:</span> <br> <span class="hljs-bullet">-</span> <span class="hljs-string">/bin/sh</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">-c</span><br> <span class="hljs-bullet">-</span> <span class="hljs-string">|</span><br><span class="hljs-string"> git reset --hard HEAD</span><br><span class="hljs-string"> git fetch</span><br><span class="hljs-string"> git checkout dev</span><br><span class="hljs-string"> git pull</span><br><span class="hljs-string"> npm i</span><br><span class="hljs-string"> npm start</span><br><span class="hljs-string"></span> <span class="hljs-attr">tty:</span> <span class="hljs-literal">true</span><br> <br></code></pre></td></tr></table></figure><h4 id="整理业务系统代码"><a href="#整理业务系统代码" class="headerlink" title="整理业务系统代码"></a>整理业务系统代码</h4><p>src 主要是用作映射业务系统的业务代码</p><p>config 的作用是用于配置各个业务系统中个性化的配置</p><p>比如webpack中独立的配置、index.ejs中的项目名称、配置的项目独立私有的key等等</p><p>目前还没有做独立的包的处理。不过有思路就是config中增加_package.json文件,在启动项目的时候增加指令处理package.json,将业务系统的私有配置和公共package.json配置合并。再执行包安装指令</p><h4 id="Docker-compose-up"><a href="#Docker-compose-up" class="headerlink" title="Docker-compose up"></a>Docker-compose up</h4><p>通过这个指令可以构建镜像,创建容器和启动容器。如果想要重新构建镜像可以<code>docker-compose build</code>指令处理</p><p>更多的操作可以找相关的教程。</p><h4 id="Docker统一构建的优势"><a href="#Docker统一构建的优势" class="headerlink" title="Docker统一构建的优势"></a>Docker统一构建的优势</h4><ul><li>解决了公共配置在多个系统里重复配置的问题</li><li>解决了环境不统一的问题,node版本npm 版本</li><li>解决了项目window和Mac 差异性问题</li><li>提供给非前端使用的可能性,无需配置简单执行指令即可启动项目</li></ul><h4 id="带来的问题也是有的"><a href="#带来的问题也是有的" class="headerlink" title="带来的问题也是有的"></a>带来的问题也是有的</h4><ul><li>docker占用资源问题,是否构建速度下降?</li><li>公共配置管理的问题,需要更严格的处理,不然影响范围比较大</li></ul>]]></content>
<tags>
<tag>docker</tag>
<tag>构建</tag>
</tags>
</entry>
<entry>
<title>react-router路由 haschange探讨</title>
<link href="/2019/11/29/history/"/>
<url>/2019/11/29/history/</url>
<content type="html"><![CDATA[<h2 id="场景"><a href="#场景" class="headerlink" title="场景"></a>场景</h2><p>针对pushState操作完,浏览器后退,以及页面返回时历史页面的处理。</p><p>需要了解的知识点</p><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/PopStateEvent">PopStateEvent</a> 浏览器后退事件</li><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState">History.pushState()</a></li><li><a href="https://developer.mozilla.org/en-US/docs/Web/API/HashChangeEvent">HashChangeEvent</a></li><li><a href="https://github.com/ReactTraining/history">History</a> 前端路由hash history</li></ul><h5 id="处理方法"><a href="#处理方法" class="headerlink" title="处理方法"></a>处理方法</h5><p>Page A 在跳转Page B前需要保存信息到url中,处理完后返回A,需要将其信息保留下来。</p><p>我们处理的方式是,在跳转B前会通过<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/History/pushState"><code>History.pushState()</code></a>,将状态保留在hash值中,又不会触发hashChange导致页面更新。然后从B返回A或者浏览器后退返回A的时候,能够获取到hash中的状态,从而根据状态完成初始化。</p><h5 id="问题与现象"><a href="#问题与现象" class="headerlink" title="问题与现象"></a>问题与现象</h5><p>页面A /#/a</p><p>通过pushState() 修改hash /#/a?v=1,然后浏览器回退按钮点击,页面并无变化。期待是会根据状态重新初始化。</p><p>通过pushState()修改hash /#/a?v=1, /#/a?v=2,然后点击浏览器回退按钮,页面会刷新了。</p><p>浏览器回退是触发了hashchange事件的,React-router中的hashHistory调用了<a href="https://github.com/ReactTraining/history">History</a>,通过阅读代码发现能够解释以上问题的原因。</p><p>1、history中使用自身的self history记录路由堆栈。但是pushState中并没有触发hashchange方法,也就没有记录该值。所以通过pushState改变的路径后,self history永远记录了/#/a,浏览器后退的时候触发了hashChange进入到逻辑层中</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// https://github.com/ReactTraining/history/blob/v4.7.0/modules/createHashHistory.js</span><br><span class="hljs-keyword">const</span> handleHashChange = <span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> path = getHashPath()<br> <span class="hljs-keyword">const</span> encodedPath = encodePath(path)<br><br> <span class="hljs-keyword">if</span> (path !== encodedPath) {<br> <span class="hljs-comment">// Ensure we always have a properly-encoded hash.</span><br> replaceHashPath(encodedPath)<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-keyword">const</span> location = getDOMLocation()<br> <span class="hljs-keyword">const</span> prevLocation = history.location <span class="hljs-comment">// 堆栈中的loaction,由于pushState改变,所以堆栈中的还是 /#/a</span><br><br> <span class="hljs-keyword">if</span> (!forceNextPop && locationsAreEqual(prevLocation, location))<span class="hljs-comment">// 浏览器后退 /#/a 与/#/a对比,一样,所以返回</span><br> <span class="hljs-keyword">return</span> <span class="hljs-comment">// A hashchange doesn't always == location change.</span><br><br> <span class="hljs-keyword">if</span> (ignorePath === createPath(location))<br> <span class="hljs-keyword">return</span> <span class="hljs-comment">// Ignore this change; we already setState in push/replace.</span><br><br> ignorePath = <span class="hljs-literal">null</span><br><br> handlePop(location)<br> }<br> }<br></code></pre></td></tr></table></figure><p>2、pustate2次的时候反而正常的原因,a =>a1 => a2, a2返回a1的时候,是a1与a对比,不一致所以会调用handlePop 触发组件更新</p><h5 id="如何处理这一问题"><a href="#如何处理这一问题" class="headerlink" title="如何处理这一问题"></a>如何处理这一问题</h5><p>在pushState前使用<a href="https://developer.mozilla.org/zh-CN/docs/Web/API/History/replaceState"><code>History.replaceState()</code></a>改变当前的值例如增加_t?new Date(),然后再pushState。这样无论是第一次还是第二次后退的时候永远对比的都是不一样的,从事解决后退无发触发更新的问题。</p><h4 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h4><p>通过研究history还有pushState,更加了解了前端路由的工作原理。以后遇到浏览器前进后退的问题,也能从容处理。</p><p>A => B 前通过pushState更改了hash /#/a?v=1,再跳转到B</p>]]></content>
<tags>
<tag>react</tag>
<tag>history</tag>
</tags>
</entry>
<entry>
<title>前端开发之接口代理</title>
<link href="/2019/11/16/http-proxy/"/>
<url>/2019/11/16/http-proxy/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>现在前端开发的方式基本都是前后端分离的开发方式,那就会遇到一些请求接口的问题,比如跨域,比如代理接口地址等等。处理这些问题的方法多种多样,常见的有<br>devServer proxy代理,nginx的反向代理方式。其原理都是接口代理转发到制定的服务端。</p><h2 id="权限认证"><a href="#权限认证" class="headerlink" title="权限认证"></a>权限认证</h2><p>通过devServer或nginx的proxy可以轻松实现代理,但是权限认证的问题则需根据后端认证方式重新配置。<br>1、jwt的认证方式<br>如果采用的是jwt认证方式,改动不大,只需要在接口请求的配置中,将token设置在header中即可,代理的时候会一同携带。</p><p>2、cookie session认证方式<br>如果采用的是cookie、session方式,则需要配置域名,完成登陆逻辑后,cookie会存在该域名下。请求会携带cookie信息,随着转发到指定服务器完成认证。</p><p>简单说以下如何配置本地域名<br>1、hosts文件配置 127.0.0.1 yourhost.com。达到访问yourhost.com:port 相当于 127.0.0.1:prot访问的效果<br>2、如果需要使用80端口,则增加nginx配置通过启动一个server 将该域名下的服务器转发到本地devServer服务器的端口实现80端口访问。</p><p>通过上述的方法,实现了开发阶段单一环境的接口代理。</p><h2 id="多环境、多域名情况"><a href="#多环境、多域名情况" class="headerlink" title="多环境、多域名情况"></a>多环境、多域名情况</h2><p>在实际项目中,会分多套环境。比如开发、测试、生产环境。这样就会遇到一个问题,在测试或者生产环境中发现有问题,但是开发环境的数据并没有问题。这就需要代理调试其他环境的数据了。</p><p>在权限认证方式为jwt的情况下,只需要更改代理的IP即可达到环境的切换。但是cookie session的方式,可能需要多思考一步。<br>目前我们是这样处理的<br>假设开发域名是A,测试是B,生产是C<br>则会增加一套B的配置<br>hosts 127.0.0.1 B<br>nginx server B<br>proxy B服务器的ip<br>全部修改后,重新启动服务器,则可以实现proxy代理转发的测试环境或者生产环境。<br>但是由于改动的地方比较多,切换环境麻烦所以一般不会切换,只有必要时候才会去切换环境调试。</p><h2 id="进阶"><a href="#进阶" class="headerlink" title="进阶"></a>进阶</h2><p>之前对不同环境的proxy修改,是修改IP地址然后重启实现的。通过阅读devServer <a href="https://github.com/chimurai/http-proxy-middleware#context-matching">proxy的文档</a>发现<em>custom matching</em>,可以自定匹配判断是否转发。<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></div></td><td class="code"><pre><code class="hljs js">proxy: [<br>{<br><span class="hljs-attr">target</span>: <span class="hljs-string">'http://A ip'</span>,<br><span class="hljs-attr">context</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">pathname, req</span>) </span>{<br><span class="hljs-keyword">if</span> (req.hostname === <span class="hljs-string">'A.cn'</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>},<br>},<br>{<br><span class="hljs-attr">target</span>: <span class="hljs-string">'http://B ip'</span>,<br><span class="hljs-attr">context</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">pathname, req</span>) </span>{<br><span class="hljs-keyword">if</span> (req.hostname === <span class="hljs-string">'B.cn'</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>}<br>},<br>{<br><span class="hljs-attr">target</span>: <span class="hljs-string">'http://C ip'</span>,<br><span class="hljs-attr">context</span>: <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">pathname, req</span>) </span>{<br><span class="hljs-keyword">if</span> (req.hostname === <span class="hljs-string">'C.cn'</span>) <span class="hljs-keyword">return</span> <span class="hljs-literal">true</span>;<br><span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br>}<br>},<br>]<br></code></pre></td></tr></table></figure><br>这样启动服务器的时候就可以针对不同的域名下进行对不同环境的转发效果。这样单一修改host就可以了。</p><h2 id="最后"><a href="#最后" class="headerlink" title="最后"></a>最后</h2><p>通过学习nginx、proxy、Charles等知识内容,可以帮助到前端开发、调试、debug工作。以后工作中遇到一些难解的问题,都能够帮助自己拓宽思路。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://github.com/chimurai/http-proxy-middleware">http-proxy-middleware</a></li><li><a href="https://www.cnblogs.com/qinyujie/p/8979464.html">nginx 语法 </a></li></ul>]]></content>
<tags>
<tag>nginx</tag>
<tag>api</tag>
</tags>
</entry>
<entry>
<title>mobx 实践与理解 (二)</title>
<link href="/2019/08/04/mobx-2/"/>
<url>/2019/08/04/mobx-2/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在《mobx 实践与理解 一》中,我们只是简单的使用了mobx,实现了类似redux那样的状态管理功能。实际工作需求要处理的场景远远不止这些。<br>今天要给大家解决《一》中提出的几个问题。</p><h2 id="实践操作"><a href="#实践操作" class="headerlink" title="实践操作"></a>实践操作</h2><h3 id="异步action问题"><a href="#异步action问题" class="headerlink" title="异步action问题"></a>异步action问题</h3><p>异步在前端的工作中是总要处理的问题,比如最简单的场景就是异步获取接口数据。那么在mobx中我们怎么使用呢?<br>可以看到官方中文文档中有介绍几种,我这里将演示async/await方法。<a href="https://github.com/CrewS/mobx-app.git">demo地址</a><br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></pre></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// store.js</span><br><span class="hljs-keyword">import</span> { observable, action, runInAction } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx"</span>;<br><span class="hljs-comment">// 异步获取数据的函数</span><br><span class="hljs-keyword">const</span> getData = <span class="hljs-keyword">async</span> () => {<br> <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resovle, reject</span>)=></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-keyword">const</span> d = [];<br> <span class="hljs-keyword">const</span> c = <span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">10</span>;<br> <span class="hljs-keyword">for</span>(<span class="hljs-keyword">let</span> i =<span class="hljs-number">0</span>;i< c;i++){<br> d.push(i);<br> }<br> resovle(d)<br> }, <span class="hljs-number">500</span>);<br> });<br> <span class="hljs-keyword">return</span> data;<br>}<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Store</span> </span>{<br> @observable list = [];<br> @observable loading = <span class="hljs-literal">false</span>;<br><span class="hljs-comment">// 异步action</span><br> @action<br> getData = <span class="hljs-keyword">async</span> () => {<br> <span class="hljs-built_in">this</span>.setLoading();<br> <span class="hljs-keyword">const</span> data = <span class="hljs-keyword">await</span> getData();<br> runInAction(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">this</span>.list = data;<br> <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">false</span>;<br> })<br> <span class="hljs-built_in">console</span>.log(data)<br> };<br> setLoading = <span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">this</span>.loading = <span class="hljs-literal">true</span>;<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> Store();<br><br></code></pre></td></tr></table></figure><br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// pageC/index.js</span><br><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">"./store"</span>;<br><span class="hljs-keyword">import</span> { observer } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx-react"</span>;<br><br>@observer<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PageA</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> <span class="hljs-function"><span class="hljs-title">componentWillMount</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-comment">// store.reset();</span><br> }<br> <span class="hljs-function"><span class="hljs-title">componentWillUnmount</span>(<span class="hljs-params"></span>)</span>{<br> store.reset();<br> }<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"render"</span>);<br> <span class="hljs-keyword">const</span> { list } = store;<br> <span class="hljs-keyword">return</span> (<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content-box"</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">10px</span>" }}></span>demo3 异步action<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> {</span><br><span class="xml"> store.loading === true ?</span><br><span class="xml"> '加载中....'</span><br><span class="xml"> :</span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span></span></span><br><span class="hljs-tag"><span class="xml"> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =></span> {</span><br><span class="xml"> store.getData();</span><br><span class="xml"> }}</span><br><span class="xml"> className="btn"</span><br><span class="xml"> ></span><br><span class="xml"> 点击获取新数据</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> }</span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">ul</span>></span></span><br><span class="xml"> {list.map(x => {</span><br><span class="xml"> return <span class="hljs-tag"><<span class="hljs-name">li</span> <span class="hljs-attr">key</span>=<span class="hljs-string">{x}</span>></span>{x}<span class="hljs-tag"></<span class="hljs-name">li</span>></span>;</span><br><span class="xml"> })}</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">ul</span>></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PageA;<br></code></pre></td></tr></table></figure><br>可以看到getData是一个异步生成数据的函数,然后action getData则是异步action。定义了action名后,用async 修饰函数。<br>在view中调用这个action和调用普通action是一样的。直接store.getdata() 即可。<br>在action中修改数据,会一次次触发渲染。这里有个工具函数 runInAction,将<br>runInAction 合并action状态操作,只触发一次渲染。可以通过将代码<code>this.list = data;this.loading = false;</code>放在外面和里面对比render的次数得到结论。</p><h3 id="数据重置问题"><a href="#数据重置问题" class="headerlink" title="数据重置问题"></a>数据重置问题</h3><p>数据的是独立的,在组件销毁后,数据对象还存在内存当中。所以再次挂载组件的时候,原数据并没有清除。这里有两种方式处理数据。<br>1、在componentWillMount()调用 reset()函数清空数据<br>2、在componentWillUnmount() 调用 reset() 函数清空数据<br>3、每个组件均有init()方法,然后componentWillMount的时候初始化数据</p><h3 id="开发规范"><a href="#开发规范" class="headerlink" title="开发规范"></a>开发规范</h3><p>接触mobx没多久,看mobx的使用都是比较简单的,怎么样都能实现功能,如果没有一套规范。那么团队代码中去维护代码就变得非常困难。<br>暂时我个人的实现规范是,一个组件中包含着</p><ul><li>view: 组件UI、处理视图的</li><li>store: 类似reducer,处理状态数据的</li><li>server: API接口请求的<br>这个是组件中的文件结构,但是一般项目中会有些公共的内容,比如公共的接口,公共的store数据这可怎么处理呢?我的想法是在根节点中注入一个全局store,这个store是存储共享数据的。公共的组件接口则抽取出来放在common目录下。整个目录结构可参考demo代码中的。</li></ul><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>这篇博客中我们处理了,异步action、数据重置、代码规范问题。我将在(三)中对mobx的源码进行一些解析。让我们知道为什么定义了观察的状态后,视图就能够响应。还有代码规范,我将参考下网上的最佳实践,看看还有没有更好的处理方案。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://cn.mobx.js.org/">mobx中文文档</a></li></ul>]]></content>
<tags>
<tag>mobx</tag>
</tags>
</entry>
<entry>
<title>mobx 实践与理解 (一)</title>
<link href="/2019/08/02/mobx/"/>
<url>/2019/08/02/mobx/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>mobx 是一个简单、可拓展的状态管理库。核心就是定义可观察的状态,对状态的变更作出相对应的响应。使用mobx,有助于理解数据驱动视图,状态管理。和redux的思想做对比,会得到更多思考和收获。</p><h2 id="环境搭建"><a href="#环境搭建" class="headerlink" title="环境搭建"></a>环境搭建</h2><p>读者可以按下面步骤搭建实践demo,也可以直接拉去代码实践操作。<a href="https://github.com/CrewS/mobx-app">demo地址</a></p><h4 id="第一步"><a href="#第一步" class="headerlink" title="第一步"></a>第一步</h4><p>这里是用yarn 通过create-react-app 去快速构建项目结构,也可以自己处理<br>yarn create react-app mobx-app</p><h4 id="第二步"><a href="#第二步" class="headerlink" title="第二步"></a>第二步</h4><p>使用eject命令,将配置释放出来<br>yarn eject</p><h4 id="第三步"><a href="#第三步" class="headerlink" title="第三步"></a>第三步</h4><p>接下来安装mobx与mobx-react框架<br>yarn add mobx-react –save<br>yarn add mobx –save</p><h4 id="第四步"><a href="#第四步" class="headerlink" title="第四步"></a>第四步</h4><p>使用mobx preset处理代码中的装饰器<br>需要安装两个包<br>yarn add babel-preset-mobx<br>yarn add @babel/plugin-transform-react-jsx-source</p><figure class="highlight json"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs json"><span class="hljs-comment">// package.json</span><br>{<br><span class="hljs-comment">//...</span><br><span class="hljs-attr">"babel"</span>: {<br><span class="hljs-attr">"presets"</span>: [<br><span class="hljs-string">"react-app"</span>,<br><span class="hljs-string">"mobx"</span><br>]<br>}<br>}<br></code></pre></td></tr></table></figure><h2 id="实践mobx"><a href="#实践mobx" class="headerlink" title="实践mobx"></a>实践mobx</h2><h4 id="demo1-计数器"><a href="#demo1-计数器" class="headerlink" title="demo1 计数器"></a>demo1 计数器</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><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// pageA index.js</span><br><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">"./store"</span>;<br><span class="hljs-keyword">import</span> { observer } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx-react"</span>;<br><br>@observer<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PageA</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"render"</span>);<br> <span class="hljs-keyword">return</span> (<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content-box"</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">10px</span>" }}></span>demo1 计数器<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">input</span></span></span><br><span class="hljs-tag"><span class="xml"> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =></span> {</span><br><span class="xml"> store.setCount(store.count + 1);</span><br><span class="xml"> }}</span><br><span class="xml"> type="button"</span><br><span class="xml"> value="+"</span><br><span class="xml"> id="add"</span><br><span class="xml"> /></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">span</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"incomeLabel"</span>></span>{store.count}<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">input</span></span></span><br><span class="hljs-tag"><span class="xml"> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{()</span> =></span> {</span><br><span class="xml"> store.setCount(store.count - 1);</span><br><span class="xml"> }}</span><br><span class="xml"> type="button"</span><br><span class="xml"> value="-"</span><br><span class="xml"> id="minus"</span><br><span class="xml"> /></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PageA;<br></code></pre></td></tr></table></figure><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><code class="hljs js"><span class="hljs-comment">// store.js</span><br><span class="hljs-keyword">import</span> { observable, action } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx"</span>;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">Store</span> </span>{<br> @observable count = <span class="hljs-number">0</span>;<br><br> @action<br> setCount = <span class="hljs-function"><span class="hljs-params">count</span> =></span> {<br> <span class="hljs-built_in">this</span>.count = count;<br> };<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> Store();<br><br></code></pre></td></tr></table></figure><p>涉及到2个文件<br>其中store.js中定义了观察的变量,和action 与redux有点类似<br>在pageA/index.js中使用了 observer,作用是封装组件的render函数,使其能够响应数据变化。<br>给<code>+</code> <code>-</code>的按钮绑定了事件触发store里定义的action方法,改变观察值,从而让组件重新渲染。<br>mobx的使用和它定义的非常符合。定义可观察的状态,改变状态,对应的视图响应状态变化</p><h4 id="demo2-跨组件通信"><a href="#demo2-跨组件通信" class="headerlink" title="demo2 跨组件通信"></a>demo2 跨组件通信</h4><p>demo1只是在同一个组件中使用了mobx,改变的状态和响应视图的都是在同一个组件里。demo2将给大家实现跨组件的状态响应。<br>这里演示兄弟组件通信。<br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// pageB index.js</span><br><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><span class="hljs-keyword">import</span> InputView <span class="hljs-keyword">from</span> <span class="hljs-string">"./input"</span>;<br><span class="hljs-keyword">import</span> ShowView <span class="hljs-keyword">from</span> <span class="hljs-string">"./show"</span>;<br><span class="hljs-keyword">import</span> store <span class="hljs-keyword">from</span> <span class="hljs-string">"./store"</span>;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">PageB</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">return</span> (<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">className</span>=<span class="hljs-string">"content-box"</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span> <span class="hljs-attr">style</span>=<span class="hljs-string">{{</span> <span class="hljs-attr">marginBottom:</span> "<span class="hljs-attr">10px</span>" }}></span>demo2 跨组件通信<span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">span</span>></span>InputView 组件<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">span</span>></span>输入值:<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">InputView</span> <span class="hljs-attr">searchStore</span>=<span class="hljs-string">{store}</span> /></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">span</span>></span>ShowView 组件<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">span</span>></span>响应输出值:<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">ShowView</span> <span class="hljs-attr">searchStore</span>=<span class="hljs-string">{store}</span> /></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br> );<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> PageB;<br><br></code></pre></td></tr></table></figure><br>pageB 作为父组件只做了store的导入,和组件的展示<br><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><code class="hljs js"><span class="hljs-comment">// store.js</span><br><span class="hljs-comment">// 这里和demo1一样,定义了观察变量和action方法</span><br><span class="hljs-keyword">import</span> { observable, action } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx"</span>;<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SearchStore</span> </span>{<br> @observable searchText = <span class="hljs-string">""</span>;<br><br> @action<br> setSearchText = <span class="hljs-function"><span class="hljs-params">searchText</span> =></span> {<br> <span class="hljs-built_in">this</span>.searchText = searchText;<br> };<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">new</span> SearchStore();<br><br></code></pre></td></tr></table></figure><br><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><code class="hljs js"><span class="hljs-comment">// input 组件中 实现了输入的交互和触发了 action方法</span><br><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><span class="hljs-keyword">import</span> { observer } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx-react"</span>;<br><br>@observer<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SearchInput</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> handleInputChanged = <span class="hljs-function"><span class="hljs-params">event</span> =></span> {<br> <span class="hljs-keyword">const</span> { searchStore } = <span class="hljs-built_in">this</span>.props;<br> searchStore.setSearchText(event.target.value);<br> };<br><br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">const</span> { searchStore } = <span class="hljs-built_in">this</span>.props;<br> <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">input</span> <span class="hljs-attr">value</span>=<span class="hljs-string">{searchStore.searchText}</span> <span class="hljs-attr">onChange</span>=<span class="hljs-string">{this.handleInputChanged}</span> /></span></span>;<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SearchInput;<br><br></code></pre></td></tr></table></figure><br><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><code class="hljs js"><span class="hljs-comment">// SearchShow 组件中 只做了searchText数据的展示。</span><br><span class="hljs-comment">// 具体效果:在SearchInput组件中改变searchText的值,会在SearchShow响应显示变化</span><br><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">"react"</span>;<br><span class="hljs-keyword">import</span> { observer } <span class="hljs-keyword">from</span> <span class="hljs-string">"mobx-react"</span>;<br><br>@observer<br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">SearchShow</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">React</span>.<span class="hljs-title">Component</span> </span>{<br> <span class="hljs-function"><span class="hljs-title">render</span>(<span class="hljs-params"></span>)</span> {<br><span class="hljs-keyword">const</span> { searchStore } = <span class="hljs-built_in">this</span>.props;<br> <span class="hljs-keyword">return</span> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">span</span>></span>{searchStore.searchText}<span class="hljs-tag"></<span class="hljs-name">span</span>></span></span>;<br> }<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> SearchShow;<br><br></code></pre></td></tr></table></figure></p><h2 id="小结"><a href="#小结" class="headerlink" title="小结"></a>小结</h2><p>通过demo1 与 demo2的实现,我们能够理解mobx的使用是比较的方便和简洁的。对于小需求小项目中应用mobx,能够比较快的实现。学习难度也不大。不过还有几个问题可能需要思考和解决的。</p><ul><li>1、如何管理store,对团队合作编程来说,代码规范也是很重要的事情</li><li>2、对异步action的处理</li><li>3、组件销毁与数据初始化问题</li></ul><p>下一篇将回答以上问题</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://cn.mobx.js.org/">mobx中文文档</a></li><li><a href="https://www.html.cn/create-react-app/docs/getting-started/">create-react-app中文文档</a></li></ul>]]></content>
<tags>
<tag>mobx</tag>
</tags>
</entry>
<entry>
<title>nas 网络附属存储</title>
<link href="/2019/07/05/nas/"/>
<url>/2019/07/05/nas/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>最近看到树莓派的一些创意 DIY,发现其中有家庭影院的概念,接着了解了相关的知识。从而接触到 NAS,开启了对将来家庭数据管理的想象。</p><h2 id="什么是-nas"><a href="#什么是-nas" class="headerlink" title="什么是 nas"></a>什么是 nas</h2><p>百科上的概念</p><blockquote><p>NAS(Network Attached Storage:网络附属存储)按字面简单说就是连接在网络上,具备资料存储功能的装置,因此也称为“网络存储器”。它是一种专用数据存储服务器。它以数据为中心,将存储设备与服务器彻底分离,集中管理数据,从而释放带宽、提高性能、降低总拥有成本、保护投资。其成本远远低于使用服务器存储,而效率却远远高于后者。目前国际著名的 NAS 企业有 Netapp、EMC、OUO 等。</p></blockquote><p>个人理解,NAS就是网络云盘,具备一些对存储资源操作功能的服务器,家庭NAS则是私有云盘。</p><h2 id="现有产品"><a href="#现有产品" class="headerlink" title="现有产品"></a>现有产品</h2><p>nas不是新名词,在了解nas后搜索了相关的资料,发现已经有很多人买了现成的nas服务器或者是自己diy一个nas,而nas服务器比较出名的是“群晖”。</p><p>1、什么是群晖,群晖具备什么功能<br>群晖nas = 群晖系统 + 硬件 为用户提供一站式服务</p><p>2、黑群晖、白群晖<br>黑群晖和白群晖的区别和黑苹果白苹果的区别差不多,就是利用自己的硬件安装了群晖系统(未授权)。<br>个人理解:白群晖的硬件性价比较低,但是软件服务与售后比较好,黑群晖则是自己购买硬件设备,安装群晖系统,通用攻略教程一步步DIY搭建<br>比较消耗时间,但是体验DIY过程,了解整个运转流程的原理,劣势可能不太稳定,遇到未知错误需要自己处理。</p><h2 id="DIY"><a href="#DIY" class="headerlink" title="DIY"></a>DIY</h2><p>必备的几个硬件产品</p><ul><li>cpu </li><li>主板</li><li>硬盘</li><li>电源</li></ul><p>目标</p><ul><li>能耗低</li><li>硬盘损耗不严重</li><li>处理性能强</li></ul><p>ps:待补充实践指南</p><h2 id="进阶玩法"><a href="#进阶玩法" class="headerlink" title="进阶玩法"></a>进阶玩法</h2><p>nas只是作为一个网络存储以及供应到端一条线。数据来源还是需要途径填充进去。一般人可能就是从已有的资源或者手动下载资源到nas中,作为存储资源<br>此时“PT”值得我们深入了解,一个获取多种资源的方式。参与PT,可以获取到各种高质量的媒体资源。作为NAS的源泉。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://baike.baidu.com/item/NAS/3465615?fr=aladdin">NAS百度百科解释</a></li><li><a href="https://baike.baidu.com/item/pt%E4%B8%8B%E8%BD%BD/6883416?fr=aladdin">pt下载</a></li></ul>]]></content>
<tags>
<tag>nas</tag>
</tags>
</entry>
<entry>
<title>yeoman-generator 深入使用</title>
<link href="/2019/06/11/yeoman-generator/"/>
<url>/2019/06/11/yeoman-generator/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在搭建前端脚手架那篇文章中,对yeoman-generator的解释比较简略,可能使用上会出现一些问题<br>所以今天来继续介绍yeoman-generator</p><h2 id="yeoman-generator"><a href="#yeoman-generator" class="headerlink" title="yeoman-generator"></a>yeoman-generator</h2><p>从<a href="https://yeoman.io/authoring/index.html">官方文档</a>来看<br>简单说下用到的功能</p><ul><li><a href="https://yeoman.io/authoring/running-context.html">任务执行系统</a></li><li><a href="https://yeoman.io/authoring/file-system.html">文件系统、模版渲染</a></li><li><a href="https://yeoman.io/authoring/user-interactions.html#prompts">命令行问答系统</a></li></ul><h4 id="任务执行系统"><a href="#任务执行系统" class="headerlink" title="任务执行系统"></a>任务执行系统</h4><p>这里有个概念自动执行task<br>命名规则影响是否自动执行,影响执行顺序<br>下划线开头命名的成员函数视为内部函数 类似 _name(){},不会被自动执行<br>命名为下面的话会按下面的顺序执行<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 1. initializing - Your initialization methods (checking current project state, getting configs, etc)</span><br><span class="hljs-comment">// 2. prompting - Where you prompt users for options (where you’d call this.prompt())</span><br><span class="hljs-comment">// 3. configuring - Saving configurations and configure the project (creating .editorconfig files and other metadata files)</span><br><span class="hljs-comment">// 4. default - If the method name doesn’t match a priority, it will be pushed to this group.</span><br><span class="hljs-comment">// 5. writing - Where you write the generator specific files (routes, controllers, etc)</span><br><span class="hljs-comment">// 6. conflicts - Where conflicts are handled (used internally)</span><br><span class="hljs-comment">// 7. install - Where installations are run (npm, bower)</span><br><span class="hljs-comment">// 8. end - Called last, cleanup, say good bye, etc</span><br></code></pre></td></tr></table></figure></p><p>通过这个规则,可以很好的规划任务及其执行顺序处理事务。</p><h4 id="文件系统、模版系统"><a href="#文件系统、模版系统" class="headerlink" title="文件系统、模版系统"></a>文件系统、模版系统</h4><p>有几个重要的API<br><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><code class="hljs js"><span class="hljs-built_in">this</span>.destinationRoot(); <span class="hljs-comment">// 获取命令行执行的目录</span><br><span class="hljs-built_in">this</span>.templatePath(); <span class="hljs-comment">// 获取该generator对应的teamplate的目录</span><br><span class="hljs-built_in">this</span>.fs.copyTpl(<span class="hljs-string">'templatePath'</span>, <span class="hljs-string">'targetPath'</span>, <span class="hljs-string">'params'</span>)<span class="hljs-comment">//三个参数模版目录,目标目录,参数</span><br></code></pre></td></tr></table></figure><br>copyTpl的方法内置了ejs模版渲染引擎,按ejs的语法可以实现非常自定义的模版渲染。</p><h4 id="命令行问答系统"><a href="#命令行问答系统" class="headerlink" title="命令行问答系统"></a>命令行问答系统</h4><p>在使用脚手架过程中,经常需要获取用户的输入,命令行输入对于即时响应的场景是非常适合的。<br>在generator中,有内置的命令行问答系统,是集成<a href="https://github.com/SBoudrias/Inquirer.js">Inquirer.js</a>,可以查看它的文档调用。<br>在实际的例子中<br>确认类型的问答如下<br><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><code class="hljs js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Generator</span> </span>{<br> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">prompting</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-built_in">this</span>.answers = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.prompt([<br> {<br> <span class="hljs-attr">type</span>: <span class="hljs-string">"confirm"</span>,<br> <span class="hljs-attr">name</span>: <span class="hljs-string">"cool"</span>,<br> <span class="hljs-attr">message</span>: <span class="hljs-string">"Would you like to enable the Cool feature?"</span><br> }<br> ]);<br> }<br> <span class="hljs-function"><span class="hljs-title">writing</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-built_in">this</span>.log(<span class="hljs-string">"cool feature"</span>, <span class="hljs-built_in">this</span>.answers.cool); <span class="hljs-comment">// user answer `cool` used</span><br> }<br>};<br></code></pre></td></tr></table></figure><br>还有更多类型的命令行式问答:列表单选择、多选、input输入、是否确认<br>通过这几个系统的结合使用可以很好的辅助我们开发脚手架。</p><h2 id="后语"><a href="#后语" class="headerlink" title="后语"></a>后语</h2><p>笔者一开始单纯阅读脚手架的代码,发现并不是很好理解。但是通过实践编码一个脚手架后,发现了解几个核心功能后,编码一个脚手架并不是一件难事<br>纸上得来终觉浅,绝知此事要躬行。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://yeoman.io/authoring/index.html">yeoman-generator</a></li><li><a href="https://github.com/tj/commander.js#readme">commander</a></li><li><a href="https://github.com/SBoudrias/Inquirer.js">promts(Inquirer.js)</a></li><li><a href="https://ejs.bootcss.com/">ejs文档</a></li></ul>]]></content>
<tags>
<tag>generator</tag>
</tags>
</entry>
<entry>
<title>前端脚手架搭建</title>
<link href="/2019/05/08/yeoman/"/>
<url>/2019/05/08/yeoman/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p><strong>什么是脚手架呢?</strong><br>在我的理解中脚手架是一个尽可能减少手工操作又可以很好的完成项目搭建的自动化工具。</p><p><strong>脚手架为什么便于项目搭建?</strong><br>在工作中,我们很可能会面对类型非常相同的多个项目,在创建新的项目的时候,一般都是通过copy文件到新的目录下,手动修改完成新项目的搭建<br>通过手工的方式,很有可能会出现一些细小的错误,导致项目运行不起来,浪费时间。<br>还有通过脚手架可以达到,创建模版文件的效果,例如一个页面是由view.js style.css reducer.js构成,通过脚手架指令可以一次性创建并完成初始化赋值效果。<br>无论工作还是学习的过程中,我们接触过很多脚手架,一直享受着脚手架带来的便利。不过却比较少去研究,这段时间都在学习和编码自己的脚手架,拓宽自己的知识领域,可以尝试探索学习更多知识。技术团队建设中,对团队框架进行脚手架化,提升团队工作效率和规范标准。</p><h2 id="yeoman-generator"><a href="#yeoman-generator" class="headerlink" title="yeoman-generator"></a>yeoman-generator</h2><p>现在搜索脚手架教程,大多数都与 yeoman 有关。确实 yeoman 上有非常多的内置模块,便于脚手架的编写。<br>例如:自执行函数模块,文件操作模块,命令行问答模块,命令行参数处理等<br>yeoman 可以写一个 generator 脚手架,使用 yo 指令去执行。不过我们想要更加自定义的方式去实现。例如:创建自己独特的指令 zz,则是利用 yeoman-generator 包,使用 yeoman 的环境调用编写好的 gennerator 结合 bin,就可以完成理想的效果。</p><h2 id="实战:脚手架编写"><a href="#实战:脚手架编写" class="headerlink" title="实战:脚手架编写"></a>实战:脚手架编写</h2><p>在实践前,笔者也是看过很多教程和参考的例子,然后才尝试动手编码。</p><h3 id="第一个知识点-:命令行命令的创建。"><a href="#第一个知识点-:命令行命令的创建。" class="headerlink" title="第一个知识点 :命令行命令的创建。"></a>第一个知识点 :命令行命令的创建。</h3><p>在目录中创建了一个 index.js</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br></pre></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"hello"</span>);<br></code></pre></td></tr></table></figure><p>平时我们使用 node index.js 是可以输出 hello 的,此时我们想尝试使用命令行指令执行,该怎么处理呢<br>先在 package.json 中增加一个 bin 的属性</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br> <span class="hljs-attr">"bin"</span>: {<br> <span class="hljs-attr">"zz"</span>: <span class="hljs-string">"./index.js"</span><br> }<br>}<br></code></pre></td></tr></table></figure><p>修改 index.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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-meta">#! /usr/bin/env node</span><br><br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">"hello"</span>);<br></code></pre></td></tr></table></figure><p>一般在发包后, 通过 npm -g 指令安装包会将 zz 指令注入到全局上,在开发过程中想要测试则使用 npm link 指令(具体效果自己可以搜索)<br>完成以上步骤后,在命令行中输入 zz,显示 hello。<br>注意事项: 如果 mac 系统下安装 zsh,可能会提示 zsh: permission denied: zz,则需要到 bin 目录下赋予 zz 命令权限<br>mac 下目录是 <code>/usr/local/bin</code>, 执行以下命令 <code>cd /usr/local/bin</code>, <code>chmod +x zz</code> 完成后即可。</p><h4 id="第二个知识点:commander"><a href="#第二个知识点:commander" class="headerlink" title="第二个知识点:commander"></a>第二个知识点:commander</h4><p>使用 commander 库,可以极大的拓展指令参数、帮助信息、提示信息、参数校验等操作<br>example</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><code class="hljs js"><span class="hljs-meta">#! /usr/bin/env node</span><br><span class="hljs-comment">// index.js</span><br><span class="hljs-keyword">const</span> program = <span class="hljs-built_in">require</span>(<span class="hljs-string">"commander"</span>);<br>program.command(<span class="hljs-string">"init <name>"</span>).action(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">name</span>) </span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">"参数name为: "</span>, name);<br>});<br>program.parse(process.argv);<br></code></pre></td></tr></table></figure><p>安装 commander 包: npm install commander –save,然后 zz init abc<br>输出结果: 参数 name 为 abc<br>在 action 的回调函数中,可以获取参数 name,还可以执行后续步骤,例如调用 generator,这样从指令到 commander 参数到执行 generator 的一条线就打通了。</p><h4 id="第三个知识点:generator"><a href="#第三个知识点:generator" class="headerlink" title="第三个知识点:generator"></a>第三个知识点:generator</h4><p>利用 yeoman-environment 调用 generator</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><code class="hljs js"><span class="hljs-keyword">var</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);<br><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">cmd: string</span>) </span>{<br> <span class="hljs-keyword">var</span> yeoman = <span class="hljs-built_in">require</span>(<span class="hljs-string">"yeoman-environment"</span>);<br> <span class="hljs-keyword">var</span> env = yeoman.createEnv(),<br> argumentsArray = <span class="hljs-built_in">Array</span>.prototype.slice.call(<span class="hljs-built_in">arguments</span>, <span class="hljs-number">0</span>);<br> <span class="hljs-keyword">var</span> gPath = <span class="hljs-string">"./"</span> + path.join(<span class="hljs-string">"generator-xxx"</span>, cmd, <span class="hljs-string">"index.js"</span>);<br> env.register(<span class="hljs-built_in">require</span>.resolve(gPath), cmd);<br> env.run(<span class="hljs-built_in">Array</span>.prototype.join.call(<span class="hljs-built_in">arguments</span>, <span class="hljs-string">" "</span>));<br>};<br></code></pre></td></tr></table></figure><p>这段函数的含义:利用 yeoman-environment 创建了 yeoman 的 env,注册了 yeoman generator,最用调用这个 generator<br>generator 的编写<br>查看<a href="https://yeoman.io/authoring/index.html">官方的教程</a>文档可以了解以下几点内容</p><ul><li>成员函数 会被自动执行</li><li>下划线前缀的成员函数 不会被自动执行例如: _inti(){}</li><li>this.sourceRoot() 获取项目路径</li><li>this.templatePath() 获取</li><li>this.fs.copyTpl(source, target, params) 文件拷贝方法,还能传递变量动态渲染</li><li>this.composeWith() 调用其他的 generator</li></ul><p>这些是笔者在构建自己的脚手架中用到的比较多的函数方法,有需要其他的可以阅读官方文档查找相关内容。</p><h4 id="第四个知识点"><a href="#第四个知识点" class="headerlink" title="第四个知识点"></a>第四个知识点</h4><p>在 generator 中一般会有 templates 文件夹,但是如果 templates 与脚手架绑定一块,只要模版有更新,则脚手架要更新,这样的耦合是不太方便的。<br>所以了解 download-git-repo 这个 npm 包后,发现可以从远端的 git 仓库下载模版文件,最后 generator 再拷贝渲染模版文件完成项目初始化。<br>使用方法</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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// libs</span><br><span class="hljs-keyword">const</span> path = <span class="hljs-built_in">require</span>(<span class="hljs-string">"path"</span>);<br><span class="hljs-keyword">const</span> download = <span class="hljs-built_in">require</span>(<span class="hljs-string">"download-git-repo"</span>);<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> (target: any) => {<br> <span class="hljs-built_in">console</span>.log(target);<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {<br> download(<br> <span class="hljs-string">"direct:https://github.com/yourname/your-project.git#master"</span>,<br> target,<br> { <span class="hljs-attr">clone</span>: <span class="hljs-literal">true</span> },<br> <span class="hljs-function">(<span class="hljs-params">err: any</span>) =></span> {<br> <span class="hljs-keyword">if</span> (err) {<br> reject(err);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 下载的模板存放在一个临时路径中,下载完成后,可以向下通知这个临时路径,以便后续处理</span><br> resolve(target);<br> }<br> },<br> );<br> });<br>};<br></code></pre></td></tr></table></figure><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><code class="hljs js"><span class="hljs-comment">// app/index.js</span><br><span class="hljs-keyword">if</span> (!fs.existsSync(<span class="hljs-built_in">this</span>.templatePath())) {<br> <span class="hljs-keyword">const</span> spinner = spin(<span class="hljs-string">"Download templates"</span>, <span class="hljs-string">"Box1"</span>);<br> spinner.start();<br> download(<span class="hljs-built_in">this</span>.templatePath())<br> .then(<span class="hljs-function">() =></span> {<br> spinner.stop();<br> })<br> .catch(<span class="hljs-function"><span class="hljs-params">err</span> =></span> <span class="hljs-built_in">console</span>.log(err));<br>}<br></code></pre></td></tr></table></figure><p>将download-git-repo封装成promise方法,然后结合命令行的加载动画效果,完成动态加载模版文件,等待模版文件加载完成后再执行后续操作。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://yeoman.io/authoring/index.html">yeoman官方文档</a></li><li><a href="https://juejin.im/post/5a31d210f265da431a43330e#heading-0">nodejs脚手架搭建教程</a></li></ul>]]></content>
<tags>
<tag>node</tag>
<tag>typescript</tag>
</tags>
</entry>
<entry>
<title>cookie与session实现持久化登陆</title>
<link href="/2019/04/23/cookie/"/>
<url>/2019/04/23/cookie/</url>
<content type="html"><![CDATA[<h2 id="简单了解session-cookie"><a href="#简单了解session-cookie" class="headerlink" title="简单了解session cookie"></a>简单了解session cookie</h2><p>常用的会话跟踪技术是cookie与session<br>cookie是存储在客户端,session是存储在server端<br>可以这么说,cookie是一种补足http协议无状态缺陷的机制<br>一个cookie的设置分为4步</p><ul><li>客户端发送http请求</li><li>服务器响应http请求 set-cookies response</li><li>客户端发送http请求 包含cookie头部 发送到服务器端</li><li>服务器返回一个http response </li></ul><p>session 是用服务器来保持状态的。专门为用户开辟存储空间,session被创建后会保存在服务器中,其中session ID则发送给客户端,客户端下次发送请求携带session ID,服务器则会查询该ID 找到对应的session读取信息。<br>在使用session机制保持持久化登陆的实现上可以将sessionID保存在 cookie、header、URL中,客户端带着sessionID过来,服务器根据这个sessionID判断是否存在当前会话。</p><h2 id="结合例子说明"><a href="#结合例子说明" class="headerlink" title="结合例子说明"></a>结合例子说明</h2><p>session一般结合cookie实现持久化登陆的,其中nodejs中更多的会使用到passport这个库来进行鉴权和持久化登陆。<br>笔者最近在学习eggjs,则使用egg-passport来说明一下。<br>egg-passport verify 认证后,会将user信息存储在session中,通过序列化(serializeUser)和反序列化(deserializeUser)对user信息进行加工处理。</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js">app.passport.serializeUser(<span class="hljs-keyword">async</span> (ctx, user) => {<br><span class="hljs-comment">// do some things</span><br><span class="hljs-comment">// 去除敏感信息</span><br><span class="hljs-keyword">return</span> user;<br>});<br>app.passport.deserializeUser(<span class="hljs-keyword">async</span> (ctx, user) => {<br><span class="hljs-comment">// do some things</span><br><span class="hljs-comment">// 通过user部分信息反查数据库得到完整信息</span><br><span class="hljs-keyword">return</span> user;<br>});<br></code></pre></td></tr></table></figure><h2 id="深度解读egg-koa中-session的存储原理"><a href="#深度解读egg-koa中-session的存储原理" class="headerlink" title="深度解读egg/koa中 session的存储原理"></a>深度解读egg/koa中 session的存储原理</h2><p>根据概念知道,session是存储在服务器中的,但是egg中的实现比较“神奇”,因为和概念似乎有点出入,导致笔者找了许久都找不到一个存储session的地方。最后通过cnode中提问,得到线索与cookie相关,然后重新阅读源码发现整个流程原来是这样的。服务器存储session到centext中,同时设置cookie为session的值,然后再次请求的时候,解析cookie,将值初始化为session<br>我们在《passport源码阅读》那篇博客中已经知道,在verify验证完成后,会执行ctx.req.session = user。将用户信息保存在session中,然后这个session其实是koa-session中间件来的。通过阅读koa-session的代码。知道session通过加密方式存储在cookie中,最后用户再次请求的时候,会将携带的cookie解析为ctx.session内容。这就是egg session的工作原理。</p><p>请看关键代码<br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">//egg-passport/lib/framework.js</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">initialize</span>(<span class="hljs-params">passport</span>) </span>{<br> <span class="hljs-comment">// https://github.com/jaredhanson/passport/blob/master/lib/middleware/initialize.js</span><br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">passportInitialize</span>(<span class="hljs-params">ctx, next</span>) </span>{<br> <span class="hljs-keyword">const</span> req = ctx.req;<br> req._passport = {<br> <span class="hljs-attr">instance</span>: passport,<br> };<br> <span class="hljs-comment">// ref to ctx</span><br>req.ctx = ctx;<br><span class="hljs-comment">// 将ctx的session挂载到req.session中</span><br> req.session = ctx.session;<br> req.query = ctx.query;<br> req.body = ctx.request.body;<br><br> <span class="hljs-keyword">if</span> (req.session && req.session[passport._key]) {<br> <span class="hljs-comment">// load data from existing session</span><br> req._passport.session = req.session[passport._key];<br> }<br><br> <span class="hljs-keyword">return</span> next();<br> };<br>}<br></code></pre></td></tr></table></figure><br>koa-session 通过defineProperties,对session的读取操作进行监听执行事件处理<br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// koa-session/index.js</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">extendContext</span>(<span class="hljs-params">context, opts</span>) </span>{<br> <span class="hljs-keyword">if</span> (context.hasOwnProperty(CONTEXT_SESSION)) {<br> <span class="hljs-keyword">return</span>;<br> }<br> <span class="hljs-built_in">Object</span>.defineProperties(context, {<br> [CONTEXT_SESSION]: {<br> <span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>[_CONTEXT_SESSION]) <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>[_CONTEXT_SESSION];<br> <span class="hljs-built_in">this</span>[_CONTEXT_SESSION] = <span class="hljs-keyword">new</span> ContextSession(<span class="hljs-built_in">this</span>, opts);<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>[_CONTEXT_SESSION];<br> },<br> },<br> <span class="hljs-attr">session</span>: {<br> <span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>[CONTEXT_SESSION].get();<br> },<br> <span class="hljs-function"><span class="hljs-title">set</span>(<span class="hljs-params">val</span>)</span> {<br> <span class="hljs-built_in">this</span>[CONTEXT_SESSION].set(val);<br> },<br> <span class="hljs-attr">configurable</span>: <span class="hljs-literal">true</span>,<br> },<br> <span class="hljs-attr">sessionOptions</span>: {<br> <span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">this</span>[CONTEXT_SESSION].opts;<br> },<br> },<br> });<br>}<br></code></pre></td></tr></table></figure><br>这个是对session保存在cookie的save方法,是commit调用的时候触发save方法<br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// koa-session/lib/context.js</span><br><span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">save</span>(<span class="hljs-params">changed</span>)</span> {<br> <span class="hljs-keyword">const</span> opts = <span class="hljs-built_in">this</span>.opts;<br> <span class="hljs-keyword">const</span> key = opts.key;<br> <span class="hljs-keyword">const</span> externalKey = <span class="hljs-built_in">this</span>.externalKey;<br> <span class="hljs-keyword">let</span> json = <span class="hljs-built_in">this</span>.session.toJSON();<br> <span class="hljs-comment">// set expire for check</span><br> <span class="hljs-keyword">let</span> maxAge = opts.maxAge ? opts.maxAge : ONE_DAY;<br> <span class="hljs-keyword">if</span> (maxAge === <span class="hljs-string">'session'</span>) {<br> <span class="hljs-comment">// do not set _expire in json if maxAge is set to 'session'</span><br> <span class="hljs-comment">// also delete maxAge from options</span><br> opts.maxAge = <span class="hljs-literal">undefined</span>;<br> json._session = <span class="hljs-literal">true</span>;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// set expire for check</span><br> json._expire = maxAge + <span class="hljs-built_in">Date</span>.now();<br> json._maxAge = maxAge;<br> }<br><br> <span class="hljs-comment">// save to external store</span><br> <span class="hljs-keyword">if</span> (externalKey) {<br> debug(<span class="hljs-string">'save %j to external key %s'</span>, json, externalKey);<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> maxAge === <span class="hljs-string">'number'</span>) {<br> <span class="hljs-comment">// ensure store expired after cookie</span><br> maxAge += <span class="hljs-number">10000</span>;<br> }<br> <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.store.set(externalKey, json, maxAge, {<br> changed,<br> <span class="hljs-attr">rolling</span>: opts.rolling,<br> });<br> <span class="hljs-keyword">if</span> (opts.externalKey) {<br> opts.externalKey.set(<span class="hljs-built_in">this</span>.ctx, externalKey);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-built_in">this</span>.ctx.cookies.set(key, externalKey, opts);<br> }<br> <span class="hljs-keyword">return</span>;<br> }<br><br> <span class="hljs-comment">// save to cookie</span><br> debug(<span class="hljs-string">'save %j to cookie'</span>, json);<br> json = opts.encode(json);<br> debug(<span class="hljs-string">'save %s'</span>, json);<br><br> <span class="hljs-built_in">this</span>.ctx.cookies.set(key, json, opts);<br> }<br></code></pre></td></tr></table></figure><br>调用commit的代码<br><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><code class="hljs js"><span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">opts, app</span>) </span>{<br> <span class="hljs-comment">// session(app[, opts])</span><br> <span class="hljs-keyword">if</span> (opts && <span class="hljs-keyword">typeof</span> opts.use === <span class="hljs-string">'function'</span>) {<br> [ app, opts ] = [ opts, app ];<br> }<br> <span class="hljs-comment">// app required</span><br> <span class="hljs-keyword">if</span> (!app || <span class="hljs-keyword">typeof</span> app.use !== <span class="hljs-string">'function'</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'app instance required: `session(opts, app)`'</span>);<br> }<br><br> opts = formatOpts(opts);<br> extendContext(app.context, opts);<br><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">session</span>(<span class="hljs-params">ctx, next</span>) </span>{<br> <span class="hljs-keyword">const</span> sess = ctx[CONTEXT_SESSION];<br> <span class="hljs-keyword">if</span> (sess.store) <span class="hljs-keyword">await</span> sess.initFromExternal();<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-comment">// 这里需要理解 koa的洋葱圈模型中间件执行顺序</span><br> <span class="hljs-keyword">await</span> next();<br> } <span class="hljs-keyword">catch</span> (err) {<br> <span class="hljs-keyword">throw</span> err;<br> } <span class="hljs-keyword">finally</span> {<br> <span class="hljs-keyword">if</span> (opts.autoCommit) {<br> <span class="hljs-keyword">await</span> sess.commit();<br> }<br> }<br> };<br>};<br></code></pre></td></tr></table></figure><br>每个请求走过中间件最后会commit一次,就是更新cookie为最新的session内容。</p><h2 id="后记"><a href="#后记" class="headerlink" title="后记"></a>后记</h2><p>深入了解node的session存储机制的过程中又阅读了几个插件的源码内容,收获还是比较丰富的。<br>记得一位博主说过,带着问题阅读源码是读源码的最好方式。通过几个问题,可能就可以由点到面的理解源码的代码和思想。<br>比较深入的理解一些代码带来的成就感还是挺棒的,小伙伴们也可以带着问题慢慢去看源码,保证有收获。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://github.com/jaredhanson/passport">passport</a></li><li><a href="https://juejin.im/post/5aede266f265da0ba266e0ef">聊一聊session和cookie</a></li><li><a href="https://github.com/koajs/session">koa-session</a></li><li><a href="https://github.com/eggjs/egg">egg</a></li></ul>]]></content>
<tags>
<tag>cookie</tag>
</tags>
</entry>
<entry>
<title>rrweb与谷歌插件的实践记录(web录制与回放)</title>
<link href="/2019/04/18/rrweb/"/>
<url>/2019/04/18/rrweb/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>看到标题,大家会好奇,rrweb究竟是什么?<a href="https://github.com/rrweb-io/rrweb">rrweb</a>是一个开源框架,目前主要用于web 屏幕录制和回放。利用rrweb,以及它future提案,将对bug收集,屏幕录制,自动化测试等等有非常大的作用。笔者看了官方对其的<a href="https://zhuanlan.zhihu.com/p/60639266">说明文章</a>后,非常期待rrweb的后续发展,并且实践了一下rrweb与谷歌插件的集合,实现了对任意网页的屏幕录制与回放功能。</p><h2 id="起步"><a href="#起步" class="headerlink" title="起步"></a>起步</h2><p>通过阅读官方的<a href="https://github.com/rrweb-io/rrweb/blob/master/guide.zh_CN.md">使用指南</a>,快速实现了一次屏幕录制和回放。然后思考怎么方便的实践。主要思路还是从解决用户怎么控制开启、停止、预览、存储方面考虑。一开始想着是直接在页面中添加对应的代码,发现这样的侵入性太大了,而且并非所有的用户都需要一部分功能,无效的资源则浪费,还增大用户的请求带宽。后来发现谷歌插件的开发思路非常符合我的需求。用户(开发、有问题的用户),安装谷歌插件后,通过热键录制,生成数据后可以观看回放,最后决定是否上传服务器。用户再将bug(问题)与该回放地址关联,指派给对应的开发解决问题。这样的流程非常的清晰,而且在代码层面上是毫无侵入感的,没有安装插件的用户对我们的系统是无感知的,拓展性很强不止单一系统可用,其他系统也可以使用这个插件完成屏幕录制。一处开发,处处使用。</p><h2 id="主要逻辑思路"><a href="#主要逻辑思路" class="headerlink" title="主要逻辑思路"></a>主要逻辑思路</h2><p>主要完成以下几个工作</p><ul><li>开启录制</li><li>结束录制</li><li>数据压缩</li><li>数据保存(地址or服务器)</li><li>数据解压</li><li>回放录屏</li></ul><p>通过监听F1键触发事件录制开启</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).addEventListener(<span class="hljs-string">'keydown'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">keydown</span> (<span class="hljs-params">e</span>) </span>{<br><span class="hljs-keyword">if</span>(e.keyCode === <span class="hljs-number">112</span>){<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'start'</span>,chrome);<br>alert(<span class="hljs-string">'准备开始'</span>)<br>start()<br>}<br>})<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">start</span>(<span class="hljs-params"></span>)</span>{<br>a = rrweb.record({<br><span class="hljs-function"><span class="hljs-title">emit</span>(<span class="hljs-params">event</span>)</span> {<br><span class="hljs-comment">// 将 event 存入 events 数组中</span><br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'event'</span>)<br>events.push(event);<br>},<br>});<br>}<br></code></pre></td></tr></table></figure><p>通过监听F2键触发事件录制停止</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><code class="hljs js"><span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'html'</span>).addEventListener(<span class="hljs-string">'keydown'</span>, <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">keydown</span> (<span class="hljs-params">e</span>) </span>{<br><span class="hljs-keyword">if</span>(e.keyCode === <span class="hljs-number">113</span>){<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'stop'</span>);<br>stop();<br>alert(<span class="hljs-string">'完成录制'</span>)<br>save();<br>e.stopImmediatePropagation();<br>}<br>})<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">stop</span>(<span class="hljs-params"></span>)</span>{<br> a && a();<br>}<br></code></pre></td></tr></table></figure><p>数据保存,数据压缩使用来pako库,至少压缩来3-4倍大小<br>插件本地存储和服务器接口存储都能够实现,本地存储可以考虑chrome.storage或者是localstorage或者数据量比较大可以使用indexDB</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><span class="line">26</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">zip</span>(<span class="hljs-params">str</span>)</span>{<br><span class="hljs-keyword">var</span> binaryString = pako.gzip(str, { <span class="hljs-attr">to</span>: <span class="hljs-string">'string'</span> });<br><span class="hljs-keyword">return</span> btoa(binaryString);<br>}<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">save</span>(<span class="hljs-params"></span>)</span>{<br><span class="hljs-keyword">if</span> ( events.length === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span>;<br><span class="hljs-keyword">const</span> data = zip(<span class="hljs-built_in">JSON</span>.stringify(events))<br><span class="hljs-keyword">const</span> body = <span class="hljs-built_in">JSON</span>.stringify({ data });<br>chrome.storage.local.get({<span class="hljs-attr">data</span>: []}, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">items</span>) </span>{<br><span class="hljs-keyword">const</span> d = items.data<br><span class="hljs-keyword">const</span> newData = body;<br>d.push({<span class="hljs-attr">data</span>:newData, <span class="hljs-attr">time</span>: <span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>()})<br>chrome.storage.local.set({<span class="hljs-attr">data</span>: d}, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'保存成功!'</span>);<br>});<br>});<br>events = [];<br><span class="hljs-keyword">return</span>;<br>fetch(<span class="hljs-string">'http://127.0.0.1:7001/review/add'</span>, {<br><span class="hljs-attr">method</span>: <span class="hljs-string">'POST'</span>,<br><span class="hljs-attr">headers</span>: {<br><span class="hljs-string">'Content-Type'</span>: <span class="hljs-string">'application/json'</span>,<br>},<br>body,<br>});<br>}<br></code></pre></td></tr></table></figure><p>录制数据的回放则是使用官方的播放器rrwebPlayer</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><code class="hljs js">id = ? <span class="hljs-comment">// 携带的ID</span><br>chrome.storage.local.get({<span class="hljs-attr">data</span>: []}, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">items</span>) </span>{<br><span class="hljs-keyword">const</span> d = items.data<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'读取成功!'</span>, d);<br><span class="hljs-keyword">const</span> events = <span class="hljs-built_in">JSON</span>.parse(unzip(<span class="hljs-built_in">JSON</span>.parse(d[id].data).data));<br><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">JSON</span>.stringify(events))<br><span class="hljs-keyword">new</span> rrwebPlayer({<br><span class="hljs-attr">target</span>: <span class="hljs-built_in">document</span>.body, <span class="hljs-comment">// 可以自定义 DOM 元素</span><br><span class="hljs-attr">data</span>: {<br>events,<br>},<br>})<br>});<br></code></pre></td></tr></table></figure><p>这样回放的数据流就简单的说明了,大家应该比较明白屏幕录制到观看回放实践的过程了。具体的代码等笔者完善后,将一次提供。</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2>]]></content>
<tags>
<tag>rrweb</tag>
</tags>
</entry>
<entry>
<title>passport源码阅读</title>
<link href="/2019/04/11/passport/"/>
<url>/2019/04/11/passport/</url>
<content type="html"><![CDATA[<h2 id="passport是如何将用户信息注入到context中"><a href="#passport是如何将用户信息注入到context中" class="headerlink" title="passport是如何将用户信息注入到context中"></a>passport是如何将用户信息注入到context中</h2><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>今天在阅读egg-conde中的本地验证的代码时候,发现有些地方不是特别理解。就尝试着阅读源码去寻找问题的根源。具体问题是: 在通过passport验证后,egg-cnode是直接可以在controller中使用ctx.user获取用户的身份信息,但是过程中是看不到这一字段产生的过程的,文档中好像也没有说明。</p><h2 id="具体解析"><a href="#具体解析" class="headerlink" title="具体解析"></a>具体解析</h2><p>涉及到几个npm 包</p><ul><li>passport</li><li>passport-local</li><li>egg-passport</li></ul><p>先给出代码</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// app.ts</span><br><span class="hljs-keyword">import</span> { Application } <span class="hljs-keyword">from</span> <span class="hljs-string">'egg'</span>;<br><span class="hljs-keyword">const</span> LocalStrategy = <span class="hljs-built_in">require</span>(<span class="hljs-string">'passport-local'</span>).Strategy;<br><br><span class="hljs-built_in">module</span>.exports = <span class="hljs-function">(<span class="hljs-params">app: Application</span>) =></span> {<br> <span class="hljs-keyword">const</span> config = app.config.passportLocal;<br> config.passReqToCallback = <span class="hljs-literal">true</span>;<br> app.passport.verify(<span class="hljs-keyword">async</span> (ctx, user) => {<br> ctx.logger.debug(<span class="hljs-string">'passport.verify'</span>, user);<br> <span class="hljs-comment">// 查数据库</span><br> <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> ctx.service.user.find({<span class="hljs-attr">username</span>: user.username, <span class="hljs-attr">password</span>: user.password});<br> <span class="hljs-keyword">if</span> (result.length > <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">return</span> result[<span class="hljs-number">0</span>];<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span>;<br> });<br> app.passport.use(<br> <span class="hljs-keyword">new</span> LocalStrategy(config, <span class="hljs-function">(<span class="hljs-params">req, username, password, done</span>) =></span> {<br> <span class="hljs-comment">// 把 Passport 插件返回的数据进行清洗处理,返回 User 对象</span><br> <span class="hljs-keyword">const</span> user = {<br> <span class="hljs-attr">provider</span>: <span class="hljs-string">'local'</span>,<br> username,<br> password,<br> };<br> <span class="hljs-comment">// 这里不处理应用层逻辑,传给 app.passport.verify 统一处理</span><br> app.passport.doVerify(req, user, done);<br> }),<br> );<br>};<br><br></code></pre></td></tr></table></figure><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><code class="hljs js"><span class="hljs-comment">// controller/admin.ts</span><br><span class="hljs-keyword">import</span> { Controller } <span class="hljs-keyword">from</span> <span class="hljs-string">'egg'</span>;<br><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">AdminController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span> </span>{<br> public <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">index</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">const</span> { ctx } = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">if</span> (ctx.isAuthenticated()) {<br> <span class="hljs-comment">// show user info</span><br> ctx.body = ctx.user;<br> <span class="hljs-comment">// await ctx.render('admin.tpl');</span><br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// redirect to origin url by ctx.session.returnTo</span><br> ctx.session.returnTo = ctx.path;<br> <span class="hljs-keyword">await</span> ctx.render(<span class="hljs-string">'login.tpl'</span>);<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><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><code class="hljs js"><span class="hljs-comment">// router.ts</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> (app: Application) => {<br> <span class="hljs-keyword">const</span> { controller, router } = app;<br><span class="hljs-comment">//...</span><br> router.post(<span class="hljs-string">'/auth'</span>, app.passport.authenticate(<span class="hljs-string">'local'</span>, { <span class="hljs-attr">successRedirect</span>: <span class="hljs-string">'/admin'</span>,<span class="hljs-attr">failureRedirect</span>: <span class="hljs-string">'/login'</span>, }));<br>};<br><br></code></pre></td></tr></table></figure><p>当通过验证后,控制器中可以直接获取到数据库查出的信息。那么明显是从<code>app.passport.verify</code>函数中返回来的但是具体过程是如何的呢?我们并不清楚。此时需要关注到下面的代码<br>egg-passport是继承passort的,所以app.passport拥有egg-passport的方法和passport的方法</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><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// node_modules/egg-passport/lib/passport.js</span><br><span class="hljs-keyword">const</span> debug = <span class="hljs-built_in">require</span>(<span class="hljs-string">'debug'</span>)(<span class="hljs-string">'egg-passport:passport'</span>);<br><span class="hljs-keyword">const</span> Passport = <span class="hljs-built_in">require</span>(<span class="hljs-string">'passport'</span>).Passport;<br><span class="hljs-keyword">const</span> SessionStrategy = <span class="hljs-built_in">require</span>(<span class="hljs-string">'passport'</span>).strategies.SessionStrategy;<br><span class="hljs-keyword">const</span> framework = <span class="hljs-built_in">require</span>(<span class="hljs-string">'./framework'</span>);<br><br><span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">EggPassport</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Passport</span> </span>{<br><span class="hljs-comment">// ....</span><br><span class="hljs-function"><span class="hljs-title">doVerify</span>(<span class="hljs-params">req, user, done</span>)</span> {<br> <span class="hljs-keyword">const</span> hooks = <span class="hljs-built_in">this</span>._verifyHooks;<br> <span class="hljs-keyword">if</span> (hooks.length === <span class="hljs-number">0</span>) <span class="hljs-keyword">return</span> done(<span class="hljs-literal">null</span>, user);<br> (<span class="hljs-keyword">async</span> () => {<br> <span class="hljs-keyword">const</span> ctx = req.ctx;<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">const</span> handler <span class="hljs-keyword">of</span> hooks) {<br> user = <span class="hljs-keyword">await</span> handler(ctx, user);<br> <span class="hljs-keyword">if</span> (!user) {<br> <span class="hljs-keyword">break</span>;<br> }<br> }<br> done(<span class="hljs-literal">null</span>, user);<br> })().catch(done);<br> }<br><span class="hljs-function"><span class="hljs-title">verify</span>(<span class="hljs-params">handler</span>)</span> {<br> <span class="hljs-built_in">this</span>._verifyHooks.push(<span class="hljs-built_in">this</span>.app.toAsyncFunction(handler));<br> }<br><span class="hljs-comment">// authenticate(){...}</span><br>}<br></code></pre></td></tr></table></figure><p>可以看到verify函数只是doverify主体函数,成功后回调执行<code>done(null,user)</code><br>done方法是从哪里传递过来的呢?查看代码app.ts 43行<br><code>new LocalStrategy(config, (req, username, password, done) => {</code><br>执行new Strategy的时候的回调函数中传递的,此时需要查看Strategy的源码。</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><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><code class="hljs js"><span class="hljs-comment">// node_modules/passport-local/lib/strategy.js</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Strategy</span>(<span class="hljs-params">options, verify</span>) </span>{<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> options == <span class="hljs-string">'function'</span>) {<br> verify = options;<br> options = {};<br> }<br> <span class="hljs-keyword">if</span> (!verify) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">TypeError</span>(<span class="hljs-string">'LocalStrategy requires a verify callback'</span>); }<br> <br> <span class="hljs-built_in">this</span>._usernameField = options.usernameField || <span class="hljs-string">'username'</span>;<br> <span class="hljs-built_in">this</span>._passwordField = options.passwordField || <span class="hljs-string">'password'</span>;<br> <br> passport.Strategy.call(<span class="hljs-built_in">this</span>);<br> <span class="hljs-built_in">this</span>.name = <span class="hljs-string">'local'</span>;<br> <span class="hljs-built_in">this</span>._verify = verify;<br> <span class="hljs-built_in">this</span>._passReqToCallback = options.passReqToCallback;<br>}<br>Strategy.prototype.authenticate = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">req, options</span>) </span>{<br><span class="hljs-comment">//...</span><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">verified</span>(<span class="hljs-params">err, user, info</span>) </span>{<br> <span class="hljs-keyword">if</span> (err) { <span class="hljs-keyword">return</span> self.error(err); }<br> <span class="hljs-keyword">if</span> (!user) { <span class="hljs-keyword">return</span> self.fail(info); }<br> self.success(user, info);<br> }<br><span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">if</span> (self._passReqToCallback) {<br> <span class="hljs-built_in">this</span>._verify(req, username, password, verified);<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-built_in">this</span>._verify(username, password, verified);<br> }<br> } <span class="hljs-keyword">catch</span> (ex) {<br> <span class="hljs-keyword">return</span> self.error(ex);<br> }<br>}<br></code></pre></td></tr></table></figure><p>从代码中 verify赋值给_verify 与 <code>this._verify(req, username, password, verified)</code>,可以看出<br><code>new LocalStrategy</code>的回调函数就是这里执行的,然后done方法就是这里的verified方法。<br>具体查看verified方法,可以看到<code>self.success(user, info)</code>,将user的信息传递。但是突然多出来的success方法是从哪里出现呢。<br>这里笔者迷惑来比较久,并没有在strategy文件中发现该方法的定义。然后观察代码,发现app.passport.use这个方法,然后阅读passport的源码,最终发现success方法是passport的中间件里给strategy添加的一个方法。(不太理解为什么作者要这么处理)</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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// node_modules/passport/lib/middleware/authenticate.js</span><br><span class="hljs-comment">//...</span><br>strategy.success = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">user, info</span>) </span>{<br><span class="hljs-comment">// ...</span><br>req.logIn(user, options, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err</span>) </span>{<br><span class="hljs-keyword">if</span> (err) { <span class="hljs-keyword">return</span> next(err); }<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">complete</span>(<span class="hljs-params"></span>) </span>{<br><span class="hljs-keyword">if</span> (options.successReturnToOrRedirect) {<br><span class="hljs-keyword">var</span> url = options.successReturnToOrRedirect;<br><span class="hljs-keyword">if</span> (req.session && req.session.returnTo) {<br>url = req.session.returnTo;<br><span class="hljs-keyword">delete</span> req.session.returnTo;<br>}<br><span class="hljs-keyword">return</span> res.redirect(url);<br>}<br><span class="hljs-keyword">if</span> (options.successRedirect) {<br><span class="hljs-keyword">return</span> res.redirect(options.successRedirect);<br>}<br>next();<br>}<br><br><span class="hljs-keyword">if</span> (options.authInfo !== <span class="hljs-literal">false</span>) {<br>passport.transformAuthInfo(info, req, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, tinfo</span>) </span>{<br><span class="hljs-keyword">if</span> (err) { <span class="hljs-keyword">return</span> next(err); }<br>req.authInfo = tinfo;<br>complete();<br>});<br>} <span class="hljs-keyword">else</span> {<br>complete();<br>}<br>});<br>};<br><span class="hljs-comment">//...</span><br></code></pre></td></tr></table></figure><p>可以看到success方法中,是通过req.logIn方法继续将user信息传递下去的,<br>而req.logIn方法则是在以下代码中增加的方法。</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><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// node_modules/passport/lib/http/request.js</span><br>req.login =<br>req.logIn = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">user, options, done</span>) </span>{<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> options == <span class="hljs-string">'function'</span>) {<br> done = options;<br> options = {};<br> }<br> options = options || {};<br> <br> <span class="hljs-keyword">var</span> property = <span class="hljs-string">'user'</span>;<br> <span class="hljs-keyword">if</span> (<span class="hljs-built_in">this</span>._passport && <span class="hljs-built_in">this</span>._passport.instance) {<br> property = <span class="hljs-built_in">this</span>._passport.instance._userProperty || <span class="hljs-string">'user'</span>;<br> }<br> <span class="hljs-keyword">var</span> session = (options.session === <span class="hljs-literal">undefined</span>) ? <span class="hljs-literal">true</span> : options.session;<br> <br> <span class="hljs-built_in">this</span>[property] = user;<br> <span class="hljs-keyword">if</span> (session) {<br> <span class="hljs-keyword">if</span> (!<span class="hljs-built_in">this</span>._passport) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'passport.initialize() middleware not in use'</span>); }<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> done != <span class="hljs-string">'function'</span>) { <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'req#login requires a callback function'</span>); }<br> <span class="hljs-keyword">var</span> self = <span class="hljs-built_in">this</span>;<br> <span class="hljs-built_in">this</span>._passport.instance.serializeUser(user, <span class="hljs-built_in">this</span>, <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">err, obj</span>) </span>{<br> <span class="hljs-keyword">if</span> (err) { self[property] = <span class="hljs-literal">null</span>; <span class="hljs-keyword">return</span> done(err); }<br> <span class="hljs-keyword">if</span> (!self._passport.session) {<br> self._passport.session = {};<br> }<br> self._passport.session.user = obj;<br> <span class="hljs-keyword">if</span> (!self.session) {<br> self.session = {};<br> }<br> self.session[self._passport.instance._key] = self._passport.session;<br> done();<br> });<br> } <span class="hljs-keyword">else</span> {<br> done && done();<br> }<br>};<br></code></pre></td></tr></table></figure><p>可以看到最终是req.logIn中<code>this[property] = user;</code>完成来用户信息的赋值<br>egg context对象是继承koa的contetxt,而koa的contetx对象是将node request和response对象封装到按个对象中去的。<br>所以最后ctx.user能够获取到用户信息。到此能够理解用户信息在整个过程中的流动。<br>以上还是忽略一些步骤,中间的启用过程</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><code class="hljs js"><span class="hljs-comment">// node_modules/passport/lib/authenticator.js</span><br><span class="hljs-comment">//...</span><br>Authenticator.prototype.init = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-built_in">this</span>.framework(<span class="hljs-built_in">require</span>(<span class="hljs-string">'./framework/connect'</span>)());<br> <span class="hljs-built_in">this</span>.use(<span class="hljs-keyword">new</span> SessionStrategy());<br>};<br><span class="hljs-comment">//...</span><br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// node_modules/passport/lib/framework/connect.js</span><br><span class="hljs-keyword">var</span> initialize = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../middleware/initialize'</span>)<br> , authenticate = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../middleware/authenticate'</span>);<br><span class="hljs-built_in">exports</span> = <span class="hljs-built_in">module</span>.exports = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> <br> <span class="hljs-comment">// HTTP extensions.</span><br> <span class="hljs-built_in">exports</span>.__monkeypatchNode();<br> <br> <span class="hljs-keyword">return</span> {<br> <span class="hljs-attr">initialize</span>: initialize,<br> <span class="hljs-attr">authenticate</span>: authenticate<br> };<br>};<br><span class="hljs-built_in">exports</span>.__monkeypatchNode = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">var</span> http = <span class="hljs-built_in">require</span>(<span class="hljs-string">'http'</span>);<br> <span class="hljs-keyword">var</span> IncomingMessageExt = <span class="hljs-built_in">require</span>(<span class="hljs-string">'../http/request'</span>);<br> http.IncomingMessage.prototype.login =<br> http.IncomingMessage.prototype.logIn = IncomingMessageExt.logIn;<br> http.IncomingMessage.prototype.logout =<br> http.IncomingMessage.prototype.logOut = IncomingMessageExt.logOut;<br> http.IncomingMessage.prototype.isAuthenticated = IncomingMessageExt.isAuthenticated;<br> http.IncomingMessage.prototype.isUnauthenticated = IncomingMessageExt.isUnauthenticated;<br>};<br></code></pre></td></tr></table></figure><p>Authenticator 初始化的时候,完成了req方法的添加,与中间件方法的搭载。<br>以上是对passport验证后,用户信息注入到context流程源码分析</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/jaredhanson/passport">passport仓库仓库代码</a></li><li><a href="https://github.com/jaredhanson/passport-local">passport-local仓库代码</a></li><li><a href="https://eggjs.org/zh-cn/tutorials/passport.html">egg 鉴权demo</a></li></ul>]]></content>
<tags>
<tag>nodejs</tag>
</tags>
</entry>
<entry>
<title>react-saga</title>
<link href="/2019/03/26/react-saga/"/>
<url>/2019/03/26/react-saga/</url>
<content type="html"><![CDATA[<h1 id="react-saga的学习和理解"><a href="#react-saga的学习和理解" class="headerlink" title="react-saga的学习和理解"></a>react-saga的学习和理解</h1><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在使用react redux的时候,会经常遇到需要处理异步action的情况。处理异步action的方法有几种。<br>其中redux-thunk,redux-saga都是处理异步action的中间件。利用这些中间件可以很好的达到我们预期效果</p><h2 id="redux-saga"><a href="#redux-saga" class="headerlink" title="redux-saga"></a>redux-saga</h2><p><code>redux-saga</code>是一个用于管理应用程序 Side Effect(副作用,例如异步获取数据,访问浏览器缓存等)的 library,它的目标是让副作用管理更容易,执行更高效,测试更简单,在处理故障时更容易。</p><p>个人是这么理解redux-saga的,在app中注入redux-saga中间件后,saga effects函数中对相对应的action进行监听,在代码中执行触发dispatch 对应action的时候,saga的effects 函数会对其进行拦截处理,中途进行一些异步、同步、或者是执行调用其他effects函数。在effects函数中,可以同步的方式书写异步代码。<br>相对于redux-thunk,saga的优势在于effects函数中对各种异步的处理比较方便和容易拓展。</p><h2 id="计数器demo例子"><a href="#计数器demo例子" class="headerlink" title="计数器demo例子"></a>计数器demo例子</h2><p>Counter 组件</p><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> React, { Component, PropTypes } <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span><br><br><span class="hljs-keyword">const</span> Counter = <span class="hljs-function">(<span class="hljs-params">{ value, onIncrement, onDecrement, onIncrementAsync }</span>) =></span><br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onIncrement}</span>></span></span><br><span class="xml"> Increment</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br><span class="xml"> {' '}</span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onDecrement}</span>></span></span><br><span class="xml"> Decrement</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">button</span> <span class="hljs-attr">onClick</span>=<span class="hljs-string">{onIncrementAsync}</span>></span></span><br><span class="xml"> delay Increment</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">button</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">hr</span> /></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">div</span>></span></span><br><span class="xml"> Clicked: {value} times</span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><span class="xml"> <span class="hljs-tag"></<span class="hljs-name">div</span>></span></span><br><br>Counter.propTypes = {<br> <span class="hljs-attr">value</span>: PropTypes.number.isRequired,<br> <span class="hljs-attr">onIncrement</span>: PropTypes.func.isRequired,<br> <span class="hljs-attr">onDecrement</span>: PropTypes.func.isRequired,<br> <span class="hljs-attr">onIncrementAsync</span>: PropTypes.func.isRequired<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> Counter<br></code></pre></td></tr></table></figure><p>main.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><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> React <span class="hljs-keyword">from</span> <span class="hljs-string">'react'</span><br><span class="hljs-keyword">import</span> ReactDOM <span class="hljs-keyword">from</span> <span class="hljs-string">'react-dom'</span><br><span class="hljs-keyword">import</span> { createStore, applyMiddleware } <span class="hljs-keyword">from</span> <span class="hljs-string">'redux'</span><br><span class="hljs-keyword">import</span> createSagaMiddleware <span class="hljs-keyword">from</span> <span class="hljs-string">'redux-saga'</span><br><span class="hljs-keyword">import</span> rootSaga <span class="hljs-keyword">from</span> <span class="hljs-string">'./saga'</span><br><span class="hljs-keyword">import</span> Counter <span class="hljs-keyword">from</span> <span class="hljs-string">'./Counter'</span><br><span class="hljs-keyword">import</span> reducer <span class="hljs-keyword">from</span> <span class="hljs-string">'./reducers'</span><br><br><span class="hljs-keyword">const</span> sagaMiddleware = createSagaMiddleware()<br><span class="hljs-keyword">const</span> store = createStore(<br> reducer,<br> applyMiddleware(sagaMiddleware)<br>)<br>sagaMiddleware.run(rootSaga)<br><span class="hljs-keyword">const</span> action = <span class="hljs-function"><span class="hljs-params">type</span> =></span> store.dispatch({type})<br><br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">render</span>(<span class="hljs-params"></span>) </span>{<br> ReactDOM.render(<br> <span class="xml"><span class="hljs-tag"><<span class="hljs-name">Counter</span></span></span><br><span class="hljs-tag"><span class="xml"> <span class="hljs-attr">value</span>=<span class="hljs-string">{store.getState()}</span></span></span><br><span class="hljs-tag"><span class="xml"> <span class="hljs-attr">onIncrement</span>=<span class="hljs-string">{()</span> =></span> action('INCREMENT')}</span><br><span class="xml"> onDecrement={() => action('DECREMENT')}</span><br><span class="xml"> onIncrementAsync={() => action('INCREMENT_ASYNC')} /></span>,<br> <span class="hljs-built_in">document</span>.getElementById(<span class="hljs-string">'root'</span>)<br> )<br>}<br><br>render()<br>store.subscribe(render)<br></code></pre></td></tr></table></figure><p>saga.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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">import</span> { delay } <span class="hljs-keyword">from</span> <span class="hljs-string">'redux-saga'</span><br><span class="hljs-keyword">import</span> { put, takeEvery, all } <span class="hljs-keyword">from</span> <span class="hljs-string">'redux-saga/effects'</span><br><br><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">incrementAsync</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">yield</span> delay(<span class="hljs-number">1000</span>)<br> <span class="hljs-keyword">yield</span> put({ <span class="hljs-attr">type</span>: <span class="hljs-string">'INCREMENT'</span> })<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">watchIncrementAsync</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">yield</span> takeEvery(<span class="hljs-string">'INCREMENT_ASYNC'</span>, incrementAsync)<br>}<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">rootSaga</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">yield</span> all([<br> watchIncrementAsync()<br> ])<br>}<br></code></pre></td></tr></table></figure><p>在计数器中有三个按钮 <code>Increment</code>、<code>Decrement</code>、<code>Delay Increment</code><br>点击<code>Increment</code>,计数马上加1,点击<code>Decrement</code>,计数马上减1,点击<code>Delay Increment</code>计数延迟1秒加1</p><p>原理解析<br>在saga中,对于<code>INCREMENT_ASYNC</code>这个action进行了监听,如果触发了这个action则会执行saga中的incrementAsync函数,在该函数中,有delay延迟函数。yield delay(1000)的意思是等待延迟1秒,然后执行yield put(…),执行触发<code>INCREMENT</code>action,然后触发reducer 对state的值的修改。</p><p>整个流程 监听<code>INCREMENT_ASYNC</code>,触发<code>INCREMENT_ASYNC</code>,执行incrementAsync,dispatch<code>INCREMENT</code>,最后修改state,触发UI更新。<br>在saga的effects函数中可以对异步处理的结果进行再次处理整合,最后再派发执行下一步。<br>整个流程是有序执行可监控的。这样我们就简单理解了redux-saga的原理和流程,进一步学习redux-saga则需要对API有更深入的了解</p><h2 id="进阶学习redux-saga"><a href="#进阶学习redux-saga" class="headerlink" title="进阶学习redux-saga"></a>进阶学习redux-saga</h2><ul><li>take</li><li>fork</li><li>takeEvery</li><li>takeLatest</li></ul><p>takeLatest & takeEvery 是监听action,一遍一遍执行,不会停止。takeLatest与takeEvery的区别在于,如果同个action多次触发,takeLatest只会执行最后一个action,往前的action会取消调用,而takeEvery则每个action均会执行调用。take则只执行一次。fork则在后台建立任务监听,非阻塞主流程。<br>所以会经常使用 fork+ while(true)+take的方式实现可控制的effect函数。<br>查看例子</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><code class="hljs js"><span class="hljs-keyword">import</span> { fork, call, take, put } <span class="hljs-keyword">from</span> <span class="hljs-string">'redux-saga/effects'</span><br><span class="hljs-keyword">import</span> Api <span class="hljs-keyword">from</span> <span class="hljs-string">'...'</span><br><br><span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">authorize</span>(<span class="hljs-params">user, password</span>) </span>{<br> <span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">const</span> token = <span class="hljs-keyword">yield</span> call(Api.authorize, user, password)<br> <span class="hljs-keyword">yield</span> put({<span class="hljs-attr">type</span>: <span class="hljs-string">'LOGIN_SUCCESS'</span>, token})<br> } <span class="hljs-keyword">catch</span>(error) {<br> <span class="hljs-keyword">yield</span> put({<span class="hljs-attr">type</span>: <span class="hljs-string">'LOGIN_ERROR'</span>, error})<br> }<br>}<br><br><span class="hljs-function"><span class="hljs-keyword">function</span>* <span class="hljs-title">loginFlow</span>(<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">while</span>(<span class="hljs-literal">true</span>) {<br> <span class="hljs-keyword">const</span> {user, password} = <span class="hljs-keyword">yield</span> take(<span class="hljs-string">'LOGIN_REQUEST'</span>)<br> <span class="hljs-keyword">yield</span> fork(authorize, user, password)<br> <span class="hljs-keyword">yield</span> take([<span class="hljs-string">'LOGOUT'</span>, <span class="hljs-string">'LOGIN_ERROR'</span>])<br> <span class="hljs-keyword">yield</span> call(Api.clearItem(<span class="hljs-string">'token'</span>))<br> }<br>}<br></code></pre></td></tr></table></figure><p>收到<code>LOGIN_REQUEST</code>的action的时候,会进入loginFLow,然后提交检验authorize,这个过程是后台任务执行的,此时如果触发了<code>LOGOUT</code>action,函数不会被阻塞,会执行<code>call(Api.clearItem('token'))</code>。<br>redux-saga的高级用法就是通过这些API能够很好的控制异步流程。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://redux-saga-in-chinese.js.org/">redux-saga中文教程</a></li><li><a href="https://github.com/redux-saga/redux-saga-beginner-tutorial.git">计数器demo例子</a></li></ul>]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title>React Router v4</title>
<link href="/2019/02/16/react-router/"/>
<url>/2019/02/16/react-router/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>之前使用react router都是参考着demo写路由,此次将整体过一遍 React Router v4的教程与文档。<br>系统学习react router,深入理解前端路由的概念。</p><h2 id="React-Router"><a href="#React-Router" class="headerlink" title="React Router"></a>React Router</h2><p>v4 的版本将路由进行了拆分,将其放到了各自的模块中,不再有单独的 router 模块,充分体现了组件化的思想;另外,<code><BrowserRouter></code> 的使用与之前作为 history 属性传入的方式也不同了。v4中route可以当作component一样使用,只不过只在路由匹配后才会生效。</p><h2 id="路由匹配规则"><a href="#路由匹配规则" class="headerlink" title="路由匹配规则"></a>路由匹配规则</h2><h4 id="1、包含式路由与exact"><a href="#1、包含式路由与exact" class="headerlink" title="1、包含式路由与exact"></a>1、包含式路由与exact</h4><p>react router的路由path匹配是包含式路由匹配。<br>例如path: /bar/foo 会匹配 [/,/bar,/bar/foo]的路由<br>如果单纯使用<Route>,路由匹配的组件将所有都生效。<br><strong>exact</strong> 当值为true时,则要求路径与location.pathname必须完全匹配。</p><h4 id="2、独立路由-Switch"><a href="#2、独立路由-Switch" class="headerlink" title="2、独立路由 Switch"></a>2、独立路由 Switch</h4><p><Switch> 组件则是渲染匹配地址的第一个 <Route> 或者 <code><Redirect></code>。</p><h4 id="3、重定向-Redirect"><a href="#3、重定向-Redirect" class="headerlink" title="3、重定向 Redirect"></a>3、重定向 Redirect</h4><p>渲染<code><Redirect></code> 的时候将会导航到一个新的地址。这个新的地址将会覆盖在访问历史记录里面的原地址,就像服务端的重定向(HTTP 3XX)一样。<br><code><Redirect></code> 参数对应的作用如下<br>to:重定向目标地址或URL<br>push:当设置为 true 时,重定向(redirecting)将会把新地址加入访问历史记录里面,而不是替换掉目前的地址。<br>from:需要被重定向的path,当渲染一个包含在<Switch>里面的<code><Redirect></code>的时候,这可以用作匹配一个地址。</p><h4 id="4、组合使用"><a href="#4、组合使用" class="headerlink" title="4、组合使用"></a>4、组合使用</h4><p>实际的开发生产中,基本都需要组合使用路由规则实现我们的需求<br>例子如下<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><main><br><span class="xml"><span class="hljs-tag"><<span class="hljs-name">Switch</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">exact</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Home}/</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/roster'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Roster}/</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'/schedule'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Schedule}/</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">Route</span> <span class="hljs-attr">path</span>=<span class="hljs-string">'error'</span> <span class="hljs-attr">component</span>=<span class="hljs-string">{Error}/</span>></span></span><br><span class="xml"> <span class="hljs-tag"><<span class="hljs-name">Redirect</span> <span class="hljs-attr">to</span>=<span class="hljs-string">'/error'</span> /></span></span><br><span class="xml"><span class="hljs-tag"></<span class="hljs-name">Switch</span>></span></span><br></main><br></code></pre></td></tr></table></figure><br>路由与页面分别对应<br>/ => home<br>/roster => roster<br>/schedule => schedule<br>/error => error<br>当什么都没匹配到的时候重定向到 /error 路由然后页面为 error</p><h2 id="嵌套布局概念"><a href="#嵌套布局概念" class="headerlink" title="嵌套布局概念"></a>嵌套布局概念</h2><p>v4中的概念是路由可以当组件一样的使用,那么在布局的时候可以采用这样的方式进行布局<br>看以下官方的介绍文档中的例子,对比两种方式区别,理解v4特性带来的好处。<br>需求是 “拓展用户模块”,需要一个可以浏览用户信息界面和每个人的个人用户信息页面<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br></pre></td><td class="code"><pre><code class="hljs react">const PrimaryLayout = props => {<br> return (<br> <div className="primary-layout"><br> <PrimaryHeader /><br> <main><br> <Switch><br> <Route path="/" exact component={HomePage} /><br> <Route path="/users" exact component={BrowseUsersPage} /><br> <Route path="/users/:userId" component={UserProfilePage} /><br> <Route path="/products" exact component={BrowseProductsPage} /><br> <Route path="/products/:productId" component={ProductProfilePage} /><br> <Redirect to="/" /><br> </Switch><br> </main><br> </div><br> )<br>}<br><br>const BrowseUsersPage = () => (<br> <div className="user-sub-layout"><br> <aside><br> <UserNav /><br> </aside><br> <div className="primary-content"><br> <BrowseUserTable /><br> </div><br> </div><br>)<br><br>const UserProfilePage = props => (<br> <div className="user-sub-layout"><br> <aside><br> <UserNav /><br> </aside><br> <div className="primary-content"><br> <UserProfile userId={props.match.params.userId} /><br> </div><br> </div><br>)<br></code></pre></td></tr></table></figure><br>以上的方法是直接对每个页面增加路由以及对应的页面,不过这样会造成重复代码<br>重构代码如下<br><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs react">const PrimaryLayout = props => {<br> return (<br> <div className="primary-layout"><br> <PrimaryHeader /><br> <main><br> <Switch><br> <Route path="/" exact component={HomePage} /><br> <Route path="/users" component={UserSubLayout} /><br> <Route path="/products" component={ProductSubLayout} /><br> <Redirect to="/" /><br> </Switch><br> </main><br> </div><br> )<br>}<br><br>const UserSubLayout = () => (<br> <div className="user-sub-layout"><br> <aside><br> <UserNav /><br> </aside><br> <div className="primary-content"><br> <Switch><br> <Route path="/users" exact component={BrowseUsersPage} /><br> <Route path="/users/:userId" component={UserProfilePage} /><br> </Switch><br> </div><br> </div><br>)<br><br>const BrowseUsersPage = () => <BrowseUserTable /><br>const UserProfilePage = props => <UserProfile userId={props.match.params.userId} /><br></code></pre></td></tr></table></figure><br>这种方式是将相似的界面抽取出来做成layout容器组件,在子组件中再去进行路由匹配渲染不同页面。route 组件化思想得到充分的利用。</p><h2 id="match-对象"><a href="#match-对象" class="headerlink" title="match 对象"></a>match 对象</h2><p>match 对象包含了 <Route path> 如何与URL匹配的信息。match 对象包含以下属性:</p><ul><li>params:( object 类型)即路径参数,通过解析URL中动态的部分获得的键值对。</li><li>isExact:( 当为 true 时,整个URL都需要匹配。</li><li>path:( string 类型)用来做匹配的路径格式。在需要嵌套 <Route> 的时候用到。</li><li>url:( string 类型)URL匹配的部分,在需要嵌套 <Link> 的时候会用到。</li></ul><p>获取match对象的方式</p><ul><li>在 Route component 中,以 this.props.match 方式。</li><li>在 Route render 中,以 ({ match }) => () 方式。</li><li>在 Route children 中,以 ({ match }) => () 方式</li><li>在 withRouter 中,以 this.props.match 方式</li><li>matchPath 的返回值</li></ul><h2 id="组件Link与Navlink"><a href="#组件Link与Navlink" class="headerlink" title="组件Link与Navlink"></a>组件Link与Navlink</h2><p>Navlink是Link的一个特定版本, 会在匹配上当前URL的时候会给已经渲染的元素添加样式参数<br><code><Link></code>属性<br>to: 目标路由<br>replace:当设置为 true 时,点击链接后将使用新地址替换掉访问历史记录里面的原地址。当设置为 false 时,点击链接后将在原有访问历史记录的基础上添加一个新的纪录。 默认为false</p><p><code><Navlink></code>属性<br>activeClassName:激活时添加类名<br>activeStyle:激活时添加样式<br>exact:控制是否完全匹配<br>strict:匹配路由是否包/闭合<br>isActive: 添加额外函数用于判断是否激活</p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://router.happyfe.com/">react router v4中文文档</a></li><li><a href="https://www.jianshu.com/p/bf6b45ce5bcc">React Router 4:痛过之后的豁然开朗</a></li><li><a href="https://css-tricks.com/react-router-4/">All About React Router 4</a></li></ul>]]></content>
<tags>
<tag>react</tag>
</tags>
</entry>
<entry>
<title>JavaScript原型链与继承</title>
<link href="/2019/01/25/prototype/"/>
<url>/2019/01/25/prototype/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>JavaScript的语言特性是每个前端工程师深入学习必须了解的内容。以下是对JavaScript原型、原型链与原型继承的归纳总结。</p><h2 id="关于原型的几个概念"><a href="#关于原型的几个概念" class="headerlink" title="关于原型的几个概念"></a>关于原型的几个概念</h2><p>构造函数、实例对象、原型、<strong>proto</strong><br>构造函数:在JavaScript中,构造方法一般命名首字母大写,通过new 命令调用。<br>实例对象:是通过new 命令调用 构造方法 创建出来的对象。<br>原型:在javascript中每个函数有个特殊的属性为原型(prototype)。<br><strong>proto</strong>: 实例对象中,有个隐藏的属性<strong>proto</strong>, 是从构造函数的prototype属性派生的。每个<strong>proto</strong>属性指向其构造函数的prototype。</p><h2 id="函数对象与实例对象"><a href="#函数对象与实例对象" class="headerlink" title="函数对象与实例对象"></a>函数对象与实例对象</h2><p>实例对象有<strong>proto</strong>属性,函数对象有prototype属性<br>类似原生的函数例如 Array、String、<br>同时是函数对象,又是实例对象,这类对象的原型情况分析<br>Array.<strong>proto</strong> 指向 Function.prototype [实例对象的proto属性指向其构造函数的原型]<br>Array.prototype.<strong>proto</strong> 指向 Object.prototype</p><p><strong>特别的</strong><br>Funtion.<strong>proto</strong> 指向 Funtion.prototype<br>Object.<strong>proto</strong> 指向 Funtion.prototype<br>Function.prototype.<strong>proto</strong> 指向 Object.prototype<br>Object.prototype.<strong>proto</strong> 指向 null</p><h2 id="类与继承"><a href="#类与继承" class="headerlink" title="类与继承"></a>类与继承</h2><p>javascript中是没有直接的类的概念,是通过JavaScript的原型继承实现类与继承的概念,即使是es6中增加的关键字class实际上是语法糖,本质还是原型继承实现的“类”概念。<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js">(<span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">Student</span>(<span class="hljs-params">name</span>) </span>{<br> <span class="hljs-comment">// this.name = name</span><br> }<br> Student.prototype.hello = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">this</span>.name)<br> }<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">F</span>(<span class="hljs-params"></span>) </span>{<br><br> }<br> F.prototype = Student.prototype;<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">P</span>(<span class="hljs-params">name</span>) </span>{<br> <span class="hljs-built_in">this</span>.name = name;<br> Student.call(<span class="hljs-built_in">this</span>);<br> }<br> P.prototype = <span class="hljs-keyword">new</span> F();<br> P.prototype.constructor = P <span class="hljs-comment">// 修复constructor指向</span><br> p = <span class="hljs-keyword">new</span> P(<span class="hljs-string">'xiaoming'</span>);<br> p.hello();<br>})()<br></code></pre></td></tr></table></figure><br>在上述的例子中,实现了P类继承了Student类,创建的实例对象p继承类student类中hello方法。通过原型继承的方式实现类别的语言中类继承特性。<br>可能大家会对F函数的使用产生疑问。<br>在原型继承中如果不使用代理函数F,采用 new Student()的方式,那么原型链是这样的,P.prototype中会有student构造函数中的一些属性<br>p => P.prototype => Student.prototype => Object.prototype => null<br>如果使用代理函数F,采用 new F()的方式,那么P.prototype中则不会有student构造函数中的属性。<br>p => P.prototype => F.prototype(Student.prototype) => Object.prototype => null</p><h2 id="原型继承的几种方式"><a href="#原型继承的几种方式" class="headerlink" title="原型继承的几种方式"></a>原型继承的几种方式</h2><p>一、<strong>原型链继承</strong><br>1、引用类型的数据被所有实例共享,一处改动,影响到所有实例<br>2、在创建实例时候,无法向父级传递参数。<br>二、<strong>借用构造函数继承</strong><br>1、避免了引用类型的属性被所有实例共享<br>2、可以在 Child 中向 Parent 传参<br>缺点:方法的继承需要在构造函数中定义,这样每次创建会重复创建方法。<br>三、<strong>组合继承</strong><br>融合原型链继承和构造函数的优点,是 JavaScript 中最常用的继承模式。<br>四、<strong>寄生组合式继承</strong><br>《JavaScript高级程序设计》: 这种方式的高效率体现它只调用了一次 Parent 构造函数,并且因此避免了在 Parent.prototype 上面创建不必要的、多余的属性。与此同时,原型链还能保持不变;因此,还能够正常使用 instanceof 和 isPrototypeOf。开发人员普遍认为寄生组合式继承是引用类型最理想的继承范式。</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/16">JavaScript深入之继承的多种方式和优缺点</a></li><li><a href="https://developer.mozilla.org/zh-CN/docs/Learn/JavaScript/Objects/Object_prototypes">MDN原型对象</a></li></ul>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>JavaScript防抖与节流(记录)</title>
<link href="/2019/01/21/debounce/"/>
<url>/2019/01/21/debounce/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在实际的开发过程中,其实我们会经常遇到需要用防抖与节流的场景。比如搜索框连续输入文字进行异步请求的搜索可以用到防抖函数进行优化。</p><h2 id="防抖"><a href="#防抖" class="headerlink" title="防抖"></a>防抖</h2><p>防抖函数,简单的理解为频发触发事件在指定时间内不触发才执行。<br>在输入框添加了onchange监听事件,只要输入框内容变化就会调用监听事件。<br>但需求是连续输入只需要查询最后的输入结果,那么给这个监听事件增加防抖即可实现。<br>具体看代码例子<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ajax</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'boom'</span>)<br>}<br>fn = debounce(ajax, <span class="hljs-number">500</span>);<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">debounce</span>(<span class="hljs-params">fn,wait</span>)</span>{<br> <span class="hljs-keyword">var</span> timer = <span class="hljs-literal">null</span>;<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> context = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">var</span> args = <span class="hljs-built_in">arguments</span>;<br> <span class="hljs-built_in">clearTimeout</span>(timer)<br> timer = <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> fn.apply(context,args);<br> }, wait);<br> }<br> <br>}<br><span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#a'</span>).onclick = fn;<br></code></pre></td></tr></table></figure><br>debounce就是防抖函数。当事件绑定了防抖函数后,触发事件,执行防抖函数,如果timer还在计时器中则被清除,只有在时间范围内不触发事件,才能真正执行该业务逻辑。</p><h2 id="节流"><a href="#节流" class="headerlink" title="节流"></a>节流</h2><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><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">ajax</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'boom'</span>)<br>}<br><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">throttle</span> (<span class="hljs-params">fn, wait</span>)</span>{<br> <span class="hljs-keyword">var</span> timer = <span class="hljs-literal">null</span>;<br> <span class="hljs-keyword">var</span> pre = <span class="hljs-number">0</span>;<br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> context = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">var</span> args = <span class="hljs-built_in">arguments</span>;<br> <span class="hljs-keyword">var</span> now = +<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>();<br> <span class="hljs-keyword">if</span> (now - pre > wait){<br> fn.apply(context,<span class="hljs-built_in">arguments</span>);<br> pre = now;<br> }<br> }<br>}<br><span class="hljs-keyword">var</span> fn2 = throttle(ajax, <span class="hljs-number">1000</span>)<br><span class="hljs-built_in">document</span>.querySelector(<span class="hljs-string">'#a'</span>).onclick = fn2;<br></code></pre></td></tr></table></figure><p>节流函数的作用:连续触发事件的时候,只会固定频率执行业务逻辑,如果触发事件的事件间隔少于设定的频率时间,也可以马上执行</p><h2 id="continue"><a href="#continue" class="headerlink" title="continue"></a>continue</h2><p>后续遇到具体的业务场景,结合着对简单应用的防抖与节流函数进行“升级”。</p>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>从JavaScript执行上下文角度理解闭包</title>
<link href="/2019/01/14/%E9%97%AD%E5%8C%85/"/>
<url>/2019/01/14/%E9%97%AD%E5%8C%85/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>闭包是js特有一个概念问题,应该是js学习者都会遇到的疑问:什么是闭包?<br>关于闭包的回答网上也有很多前辈的总结,笔者也看的比较多,都是从不同角度去阐述闭包这个问题。<br>笔者最近复习闭包的概念,从js执行上下文角度去理解闭包,从而有更深入的理解。</p><h2 id="什么是闭包"><a href="#什么是闭包" class="headerlink" title="什么是闭包"></a>什么是闭包</h2><p>在MDN中给出闭包的概念是</p><blockquote><p>JavaScript中的函数会形成闭包。 闭包是由函数以及创建该函数的词法环境组合而成。这个环境包含了这个闭包创建时所能访问的所有局部变量。</p></blockquote><p>联系到《js执行上下文》这篇博客中提及函数执行上下文中的作用域链非常的相似。<br>查看一个简单的闭包例子<br><figure class="highlight html"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">ul</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">ul</span> <span class="hljs-attr">id</span>=<span class="hljs-string">"list"</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>1<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>2<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>3<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"><<span class="hljs-name">li</span>></span>4<span class="hljs-tag"></<span class="hljs-name">li</span>></span><br> <span class="hljs-tag"></<span class="hljs-name">ul</span>></span><br><span class="hljs-tag"></<span class="hljs-name">ul</span>></span><br></code></pre></td></tr></table></figure><br><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><code class="hljs js"><span class="hljs-keyword">var</span> dom = <span class="hljs-built_in">document</span>.querySelectorAll(<span class="hljs-string">'#list li'</span>);<br><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, len = dom.length; i < len; i++){<br> dom[i].onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(i);<br> }<br>}<br></code></pre></td></tr></table></figure><br>此时点击列表1234分别输出都是4,想要输出对应输出相同的序号则需要创建闭包<br><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><code class="hljs js"><span class="hljs-keyword">for</span> (<span class="hljs-keyword">var</span> i = <span class="hljs-number">0</span>, len = dom.length; i < len; i++){<br> (<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">i</span>)</span>{<br> dom[i].onclick = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(i+<span class="hljs-number">1</span>);<br> }<br> })(i)<br> }<br></code></pre></td></tr></table></figure><br>此时点击列表1234分别对应输出 1、2、3、4<br>相信大家也能够理解这个简单的例子,笔者从函数创建执行上下文的概念来解释下产生两种情况的原因。<br>在第一个例子的代码中<br>1、匿名函数创建的时候,确定了函数作用域(当前词法环境),当前词法环境是全局执行上下文的作用域,即对i的访问是全局作用域的访问。<br>2、for循环结束后,匿名函数分别绑定在对应的dom上,此时全局作用域的i为4。<br>3、触发列表的click事件,调用绑定的函数<br>4、函数调用,创建当前执行上下文,确定执行上下文的作用域链[VO,globalContext.VO]<br>5、执行代码console.log(i),在作用域链中查找i,找到全局作用域中的i为4 输出4.</p><p>在第二个例子的代码中<br>1、(function(){})()是IIFF(立即执行函数),在创建匿名函数后又立即执行。在该匿名函数(称作x)调用的时候,该函数执行上下文被创建。变量对象VO创建的过程中,参数i添加到变量对象VO。该函数作用域为[VO,globalContext.VO]。<br>2、dom[i].onclick = function(){..},匿名函数(称作y)创建,确定该函数作用域(当前词法环境),当前词法环境是x函数执行上下文的作用域。此时x函数执行上下文中i的值为对应传入参数的i的值<br>3、for循环结束后,dom 列表都绑定了对应的匿名函数,此时全局作用域的i为4<br>4、触发列表“1”的click事件,调用绑定的函数。<br>5、函数调用,创建当前执行上下文,确定执行上下文的作用域链[VO,xContext.VO]<br>6、执行代码console.log(i + 1),在作用域链中查找i,从顶端往下查找,找到xContext.VO中有变量i,其值为0,输出 i + 1为1。<br>7、点击其他列表同理。</p><p>通过对两个例子的解析,可以理解“闭包是由函数以及创建该函数的词法环境组合而成。”</p><h2 id="闭包实际应用"><a href="#闭包实际应用" class="headerlink" title="闭包实际应用"></a>闭包实际应用</h2><p>1、闭包实现工厂函数</p><p>2、用闭包模拟私有方法<br>通过闭包的原理实现,私有变量方法的创建<br><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><code class="hljs js"><span class="hljs-keyword">var</span> person = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <br> <span class="hljs-comment">//变量作用域为函数内部,外部无法访问 </span><br> <span class="hljs-keyword">var</span> name = <span class="hljs-string">"default"</span>; <br> <span class="hljs-keyword">return</span> { <br> <span class="hljs-attr">getName</span> : <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <br> <span class="hljs-keyword">return</span> name; <br> }, <br> <span class="hljs-attr">setName</span> : <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">newName</span>)</span>{ <br> name = newName; <br> } <br> } <br>}();<br>print(person.name);<span class="hljs-comment">//直接访问,结果为undefined </span><br>print(person.getName()); <br>person.setName(<span class="hljs-string">"abruzzi"</span>); <br>print(person.getName()); <br><br><span class="hljs-comment">// 得到结果如下: </span><br><span class="hljs-comment">// undefined </span><br><span class="hljs-comment">// default </span><br><span class="hljs-comment">// abruzzi</span><br></code></pre></td></tr></table></figure><br>3、缓存数据</p><h2 id="闭包的副作用"><a href="#闭包的副作用" class="headerlink" title="闭包的副作用"></a>闭包的副作用</h2><p>不合理使用闭包可能会造成内存高,JavaScript性能降低。 </p><h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Closures">MDN闭包</a></li></ul>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>JavaScript执行上下文</title>
<link href="/2019/01/13/js%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87/"/>
<url>/2019/01/13/js%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>笔者在学习JavaScript基础知识的时候,对于一些概念的理解都是看网上前辈的经验总结,但是对于为什么是这样没有过多深入理解,比如变量提升,函数作用域,闭包的原理。今天我们从JavaScript执行上下文开始,从原理出发理解概念。</p><h2 id="1-什么是JavaScript执行上下文"><a href="#1-什么是JavaScript执行上下文" class="headerlink" title="1.什么是JavaScript执行上下文"></a>1.什么是JavaScript执行上下文</h2><p>JavaScript运行代码时环境(三种)</p><ul><li>全局代码:代码默认运行的环境,最先会进入到全局环境中</li><li>函数代码:在函数的局部环境中运行的代码</li><li>Eval代码:在Eval()函数中运行的代码</li></ul><p>javascript是一个单线程语言,这意味着在浏览器中同时只能做一件事情。当javascript解释器初始执行代码,它首先默认进入全局上下文。每次调用一个函数将会创建一个新的执行上下文。<br>每次新创建的一个执行上下文会被添加到作用域链的顶部,有时也称为执行或调用栈。浏览器总是运行位于作用域链顶部的当前执行上下文。一旦完成,当前执行上下文将从栈顶被移除并且将控制权归还给之前的执行上下文。</p><h2 id="2、执行上下文创建的过程"><a href="#2、执行上下文创建的过程" class="headerlink" title="2、执行上下文创建的过程"></a>2、执行上下文创建的过程</h2><p>全局执行上下文是在JavaScript代码初始执行时候创建的,所以全局上下文在作用域链的底部。<br>函数执行下文是函数调用的时候创建的,创建的过程如下<br>1、创建变量对象vo<br>2、确定作用域链scope<br>3、确定this指向</p><h4 id="2-1、变量对象vo创建过程"><a href="#2-1、变量对象vo创建过程" class="headerlink" title="2.1、变量对象vo创建过程"></a>2.1、变量对象vo创建过程</h4><p>1、建立arguments对象,检查当前上下文中的参数,建立该对象下的属性以及属性<br>2、检查当前上下文中的函数声明,在vo下建立一个属性,属性值就是指向该函数在内存中的地址的一个引用,如果函数名已经在vo对象下了,那么该值会被新的引用覆盖<br>3、检查当前上下文的变量声明,在vo下建立对应的属性,并赋值该属性undefined,如果vo下存在该属性,则跳过</p><p>从变量对象创建的过程中,我们能够理解到为什么有时候会<strong>变量提升(hoisting)</strong>了。<br>举个经典的面试题目<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">test</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">console</span>.log(a);<br> <span class="hljs-built_in">console</span>.log(b);<br> <span class="hljs-keyword">var</span> a = <span class="hljs-number">1</span>;<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">a</span>(<span class="hljs-params"></span>)</span>{}<br> <span class="hljs-keyword">var</span> b= <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{};<br> <span class="hljs-built_in">console</span>.log(a)<br>}<br>test();<br></code></pre></td></tr></table></figure><br><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><code class="hljs js"><span class="hljs-comment">// 以下为输出</span><br><span class="hljs-comment">// ƒ a(){}</span><br><span class="hljs-comment">// undefined</span><br><span class="hljs-comment">// 1</span><br></code></pre></td></tr></table></figure><br>test函数调用的时候,创建了函数执行上下文,以下为变量对象的创建过程<br>1、建立arguments对象<br>2、检索函数声明,找到function a,在vo对象下建立属性a指向function a的地址的引用<br>3、检索变量声明,找到变量b, 在vo对象下建立属性b,赋值为undefined。<br>到了代码执行过程<br>1、console.log(a),此时变量对象vo下 a的指向是函数地址<br>2、console.log(b),此时变量对象vo下 b的值为undefined<br>3、变量a赋值1,<br>4、函数地址的引用赋值给b<br>5、console.log(a),此时变量对象vo下a的值为1.<br>这样就解释了变量提升的原理,是由于创建函数执行下文过程中,对函数声明与变量声明处理的不一致造成的结果。</p><h4 id="2-2、确定作用域链scope"><a href="#2-2、确定作用域链scope" class="headerlink" title="2.2、确定作用域链scope"></a>2.2、确定作用域链scope</h4><p>关于scope作用域链的解释</p><blockquote><p>The scope chain property of each execution context is simply a collection of the current context’s [VO] + all parent’s lexical [VO]s.<br>Scope = VO + All Parent VOs<br>Eg: scopeChain = [ [VO] + [VO1] + [VO2] + [VO n+1] ];<br>译:每一个执行上下文的作用域链属性就是简单的收集当前上下文中的VO(变量对象)和其所有父级上下文中的词法VO。</p></blockquote><p>创建函数的时候,会将创建函数当前执行上下文的scope的引用赋值给函数的属性scope,<br>在函数调用的时候,会创建函数执行上下文,此时会将函数的scope与当前执行上下文的变量对象vo组合赋值给当前执行上下文的scope,形成当前执行上下文的作用域链scope。<br><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><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">example</span> (<span class="hljs-params"></span>)</span>{<br><br>}<br>example()<br></code></pre></td></tr></table></figure><br>1、在example函数创建的时候,将当前执行上下文的scope保存在函数内部属性[[scope]]中<br><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><code class="hljs js">example.[[scope]] = [<br> globalContext.VO<br>];<br></code></pre></td></tr></table></figure><br>2、在example函数调用的时候,会创建函数执行上下文和变量对象vo,在确定scope的时候<br>会将函数[[scope]]属性赋值到执行上下文的scope中<br><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><code class="hljs js">exampleContext = {<br> <span class="hljs-attr">scope</span>: globalContext.VO<br>}<br></code></pre></td></tr></table></figure><br>然后会将当前执行上下文的vo压入作用域链顶部,形成当前执行上下文的作用域链<br><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><code class="hljs js">exampleContext = {<br> <span class="hljs-attr">scope</span>: [<br> vo,<br> globalContext.VO<br> ]<br>}<br></code></pre></td></tr></table></figure></p><h4 id="2-3、确定this指针的指向"><a href="#2-3、确定this指针的指向" class="headerlink" title="2.3、确定this指针的指向"></a>2.3、确定this指针的指向</h4><p>等待理解补充<br>参考<a href="https://github.com/mqyqingfeng/Blog/issues/7">avaScript深入之从ECMAScript规范解读this</a></p><h2 id="补充说明"><a href="#补充说明" class="headerlink" title="补充说明"></a>补充说明</h2><p><a href="https://github.com/mqyqingfeng/Blog/issues/5">JavaScript深入变量对象</a> VO/AO的理解</p><blockquote><p>未进入执行阶段之前,变量对象(VO)中的属性都不能访问!但是进入执行阶段之后,变量对象(VO)转变为了活动对象(AO),里面的属性都能被访问了,然后开始进行执行阶段的操作。<br>它们其实都是同一个对象,只是处于执行上下文的不同生命周期。</p></blockquote><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://github.com/mqyqingfeng/Blog/issues/6">JavaScript深入之作用域链</a></li><li><a href="https://juejin.im/post/5c257b61e51d451b1c6de48c">从执行上下文深入理解闭包</a></li><li><a href="https://www.cnblogs.com/MinLee/p/5862271.html">理解Javascript之执行上下文(Execution Context)</a></li></ul>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>Promise 学习笔记</title>
<link href="/2019/01/08/promise/"/>
<url>/2019/01/08/promise/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Promise是异步编程的一种解决方案,它可以解决异步回调地狱的问题,防止层层嵌套对程序代码带来的难维护性。<br>在用javascript编码的过程中,我们少不了使用异步回调的时候。学会使用promise解决异步编程问题,将更有效得解决异步问题。<br>接下来让我们一起学习ES6中的Promise。</p><h2 id="promise对象"><a href="#promise对象" class="headerlink" title="promise对象"></a>promise对象</h2><blockquote><p>一个 Promise 就是一个代表了异步操作最终完成或者失败的对象。大多数人都在使用由其他函数创建并返回的 Promise。</p></blockquote><p>promise是一个代理对象(代理一个值),一个 Promise有以下几种状态:</p><ul><li>pending: 初始状态,既不是成功,也不是失败状态。</li><li>fulfilled: 意味着操作成功完成。</li><li>rejected: 意味着操作失败。</li><li>在应用 Promise 时,我们将会有以下约定:</li><li>在 JavaScript 事件队列的当前运行完成之前,回调函数永远不会被调用。</li><li>通过 .then 形式添加的回调函数,甚至都在异步操作完成之后才被添加的函数,都会被调用,如上所示。</li><li>通过多次调用 .then,可以添加多个回调函数,它们会按照插入顺序并且独立运行。</li></ul><p>因此,Promise 最直接的好处就是链式调用。</p><h2 id="实践"><a href="#实践" class="headerlink" title="实践"></a>实践</h2><p>可能纯概念的内容无法理解,举两个例子讲解promise如何解决异步回调问题<br>定时器是最简单的异步回调问题<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// example1:</span><br><span class="hljs-keyword">const</span> clock = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'已经过了5秒钟'</span>);<br> <span class="hljs-comment">// callback</span><br> }, <span class="hljs-number">5000</span>);<br>}<br>clock()<br><br><span class="hljs-comment">// example2</span><br><span class="hljs-keyword">const</span> clock = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">resolve, reject</span>)</span>{<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'已经过了5秒钟'</span>);<br> resolve(<span class="hljs-string">'执行回调函数'</span>);<br> }, <span class="hljs-number">5000</span>);<br> })<br>}<br>clock().then(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">dialog</span>)</span>{<br> <span class="hljs-built_in">console</span>.log(dialog)<br>})<br><br></code></pre></td></tr></table></figure><br>以上两种方法均是设置定时器为5秒,在指定时间到了以后执行回调函数<br>只不过第二种方式是用promise的方法解决,可能单纯一层没办法理解当多层调用的时候,会发现promise的方法更加优雅扁平<br><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// example3</span><br><span class="hljs-keyword">const</span> task = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'1'</span>);<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">()=></span>{<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2'</span>);<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'3'</span>)<br> }, <span class="hljs-number">3000</span>);<br> },<span class="hljs-number">3000</span>)<br> }, <span class="hljs-number">3000</span>);<br>}<br>task();<br><span class="hljs-comment">// example4</span><br><span class="hljs-keyword">const</span> task2 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve,reject</span>) =></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'1'</span>);<br> resolve(<span class="hljs-string">'1'</span>)<br> }, <span class="hljs-number">3000</span>);<br> })<br>}<br>task2()<br>.then(<span class="hljs-function">(<span class="hljs-params">r</span>) =></span> {<br> <span class="hljs-comment">//callback</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve,reject</span>) =></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2'</span>);<br> resolve(<span class="hljs-string">'2'</span>)<br> }, <span class="hljs-number">3000</span>);<br> })<br>})<br>.then(<span class="hljs-function">() =></span> {<br> <span class="hljs-comment">//callback</span><br> <span class="hljs-keyword">return</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve,reject</span>) =></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'3'</span>);<br> <span class="hljs-comment">// resolve('3')</span><br> }, <span class="hljs-number">3000</span>);<br> })<br>}).then(<span class="hljs-function">() =></span> {<br> <span class="hljs-comment">//callback</span><br>})<br></code></pre></td></tr></table></figure><br>exmaple3 是一个典型的多层嵌套的回调函数,当层数越多的时候,代码维护性越差<br>通过promise的办法,改造多层嵌套为链式调用函数,代码的阅读性和可维护性提高了<br>其中promise还有其他API满足业务场景需求</p><ul><li>Promise.prototype.catch()</li><li>Promise.all() // 全部执行,若有一个失败的结果,promise会将结果传入失败的回调中,而不管其他promise是否完成</li><li>Promise.race() // 率先改变的promise实例的返回值 传递给 回去</li><li>Promise.resolve() //有时需要将现有对象转为Promise对象 返回状态为resolve </li><li>Promise.reject() // 返回状态为reject</li></ul><h2 id="async-await与promise结合"><a href="#async-await与promise结合" class="headerlink" title="async/await与promise结合"></a>async/await与promise结合</h2><p>async/await是一种特殊的语法可以和promise系统工作,在语意层面更加容易理解代码</p><p><strong>async</strong>关键字放置在函数前,表明该函数是一个异步函数<br><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><code class="hljs js"><span class="hljs-keyword">const</span> example = <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">const</span> p = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Promise</span>(<span class="hljs-function">(<span class="hljs-params">resolve, reject</span>) =></span> {<br> <span class="hljs-built_in">setTimeout</span>(<span class="hljs-function">() =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'2秒后执行'</span>)<br> resolve(<span class="hljs-string">''</span>)<br> }, <span class="hljs-number">2000</span>);<br> })<br> <span class="hljs-keyword">await</span> p;<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'await p之后'</span>);<br>}<br>example()<br><span class="hljs-built_in">console</span>.log(<span class="hljs-string">'expample后'</span>)<br><span class="hljs-comment">// expample后</span><br><span class="hljs-comment">// 2秒后执行</span><br><span class="hljs-comment">// await p之后</span><br></code></pre></td></tr></table></figure><br>可以看出<strong>async</strong>函数是不会阻塞程序的,在<strong>async</strong>函数内部,可以采用await的方式代替promise.then回调方式<br>用同步的方式书写异步的代码更加直观, <code>await p</code>指等待promise对象 p 完成后,再继续执行后面的代码语意也直观</p><p>在实际开发的过程中,异步方法经常需要监听成功与失败的结果,这里需要关注 async/await 的方式书写代码的时候<br>错误捕抓一般使用try catch的方式,能够有效捕获错误。<br><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><code class="hljs js"><span class="hljs-keyword">try</span> {<br> <span class="hljs-keyword">await</span> xxx<br>} <span class="hljs-keyword">catch</span> (e){<br> <span class="hljs-comment">// do somethings</span><br>}<br></code></pre></td></tr></table></figure></p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Using_promises">MDN使用 Promises</a></li><li><a href="https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/0014345008539155e93fc16046d4bb7854943814c4f9dc2000">廖雪峰Promise</a></li></ul>]]></content>
<tags>
<tag>promise</tag>
<tag>es6</tag>
</tags>
</entry>
<entry>
<title>初探babel与babel插件</title>
<link href="/2019/01/03/babel/"/>
<url>/2019/01/03/babel/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>在使用es6编码开发的时候,或多或少都会遇到浏览器兼容的问题<br>Babel就是解决这个问题的<br>但是在使用的时候却发现有Babel-polyfill,有Babel-transform-runtime等等,一下子分不清他们的区别是什么<br>今天的主题就是了解一下他们的区别以及我们该如何去配置</p><h2 id="1-babel"><a href="#1-babel" class="headerlink" title="1.babel"></a>1.babel</h2><p>babel可以理解为javascript的编译器,更确切地说是源码到源码的编译器,通常也叫做“转换编译器“<br>babel 的三个主要处理步骤分别是:解析(parse),转换(transform),生成(generate)。<br>在转换的过程中,babel默认只对javascript的语法进行处理,并不会处理转换新的API<br>如果需要兼容低版本浏览器使用新的API则需要用 babel-polyfill或者babel-transform-runtime等插件介入转换过程处理js代码</p><h2 id="2-babel-polyfill与babel-transform-runtime-对比"><a href="#2-babel-polyfill与babel-transform-runtime-对比" class="headerlink" title="2.babel-polyfill与babel-transform-runtime 对比"></a>2.babel-polyfill与babel-transform-runtime 对比</h2><p><strong>babel-polyfill </strong>是当前环境注入这些 es6+ 标准的垫片<br>优点:一次引用,不再担心兼容问题适合在大型项目中使用<br>缺点:因为是全局变量覆盖的方式,会污染原生的方法</p><p><strong>babel-transform-runtime</strong> 则是识别并替换代码中的新特性,按需替换<br>优点:因为是按需替换,体积小,而且不会污染全局变量适合在开发工具中使用<br>缺点:一些新增的实例方法是没有处理的例如数组的 includes, filter, fill 等</p><h2 id="3-实验"><a href="#3-实验" class="headerlink" title="3.实验"></a>3.实验</h2><p>1、在webpack中采用引用 <code>@babel/polyfill</code><br>体积416kb,在IE11下正常运行,且在控制台中输入Promise、Object.assign均正常<br>2、不引入polyfill,在.babelrc中配置 <code>@babel/transform-runtime</code>,默认配置corejs:false<br>体积38.3kb,在IE11下报错 promise未定义<br>3、在.babelrc中配置 <code>@babel/transform-runtime</code>,设置corejs:2<br>体积140kb,在IE11下正常运行,控制台输入Promise报错</p><h2 id="4-webpack中实践配置"><a href="#4-webpack中实践配置" class="headerlink" title="4.webpack中实践配置"></a>4.webpack中实践配置</h2><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">npm install babel-loader @babel/core @babel/preset-env @babel/plugin-transform-runtime<br>npm install @babel/runtime-corejs2<br>npm install @babel/polyfill<br></code></pre></td></tr></table></figure><p>安装好babel相关依赖后进行配置<br>在webpack.config.js中 配置loader<br><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><code class="hljs js"><span class="hljs-built_in">module</span>: {<br> <span class="hljs-attr">rules</span>: [<br> {<br> <span class="hljs-attr">test</span>: <span class="hljs-regexp">/\.js$/</span>,<br> exclude: <span class="hljs-regexp">/node_modules/</span>,<br> use: {<br> <span class="hljs-attr">loader</span>: <span class="hljs-string">'babel-loader'</span><br> }<br> <span class="hljs-comment">// test 符合此正则规则的文件,运用 loader 去进行处理,除了exclude 中指定的内容</span><br> }<br> ]<br>}<br></code></pre></td></tr></table></figure><br>在.babelrc中配置 transform-runtime方式处理JavaScript代码<br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br> <span class="hljs-attr">"presets"</span>: [<br> [<br> <span class="hljs-string">"@babel/preset-env"</span>,<br> {<br> <span class="hljs-attr">"targets"</span>: [<br> <span class="hljs-string">"last 1 version"</span>,<br> <span class="hljs-string">"> 2%"</span>,<br> <span class="hljs-string">"IE >= 9"</span>,<br> <span class="hljs-string">"not dead"</span><br> ],<br> <span class="hljs-attr">"useBuiltIns"</span>: <span class="hljs-literal">false</span><br> }<br> ]<br> ],<br> <span class="hljs-attr">"plugins"</span>: [<br> [<br> <span class="hljs-string">"@babel/transform-runtime"</span>,<br> {<br> <span class="hljs-attr">"corejs"</span>: <span class="hljs-number">2</span>,<br> <span class="hljs-attr">"helpers"</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">"regenerator"</span>: <span class="hljs-literal">true</span>,<br> <span class="hljs-attr">"useESModules"</span>: <span class="hljs-literal">false</span><br> }<br> ]<br> ]<br>}<br></code></pre></td></tr></table></figure><br>如果想要引入babel-polyfill的方式,有两种方式<br>如果是按需引用的话,设置属性 “useBuiltIns”: “usage” 即可<br>如果是全局引用 “useBuiltIns”: “entry”,且在入口文件最顶部 <code>import "@babel/polyfill"</code><br><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs json">{<br> <span class="hljs-attr">"presets"</span>: [<br> [<br> <span class="hljs-string">"@babel/preset-env"</span>,<br> {<br> <span class="hljs-attr">"targets"</span>: [<br> <span class="hljs-string">"last 1 version"</span>,<br> <span class="hljs-string">"> 2%"</span>,<br> <span class="hljs-string">"IE >= 9"</span>,<br> <span class="hljs-string">"not dead"</span><br> ],<br> <span class="hljs-attr">"useBuiltIns"</span>: <span class="hljs-string">"usage"</span><br> }<br> ]<br> ]<br>}<br></code></pre></td></tr></table></figure></p><h2 id="补充"><a href="#补充" class="headerlink" title="补充"></a>补充</h2><p>babel-polyfill是不支持fetch的,所以对fetch兼容的话需要额外添加polyfill</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><p>参考资料均为参考,实验用到的环境是Babel7.0+,相关插件配置可能有变动</p><ul><li><a href="https://github.com/sunyongjian/blog/issues/30">你真的会用Babel吗</a></li><li><a href="https://babeljs.io/docs/en/">babel官方文档</a></li><li><a href="https://juejin.im/post/5b2cc31f51882574d02facff">关于babel-polyfill和babel-runtime</a></li></ul>]]></content>
<tags>
<tag>javascript</tag>
<tag>babel</tag>
</tags>
</entry>
<entry>
<title>webpack(一)基础知识</title>
<link href="/2018/12/28/webpack/"/>
<url>/2018/12/28/webpack/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前端工程流越来越被大家所了解使用后,webpack也是目前各位前端工程师所必备的技能了<br>但是webapck所涵盖的内容又太多了,所以对于日常使用来说,我们优先掌握基础知识和优化策略即可</p><h2 id="webpack基础知识"><a href="#webpack基础知识" class="headerlink" title="webpack基础知识"></a>webpack基础知识</h2><p>1、webpack是什么?<br>官网文档的解释</p><blockquote><p>本质上,webpack 是一个现代 JavaScript 应用程序的静态模块打包器(static module bundler)。在 webpack 处理应用程序时,它会在内部创建一个依赖图(dependency graph),用于映射到项目需要的每个模块,然后将所有这些依赖生成到一个或多个bundle。</p></blockquote><p>简单来说webpack就是模块打包器</p><p>2、webpack的核心概念</p><ul><li>entry:入口文件,在webpack中可以定义一个或者多个入口。webpack启动后会跟据入口文件创建依赖图</li><li>output:输出文件,可在webpack中配置输出的文件名称以及文件的输出目录</li><li>loader:模块转换和预处理器:可以在加载时处理文件,也可以将不同问文件转化未JavaScript模块</li><li>plugins:webpack插件:可以解决loader无法解决的问题</li></ul><p>3、webpack常用的loader与插件<br><strong>loader</strong> </p><ul><li>样式:style-loader、css-loader、less-loader、sass-loader等</li><li>文件:raw-loader、file-loader 、url-loader等</li><li>编译:babel-loader、coffee-loader 、ts-loader等</li><li>校验测试:mocha-loader、jshint-loader 、eslint-loader等</li></ul><p><strong>plugin</strong></p><ul><li>UglifyJsPlugin: 压缩和混淆代码。</li><li>CommonsChunkPlugin: 提高打包效率,将第三方库和业务代码分开打包.</li><li>ProvidePlugin: 自动加载模块,代替require和import</li><li>html-webpack-plugin: 可以根据模板自动生成html代码,并自动引用css和js文件。</li><li>extract-text-webpack-plugin: 将js文件中引用的样式单独抽离成css文件</li><li>DefinePlugin: 编译时配置全局变量。</li><li>HotModuleReplacementPlugin: 热更新。</li><li>compression-webpack-plugin:生产环境可采用gzip压缩JS和CSS。</li></ul><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://webpack.docschina.org/concepts/">wepack中文文档</a></li></ul>]]></content>
<tags>
<tag>webpack</tag>
</tags>
</entry>
<entry>
<title>微信h5分享自定义设置(二)</title>
<link href="/2018/11/26/wxshare-signatrue/"/>
<url>/2018/11/26/wxshare-signatrue/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>一般来说,前端开发者只需要完成微信分享一的实现即可。但是总有些时候,前端开发需要做更多的事情<br>继微信分享(一),这次将用 egg 实现后端签名算法。</p><h2 id="快速搭建环境"><a href="#快速搭建环境" class="headerlink" title="快速搭建环境"></a>快速搭建环境</h2><p>第一步: 配置开发环境<br><a href="https://eggjs.org/zh-cn/intro/quickstart.html">参考 egg 的官方文档</a>,快速搭建一个 egg 环境</p><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><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></div></td><td class="code"><pre><code class="hljs bash">$ mkdir egg-example && <span class="hljs-built_in">cd</span> egg-example<br>$ npm init egg --<span class="hljs-built_in">type</span>=ts<br>$ npm i<br>$ npm run dev<br></code></pre></td></tr></table></figure><p>配置 egg-chache (缓存), egg-view-nunjucks(模版引擎)。</p><p>第二部:配置 ngrok(内网穿透,让微信服务器访问获取设置安全域名)<br><a href="http://www.imooc.com/article/79754">搭建教程</a></p><p>##</p><p>验证服务器 token</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><code class="hljs js"><span class="hljs-comment">// router.js</span><br>router.get(<span class="hljs-string">"/api/wx"</span>, controller.api.wx.get);<br></code></pre></td></tr></table></figure><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// controller/api/wx.ts</span><br><span class="hljs-keyword">const</span> getSha1 = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">str</span>) </span>{<br> <span class="hljs-keyword">const</span> sha1 = crypto.createHash(<span class="hljs-string">'sha1'</span>); <span class="hljs-comment">// 定义加密方式:md5不可逆,此处的md5可以换成任意hash加密的方法名称;</span><br> sha1.update(str);<br> <span class="hljs-keyword">const</span> res = sha1.digest(<span class="hljs-string">'hex'</span>); <span class="hljs-comment">// 加密后的值d</span><br> <span class="hljs-keyword">return</span> res;<br>};<br><span class="hljs-comment">//</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WxController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span> </span>{<br> public <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">get</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">const</span> { ctx } = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">const</span> { signature, timestamp, nonce, echostr } = ctx.query;<br> <span class="hljs-keyword">const</span> token = <span class="hljs-string">'wxjsdk'</span>;<br> <span class="hljs-keyword">const</span> list = [ token, timestamp, nonce ];<br> list.sort();<br> <span class="hljs-keyword">const</span> hashcode = getSha1(list.join(<span class="hljs-string">''</span>));<br> <span class="hljs-keyword">if</span> (signature === hashcode) {<br> ctx.body = echostr;<br> } <span class="hljs-keyword">else</span> {<br> ctx.body = <span class="hljs-string">''</span>;<br> }<br> }<br>}<br></code></pre></td></tr></table></figure><p>具体分享页面请求获取 signature 算法</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><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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// controller/api/wx.ts</span><br><br><span class="hljs-comment">// 随机字符串</span><br><span class="hljs-keyword">var</span> createNonceStr = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">Math</span>.random().toString(<span class="hljs-number">36</span>).substr(<span class="hljs-number">2</span>, <span class="hljs-number">15</span>);<br>};<br><br><span class="hljs-comment">// 时间戳</span><br><span class="hljs-keyword">var</span> createTimestamp = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">return</span> <span class="hljs-built_in">parseInt</span>((<span class="hljs-keyword">new</span> <span class="hljs-built_in">Date</span>().getTime() / <span class="hljs-number">1000</span>).toString()).toString();<br>};<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-class"><span class="hljs-keyword">class</span> <span class="hljs-title">WxController</span> <span class="hljs-keyword">extends</span> <span class="hljs-title">Controller</span> </span>{<br> <span class="hljs-comment">// 内部函数</span><br> private <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">getSignature</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-comment">// 从缓存中获取 token和jsticket 判断是否过期</span><br> <span class="hljs-keyword">const</span> token = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.cache.get(<span class="hljs-string">'token'</span>);<br> <span class="hljs-keyword">const</span> jsapiTicket = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.cache.get(<span class="hljs-string">'jsapiTicket'</span>);<br> <span class="hljs-keyword">if</span> (token && jsapiTicket) {<br> <span class="hljs-keyword">return</span> jsapiTicket;<br> } <span class="hljs-keyword">else</span> {<br> <span class="hljs-comment">// 分步获取token和jsticket</span><br> <span class="hljs-comment">// apppid和appsecret从微信公众号后台设置里获取</span><br> <span class="hljs-keyword">const</span> appid = <span class="hljs-string">'your appid'</span>;<br> <span class="hljs-keyword">const</span> appsecret = <span class="hljs-string">'your appsecret'</span>;<br> <span class="hljs-keyword">const</span> result = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.curl(<br> <span class="hljs-string">`https://api.weixin.qq.com/cgi-bin/token?grant_type=client_credential&appid=<span class="hljs-subst">${appid}</span>&secret=<span class="hljs-subst">${appsecret}</span>`</span>,<br> {<br> <span class="hljs-attr">dataType</span>: <span class="hljs-string">'json'</span>,<br> },<br> );<br> <span class="hljs-keyword">if</span> (result.data && result.data.access_token) {<br> <span class="hljs-keyword">const</span> access_token = result.data.access_token;<br> <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.cache.set(<span class="hljs-string">'token'</span>, access_token, <span class="hljs-number">7200</span>);<br> <span class="hljs-keyword">const</span> jsapi = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.curl(<br> <span class="hljs-string">`https://api.weixin.qq.com/cgi-bin/ticket/getticket?access_token=<span class="hljs-subst">${</span></span><br><span class="hljs-subst"><span class="hljs-string"> access_token</span></span><br><span class="hljs-subst"><span class="hljs-string"> }</span>&type=jsapi`</span>,<br> {<br> <span class="hljs-attr">dataType</span>: <span class="hljs-string">'json'</span>,<br> },<br> );<br> <span class="hljs-keyword">if</span> (jsapi.data && jsapi.data.errcode === <span class="hljs-number">0</span>) {<br> <span class="hljs-keyword">const</span> ticket = jsapi.data.ticket<br> <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.app.cache.set(<span class="hljs-string">'jsapiTicket'</span>, ticket, <span class="hljs-number">7200</span>);<br> <span class="hljs-keyword">return</span> ticket<br> }<br> }<br> }<br> <span class="hljs-keyword">return</span> <span class="hljs-literal">false</span><br> }<br> <span class="hljs-comment">// 接口请求控制</span><br> public <span class="hljs-keyword">async</span> <span class="hljs-function"><span class="hljs-title">sg</span>(<span class="hljs-params"></span>)</span> {<br> <span class="hljs-keyword">const</span> { ctx } = <span class="hljs-built_in">this</span>;<br> <span class="hljs-keyword">const</span> jsticket = <span class="hljs-keyword">await</span> <span class="hljs-built_in">this</span>.getSignature();<br> <span class="hljs-built_in">console</span>.log(jsticket)<br> <span class="hljs-keyword">if</span> (!jsticket) {<br> ctx.body = {<br> <span class="hljs-attr">errmsg</span>: <span class="hljs-string">'出错了'</span>,<br> };<br> }<br> <span class="hljs-keyword">const</span> { url } = ctx.query;<br> <span class="hljs-keyword">const</span> nonceStr = createNonceStr();<br> <span class="hljs-keyword">const</span> timestamp = createTimestamp();<br> <span class="hljs-keyword">const</span> str = <span class="hljs-string">`jsapi_ticket=<span class="hljs-subst">${jsticket}</span>&noncestr=<span class="hljs-subst">${nonceStr}</span>&timestamp=<span class="hljs-subst">${timestamp}</span>&url=<span class="hljs-subst">${url}</span>`</span><br> <span class="hljs-keyword">const</span> signature = getSha1(str);<br> ctx.body = {<br> <span class="hljs-attr">appId</span>: <span class="hljs-string">'your appid'</span>,<br> timestamp,<br> nonceStr,<br> signature,<br> }<br> }<br>}<br></code></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></pre></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// router.js</span><br>router.get(<span class="hljs-string">"/api/wxsg"</span>, controller.api.wx.sg);<br></code></pre></td></tr></table></figure><p>此上完成签名算法的接口请求<br>配合微信分享(一)中的前端代码,能够实现分享自定义</p><h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><p>一、设置接口配置信息的时候,微信服务器会主动访问自己的服务器(所以需要具备自己的服务器或者用 ngrok 配置内网穿透)完成验证<br>二、token 和 jsticket 都是 7200 秒的有效期且不能频繁访问获取,所以需要缓存存起来,需要用户配置相关策略<br>三、调试接口和网页可以使用微信开发者工具,具备更详细的 log 信息</p><h2 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h2><ul><li><a href="https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login">微信公众号配置地址</a></li><li><a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115">微信公众号签名算法</a></li><li><a href="http://www.imooc.com/article/79754">ngrok 配置地址</a></li><li><a href="https://www.jianshu.com/p/1a35e1dbe1ad">nodejs 微信 jsdk</a></li></ul>]]></content>
<tags>
<tag>微信分享</tag>
</tags>
</entry>
<entry>
<title>微信h5分享自定义设置(一)</title>
<link href="/2018/11/25/wxshare/"/>
<url>/2018/11/25/wxshare/</url>
<content type="html"><![CDATA[<h1 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h1><p>做过手机端h5页面的前端同学都知道,离不开宣传页面在微信中的传播与分享<br>今天就这个问题,我们来总结下前端在做h5微信分享的时候应该处理的问题<br>因为整个微信分享的流程比较长,在“一”中,我们只关注前端将要面临的问题以及怎么处理</p><h1 id="微信h5分享"><a href="#微信h5分享" class="headerlink" title="微信h5分享"></a>微信h5分享</h1><p>前端涉及到的流程处理</p><ul><li>判断浏览器ua</li><li>动态加载wx-sdk</li><li>异步请求后端接口获取signature签名信息(需要将当前url传递给服务器做签名,常见错误附录1提醒)</li><li>利用签名配置wx sdk</li><li>设置个性化分享信息,图标,标题,描述,链接[logo,title,desc,link]</li><li>绑定wx sdk事件</li><li>debug调试</li><li>上线</li></ul><p>代码部分<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></pre></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-comment">// 1.1判断ua</span><br><span class="hljs-keyword">var</span> ua = navigator.userAgent;<br><span class="hljs-keyword">if</span>(<span class="hljs-regexp">/micromessenger/i</span>.test(ua)){<br> <span class="hljs-comment">// 1.2动态加载wx-sdk</span><br> <span class="hljs-keyword">var</span> head = <span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">'head'</span>)[<span class="hljs-number">0</span>];<br> <span class="hljs-keyword">var</span> script = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'script'</span>);<br> script.src = <span class="hljs-string">'//res2.wx.qq.com/open/js/jweixin-1.4.0.js '</span>;<br> head.appendChild(script);<br> <span class="hljs-keyword">var</span> url = <span class="hljs-built_in">encodeURIComponent</span>(location.href.split(<span class="hljs-string">'#'</span>)[<span class="hljs-number">0</span>]); <span class="hljs-comment">// 需要将url 编码</span><br> script.onload = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">bindWxShare</span>(<span class="hljs-params">obj</span>) </span>{<br> wx.onMenuShareTimeline(obj)<br> wx.onMenuShareAppMessage(obj)<br> wx.onMenuShareQQ(obj)<br> wx.onMenuShareQZone(obj)<br> }<br> <span class="hljs-comment">// 1.3 异步获取签名信息</span><br> $.ajax({<br> <span class="hljs-attr">url</span>: <span class="hljs-string">'/api?url='</span>+url,<br> <span class="hljs-attr">success</span>: <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">response</span>) </span>{<br> <span class="hljs-built_in">console</span>.log(response)<br> !response.appId && (response = <span class="hljs-built_in">JSON</span>.parse(response));<br> <span class="hljs-comment">// 1.4配置wx-sdk config</span><br> wx.config({<br> <span class="hljs-comment">// debug: true, // 开启调试模式,调用的所有api的返回值会在客户端alert出来,若要查看传入的参数,可以在pc端打开,参数信息会通过log打出,仅在pc端时才会打印。</span><br> <span class="hljs-attr">appId</span>: response.appId,<br> <span class="hljs-attr">timestamp</span>: response.timestamp, <span class="hljs-comment">// 必填,生成签名的时间戳</span><br> <span class="hljs-attr">nonceStr</span>: response.nonceStr, <span class="hljs-comment">// 必填,生成签名的随机串</span><br> <span class="hljs-attr">signature</span>: response.signature,<span class="hljs-comment">// 必填,签名,见附录1</span><br> <span class="hljs-attr">jsApiList</span>: [<span class="hljs-string">'onMenuShareTimeline'</span>, <span class="hljs-string">'onMenuShareAppMessage'</span>, <span class="hljs-string">'onMenuShareQQ'</span>, <span class="hljs-string">'onMenuShareQZone'</span>] <span class="hljs-comment">// 必填,需要使用的JS接口列表,所有JS接口列表见附录2</span><br> });<br> <span class="hljs-comment">// 1.5 配置个性化设置</span><br> <span class="hljs-comment">// 确保分享的图片都是设置的图片,而不是微信默认抓取的</span><br> wx.ready(<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{<br> <span class="hljs-keyword">var</span> wxShareImg = <span class="hljs-string">'logo address'</span>;<br> bindWxShare({<br> <span class="hljs-attr">title</span>: <span class="hljs-string">'自定义标题'</span>,<br> <span class="hljs-attr">link</span>: <span class="hljs-string">'your link'</span>,<br> <span class="hljs-attr">imgUrl</span>: wxShareImg,<br> <span class="hljs-attr">desc</span>: <span class="hljs-string">'自定义描述'</span><br> })<br> })<br> }<br> })<br> }<br> <br>}<br></code></pre></td></tr></table></figure><br>通过以上配置基本可以完成对微信分享的功能的实现,介入调试的过程可能是比较麻烦的<br>因为公众号那边需要有安全域名的设置,只有该域名的及子域名才能通过签名认证<br>前端这边调试有两种方式</p><h4 id="本地调试方式-适用于在后端提供好接口,前端沙箱开发测试"><a href="#本地调试方式-适用于在后端提供好接口,前端沙箱开发测试" class="headerlink" title="本地调试方式 (适用于在后端提供好接口,前端沙箱开发测试)"></a>本地调试方式 (适用于在后端提供好接口,前端沙箱开发测试)</h4><p>前提知识预备</p><ul><li>wx-jssdk安全域名相关知识(只有该域名或者子域名下的页面才能应用wxjssdk)</li><li>devserver porxy代理方式</li><li>charles本地代理</li><li>wx-jssdk 代码调试(错误代码附录一)</li></ul><p><strong>第一步</strong>:创建一个精简的webpack server 修改<code>hosts</code>配置好域名<code>test.example.com</code>(假定安全域名为example.com)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash">sudo vim /etc/hosts<br>127.0.0.1 test.example.com<br></code></pre></td></tr></table></figure><br><strong>第二步</strong>:根据后端接口的开放程度,如果是允许跨域的接口,无需处理直接异步请求获取即可。如果有跨域限制需要在<code>webpack.config.js</code>中配置<code>devServer</code>字段,代理转接后端接口<br>此时本地能够访问 <code>test.example.com:port</code>(目标分享页面),同时能够访问目标域名下的后端接口信息<br><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><code class="hljs js">{<br> ...<br> <span class="hljs-attr">proxy</span>: {<br> <span class="hljs-string">'/api'</span>: {<br> <span class="hljs-attr">target</span>: <span class="hljs-string">'target.com'</span>,<br> <span class="hljs-attr">changeOrigin</span>: <span class="hljs-literal">true</span><br> }<br> }<br> ...<br>}<br></code></pre></td></tr></table></figure><br><strong>第三步</strong>:配置Charles代理抓包工具:因为微信分享的测试是在手机端进行的(模拟真实环境,也可以采取微信开发者工具),手机并不能直接访问 <code>test.example.com:port</code>。此时需要到charles,在手机上配置好了相关代理信息后,即可手机访问<code>test.example.com:port</code>进行真机调试与测试<a href="https://www.jianshu.com/p/68684780c1b0">参考地址</a><br>局限性:通过本机代理的设备才可以访问该页面</p><h4 id="ngrok开内网穿透技术(进阶方式,方便广泛调试测试,调研功能)"><a href="#ngrok开内网穿透技术(进阶方式,方便广泛调试测试,调研功能)" class="headerlink" title="ngrok开内网穿透技术(进阶方式,方便广泛调试测试,调研功能)"></a>ngrok开内网穿透技术(进阶方式,方便广泛调试测试,调研功能)</h4><p>前提知识</p><ul><li>ngrok 本地开映射内网穿透</li><li>微信公众号测试号配置</li><li>node server 编码签名算法、ACCESS_TOKEN、jsticket获取</li></ul><p>思路:就是完全实现一个本地的微信jssdk的调用过程。生成签名,页面异步获取签名,调用wxjssdk<br>ngrok只是开了一个公网可以访问的域名方便测试,以及符合微信配置里的安全域名设置<br>优点:前端可以完全控制,能够方便调用wxjssdk各种功能<br>缺点:需要熟悉node知识,最好有完善的一套工具生成接口,方便开发<br>ps:第二种方式将在 微信分享自定义设置(二)中体现</p><h1 id="后续(to-be-continue)"><a href="#后续(to-be-continue)" class="headerlink" title="后续(to be continue)"></a>后续(to be continue)</h1><ul><li>新版本jssdk旧的接口将被遗弃,需要适配低版本微信客户端则要做版本配置</li><li>补充(二)</li><li>封装函数,改成一键配置方式</li></ul><h1 id="参考资料"><a href="#参考资料" class="headerlink" title="参考资料"></a>参考资料</h1><ul><li><a href="https://mp.weixin.qq.com/debug/cgi-bin/sandbox?t=sandbox/login">微信公众号测试号地址</a></li><li><a href="https://mp.weixin.qq.com/wiki?t=resource/res_main&id=mp1421141115">微信jssdk说明文档</a></li><li><a href="https://webpack.js.org/configuration/dev-server/#devserver-proxy">webpack devserver 文档</a></li><li><a href="https://www.jianshu.com/p/68684780c1b0">charles配置</a></li></ul>]]></content>
<tags>
<tag>微信分享</tag>
<tag>h5</tag>
<tag>移动开发调试</tag>
</tags>
</entry>
<entry>
<title>js跨域</title>
<link href="/2018/11/02/js%E8%B7%A8%E5%9F%9F/"/>
<url>/2018/11/02/js%E8%B7%A8%E5%9F%9F/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>跨域是前端开发经常会遇到的问题,过去的项目中也处理不少。<br>对这类问题系统的总结尤为必要,无论是以后处理该问题还是对项目架构的思考也有帮助<br><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002442.png" alt=""></p><h2 id="跨域原理"><a href="#跨域原理" class="headerlink" title="跨域原理"></a>跨域原理</h2><p>什么是跨域?为什么会出现跨域呢<br>要了解跨域首先要知道同源策略,同源策略的定义</p><blockquote><p>如果两个页面的协议,端口(如果有指定)和域名都相同,则两个页面具有相同的源。<br>同源策略限制了从同一个源加载的文档或脚本如何与来自另一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制。</p></blockquote><p>造成跨域的两种分类<br>1、dom同源策略:禁止对不同源页面DOM进行操作。这里主要场景是iframe跨域的情况,不同域名的iframe是限制互相访问的。<br>2、XmlHttpRequest同源策略:禁止使用XHR对象向不同源的服务器地址发起HTTP请求。</p><h2 id="跨域解决方案"><a href="#跨域解决方案" class="headerlink" title="跨域解决方案"></a>跨域解决方案</h2><p>网络请求跨域解决方案</p><ul><li>CORS方法解决跨域</li><li>JSONP跨域请求</li><li>服务器代理方式解决</li></ul><p>dom跨域操作解决方案</p><ul><li>修改document.domain实现子域不同的页面进行跨域交互</li><li>在同一窗体下,不同页面中修改window.name的值,能够互相读取</li></ul><h4 id="一、CORS"><a href="#一、CORS" class="headerlink" title="一、CORS"></a>一、CORS</h4><p>简介:</p><blockquote><p>MDN:跨域资源共享(CORS) 是一种机制,它使用额外的 HTTP 头来告诉浏览器 让运行在一个 origin (domain) 上的Web应用被准许访问来自不同源服务器上的指定的资源。当一个资源从与该资源本身所在的服务器不同的域或端口请求一个资源时,资源会发起一个跨域 HTTP 请求。</p></blockquote><p>兼容性<a href="https://caniuse.com/#search=cors">查询</a><br>注意【ie(8、9)解决方案】<br>CORS两种请求:</p><ul><li>简单请求(simple request)</li><li>非简单请求(not-so-simple request)</li></ul><p>满足以下2大条件的就是简单请求<br>1、【HEAD,GET,POST】其中一个请求方法<br>2、HTTP的头部信息不超过以下几种</p><ul><li>Accept</li><li>Accept-Language</li><li>Content-Language</li><li>Last-Event-ID</li><li>Content-Type:只限于三个值application/x-www-form-urlencoded、multipart/form-data、text/plain</li></ul><p>基本流程<a href="http://www.ruanyifeng.com/blog/2016/04/cors.html">参考</a><br>简单请求,浏览器会直接发出CORS请求,在请求头信息中增加一个origin字段,服务器根据origin源是否在允许范围内返回不同值,浏览器根据是否返回Access-Control-Allow-Origin字段来做出不同反应<br>如果没有返回Access-Control-Allow-Origin字段,浏览器会抛出一个错误被XMLHttpRequest的onerror回调函数捕获<br>非简单请求,会在正式通信之前,增加一次HTTP查询请求,称为“预检”(preflight)<br>“预检”请求用的请求方法是OPTIONS,表示这个请求是用来询问的。头信息里面,关键字段是Origin,表示请求来自哪个源。通过“预检”,则发出请求,没通过则报错<br>面试可能会被问到跨域报错的时候发出了请求吗?<br>具体实践操作<br>1、服务器设置 Access-Control-相关字段允许跨域,浏览器正常发出xmlhttprequest请求<br>2、如果需要携带cookies跨域请求,需要服务器设置Credentials, 浏览器请求设置withCredentials为true</p><h4 id="二、JSONP的方式"><a href="#二、JSONP的方式" class="headerlink" title="二、JSONP的方式"></a>二、JSONP的方式</h4><p>jsonp跨域请求的方式,是利用了同源策略中通常允许跨域资源嵌入(Cross-origin embedding)<a href="https://developer.mozilla.org/zh-CN/docs/Web/Security/Same-origin_policy">点击查看</a><br>简单来说就是动态增加来一个script标签,请求来服务器一段js代码文件<br>加载完成后执行该代码,在请求中可以增加参数<br>例子<br><figure class="highlight html"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"> <span class="hljs-keyword">var</span> functionHandler = <span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params">data</span>)</span>{</span><br><span class="javascript"> <span class="hljs-built_in">console</span>.log(data);</span><br><span class="javascript"> }</span><br><span class="javascript"> <span class="hljs-keyword">var</span> url = <span class="hljs-string">'http://xxx.com/xxxx?prams=xxx&callback=functionHandler'</span>;</span><br><span class="javascript"> <span class="hljs-keyword">var</span> script = <span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">'script'</span>);</span><br><span class="javascript"> script.setAttribute(<span class="hljs-string">'src'</span>, url);</span><br><span class="javascript"> <span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">'head'</span>)[<span class="hljs-number">0</span>].appendChild(script); </span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><br>服务端代码(简略)<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># 通过参数查询data</span><br>functionHandler(data)<br></code></pre></td></tr></table></figure><br>jsonp限制性:只能get请求</p><h4 id="三、服务器代理的方式"><a href="#三、服务器代理的方式" class="headerlink" title="三、服务器代理的方式"></a>三、服务器代理的方式</h4><p>在服务器端配置好代理,浏览器端就不会出现跨域的问题<br>在开发阶段比较常实现<br>devsever的proxy就是用来该原理<br>在devsever中配置代理,原指向devserver的请求被代理到目标地址,在服务器中http请求没有跨域限制,所以解决了浏览器js跨域的问题</p><h4 id="四、document-domain"><a href="#四、document-domain" class="headerlink" title="四、document.domain"></a>四、document.domain</h4><p>document.domain解决了子域名不同页面互相交互的问题,但是也只限制于子域名不一样,端口和协议必须一样<br>具体操作就是限制document.domain = 顶级域名</p><h4 id="五、window-name"><a href="#五、window-name" class="headerlink" title="五、window.name"></a>五、window.name</h4><p>window.name则利用同一窗体下加载不同的页面,window.name的值不会清除,达到传递数据的效果 数据大小支持到2MB<br>具体操作需要3个页面<br>a 域名下的 origin page<br>a 域名下的 proxy page<br>b 域名下的 data page<br>a 域名下origin page 通过动态的iframe 加载 data page, data page中设置了window.name = data数据<br>可是此时 origin page的域名与data page域名不一致,浏览器限制交互,所以需要将iframe跳转到proxy page (即iframe的scr值设置为proxy page)。此时iframe与 origin page同源,可以操作获取到iframe 的window.name中的数据,获取完毕后销毁iframe<br>这样origin page就可以获取到非同源下的 data page数据<br>a域名下的 origin page<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">script</span> <span class="hljs-attr">type</span>=<span class="hljs-string">"text/javascript"</span>></span><span class="javascript"></span><br><span class="javascript"> <span class="hljs-keyword">var</span> a=<span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">"button"</span>)[<span class="hljs-number">0</span>];</span><br><span class="javascript"> a.onclick=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{ <span class="hljs-comment">//button添加click事件</span></span><br><span class="javascript"> <span class="hljs-keyword">var</span> inf=<span class="hljs-built_in">document</span>.createElement(<span class="hljs-string">"iframe"</span>); <span class="hljs-comment">//创建iframe</span></span><br><span class="javascript"> inf.src=<span class="hljs-string">"http://www.b.com/data.html"</span>+<span class="hljs-string">"?h=5"</span> <span class="hljs-comment">//加载数据页www.b.com/data.html同时携带参数h=5</span></span><br><span class="javascript"> <span class="hljs-keyword">var</span> body=<span class="hljs-built_in">document</span>.getElementsByTagName(<span class="hljs-string">"body"</span>)[<span class="hljs-number">0</span>];</span><br><span class="javascript"> body.appendChild(inf); <span class="hljs-comment">//引入a页面</span></span><br><span class="javascript"></span><br><span class="javascript"> inf.onload=<span class="hljs-function"><span class="hljs-keyword">function</span>(<span class="hljs-params"></span>)</span>{</span><br><span class="javascript"> inf.src=<span class="hljs-string">'http://www.a.com/proxy.html'</span> <span class="hljs-comment">//iframe加载完成,加载www.a.com域下边的空白页proxy.html</span></span><br><span class="javascript"> <span class="hljs-built_in">console</span>.log(inf.contentWindow.name) <span class="hljs-comment">//输出window.name中的数据</span></span><br><span class="javascript"> body.removeChild(inf) <span class="hljs-comment">//清除iframe</span></span><br><span class="javascript"> }</span><br><span class="javascript"> }</span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure><br>b域名下 data page<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag"><<span class="hljs-name">script</span>></span><span class="javascript"></span><br><span class="javascript"> <span class="hljs-comment">// var str=window.location.href.substr(-1,1); //获取url中携带的参数值</span></span><br><span class="javascript"> <span class="hljs-comment">// 因为已经是b域名下的页面了,可以通过请求各种b域名下的数据再设置window.name的值</span></span><br><span class="javascript"> <span class="hljs-built_in">window</span>.name = <span class="hljs-string">'some data'</span></span><br><span class="javascript"></span><span class="hljs-tag"></<span class="hljs-name">script</span>></span><br></code></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>回顾css3相关知识</title>
<link href="/2018/10/23/css3/"/>
<url>/2018/10/23/css3/</url>
<content type="html"><![CDATA[<h2 id="css3中的变形(transform)、过渡-transtion-、动画-animation"><a href="#css3中的变形(transform)、过渡-transtion-、动画-animation" class="headerlink" title="css3中的变形(transform)、过渡(transtion)、动画(animation)"></a>css3中的变形(transform)、过渡(transtion)、动画(animation)</h2><h3 id="一、transform"><a href="#一、transform" class="headerlink" title="一、transform"></a>一、transform</h3><p>用法:transform: none|transform-functions;<br>transform-functions:rotate | scale | skew | translate |matrix;<br><figure class="highlight css"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-id">#div1</span>{<br> <span class="hljs-attribute">width</span>: <span class="hljs-number">50px</span>;<br> <span class="hljs-attribute">height</span>: <span class="hljs-number">50px</span>;<br> <span class="hljs-attribute">background</span>:<span class="hljs-number">#de0010</span>;<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">45deg</span>) <span class="hljs-built_in">scale</span>(<span class="hljs-number">0.5</span>);<br>}<br></code></pre></td></tr></table></figure><br>效果是旋转45读,缩小50%</p><h5 id="rotate"><a href="#rotate" class="headerlink" title="rotate"></a>rotate</h5><p>ratate(angle) 定义 2D 旋转,在参数中规定角度。正直顺时针,负值逆时针</p><h5 id="scale"><a href="#scale" class="headerlink" title="scale"></a>scale</h5><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">scale(<number><span class="hljs-selector-attr">[, <number>]</span>):提供执行<span class="hljs-selector-attr">[sx,sy]</span>缩放矢量的两个参数指定一个<span class="hljs-number">2</span>D scale(<span class="hljs-number">2</span>D缩放)<br></code></pre></td></tr></table></figure><p>scaleX()只对x轴方向进行矢量缩放,scaleY()则对Y轴<br>基点为元素中心点,也可以通过transform-origin来改变元素基点位置</p><h5 id="skew"><a href="#skew" class="headerlink" title="skew"></a>skew</h5><p>扭曲变形<br>与scale一样同样具有三种情况:skew(x,y)使元素在水平和垂直方向同时扭曲(X轴和Y轴同时按一定的角度值进行扭曲变形);skewX(x)仅使元素在水平方向扭曲变形(X轴扭曲变形);skewY(y)仅使元素在垂直方向扭曲变形(Y轴扭曲变形)</p><h5 id="tanslate"><a href="#tanslate" class="headerlink" title="tanslate"></a>tanslate</h5><p>移动translate我们分为三种情况:translate(x,y)水平方向和垂直方向同时移动(也就是X轴和Y轴同时移动);translateX(x)仅水平方向移动(X轴移动);translateY(Y)仅垂直方向移动(Y轴移动)</p><h5 id="matrix"><a href="#matrix" class="headerlink" title="matrix"></a>matrix</h5><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs css">matrix(<number>, <number>, <number>, <number>, <number>, <number>)<br></code></pre></td></tr></table></figure><p> matrix以一个含六值的(a,b,c,d,e,f)变换矩阵的形式指定一个2D变换,相当于直接应用一个[a b c d e f]变换矩阵。就是基于水平方向(X轴)和垂直方向(Y轴)重新定位元素</p><h5 id="改变元素基点transform-origin"><a href="#改变元素基点transform-origin" class="headerlink" title="改变元素基点transform-origin"></a>改变元素基点transform-origin</h5><p>transform-origin(X,Y)或者transform-origin: x;改变元素基点</p><h3 id="二、transition"><a href="#二、transition" class="headerlink" title="二、transition"></a>二、transition</h3><p>transition主要包含四个属性值:执行变换的属性:transition-property,变换延续的时间:transition-duration,在延续时间段,变换的速率变化transition-timing-function,变换延迟时间transition-delay。下面分别来看这四个属性值</p><h5 id="transition-property"><a href="#transition-property" class="headerlink" title="transition-property"></a>transition-property</h5><p>transition-property是用来指定当元素其中一个属性改变时执行transition效果,其主要有以下几个值:none(没有属性改变);all(所有属性改变)这个也是其默认值;indent(元素属性名)。当其值为none时,transition马上停止执行,当指定为all时,则元素产生任何属性值变化时都将执行transition效果,ident是可以指定元素的某一个属性值。</p><h5 id="transition-duration"><a href="#transition-duration" class="headerlink" title="transition-duration"></a>transition-duration</h5><p>transition-duration是用来指定元素 转换过程的持续时间,取值:<time>为数值,单位为s(秒)或者ms(毫秒),可以作用于所有元素,包括:before和:after伪元素。其默认值是0,也就是变换时是即时的。</p><h5 id="transition-timing-function"><a href="#transition-timing-function" class="headerlink" title="transition-timing-function"></a>transition-timing-function</h5><p>transition-timing-function的值允许你根据时间的推进去改变属性值的变换速率,transition-timing-function有6个可能值:</p><ul><li>ease:(逐渐变慢),默认值,ease函数等同于贝塞尔曲线(0.25, 0.1, 0.25, 1.0).</li><li>linear:(匀速),linear 函数等同于贝塞尔曲线(0.0, 0.0, 1.0, 1.0).</li><li>ease-in:(加速),ease-in 函数等同于贝塞尔曲线(0.42, 0, 1.0, 1.0).</li><li>ease-out:(减速),ease-out 函数等同于贝塞尔曲线(0, 0, 0.58, 1.0).</li><li>ease-in-out:(加速然后减速),ease-in-out 函数等同于贝塞尔曲线(0.42, 0, 0.58, 1.0)</li><li>cubic-bezier:(该值允许你去自定义一个时间曲线),特定的cubic-bezier曲线。(x1, y1, x2, y2)四个值特定于曲线上点P1和点P2。所有值需在[0, 1]区域内,否则无效。</li></ul><h5 id="transition-delay"><a href="#transition-delay" class="headerlink" title="transition-delay"></a>transition-delay</h5><p>transition-delay是用来指定一个动画开始执行的时间,也就是说当改变元素属性值后多长时间开始执行transition效果,其取值:<time>为数值,单位为s(秒)或者ms(毫秒),其使用和transition-duration极其相似,也可以作用于所有元素,包括:before和:after伪元素。 默认大小是”0”,也就是变换立即执行,没有延迟。</p><h3 id="三、animation"><a href="#三、animation" class="headerlink" title="三、animation"></a>三、animation</h3><p>css3中animation与html的canvasb不一样,animation只应用在已有的元素上<br>在w3school里 animation属性<br>一、animation-name:是用来定义一个动画的名称,其主要有两个值:IDENT是由Keyframes创建的动画名,换句话说此处的IDENT要和Keyframes中的IDENT一致,如果不一致,将不能实现任何动画效果;none为默认值,当值为none时,将没有任何动画效果。另外我们这个属性跟前面所讲的transition一样,我们可以同时附几个animation给一个元素,我们只需要用逗号“,”隔开。<br>二、animation-duration:是用来指定元素播放动画所持续的时间长,默认值为0<br>三、animation-timing-function:它和transition中的transition-timing-function一样,6个取值<br>四、animation-delay:是用来指定元素动画开始时间<br>五、animation-iteration-count:用来指定元素播放动画的循环次数,默认值为1,infinite为无限次数循环<br>六、animation-direction: 动画播放方向<br>七、animation-play-state:主要是用来控制元素动画的播放状态。其主要有两个值,running和paused其中running为默认值。</p><p>css3的animation是由Keyframes(帧)实现效果的,那么keyframes怎么定义呢<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-keyword">@keyframes</span> IDENT {<br> <span class="hljs-selector-tag">from</span> {<br> Properties:Properties value;<br> }<br> Percentage {<br> Properties:Properties value;<br> }<br> <span class="hljs-selector-tag">to</span> {<br> Properties:Properties value;<br> }<br>}<br></code></pre></td></tr></table></figure><br>IDENT是一个动画名称,percentage是过程的百分比,Properties是属性,value是属性值<br>一个简单的例子,红色的方块一直在旋转<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-id">#div1</span>{<br> <span class="hljs-attribute">width</span>: <span class="hljs-number">100px</span>;<br> <span class="hljs-attribute">height</span>: <span class="hljs-number">100px</span>;<br> <span class="hljs-attribute">background</span>:<span class="hljs-number">#de0010</span>;<br> -webkit-<span class="hljs-attribute">animation</span>:mymove infinite;<br> -webkit-<span class="hljs-attribute">animation-duration</span>:<span class="hljs-number">2s</span>;<br>}<br><span class="hljs-keyword">@-webkit-keyframes</span> mymove{<br> <span class="hljs-number">0%</span> {<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">0</span>); <br> }<br> <span class="hljs-number">25%</span> {<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">45deg</span>); <br> }<br> <span class="hljs-number">50%</span> {<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">180deg</span>); <br> }<br> <span class="hljs-number">75%</span> {<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">225deg</span>); <br> }<br> <span class="hljs-number">100%</span> {<br> <span class="hljs-attribute">transform</span>: <span class="hljs-built_in">rotate</span>(<span class="hljs-number">360deg</span>); <br> }<br>}<br></code></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag>css3</tag>
<tag>动画效果</tag>
</tags>
</entry>
<entry>
<title>js 数组去重复的两三事</title>
<link href="/2018/10/17/js-array/"/>
<url>/2018/10/17/js-array/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>js 数组去重复是我们经常会遇到的问题,今天再次看到有人问,第一时间就是想到es6里的set的一个特性,通过这个特性到达数组去重复的效果<br>可是仔细想想,并不知道底层的实现原理是怎样的,也不知道算法效率,也不知道这个的比较是 == 还是 ===<br>所以今天来探究一下js数组去重复</p><h3 id="方法一:es6方式去重复"><a href="#方法一:es6方式去重复" class="headerlink" title="方法一:es6方式去重复"></a>方法一:es6方式去重复</h3><p>es6 set数据结构的特性就是 成员的值是唯一的,没有重复值<br>通过数组构建set数据结构的时候自动去重复,然后再转回数组的时候就完成了数组去重复的工作<br><figure class="highlight js"><table><tr><td class="gutter"><div class="code-wrapper"><pre><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></div></td><td class="code"><pre><code class="hljs js"><span class="hljs-keyword">let</span> arr = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">34</span>,<span class="hljs-number">41</span>,<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">34</span>];<br><span class="hljs-keyword">let</span> set = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>(arr);<br><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Array</span>.from(set));<br><span class="hljs-comment">// [1, 2, 34, 41]</span><br></code></pre></td></tr></table></figure><br>想要知道这个去重复的效果是 == 还是 ===,则需要看set的成员值唯一性<br>1 与 “1” 是不同的<br>obj 与 obj是不同的<br>NaN 与 NaN是相同的(在set里)<br><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><code class="hljs js"><span class="hljs-keyword">let</span> set = <span class="hljs-keyword">new</span> <span class="hljs-built_in">Set</span>();<br><span class="hljs-keyword">let</span> a = <span class="hljs-literal">NaN</span>;<br><span class="hljs-keyword">let</span> b = <span class="hljs-literal">NaN</span>;<br>set.add(a);<br>set.add(b);<br>set <span class="hljs-comment">// Set {NaN}</span><br></code></pre></td></tr></table></figure></p><h3 id="方法二-object-keys的去重方式"><a href="#方法二-object-keys的去重方式" class="headerlink" title="方法二 object keys的去重方式"></a>方法二 object keys的去重方式</h3><p>在纯数字的数组中进行去重复的操作,可以用以下方法<br>只是最后需要将其再转回数字类型<br>这种方法的去重复<br>1 与 “1” 是相同的<br>NaN 与 NaN 是相同的<br>对象 与对象是相同的<br><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><code class="hljs js">arr = [<span class="hljs-number">1</span>,<span class="hljs-number">2</span>,<span class="hljs-number">3</span>,<span class="hljs-number">45</span>,<span class="hljs-number">1</span>]<br><span class="hljs-keyword">let</span> object = {}<br>arr.map(<span class="hljs-function"><span class="hljs-params">item</span> =></span> {<br> object[item] = <span class="hljs-string">''</span>;<br>})<br><span class="hljs-built_in">console</span>.log(<span class="hljs-built_in">Object</span>.keys(object));<br><span class="hljs-comment">// ["1", "2", "3", "45"]</span><br></code></pre></td></tr></table></figure></p><h3 id="方法三:-array-inclues-去重方式"><a href="#方法三:-array-inclues-去重方式" class="headerlink" title="方法三: array inclues 去重方式"></a>方法三: array inclues 去重方式</h3><p>遍历数组将数组,判断是否存在新组中,不存在则添加<br>1 与 “1” 不同<br>NaN 与 NaN 相同<br>对象与对象不同<br><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><code class="hljs js">arr = [<span class="hljs-number">1</span>,<span class="hljs-literal">NaN</span>,<span class="hljs-number">3</span>,<span class="hljs-number">45</span>,<span class="hljs-literal">NaN</span>, <span class="hljs-number">1</span>, <span class="hljs-number">3</span>]<br><span class="hljs-keyword">let</span> start = <span class="hljs-built_in">Date</span>.now()<br><span class="hljs-keyword">let</span> result = []<br>arr.map(<span class="hljs-function"><span class="hljs-params">item</span> =></span> {<br> result.includes(item) ? <span class="hljs-literal">null</span> : result.push(item)<br>});<br><span class="hljs-comment">// [1, NaN, 3, 45]</span><br></code></pre></td></tr></table></figure></p><h3 id="测试10万数组去重复的性能时间"><a href="#测试10万数组去重复的性能时间" class="headerlink" title="测试10万数组去重复的性能时间"></a>测试10万数组去重复的性能时间</h3><p>数组生成代码<br><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><code class="hljs js"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">generateRandomArray</span> (<span class="hljs-params"></span>) </span>{<br> <span class="hljs-keyword">let</span> arr = []<br> <span class="hljs-keyword">let</span> i = <span class="hljs-number">100000</span><br> <span class="hljs-keyword">while</span>(i >= <span class="hljs-number">0</span>){<br> arr.push(<span class="hljs-built_in">Math</span>.ceil(<span class="hljs-built_in">Math</span>.random() * <span class="hljs-number">10000</span>))<br> i--<br> }<br> <span class="hljs-keyword">return</span> arr<br>}<br></code></pre></td></tr></table></figure><br>方法一时间:5ms<br>方法二时间:15ms<br>方法三时间:550ms</p><p>由次可见 1 > 2 > 3 性能效率</p><h3 id="continue…"><a href="#continue…" class="headerlink" title="continue…"></a>continue…</h3><p>疑问</p><ul><li>性能是 N, logN 还是其他 值得去探究</li><li>set 构造函数的处理方法原理</li></ul>]]></content>
<tags>
<tag>javascript</tag>
</tags>
</entry>
<entry>
<title>edx-theme</title>
<link href="/2018/10/16/edx-theme/"/>
<url>/2018/10/16/edx-theme/</url>
<content type="html"><![CDATA[<h2 id="edx主题相关教程"><a href="#edx主题相关教程" class="headerlink" title="edx主题相关教程"></a>edx主题相关教程</h2><h3 id="第一步当然是阅读官方文档-地址"><a href="#第一步当然是阅读官方文档-地址" class="headerlink" title="第一步当然是阅读官方文档 地址"></a>第一步当然是阅读官方文档 <a href="https://edx.readthedocs.io/projects/edx-installing-configuring-and-running/en/latest/configuration/changing_appearance/theming/index.html">地址</a></h3><h3 id="对edx原有主题的更换"><a href="#对edx原有主题的更换" class="headerlink" title="对edx原有主题的更换"></a>对edx原有主题的更换</h3><p>查看目前edx原来有的主题目录是 /themes<br>该目录下有以下内容<br><figure class="highlight stylus"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs stylus">├── conf<br>├── dark-theme<br>├── edge<span class="hljs-selector-class">.edx</span><span class="hljs-selector-class">.org</span><br>├── edx<span class="hljs-selector-class">.org</span><br>├── mytheme<br>├── open-edx<br>├── red-theme<br>└── stanford-style<br></code></pre></td></tr></table></figure><br>了解了这些内容后开始进行edx主题的切换[以修改studio为例子]</p><h4 id="第一步:修改环境变量-()"><a href="#第一步:修改环境变量-()" class="headerlink" title="第一步:修改环境变量 ()"></a>第一步:修改环境变量 ()</h4><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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">cd</span> devstack <span class="hljs-comment"># 进入devstack目录</span><br>make dev.up <span class="hljs-comment"># 启动服务器</span><br>docker ps <span class="hljs-comment"># 查看 edx.devstack.studio 的容器ID</span><br>docker <span class="hljs-built_in">exec</span> -it containerid /bin/bash <span class="hljs-comment"># 进入容器</span><br><span class="hljs-built_in">cd</span> /edx/app/edxapp <span class="hljs-comment"># 进入文件目录</span><br>vim cms.env.json<br><span class="hljs-comment"># 修改环境变量文件 ENABLE_COMPREHENSIVE_THEMING: true</span><br><span class="hljs-comment"># "COMPREHENSIVE_THEME_DIRS": ["/edx/app/edxapp/edx-platform/themes"],</span><br><span class="hljs-comment"># "THEME_NAME": "red-theme"</span><br></code></pre></td></tr></table></figure><p>环境变量至此配置完毕</p><h3 id="第二步:重启服务并更新静态资源"><a href="#第二步:重启服务并更新静态资源" class="headerlink" title="第二步:重启服务并更新静态资源"></a>第二步:重启服务并更新静态资源</h3><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><code class="hljs bash"><span class="hljs-built_in">cd</span> devstack <br>docker-compose restart<br>make studio-shell <span class="hljs-comment"># 进入studio的shell 以便执行paver 指令</span><br>paver update_assets cms --settings=devstack_docker <span class="hljs-comment"># 编译静态资源</span><br></code></pre></td></tr></table></figure><p>如果出现访问不成功的情况可以 <code>make studio-logs</code> 查看相应服务器的日志信息</p><h3 id="第三步:登录后台配置"><a href="#第三步:登录后台配置" class="headerlink" title="第三步:登录后台配置"></a>第三步:登录后台配置</h3><p>访问 <code>http://0.0.0.0:18010/admin</code> 账号密码 edx/edx 登录<br>找到 Site themes 选项 点击进入<br>点击 ADD SITE THEME 按钮添加网站主题<br>选择目标域名,如果没有需要手动添加,点击+号填写表单 域名0.0.0.0:18010,名字studio<br>填写主题目录 red-theme<br>点击save 保存成功<br>访问配置主题的目标网站这里是(<a href="http://0.0.0.0:18010),刷新页面发现主题配置成功!">http://0.0.0.0:18010),刷新页面发现主题配置成功!</a></p><h2 id="回顾难点"><a href="#回顾难点" class="headerlink" title="回顾难点"></a>回顾难点</h2><ul><li>需要了解docker 相关知识,edx相关配置基础</li><li>文档不够详尽,需要有一定知识才能够配置</li><li>出错信息没有明显提醒,需要了解相关知识才能自发通过日志系统查阅</li></ul>]]></content>
<tags>
<tag>edx</tag>
</tags>
</entry>
<entry>
<title>阿里云服务器网站配置https</title>
<link href="/2018/09/07/https/"/>
<url>/2018/09/07/https/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>https的普及,还有ca证书有免费的申请,让笔者兴起了给服务器增加https协议<br>学习与实现 配置https</p><h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><p>Let’s Encrypt作为一个公共且免费SSL的项目逐渐被广大用户传播和使用,是由Mozilla、Cisco、Akamai、IdenTrust、EFF等组织人员发起,主要的目的也是为了推进网站从HTTP向HTTPS过度的进程,目前已经有越来越多的商家加入和赞助支持。<br>目前,默认的免费证书是90天,不过官方有相对应的证书生成客户端。只需要相应配置,即可做到证书一直有效<br>通过cerbot脚本自动生产安装证书,这里的例子是 阿里云上centos7.4 nginx服务器配置的https</p><h4 id="安装cerbot与服务器配置-参考地址"><a href="#安装cerbot与服务器配置-参考地址" class="headerlink" title="安装cerbot与服务器配置 (参考地址)"></a>安装cerbot与服务器配置 (<a href="https://www.yuzhi100.com/article/centos-7-install-lets-encrypt-certbot">参考地址</a>)</h4><p>这里用virtualenv的方式安装</p><ul><li>安装虚拟环境软件包(针对于Python2.7) <code>sudo yum install python-virtualenv</code></li><li>创建虚拟环境 <code>sudo virtualenv /usr/local/python-certbot</code></li><li>进入虚拟环境 <code>source /usr/local/python-certbot/bin/activate</code></li><li>更新pip <code>pip install --upgrade pip</code></li><li>安装cerbot <code>pip install certbot</code></li><li>安装 cerbot nginx 插件(如果使用其他server可安装对应插件)<code>pip install certbot-nginx</code></li><li>已有nginx服务器下,只需要一键 <code>sudo certbot --nginx</code>, 脚本会自动生成证书与修改nginx 配置</li></ul><p>至此,正常流程下,对应的 https://域名 可以访问到服务了</p><h4 id="问题流程"><a href="#问题流程" class="headerlink" title="问题流程"></a>问题流程</h4><p><strong>安装问题</strong><br>参考教程中给出两个常见的安装问题</p><ul><li><a href="https://www.yuzhi100.com/article/centos-7-install-certbot-python-urllib3-failure">python-urllib3安装失败</a></li><li><a href="https://www.yuzhi100.com/article/centos-7-certbot-pyopenssl-missing-required-functionality">pyOpenSSL错误</a><br>笔者在实践的过程中,也遇到了pyOpenSSL版本问题与其他依赖版本问题,网上参考了一些资料都说,跟着提示走,有什么更新就更新就可以了<br><code>pip update xxxxx</code><br>其中可能会遇到无法更新的问题,我是通过yum erase pyOpenSSL 重新下载才完成(环境依赖的问题错误可能会比较多,不同服务器的问题也不太一样)</li></ul><p><strong>端口问题</strong><br>cerbot –nginx配置好了后,访问地址,发现无法访问,可能以下指令排除下问题</p><ul><li>查看nginx是否监听了443端口服务: <code>netstat -ntl</code>, 如果有可以看到443端口对应有nginx服务器</li><li>查看防火墙是否开放443端口:<code>firewall-cmd --list-ports</code>, 查看443/tcp 是否存在</li><li>(重要)阿里云服务器安全策略组是否开放443端口:需要登录到控制台里,安全策略组,添加对应规则443/443端口</li></ul><p>可能需要用到的相关指令<br><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">nginx -t <span class="hljs-comment"># 测试nginx配置问题</span><br>nginx -s reload <span class="hljs-comment"># 重启nginx服务器</span><br>nginx -s stop <span class="hljs-comment"># 关闭nginx服务器</span><br></code></pre></td></tr></table></figure></p>]]></content>
<tags>
<tag>https</tag>
<tag>server</tag>
<tag>阿里云</tag>
</tags>
</entry>
<entry>
<title>docker初步学习</title>
<link href="/2018/09/03/docker/"/>
<url>/2018/09/03/docker/</url>
<content type="html"><![CDATA[<h3 id="什么是docker?"><a href="#什么是docker?" class="headerlink" title="什么是docker?"></a>什么是docker?</h3><p>Docker 项目的目标是实现轻量级的操作系统虚拟化解决方案。 Docker 的基础是 Linux 容器(LXC)等技术。在 LXC 的基础上 Docker 进行了进一步的封装,让用户不需要去关心容器的管理,使得操作更为简便。用户操作 Docker 的容器就像操作一个快速轻量级的虚拟机一样简单。</p><h3 id="为什么用docker"><a href="#为什么用docker" class="headerlink" title="为什么用docker?"></a>为什么用docker?</h3><ul><li><span data-type="color" style="color:#1890FF">更快速的交付和部署</span><ul><li>Docker在整个开发周期都可以完美的辅助你实现快速交付。Docker允许开发者在装有应用和服务本地容器做开发。可以直接集成到可持续开发流程中。</li></ul></li><li><span data-type="color" style="color:#1890FF">高效的部署和扩容</span><ul><li>Docker 容器几乎可以在任意的平台上运行,包括物理机、虚拟机、公有云、私有云、个人电脑、服务器等。 这种兼容性可以让用户把一个应用程序从一个平台直接迁移到另外一个。</li><li>Docker的兼容性和轻量特性可以很轻松的实现负载的动态管理。你可以快速扩容或方便的下线的你的应用和服务,这种速度趋近实时。</li></ul></li><li><span data-type="color" style="color:#1890FF">更高的资源利用率</span><ul><li>Docker 对系统资源的利用率很高,一台主机上可以同时运行数千个 Docker 容器。容器除了运行其中应用外,基本不消耗额外的系统资源,使得应用的性能很高,同时系统的开销尽量小。传统虚拟机方式运行 10 个不同的应用就要起 10 个虚拟机,而Docker 只需要启动 10 个隔离的应用即可。</li></ul></li><li><span data-type="color" style="color:#1890FF">更简单的管理</span><ul><li>使用 Docker,只需要小小的修改,就可以替代以往大量的更新工作。所有的修改都以增量的方式被分发和更新,从而实现自动化并且高效的管理。</li></ul></li></ul><h3 id="Docker引擎"><a href="#Docker引擎" class="headerlink" title="Docker引擎"></a>Docker引擎</h3><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002521.png" alt="image.png | left | 492x385"></p><ul><li>Server是一个常驻进程</li><li>REST API实现了client和server间的交互协议</li><li>CLI 实现了容器和镜像的管理,为用户提供统一的操作界面</li></ul><h3 id="docker核心概念"><a href="#docker核心概念" class="headerlink" title="docker核心概念"></a>docker核心概念</h3><ul><li>镜像(images)只读模版,用来创建Docker容器</li><li>仓库(repository)集中存放镜像文件的场所,仓库分公有仓库和私有仓库。最大的仓库是Docker Hub</li><li>容器(container)</li></ul><h3 id="docker常用命令"><a href="#docker常用命令" class="headerlink" title="docker常用命令"></a>docker常用命令</h3><ul><li>docker pull <a href="images:tag">images:tag</a></li><li>docker images</li><li>docker ps [-a|-l]<ul><li>列出正在运行的docker容器</li><li>-a 列出所有已构建的docker容器</li><li>-l <span data-type="color" style="color:rgb(51, 51, 51)"><span data-type="background" style="background-color:rgb(255, 255, 255)">显示最近创建的容器</span></span></li></ul></li><li>docker rm <container_id><h3 id="第一个docker应用"><a href="#第一个docker应用" class="headerlink" title="第一个docker应用"></a>第一个docker应用</h3>可以发现构建一个docker应用的指令非常的简单<figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">docker pull hello-world<br>docker images <span class="hljs-comment">#列出所有images,发现hello-world 镜像被拉到本地了</span><br>docker run hello-world <span class="hljs-comment">#查看输出了logs信息</span><br></code></pre></td></tr></table></figure><h3 id="Dockerfile构建镜像"><a href="#Dockerfile构建镜像" class="headerlink" title="Dockerfile构建镜像"></a>Dockerfile构建镜像</h3>镜像可以通过pull从仓库中拉取别人已经做好的镜像,也可以自己制作镜像<br>通过Dockerfile可以构建属于自己的docker镜像<h4 id="构建镜像的相关知识"><a href="#构建镜像的相关知识" class="headerlink" title="构建镜像的相关知识"></a>构建镜像的相关知识</h4></li><li>Dockerfile文件</li><li>docker build指令<ul><li>docker build -t crews/example . </li><li>-t 是tag, . 是dockerfile的目录</li></ul></li><li>docker push【push到dokcer仓库】<h4 id="Dockerfile指令"><a href="#Dockerfile指令" class="headerlink" title="Dockerfile指令"></a>Dockerfile指令</h4></li><li>CMD: CMD指令用于指定一个容器启动时要运行的命令。这有点儿类似于RUN指令,只是RUN指令是指定镜像被构建时要运行的命令,而CMD是指定容器被启动时要运行的命令。</li><li>ENTRYPOINT:</li><li>WORKDIR:WORKDIR指令用来在从镜像创建一个新容器时,在容器内部设置一个工作目录,ENTRYPOINT和/或CMD指定的程序会在这个目录下执行。</li><li>RUN: 在工作目录下执行命令</li><li>ENV:在dockerfile文件中设置环境变量,在后续任意的RUN指令中可以使用</li><li>VOLUME:设置卷(类似共享目录)【】</li><li>ADD:会将指定文件 添加到镜像中目录下<h4 id="Example"><a href="#Example" class="headerlink" title="Example"></a>Example</h4><figure class="highlight plaintext"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs plain">FROM ubuntu:14.04<br>MAINTAINER crews "[email protected]"<br>ENV REFRESHED_AT 2018-08-30<br>RUN apt-get -yqq update && apt-get -yqq install nginx<br>RUN mkdir -p /var/www/html/website<br>ADD nginx/global.conf /etc/nginx/conf.d/<br>ADD nginx/nginx.conf /etc/nginx/nginx.conf<br>EXPOSE 80<br></code></pre></td></tr></table></figure>构建镜像:<code>sudo docker build -t crews/test .</code><br>推送镜像到仓库:<code>docker push crews/test</code><br>通过镜像创建容器:<br><code>sudo docker run -d -p 80 --name website -v $PWD/website:/home/www crews/test nginx</code><br>创建了容器后下次启动,只需要 docker start <container_id | name><br><strong>简单理解docker run</strong></li><li>docker create (利用镜像创建容器)</li><li>docker start (启动该容器)</li></ul>]]></content>
<tags>
<tag>docker</tag>
</tags>
</entry>
<entry>
<title>redux 原理理解 & 源码阅读 (一)</title>
<link href="/2018/08/22/redux/"/>
<url>/2018/08/22/redux/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>react-redux在实现react前端项目中使用还是比较频繁的,虽然用了比较多次。但是对原理的理解还是不够深入,所以今天回顾下redux。</p><h3 id="redux"><a href="#redux" class="headerlink" title="redux"></a>redux</h3><p>redux是什么,为什么要用redux,这应该是许多初步接触redux开发者的疑问<br>这里摘抄官方文档的说明 </p><h4 id="what"><a href="#what" class="headerlink" title="what"></a>what</h4><blockquote><p>Redux 是 JavaScript 状态容器,提供可预测化的状态管理。 </p></blockquote><h4 id="why"><a href="#why" class="headerlink" title="why"></a>why</h4><blockquote><p>随着 JavaScript 单页应用开发日趋复杂,JavaScript 需要管理比任何时候都要多的 state。(状态)state 在什么时候,由于什么原因,如何变化已然不受控制。</p></blockquote><p>redux通过一些原则限制状态更新发生的时间和方式,让这些变化变得可测</p><h3 id="三大原则"><a href="#三大原则" class="headerlink" title="三大原则"></a>三大原则</h3><ul><li>单一数据源</li><li>state是只读</li><li>使用纯函数来执行(reducer修改state状态,reducer是纯函数)</li></ul><h3 id="数据流"><a href="#数据流" class="headerlink" title="数据流"></a>数据流</h3><p>严格的单向数据流是 Redux 架构的设计核心。<br>redux 应用中数据的生命周期(数据流动)</p><ul><li>调用 store.dispatch(action)</li><li>Redux store 调用传入的 reducer 函数。</li><li>根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。</li><li>Redux store 保存了根 reducer 返回的完整 state 树。</li></ul><h3 id="redux源码阅读"><a href="#redux源码阅读" class="headerlink" title="redux源码阅读"></a>redux源码阅读</h3><figure class="highlight javascript"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></div></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">dispatch</span>(<span class="hljs-params">action</span>) </span>{<br> <span class="hljs-keyword">if</span> (!isPlainObject(action)) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<br> <span class="hljs-string">'Actions must be plain objects. '</span> +<br> <span class="hljs-string">'Use custom middleware for async actions.'</span><br> )<br> }<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> action.type === <span class="hljs-string">'undefined'</span>) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<br> <span class="hljs-string">'Actions may not have an undefined "type" property. '</span> +<br> <span class="hljs-string">'Have you misspelled a constant?'</span><br> )<br> }<br><br> <span class="hljs-keyword">if</span> (isDispatching) {<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(<span class="hljs-string">'Reducers may not dispatch actions.'</span>)<br> }<br><br> <span class="hljs-keyword">try</span> {<br> isDispatching = <span class="hljs-literal">true</span><br> currentState = currentReducer(currentState, action)<br> } <span class="hljs-keyword">finally</span> {<br> isDispatching = <span class="hljs-literal">false</span><br> }<br><br> <span class="hljs-keyword">const</span> listeners = (currentListeners = nextListeners)<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < listeners.length; i++) {<br> <span class="hljs-keyword">const</span> listener = listeners[i]<br> listener()<br> }<br><br> <span class="hljs-keyword">return</span> action<br> }<br></code></pre></td></tr></table></figure><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">createStore</span>(<span class="hljs-params">reducer, preloadedState, enhancer</span>)</span>{<br> <span class="hljs-keyword">let</span> currentReducer = reducer;<br> ....<br>}<br></code></pre></td></tr></table></figure><p>dispatch 将 currentState传入 currentReducer,执行currentReducer函数,<br>currentReducer是createStore时候传入的reducer<br>所以可以看出dispatch是这里将action传递给reducer </p><h4 id="疑问:dispatch的匹配原理(或者说action与reducer的匹配原理)是什么?"><a href="#疑问:dispatch的匹配原理(或者说action与reducer的匹配原理)是什么?" class="headerlink" title="疑问:dispatch的匹配原理(或者说action与reducer的匹配原理)是什么?"></a><strong>疑问:dispatch的匹配原理(或者说action与reducer的匹配原理)是什么?</strong></h4><p>开始我理解的就是,每次dispatch一个action,会只走到对应reducer中,去匹配对应的action执行相应代码,然后我将官方例子的代码修改了一下,去验证是否对的<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><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><code class="hljs javascript"><span class="hljs-keyword">let</span> id = <span class="hljs-number">1000</span>;<span class="hljs-comment">// 增加的</span><br><span class="hljs-keyword">const</span> todos = <span class="hljs-function">(<span class="hljs-params">state = [], action</span>) =></span> {<br> <span class="hljs-built_in">console</span>.log(<span class="hljs-string">'todos'</span>,state)<br> <span class="hljs-keyword">switch</span> (action.type) {<br> <span class="hljs-keyword">case</span> <span class="hljs-string">'ADD_TODO'</span>:<br> <span class="hljs-keyword">return</span> [<br> ...state,<br> {<br> <span class="hljs-attr">id</span>: action.id,<br> <span class="hljs-attr">text</span>: action.text,<br> <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span><br> }<br> ]<br> <span class="hljs-keyword">case</span> <span class="hljs-string">'TOGGLE_TODO'</span>:<br> <span class="hljs-keyword">return</span> state.map(<span class="hljs-function"><span class="hljs-params">todo</span> =></span><br> (todo.id === action.id) <br> ? {...todo, <span class="hljs-attr">completed</span>: !todo.completed}<br> : todo<br> )<br> <span class="hljs-comment">//修改的</span><br> <span class="hljs-attr">default</span>:<br> <span class="hljs-keyword">return</span> [<br> ...state,<br> {<br> <span class="hljs-attr">id</span>: id++,<br> text: <span class="hljs-string">`default <span class="hljs-subst">${id++}</span>`</span>,<br> <span class="hljs-attr">completed</span>: <span class="hljs-literal">false</span><br> }<br> ]<br> }<br> }<br> <br> <span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> todos<br></code></pre></td></tr></table></figure><br>结果就是dispatch非 ADD_TODO or TOGGLE_TODO的时候,也会增加todo任务<br>就证明了我的猜想是错误的,dispatch给reducer后,reducer会将这个action匹配给它下面所有子reducer,这也说明了为什么action type不能重复命名的原因<br>还有规范的开发中,需要用actionType.js,用常量定义actiontype,方便协同开发避免出错</p><h4 id="疑问:数据流的第三步,“根-reducer-应该把多个子-reducer-输出合并成一个单一的-state-树。”-这里怎么理解,store-tree是怎么形成的"><a href="#疑问:数据流的第三步,“根-reducer-应该把多个子-reducer-输出合并成一个单一的-state-树。”-这里怎么理解,store-tree是怎么形成的" class="headerlink" title="疑问:数据流的第三步,“根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。” 这里怎么理解,store tree是怎么形成的"></a><strong>疑问:数据流的第三步,“根 reducer 应该把多个子 reducer 输出合并成一个单一的 state 树。” 这里怎么理解,store tree是怎么形成的</strong></h4><p>阅读源码 combineReducer<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><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></pre></td><td class="code"><pre><code class="hljs javascript">....<br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combineReducers</span>(<span class="hljs-params">reducers</span>) </span>{<br> <span class="hljs-keyword">const</span> reducerKeys = <span class="hljs-built_in">Object</span>.keys(reducers)<br> <span class="hljs-keyword">const</span> finalReducers = {}<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < reducerKeys.length; i++) {<br> <span class="hljs-keyword">const</span> key = reducerKeys[i]<br><br> <span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'production'</span>) {<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> reducers[key] === <span class="hljs-string">'undefined'</span>) {<br> warning(<span class="hljs-string">`No reducer provided for key "<span class="hljs-subst">${key}</span>"`</span>)<br> }<br> }<br><br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> reducers[key] === <span class="hljs-string">'function'</span>) {<br> finalReducers[key] = reducers[key]<br> }<br> }<br> <span class="hljs-keyword">const</span> finalReducerKeys = <span class="hljs-built_in">Object</span>.keys(finalReducers)<br><br> <span class="hljs-keyword">let</span> unexpectedKeyCache<br> <span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'production'</span>) {<br> unexpectedKeyCache = {}<br> }<br><br> <span class="hljs-keyword">let</span> shapeAssertionError<br> <span class="hljs-keyword">try</span> {<br> assertReducerShape(finalReducers)<br> } <span class="hljs-keyword">catch</span> (e) {<br> shapeAssertionError = e<br> }<br><br> <span class="hljs-keyword">return</span> <span class="hljs-function"><span class="hljs-keyword">function</span> <span class="hljs-title">combination</span>(<span class="hljs-params">state = {}, action</span>) </span>{<br> <span class="hljs-keyword">if</span> (shapeAssertionError) {<br> <span class="hljs-keyword">throw</span> shapeAssertionError<br> }<br><br> <span class="hljs-keyword">if</span> (process.env.NODE_ENV !== <span class="hljs-string">'production'</span>) {<br> <span class="hljs-keyword">const</span> warningMessage = getUnexpectedStateShapeWarningMessage(<br> state,<br> finalReducers,<br> action,<br> unexpectedKeyCache<br> )<br> <span class="hljs-keyword">if</span> (warningMessage) {<br> warning(warningMessage)<br> }<br> }<br><br> <span class="hljs-keyword">let</span> hasChanged = <span class="hljs-literal">false</span><br> <span class="hljs-keyword">const</span> nextState = {}<br> <span class="hljs-keyword">for</span> (<span class="hljs-keyword">let</span> i = <span class="hljs-number">0</span>; i < finalReducerKeys.length; i++) {<br> <span class="hljs-keyword">const</span> key = finalReducerKeys[i]<br> <span class="hljs-keyword">const</span> reducer = finalReducers[key]<br> <span class="hljs-keyword">const</span> previousStateForKey = state[key]<br> <span class="hljs-keyword">const</span> nextStateForKey = reducer(previousStateForKey, action)<br> <span class="hljs-keyword">if</span> (<span class="hljs-keyword">typeof</span> nextStateForKey === <span class="hljs-string">'undefined'</span>) {<br> <span class="hljs-keyword">const</span> errorMessage = getUndefinedStateErrorMessage(key, action)<br> <span class="hljs-keyword">throw</span> <span class="hljs-keyword">new</span> <span class="hljs-built_in">Error</span>(errorMessage)<br> }<br> nextState[key] = nextStateForKey<br> hasChanged = hasChanged || nextStateForKey !== previousStateForKey<br> }<br> <span class="hljs-keyword">return</span> hasChanged ? nextState : state<br> }<br>}<br></code></pre></td></tr></table></figure><br>可以看到combineReducers将reducers的每个子节点(key)都获取出来 reducerKeys<br>中途校验子节点,最后得到 finalReducerKeys 最终的一个节点列表。<br>然后循环这个节点列表,构造state tree,以下这两句就是构造state tree的过程<br><code>const nextState = {}</code> 与 <code>nextState[key] = nextStateForKey</code><br>但是你会发现,在createStore后,执行store.getState(),也能输出完整的state tree为何呢<br>查看源码发现在createStore.js中有 dispatch({ type: ActionTypes.INIT }),完成初始化的过程</p><h3 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h3><p>这次回顾redux,收获的不仅是对redux原理的理解,还有学习源码的方式,因为很多源码晦涩难懂<br>而且读着读着就走偏,不是所有都是你关心的内容,所以认识到带着问题去阅读源码是比较高效的一种方式<br>下面是我这次源码阅读的思路</p><h4 id="问题驱动型阅读源码方式"><a href="#问题驱动型阅读源码方式" class="headerlink" title="问题驱动型阅读源码方式"></a>问题驱动型阅读源码方式</h4><p>在重新阅读redux 中文文档的时候,有了以下的疑问,发现文档理也没有深入说明所以就带着疑问驱动去阅读框架的源码</p><ul><li>理解redux的数据流提出疑问 </li><li>对store tree 形成的好奇探究 => combineReducer</li><li>对dispatch 匹配规则的深究 => createStore.dispatch </li></ul>]]></content>
<tags>
<tag>redux</tag>
<tag>源码</tag>
</tags>
</entry>
<entry>
<title>nginx 学习笔记(一)</title>
<link href="/2018/08/12/nginx-server-name/"/>
<url>/2018/08/12/nginx-server-name/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><blockquote><p>Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。<br>在前端的开发过程中,经常会遇到一些跨域的问题,这时候可能就需要到配置nginx,做一个反向代理<br>既然nginx这么有用,让我们开始学习如何使用</p></blockquote><h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><p>如何快速开始nginx的使用呢<br>在mac电脑上,可以通过brew快速安装 <code>brew install nginx</code><br>在终端命令行中 nginx 就可以启动了,一个默认的nginx服务器</p><h3 id="指令"><a href="#指令" class="headerlink" title="指令"></a>指令</h3><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs bash">nginx -t // 检查配置是否正确<br>nginx -s reload // 重启nginx服务器<br>nginx -s stop // 关闭nginx服务器<br></code></pre></td></tr></table></figure><h3 id="配置"><a href="#配置" class="headerlink" title="配置"></a>配置</h3><p>默认配置文件的目录 <code>/usr/local/etc/nginx</code><br>默认服务器目录 <code>/usr/local/var/www</code></p><h3 id="反向代理"><a href="#反向代理" class="headerlink" title="反向代理"></a>反向代理</h3><p>日常开发过程中,会有遇到在<code>localhost:8080</code>域名下,跨域请求<code>xxx.xxx.com</code>下的接口<br>如果后端不做配置的话,<br>前端也可以通过反向代理,请求成功<br>打开 <code>/usr/local/etc/nginx/nginx.conf</code>文件<br>在http{}中增加 server{}模块<br>具体设置:<br><figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs crmsh">server{<br> listen <span class="hljs-number">80</span>;<br> server_name servername.com;<br> <span class="hljs-keyword">location</span> <span class="hljs-title">/ {</span><br><span class="hljs-title"> proxy_pass</span> localhost:<span class="hljs-number">8080</span><br> }<br> <span class="hljs-keyword">location</span> <span class="hljs-title">/api</span> {<br> proxy_pass http://xxx.xxx.com<br> }<br>}<br></code></pre></td></tr></table></figure><br>这样访问 <code>servername.com</code>的时候,直接获取到<code>localhost:8080</code>的资源,当访问<code>servername.com/api</code> 下的接口的时候,会被代理转发到 <code>htto://xxx.xxx.com</code>,这样就解决了跨域问题</p><h3 id="奇怪的“BUG”"><a href="#奇怪的“BUG”" class="headerlink" title="奇怪的“BUG”"></a>奇怪的“BUG”</h3><p>server_name 配置问题<br>场景:</p><p>配置了nginx server ,监听了端口,设置好server_name 后,第一次访问这个server_name是生效的</p><p>再次修改这个server_name后,第二次设置的server_name不生效,且第一次设置的生效</p><p>假定第一次设置 test.com 第二次设置abc.com</p><p>修改配置后 test.com还生效,abc.com不生效</p><p>最后查了许多资料,发现是server_name的问题</p><p>因为修改location的配置的时候,这个配置是生效的,只是域名不匹配</p><p>最后得出解决办法是</p><p>配置一个新的server_name的时候</p><p>需要修改hosts文件</p><p><code>127.0.0.1 server_name</code></p><p>配置好了以后,发现访问server_name 有效</p>]]></content>
<tags>
<tag>nginx</tag>
<tag>server</tag>
</tags>
</entry>
<entry>
<title>edx-devstack搭建</title>
<link href="/2018/07/31/edx-devstack/"/>
<url>/2018/07/31/edx-devstack/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a><a name="0wimyg"></a>前言</h2><p>edx devstack搭建的过程也不是一帆风顺的,特别笔者是一名前端工程师,爬了许多坑<br>接下来,笔者就以一个前端开发者的身份去搭建 open edx devstack</p><h2 id="起步"><a href="#起步" class="headerlink" title="起步"></a><a name="i0zovs"></a>起步</h2><p>环境配置</p><ul><li>Mac OS</li><li><a href="https://docs.docker.com/docker-for-mac/">Docker for Mac</a></li><li>安装 pip、make<br>pip安装 <code>sudo easy_install pip</code><h4 id="第一步"><a href="#第一步" class="headerlink" title="第一步"></a><a name="nofihb"></a>第一步</h4>克隆代码到本地<br><code>git clone [email protected]:edx/devstack.git</code><br><code>cd devstack</code><h4 id="第二步"><a href="#第二步" class="headerlink" title="第二步"></a><a name="gfp0vp"></a>第二步</h4>打开官方的<a href="https://github.com/edx/devstack">README</a><br>按照教程走,执行以下指令<figure class="highlight vim"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></div></td><td class="code"><pre><code class="hljs vim"><span class="hljs-keyword">make</span> down<br><span class="hljs-keyword">make</span> pull<br><span class="hljs-keyword">make</span> dev.<span class="hljs-keyword">up</span><br></code></pre></td></tr></table></figure></li></ul><h4 id="第三步"><a href="#第三步" class="headerlink" title="第三步"></a><a name="d2a7pr"></a>第三步</h4><ul><li>安装 <span data-type="color" style="color:rgb(36, 41, 46)"><span data-type="background" style="background-color:rgb(255, 255, 255)"> </span></span><a href="http://docs.python-guide.org/en/latest/dev/virtualenvs/#lower-level-virtualenv">Python virtualenv</a> 配置虚拟环境</li><li>设置docker的配置 2G cpu 6G内存,这里不设置 docker会卡死(踩坑)<blockquote><p><strong>NOTE:</strong><span data-type="color" style="color:rgb(36, 41, 46)"><span data-type="background" style="background-color:rgb(255, 255, 255)"> </span></span>Since a Docker-based devstack runs many containers, you should configure Docker with a sufficient amount of resources. We find that <a href="https://docs.docker.com/docker-for-mac/#/advanced">configuring Docker for Mac</a><span data-type="color" style="color:rgb(36, 41, 46)"><span data-type="background" style="background-color:rgb(255, 255, 255)"> </span></span>with a minimum of 2 CPUs and 6GB of memory works well.</p></blockquote></li><li>在目录下进入virtualenv,具体方法点击<a href="https://docs.python-guide.org/dev/virtualenvs/#lower-level-virtualenv">教程</a>(踩坑)</li><li>执行命令 <figure class="highlight crmsh"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs crmsh">make requirements<br>make dev.<span class="hljs-keyword">clone</span><br><span class="hljs-title">make</span> dev.provision<br></code></pre></td></tr></table></figure>命令全部完成后,devstack搭建完成</li><li>对应的服务列表</li></ul><table><thead><tr><th style="text-align:left">Service</th><th style="text-align:left">URL</th></tr></thead><tbody><tr><td style="text-align:left">Credentials</td><td style="text-align:left"><a href="http://localhost:18150/api/v2/">http://localhost:18150/api/v2/</a></td></tr><tr><td style="text-align:left">Catalog/Discovery</td><td style="text-align:left"><a href="http://localhost:18381/api-docs/">http://localhost:18381/api-docs/</a></td></tr><tr><td style="text-align:left">E-Commerce/Otto</td><td style="text-align:left"><a href="http://localhost:18130/dashboard/">http://localhost:18130/dashboard/</a></td></tr><tr><td style="text-align:left">LMS</td><td style="text-align:left"><a href="http://localhost:18000/">http://localhost:18000/</a></td></tr><tr><td style="text-align:left">Notes/edx-notes-api</td><td style="text-align:left"><a href="http://localhost:18120/api/v1/">http://localhost:18120/api/v1/</a></td></tr><tr><td style="text-align:left">Studio/CMS</td><td style="text-align:left"><a href="http://localhost:18010/">http://localhost:18010/</a></td></tr></tbody></table><div class="code-wrapper"><pre><code class="hljs">打开[http://localhost:18010/](http://localhost:18010/) 即可看到cms页面</code></pre></div><ul><li>退出docker关闭服务 <code>make stop</code></li></ul>]]></content>
<tags>
<tag>edx</tag>
</tags>
</entry>
<entry>
<title>open edx 前后端分离初探</title>
<link href="/2018/07/28/edx-fronted/"/>
<url>/2018/07/28/edx-fronted/</url>
<content type="html"><![CDATA[<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>opene edx 是开源的教育平台,在未来的开发计划中,open edx也在往前后端分离的方向进行。现在就来初探一下,edx前后端分离的思路<br>笔者从开始了解edx的studio-frontend前端仓库,到尝试构建,到成功搭建启动的过程,还是遇到了许多问题,有可能读者按官方的流程来走,可能会遇到挺多坑。</p><h2 id="初探edx前后端分离"><a href="#初探edx前后端分离" class="headerlink" title="初探edx前后端分离"></a>初探edx前后端分离</h2><h3 id="前提说明"><a href="#前提说明" class="headerlink" title="前提说明"></a>前提说明</h3><p>笔者搭建的环境:Mac os<br>后端server: <a href="https://github.com/edx/devstack">edx/devstack [h版本]</a><br>Docker版本: <a href="https://www.docker.com/community-edition">Version 18.06.0-ce-mac70</a></p><h3 id="安装搭建"><a href="#安装搭建" class="headerlink" title="安装搭建"></a>安装搭建</h3><p>假设devstack已搭建。如果没有搭建,请看<a href="../edx-fronted/">devstack搭建教程</a></p><p>第一步: 克隆仓库代码 <code>git clone [email protected]:edx/studio-frontend.git</code></p><p>第二步: <code>cd studio-frontend</code> 编辑 Dockerfile文件第六行<br><del>RUN npm i -g <a href="mailto:[email protected]">[email protected]</a></del><br>RUN npm i -g npm@latest // 简单来说就是将npm的版本改为最新版本</p><p>第三步: <code>make up</code></p><p>第四步: 等待启动完毕后,打开 <code>http://localhost:18011/assets.html</code>,点击 <code>Log in</code>,输入账号 <a href="mailto:[email protected]">[email protected]</a> 密码 edx 登录系统后,回到 assets页面,即可看到如下页面</p><p><img src="https://crews-note-1253247308.cos.ap-guangzhou.myqcloud.com/note/20220307002603.png" alt="image.png | left | 747x373"></p><p>至此,一个官方的studio-frontend 前端搭建起来</p><h3 id="分析"><a href="#分析" class="headerlink" title="分析"></a>分析</h3><p><strong>studio-frontend </strong>前后端分离采用的技术是<br><strong>webpack</strong> + <strong>react</strong> + <strong>redux</strong> + <strong>bootstrap</strong> + <strong>paragon</strong><br><strong>webpack</strong>是前端工程流工具<br><strong>react</strong> 与 <strong>paragon</strong>、<strong>bootstrap</strong> 构建了高级的功能组件<br><strong>redux</strong> 集中式对页面数据进行管理<br>该项目采取的是多页面多入口的打包方式,打包构建后的结果如下<br>├── accessibilityPolicy.min.css<br>├── accessibilityPolicy.min.js<br>├── accessibilityPolicy.min.js.map<br>├── assets.min.css<br>├── assets.min.js<br>├── assets.min.js.map<br>├── common.min.css<br>├── common.min.js<br>├── common.min.js.map<br>├── courseHealthCheck.min.css<br>├── courseHealthCheck.min.js<br>├── courseHealthCheck.min.js.map<br>├── courseOutlineHealthCheck.min.css<br>├── courseOutlineHealthCheck.min.js<br>├── courseOutlineHealthCheck.min.js.map<br>├── editImageModal.min.css<br>├── editImageModal.min.js<br>├── editImageModal.min.js.map<br>├── i18n<br>│ └── messages<br>│ ├── ar.json<br>│ ├── es_419.json<br>│ ├── fr.json<br>│ └── zh_CN.json<br>├── i18nMessages.min.js<br>├── i18nMessages.min.js.map<br>├── runtime.min.js<br>└── runtime.min.js.map<br>最后将js.css文件嵌入在后端模版文件中,构成页面</p><p><img src="https://cdn.nlark.com/yuque/0/2018/png/151680/1533008098233-a538f9fa-9af0-410d-844d-737157a297b6.png" alt="image.png | left | 747x380"></p><p>这样做其实还没有完全独立前后端分离,并且一些css可能会与原来系统中的css有冲突<br>官方表示在未来,理想中的前后端分离是前端代码完全静态托管在另一个server服务器中,该服务器通过REST(或<a href="http://graphql.cn/">GraphQL</a>)API 与Studio通信</p><h3 id="文件与目录"><a href="#文件与目录" class="headerlink" title="文件与目录"></a>文件与目录</h3><h4 id="目录结构"><a href="#目录结构" class="headerlink" title="目录结构"></a>目录结构</h4><p><img src="https://cdn.nlark.com/yuque/0/2018/png/151680/1533004031441-ab1ae521-9a1d-4286-bcfa-e601d8c15eb4.png" alt="image.png | left | 334x435"></p><p>package.json包相关内容</p><p><img src="https://cdn.nlark.com/yuque/0/2018/png/151680/1533005413128-8a486aee-d770-484f-baa2-720fbdc74aff.png" alt="image.png | left | 332x387"></p><p>可以看到几个比较重要的包</p><ul><li>@edx/edx-bootstrap</li><li>@edx/paragon</li><li>react、react-redux、redux-thunk</li><li>react-intl<br>在一篇介绍<a href="https://openedx.atlassian.net/wiki/spaces/FEDX/pages/548766004/Studio-Frontend+Developing+Frontend+Separate+from+Platform">studio-frontend</a> 前后端分离思路的文章中提及到 edx前后端分离采用的是react作为基础框架,在此基础上,进行前后端分离开发, paragon是官方的组件库,用来组合构建更高级的组件服务于edx平台,bootrstrap + Open edX 主题将为paragon设置样式内容。redux ,react-intl则是react生态圈中的一些插件,组合使用完成系统功能需求。<h4 id="构建调试"><a href="#构建调试" class="headerlink" title="构建调试"></a>构建调试</h4>官方的方式是构建出一个docker,在docker中配置好了环境,启动了项目,再将地址共享在主机中访问<br>API的请求则是采用了proxy代理的方式,代理转发到后端服务中<br>有特定的需求可以在webpack.dev.config中配置,例如接口地址的改变<br>笔者也尝试过,直接克隆项目代码。在项目下安装依赖包,然后开始构建调试前端代码,只不过需要对webpack proxy代理地址做一些调整</li></ul><h4 id="生产发布"><a href="#生产发布" class="headerlink" title="生产发布"></a>生产发布</h4><p>指令很简单 <code>npm run build</code><br>如果需要增加入口,则需在 webpack.common.config 的entry中相应增加<br>如果对生产发布的个性化调整,只需要对webpack进行相关配置即可</p><h3 id="结语"><a href="#结语" class="headerlink" title="结语"></a>结语</h3><p>这个只是笔者对open edx前后端分离思路的初步探索的一些总结,还有许多内容的值得去深挖。<br>将来如果对此有更深的理解和实践,再给大家进行分享<br>continue…</p>]]></content>
<tags>
<tag>edx</tag>
</tags>
</entry>
<entry>
<title>gitlab本地搭建</title>
<link href="/2018/07/26/2018-07-26-gitlab%E6%90%AD%E5%BB%BA/"/>
<url>/2018/07/26/2018-07-26-gitlab%E6%90%AD%E5%BB%BA/</url>
<content type="html"><![CDATA[<h3 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h3><p>git基本是每个开发者都会接触到的工具。如果只是个人使用代码仓库的话,可以使用GitHub或者Gitee,完全能够满足个人需求。但是一个公司的或者一个技术团队的代码需要代码仓库保存的话,可能上述的工具并不能够满足团队的需求,这时候就给大家介绍一个开源的git系统gitlab</p><h3 id="Gitlab"><a href="#Gitlab" class="headerlink" title="Gitlab"></a>Gitlab</h3><blockquote><p>GitLab是利用 Ruby on Rails 一个开源的版本管理系统,实现一个自托管的Git项目仓库,可通过Web界面进行访问公开的或者私人项目。它拥有与Github类似的功能,能够浏览源代码,管理缺陷和注释。可以管理团队对仓库的访问,它非常易于浏览提交过的版本并提供一个文件历史库。团队成员可以利用内置的简单聊天程序(Wall)进行交流。它还提供一个代码片段收集功能可以轻松实现代码复用,便于日后有需要的时候进行查找</p></blockquote><p>既然Gitlab这么强大,并且自由为何不尝试使用一下呢</p><h3 id="快速搭建"><a href="#快速搭建" class="headerlink" title="快速搭建"></a>快速搭建</h3><p>笔者是通过查询相关资料了解了gitlab的搭建<br>如果是通过自己搭建的话,需要配置若干环境和下载一系列软件,步骤十分繁琐<br>后来发现一个办法:通过docker搭建,十分快捷</p><p>这里演示本地快速搭建的步骤(是以Mac系统为基础)</p><ul><li>下载docker软件</li><li>下载kitematic docker工具插件件</li><li>打开kitematic,搜索gitlab-ce docker,找到并点击create</li><li>下载完成后,点击start</li><li>启动完成后,点击webpreview,即可看到一个完整的gitlab web应用</li></ul><p>可以做相关的优化</p><ul><li>权限分配</li><li>内网限制</li><li>更多个性化制订</li></ul><h3 id="continue…"><a href="#continue…" class="headerlink" title="continue…"></a>continue…</h3><ul><li>在server环境下搭建docker</li><li>通过docker搭建Gitlab</li><li>演示各种个性化定制</li></ul>]]></content>
</entry>
<entry>
<title>hexo admin的使用记录</title>
<link href="/2018/07/20/2018-07-20-hexo-admin/"/>
<url>/2018/07/20/2018-07-20-hexo-admin/</url>
<content type="html"><![CDATA[<h2 id="hexo-admin"><a href="#hexo-admin" class="headerlink" title="hexo admin"></a>hexo admin</h2><h4 id="hexo在本地编辑的功能还是挺强大的,不过在多端同步问题上,可能就没有那么方便了,开始是想着自己做一套后台系统,不过发现流程真的挺复杂了,就找大佬相关的资料,发现了这个admin后台系统可能样式什么的并不能满足,不过后期可以fork代码进行编辑优化"><a href="#hexo在本地编辑的功能还是挺强大的,不过在多端同步问题上,可能就没有那么方便了,开始是想着自己做一套后台系统,不过发现流程真的挺复杂了,就找大佬相关的资料,发现了这个admin后台系统可能样式什么的并不能满足,不过后期可以fork代码进行编辑优化" class="headerlink" title="hexo在本地编辑的功能还是挺强大的,不过在多端同步问题上,可能就没有那么方便了,开始是想着自己做一套后台系统,不过发现流程真的挺复杂了,就找大佬相关的资料,发现了这个admin后台系统可能样式什么的并不能满足,不过后期可以fork代码进行编辑优化"></a>hexo在本地编辑的功能还是挺强大的,不过在多端同步问题上,可能就没有那么方便了,开始是想着自己做一套后台系统,不过发现流程真的挺复杂了,就找大佬相关的资料,发现了这个admin后台系统可能样式什么的并不能满足,不过后期可以fork代码进行编辑优化</h4><h3 id="开始"><a href="#开始" class="headerlink" title="开始"></a>开始</h3><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs bash">// package.json 编辑 hexo-admin 为 git+ssh://fork地址<br>// 目前是采用胡子哥修改过的代码 git+ssh://[email protected]:barretlee/hexo-admin.git<br>$ npm i<br>$ hexo server -d<br>$ open http://localhost:4000/admin/<br></code></pre></td></tr></table></figure><h3 id="备注"><a href="#备注" class="headerlink" title="备注"></a>备注</h3><ul><li>可能遇到hexo插件里引用包版本问题,例如hexo-fs中使用 fs.SyncWriteStream node 8.0+ 弃用的API 需要更新</li><li>server数据库出异常,需要清空缓存, 使用指令 <strong>hexo clean</strong></li><li>deploy功能的使用,需要在config中配置,后面可能针对优化<figure class="highlight verilog"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs verilog">admin:<br>deployCommand: hexo <span class="hljs-keyword">generate</span> --deploy<br></code></pre></td></tr></table></figure></li></ul>]]></content>
<tags>
<tag>hexo</tag>
</tags>
</entry>
<entry>
<title>hexo博客起步</title>
<link href="/2018/06/05/newpage/"/>
<url>/2018/06/05/newpage/</url>
<content type="html"><![CDATA[<h2 id="新博客系统"><a href="#新博客系统" class="headerlink" title="新博客系统"></a>新博客系统</h2><h2 id="hexo-配置流程"><a href="#hexo-配置流程" class="headerlink" title="hexo 配置流程"></a>hexo 配置流程</h2><figure class="highlight bash"><table><tr><td class="gutter"><div class="code-wrapper"><pre><span class="line">1</span><br><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></div></td><td class="code"><pre><code class="hljs bash">npm install hexo-cli -g<br>hexo init blog<br><span class="hljs-built_in">cd</span> blog<br>npm install<br>hexo server<br></code></pre></td></tr></table></figure><h2 id="hexo-相关指令"><a href="#hexo-相关指令" class="headerlink" title="hexo 相关指令"></a>hexo 相关指令</h2><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><code class="hljs bash">$ hexo new page // 建文章<br>$ hexo generate //生成<br>$ hexo deploy //发布<br><br></code></pre></td></tr></table></figure>]]></content>
<tags>
<tag>hexo</tag>
</tags>
</entry>
</search>