-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathatom.xml
More file actions
1011 lines (822 loc) · 128 KB
/
Copy pathatom.xml
File metadata and controls
1011 lines (822 loc) · 128 KB
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>njafei's blog</title>
<link href="/atom.xml" rel="self"/>
<link href="https://github.com/njafei/njafei.github.io/"/>
<updated>2019-02-25T03:02:08.237Z</updated>
<id>https://github.com/njafei/njafei.github.io/</id>
<author>
<name>njafei</name>
</author>
<generator uri="http://hexo.io/">Hexo</generator>
<entry>
<title>iOS集成flutter[一] 已有项目集成flutter资源包</title>
<link href="https://github.com/njafei/njafei.github.io/2019/02/25/addFlutterToExistingiOSProject/"/>
<id>https://github.com/njafei/njafei.github.io/2019/02/25/addFlutterToExistingiOSProject/</id>
<published>2019-02-25T03:01:29.000Z</published>
<updated>2019-02-25T03:02:08.237Z</updated>
<content type="html"><![CDATA[<p>首先,确保你的mac上面安装了flutter环境,如果没有,请参考<a href="https://flutterchina.club/setup-macos/" target="_blank" rel="noopener">入门: 在macOS上搭建Flutter开发环境</a></p>
<p>二是目前flutter提供的集成方案是cocoapods,所以项目必须是pod框架来搭建的,如果不是,请参考cocoapods的指南<a href="https://guides.cocoapods.org/using/getting-started.html" target="_blank" rel="noopener">https://guides.cocoapods.org/using/getting-started.html</a></p>
<p>本篇内容都是根据flutter的github指南完成,主要参考了<a href="https://github.com/flutter/flutter/wiki/Add-Flutter-to-existing-apps">Add Flutter to existing apps</a>,如果可能,建议先看完这边指南,尝试集成。本篇文章主要是记录集成中遇到的问题和解决方案,供读者参考。</p>
<p>这里简单归纳下指南中的步骤:</p>
<p>1、在iOS项目中创建flutter目录,并初始化flutter </p>
<pre><code><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ cd some/path/ //这里的path是iOS项目文件夹,也就是说,最终的flutter文件夹和iOS项目平级</div><div class="line">$ flutter create -t module my_flutter</div></pre></td></tr></table></figure>
</code></pre><p>2、在podfile中添加flutter相关命令,并执行pod命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">flutter_application_path = 'path/to/my_flutter/'</div><div class="line"> eval(File.read(File.join(flutter_application_path, '.ios', 'Flutter', 'podhelper.rb')), binding)</div></pre></td></tr></table></figure>
<p>执行pod install <code>pod install</code></p>
<p>3、在xcode build phase中添加shell命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" build</div><div class="line">"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh" embed</div></pre></td></tr></table></figure>
<p>4、 改造AppDelegate</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">#import <UIKit/UIKit.h></div><div class="line">#import <Flutter/Flutter.h></div><div class="line"></div><div class="line">@interface AppDelegate : FlutterAppDelegate</div><div class="line">@property (nonatomic,strong) FlutterEngine *flutterEngine;</div><div class="line">@end</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">#import <FlutterPluginRegistrant/GeneratedPluginRegistrant.h> // Only if you have Flutter Plugins</div><div class="line"></div><div class="line">#include "AppDelegate.h"</div><div class="line"></div><div class="line">@implementation AppDelegate</div><div class="line"></div><div class="line">// This override can be omitted if you do not have any Flutter Plugins.</div><div class="line">- (BOOL)application:(UIApplication *)application</div><div class="line"> didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {</div><div class="line"> self.flutterEngine = [[FlutterEngine alloc] initWithName:@"io.flutter" project:nil];</div><div class="line"> [self.flutterEngine runWithEntrypoint:nil];</div><div class="line"> [GeneratedPluginRegistrant registerWithRegistry:self.flutterEngine];</div><div class="line"> return [super application:application didFinishLaunchingWithOptions:launchOptions];</div><div class="line">}</div><div class="line"></div><div class="line">@end</div></pre></td></tr></table></figure>
<p>5、 路由到FlutterViewController</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">FlutterEngine *flutterEngine = [(AppDelegate *)[[UIApplication sharedApplication] delegate] flutterEngine];</div><div class="line"> FlutterViewController *flutterViewController = [[FlutterViewController alloc] initWithEngine:flutterEngine nibName:nil bundle:nil];</div><div class="line"> [self presentViewController:flutterViewController animated:false completion:nil];</div><div class="line">}</div></pre></td></tr></table></figure>
<p>机会每个步骤都遇到了问题,下面一一描述</p>
<p>1、在iOS项目中创建flutter目录,并初始化flutter ,</p>
<p>这里是因为项目本身是集成了<code>react-native</code>的,而flutter又是一个独立的仓库,所以在<code>react-native</code>中增加了一个更新flutter代码的脚本,然后通过脚本来更新。</p>
<p>遇到的第二个问题是flutter项目因为是和android同时集成的,而使用了一个仓库的结果就是无法直接在目录中执行<code>flutter create</code>(因为该目录已存在),我的解决方案是<code>flutter create tmp</code>,然后将tmp中的iOS相关的文件复制到已存在的仓库中,主要是.ios文件夹,还有Flutter文件,还有pubspec.lock</p>
<p>2、在podfile中添加flutter相关命令,并执行pod命令</p>
<p>这里遇到的困难比较多,因为flutter为了保证bitCode等是关闭状态,执行了<code>podhelper.rb</code>的脚本,这个脚本中执行了post_install命令,如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">#post_install do |installer|</div><div class="line"># installer.pods_project.targets.each do |target|</div><div class="line"># target.build_configurations.each do |config|</div><div class="line"># config.build_settings['ENABLE_BITCODE'] = 'NO'</div><div class="line"># xcconfig_path = config.base_configuration_reference.real_path</div><div class="line"># File.open(xcconfig_path, 'a+') do |file|</div><div class="line"># file.puts "#include \"#{File.realpath(File.join(framework_dir, 'Generated.xcconfig'))}\""</div><div class="line"># end</div><div class="line"># end</div><div class="line"># end</div><div class="line">#end</div></pre></td></tr></table></figure>
<p>而我们的项目中也使用了这个命令,pod不支持多次使用,所以我修改了podhelper.rb文件,讲其中的代码,放到了iOS项目的podfile中。而因为.ios是ignore文件,所以我不得不将ios文件改为非ignore,这样带来的坏处是其中的Generated.xcconfig是生成的,记录的flutter的一些宏定义,而每个人的项目地址不同,所以需要其他使用者修改这个文件</p>
<p>3、在xcode build phase中添加shell命令</p>
<p>这个命令第一次执行非常慢,估计是需要将资源copy出来,后面就会快很多,但是因为和android的本地环境略有不同,所以有时候脚本会自动修改android的配置,需要后面想办法优化</p>
<p>4、改造AppDelegate</p>
<p>直接修改AppDelegate其实有些强耦合了,但是目前还看到更好的办法,我看咸鱼漏出来的一点代码是AppDelegate实现Flutter的一个协议,之后可以试试看</p>
<p>5、路由到FlutterViewController</p>
<p>这里首先遇到的问题是编译代码,提示<code>[!] Unable to find a specification for path_provider depended upon by FlutterPluginRegistrant</code>,查了文档,使用<br><code>cd my_flutter; flutter packages get</code>可以解决</p>
<p>我们使用的新建一个viewController壳来加载这个<code>FlutterViewController</code>,因为可能还要做譬如打点等业务相关统计工作。</p>
]]></content>
<summary type="html">
<p>首先,确保你的mac上面安装了flutter环境,如果没有,请参考<a href="https://flutterchina.club/setup-macos/" target="_blank" rel="noopener">入门: 在macOS上搭建Flutter开发环境
</summary>
<category term="flutter" scheme="https://github.com/njafei/njafei.github.io/tags/flutter/"/>
</entry>
<entry>
<title>iOS中32位和64位处理器 NSInteger的数值范围</title>
<link href="https://github.com/njafei/njafei.github.io/2018/08/21/NSIntegerIn32And64Cpu/"/>
<id>https://github.com/njafei/njafei.github.io/2018/08/21/NSIntegerIn32And64Cpu/</id>
<published>2018-08-21T05:57:23.000Z</published>
<updated>2018-08-21T06:09:25.537Z</updated>
<content type="html"><![CDATA[<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>最近遇到了一个需求,我们有一批小程序的用户,在小程序产生的内容想要在app中展示,这批用户的id是从50亿开始的(很难想象,我们公司的用户什么时候能突破50亿。。。),而我们app现在的userId是使用<code>NSInteger</code>,我们要确保userId的使用没有问题。</p>
<p>我搜索了下相关资料,iOS的设备,从iphone5s起,全部使用64位的处理器,之前的都是32位处理器。而<code>NSInteger</code>在32位和64位机器上面表现范围如何呢?且往下看</p>
<h1 id="32位和64位NSInteger定义"><a href="#32位和64位NSInteger定义" class="headerlink" title="32位和64位NSInteger定义"></a>32位和64位NSInteger定义</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">#if __LP64__ || (TARGET_OS_EMBEDDED && !TARGET_OS_IPHONE) || TARGET_OS_WIN32 || NS_BUILD_32_LIKE_64</div><div class="line">typedef long NSInteger;</div><div class="line">typedef unsigned long NSUInteger;</div><div class="line">#else</div><div class="line">typedef int NSInteger;</div><div class="line">typedef unsigned int NSUInteger;</div><div class="line">#endif</div></pre></td></tr></table></figure>
<h2 id="32位系统"><a href="#32位系统" class="headerlink" title="32位系统"></a>32位系统</h2><p>int占4个字节(byte) 32位(bit) </p>
<p>2^32 = 4294967296, NSInteger有正负,则范围为: -2147483648 ~ 2147483647</p>
<p>NSInteger 是 int 别称,占4个字节,32位,范围: -2147483648 ~ 2147483647</p>
<p>NSUInteger 不带符号,占4个字节,32位 范围: 0 ~ 4294967295</p>
<p>long 占4个字节 32位 范围: -2147483648 ~ 2147483647</p>
<p>long long 占8个字节 64位 范围: -9223372036854775808 ~ 9223372036854775807 </p>
<h2 id="64位系统"><a href="#64位系统" class="headerlink" title="64位系统"></a>64位系统</h2><p>int占4个字节(byte) 32位(bit) 范围: -2147483648 ~ 2147483647</p>
<p>long 占8个字节 64位 范围: -9223372036854775808 ~ 9223372036854775807 </p>
<p>NSInteger 是 long 的别称,占8个字节 64位 范围等同long: -9223372036854775808 ~ 9223372036854775807 </p>
<p>NSUInteger 不带符号,占 8个字节, 64 位, 范围: 0 ~2^64-1</p>
<p>long long 占8个字节,64位: -9223372036854775808 ~ 9223372036854775807 </p>
<h1 id="id之类的,该用什么类型呢?"><a href="#id之类的,该用什么类型呢?" class="headerlink" title="id之类的,该用什么类型呢?"></a>id之类的,该用什么类型呢?</h1><p>目前看,前端大部分都是使用int或者string,这两个类型其实都可以,但考虑到后期发展可能遇到的类似我遇到的范围问题,我觉得统一成string会好一些。</p>
<p>后台的话,考虑的就不只是前端的兼容和适配问题,还有诸如性能,容量,速度等,我看很多人推荐使用<code>UUID</code>,以下两篇文章是我阅读中看到的,感兴趣的话可以都查下相关资料。</p>
<p>1 <a href="https://www.jianshu.com/p/2656f2ecac09" target="_blank" rel="noopener">Integer还是String?</a><br>*. <a href="https://zh.wikipedia.org/zh/%E9%80%9A%E7%94%A8%E5%94%AF%E4%B8%80%E8%AF%86%E5%88%AB%E7%A0%81" target="_blank" rel="noopener">通用唯一识别码</a></p>
]]></content>
<summary type="html">
<h1 id="背景"><a href="#背景" class="headerlink" title="背景"></a>背景</h1><p>最近遇到了一个需求,我们有一批小程序的用户,在小程序产生的内容想要在app中展示,这批用户的id是从50亿开始的(很难想象,我们公司的用户什
</summary>
<category term="Objective-C" scheme="https://github.com/njafei/njafei.github.io/tags/Objective-C/"/>
</entry>
<entry>
<title>微信图片浏览效果模仿--如何解决`touch`事件的冲突</title>
<link href="https://github.com/njafei/njafei.github.io/2018/08/15/weichatImageBrowserDemo/"/>
<id>https://github.com/njafei/njafei.github.io/2018/08/15/weichatImageBrowserDemo/</id>
<published>2018-08-15T07:51:56.000Z</published>
<updated>2018-08-15T07:52:27.346Z</updated>
<content type="html"><![CDATA[<p>目前微信的交互效果是国内顶级的,很多人也都比较熟悉和习惯它的交互,很多公司的应用也会参考它的效果,我们今天就来看下图片浏览的效果如何实现。</p>
<p>大概有三个功能点:</p>
<pre><code>1. 可以左右分页滑动
2. 可以拖动并缩小
3. 可以双击放大某一部分
</code></pre><p>这里的<code>touch</code>事件就需要三种:</p>
<pre><code>1. 滚动
2. 拖动
3. 点击
</code></pre><p>技术难题出来了:双击没影响,左右滑动和拖动其实是矛盾的,即用户开始触摸的时候,是没法知道是想拖动,还是想左右滑动的。上升下就是如何解决不同touch事件的冲突?</p>
<p>要做这个判断,我们可根据这条规则:判断用户开始滑动时的方向,向下则认为用户想拖动,向左右则认为其想滑动。</p>
<p>这里有三个思路来实现:</p>
<pre><code>1. 使用scrollView的`Delegate`等来实现功能
2. 继承`UIGestureRecongnizer`类,重写部分的方法。 或者增加其的category,增加部分需要的方法
3. 从底层重写,即重写 `touchBegan` 等方法
</code></pre><p>这三个思路我都在不同的文章中看到,下面说下我的看法:</p>
<h2 id="scrolView的Delegate"><a href="#scrolView的Delegate" class="headerlink" title="scrolView的Delegate"></a>scrolView的<code>Delegate</code></h2><p>先说结论,这个是实现最简单,但是效果最差的。</p>
<p>很多人可能第一直觉就是在<code>delegate</code>去判断,这也是相对最好实现的.但是其效果是最差的,因为ScrollView的<code>delegate</code>其实是最晚拿到滚动消息的,这就带来了刚开始滑动时候的卡顿。</p>
<p>代码类似于这样,向下滚动则使用拖动</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">- (void)scrollViewDidScroll:(UIScrollView *)scrollView</div><div class="line">{</div><div class="line"> static float scrollNewY = 0;</div><div class="line"> static float scrollOldY = 0;</div><div class="line"> scrollNewY = scrollView.contentOffset.y;</div><div class="line"> if ((scrollView.contentOffset.y < 0 || self.doingPan) && (self.doingZoom == NO))</div><div class="line"> {</div><div class="line"> [self doPan:self.mainScrollView.panGestureRecognizer];</div><div class="line"> }</div><div class="line"> scrollOldY = scrollNewY;</div><div class="line">}</div></pre></td></tr></table></figure>
<h2 id="UIGestureRecongnizer"><a href="#UIGestureRecongnizer" class="headerlink" title="UIGestureRecongnizer"></a>UIGestureRecongnizer</h2><p>这是相对来讲效果较好,实现也比较方便的方法。</p>
<p>无论是写category还是继承子类,功能都不复杂,而且效果也会比较好。</p>
<p>实现代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">- (void)touchesMoved:(NSSet<UITouch *> *)touches withEvent:(UIEvent *)event</div><div class="line">{</div><div class="line"> if (!self.isMoving){</div><div class="line"> UITouch *touch = [touches anyObject];</div><div class="line"> CGPoint nowPoint = [touch locationInView:self.view];</div><div class="line"> CGPoint prevPoint = [touch previousLocationInView:self.view];</div><div class="line"> </div><div class="line"> BOOL isHorzitional = prevPoint.x - nowPoint.x > 5;</div><div class="line"> BOOL isVertical = nowPoint.y - prevPoint.y > 5;</div><div class="line"> if (!isHorzitional && isVertical){</div><div class="line"> self.isMoving = YES;</div><div class="line"> self.isVerticalDownPan = YES;</div><div class="line"> } else {</div><div class="line"> self.isVerticalDownPan = NO;</div><div class="line"> self.state = UIGestureRecognizerStateFailed;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> </div><div class="line"> [super touchesMoved:touches withEvent:event];</div><div class="line">}</div></pre></td></tr></table></figure>
<p>我这里使用的是直接重写<code>touchesMoved</code>来判断,也可以使用<code>getureRecongnizer</code>的方法来做。</p>
<h2 id="UITouch"><a href="#UITouch" class="headerlink" title="UITouch"></a>UITouch</h2><p>这个方式可以用来处理非常复杂的效果,但是这里不推荐,因为要花费的精力实在太大,如果有选择,尽量使用系统提供的单击、双击、拖动等类来做,没必要重新造轮子。</p>
<p>实现即重写<code>touchesBegan</code>,<code>touchesMoved</code>,<code>touchesEnded</code>,<code>touchesCanceled</code>等方法</p>
<p><a href="https://github.com/njafei/lfImageBrowserImitateWechat">githubDemo: lfImageBrowserImitateWechat</a></p>
<p>其他类似项目参考:</p>
<pre><code>1. https://github.com/JiongXing/PhotoBrowser
2. https://github.com/MSS0306/MSSBrowse
3. https://github.com/tianliangyihou/LBPhotoBrowser
4. https://github.com/QuintGao/GKPhotoBrowser
</code></pre>]]></content>
<summary type="html">
<p>目前微信的交互效果是国内顶级的,很多人也都比较熟悉和习惯它的交互,很多公司的应用也会参考它的效果,我们今天就来看下图片浏览的效果如何实现。</p>
<p>大概有三个功能点:</p>
<pre><code>1. 可以左右分页滑动
2. 可以拖动并缩小
3. 可以双击放大某一部
</summary>
<category term="UITouch" scheme="https://github.com/njafei/njafei.github.io/categories/UITouch/"/>
<category term="Demo" scheme="https://github.com/njafei/njafei.github.io/tags/Demo/"/>
</entry>
<entry>
<title>前端生命周期设计思考</title>
<link href="https://github.com/njafei/njafei.github.io/2018/06/06/lifeCycleDesignThought/"/>
<id>https://github.com/njafei/njafei.github.io/2018/06/06/lifeCycleDesignThought/</id>
<published>2018-06-06T09:04:30.000Z</published>
<updated>2018-06-06T09:08:58.501Z</updated>
<content type="html"><![CDATA[<p>这两天在设计完善框架的生命周期,所以当我回头去看之前写过的<code>iOS</code>和<code>react-native</code>的代码时,我不禁产生了疑问,之前用的这么多的生命周期的函数,为什么是这样设计的?每个生命周期用来做什么比较合适?比如<code>react-native</code>中想要去请求接口,是在<code>componentWillMount</code>还是在<code>componentDidMount</code>中更合适的?为什么?查了一些资料,结合我的经验思考,遂有此文。</p>
<p>生命周期主要讲的就是前端页面从生成-渲染-更新-销毁等几个阶段的过程,框架一般会提供该过程的回调和可用参数,提供给用户使用。只有了解清楚生命周期中每个阶段做的事情和状态后,我们才能因地制宜,写出更优雅的代码。</p>
<p>下面我们来通过对比看下iOS和react中生命周期,看看生命周期究竟是怎么设计的,为什么不同的框架的生命周期不太一样呢</p>
<h2 id="iOS"><a href="#iOS" class="headerlink" title="iOS"></a>iOS</h2><p>iOS的文档核心是将页面的状态分成四种,生命周期的不同阶段就是围绕这四种状态的变化:</p>
<ol>
<li>Appearing 正在绘制</li>
<li>Appeared 已绘制</li>
<li>Disappearing 正在销毁</li>
<li>Disappeared 已销毁</li>
</ol>
<p>状态变化如下图所示:</p>
<p><img src="http://on0hv7n2x.bkt.clouddn.com/ios-controller-lifeCycle.png" alt="生命周期"></p>
<p>iOS生命周期</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">ViewDidLoad - Called when you create the class and load from xib. Great for initial setup and one-time-only work.</div><div class="line"></div><div class="line">ViewWillAppear - Called right before your view appears, good for hiding/showing fields or any operations that you want to happen every time before the view is visible. Because you might be going back and forth between views, this will be called every time your view is about to appear on the screen.</div><div class="line"></div><div class="line">ViewDidAppear - Called after the view appears - great place to start an animations or the loading of external data from an API.</div><div class="line"></div><div class="line">ViewWillDisappear/DidDisappear - Same idea as ViewWillAppear/ViewDidAppear.</div><div class="line"></div><div class="line">ViewDidUnload/ViewDidDispose - In Objective C, this is where you do your clean-up and release of stuff, but this is handled automatically so not much you really need to do here.</div></pre></td></tr></table></figure>
<h2 id="react-生命周期"><a href="#react-生命周期" class="headerlink" title="react 生命周期"></a>react 生命周期</h2><p>react的文档中,是根据行为来分类生命周期的:</p>
<ol>
<li>渲染相关</li>
<li>更新相关</li>
<li>销毁渲染相关</li>
<li>其他</li>
</ol>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">#Mouting</div><div class="line">constructor()</div><div class="line">static getDerivedStateFromProps()</div><div class="line">componentWillMount() / UNSAFE_componentWillMount()</div><div class="line">render()</div><div class="line">componentDidMount()</div><div class="line"></div><div class="line">#Updating</div><div class="line">componentWillReceiveProps() / UNSAFE_componentWillReceiveProps()</div><div class="line">static getDerivedStateFromProps()</div><div class="line">shouldComponentUpdate()</div><div class="line">componentWillUpdate() / UNSAFE_componentWillUpdate()</div><div class="line">render()</div><div class="line">getSnapshotBeforeUpdate()</div><div class="line">componentDidUpdate()</div><div class="line"></div><div class="line">#Unmounting</div><div class="line"></div><div class="line">componentWillUnmount()</div><div class="line"></div><div class="line">#Error Handling</div><div class="line">componentDidCatch()</div></pre></td></tr></table></figure>
<p>可以注意到,两个框架的生命周期设计是不同的,我认为这种不同更多是来源于设计思想的不同,react使用DOM和Virtual DOM,所以而DOM的刷新是根据props和state来驱动的,可以理解为数据驱动,所以过程中会多一个些props和state的相关函数。而iOS的渲染是偏向于编码控制每个地方的变化,所以生命周期主要关注页面的生成和销毁,而不太关心中间页面的更新(因为页面更新是代码主动控制的)。</p>
<p>比较完iOS和react,生命周期究竟该怎么设计,重点就是根据你的框架的设计方案,把影响到你页面刷新的步骤提炼出来,设计成不同周期,最终目的地是让你的框架更好用,使用方能更好地理解你的框架,从而写出更优雅的代码。</p>
<h2 id="参考文章"><a href="#参考文章" class="headerlink" title="参考文章"></a>参考文章</h2><ul>
<li><p><a href="http://www.html-js.com/article/3229" target="_blank" rel="noopener">关于软件的生命周期</a></p>
</li>
<li><p><a href="https://daveceddia.com/where-fetch-data-componentwillmount-vs-componentdidmount/" target="_blank" rel="noopener">Where to Fetch Data: componentWillMount vs componentDidMount</a></p>
</li>
<li><p><a href="https://reactjs.org/docs/react-component.html" target="_blank" rel="noopener">react 生命周期</a></p>
</li>
<li><p><a href="https://developer.apple.com/library/archive/referencelibrary/GettingStarted/DevelopiOSAppsSwift/WorkWithViewControllers.html#//apple_ref/doc/uid/TP40015214-CH6-SW1" target="_blank" rel="noopener">controller 生命周期[swift]</a></p>
</li>
</ul>
]]></content>
<summary type="html">
<p>这两天在设计完善框架的生命周期,所以当我回头去看之前写过的<code>iOS</code>和<code>react-native</code>的代码时,我不禁产生了疑问,之前用的这么多的生命周期的函数,为什么是这样设计的?每个生命周期用来做什么比较合适?比如<code>re
</summary>
<category term="frame" scheme="https://github.com/njafei/njafei.github.io/categories/frame/"/>
<category term="lifeCycle" scheme="https://github.com/njafei/njafei.github.io/tags/lifeCycle/"/>
<category term="frame" scheme="https://github.com/njafei/njafei.github.io/tags/frame/"/>
</entry>
<entry>
<title>常用的包管理工具 npm rvm brew gem</title>
<link href="https://github.com/njafei/njafei.github.io/2018/06/06/packageaManagerTools/"/>
<id>https://github.com/njafei/njafei.github.io/2018/06/06/packageaManagerTools/</id>
<published>2018-06-06T03:11:17.000Z</published>
<updated>2018-06-06T03:15:07.783Z</updated>
<content type="html"><![CDATA[<p>工作中用到了很多的包管理工具,这里做个总结和维护,以便快速查找和记录踩过的坑</p>
<h1 id="npm-管理js-package"><a href="#npm-管理js-package" class="headerlink" title="npm 管理js package"></a>npm 管理js package</h1><p>官网地址: <a href="https://www.npmjs.com.cn/" target="_blank" rel="noopener">www.npmjs.com.cn</a></p>
<h2 id="常用命令"><a href="#常用命令" class="headerlink" title="常用命令"></a>常用命令</h2><p>安装包:<code>npm install npm@version -g.</code> version : <code>latest/next/1.0.0</code></p>
<p>卸载包:<code>npm uninstall npm@version -g.</code></p>
<p><code>-g</code>, 全局安装,可以在任何目录下使用该包</p>
<h2 id="常见错误和解决"><a href="#常见错误和解决" class="headerlink" title="常见错误和解决"></a>常见错误和解决</h2><h1 id="rvm-管理ruby版本"><a href="#rvm-管理ruby版本" class="headerlink" title="rvm 管理ruby版本"></a>rvm 管理ruby版本</h1><p>官网地址:<a href="https://ruby-china.org/wiki/rvm-guide" target="_blank" rel="noopener">ruby-china</a>`</p>
<p>mac上面是默认安装了ruby的,但是不同的软件或者工具对于版本的要求不一致,所以一般使用<code>rvm</code>的ruby来代替</p>
<h2 id="常用命令-1"><a href="#常用命令-1" class="headerlink" title="常用命令"></a>常用命令</h2><p>安装<code>rvm</code></p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">$ gpg --keyserver hkp://keys.gnupg.net --recv-keys 409B6B1796C275462A1703113804BB82D39DC0E3</div><div class="line">$ \curl -sSL https://get.rvm.io | bash -s stable</div><div class="line">$ source ~/.bashrc</div><div class="line">$ source ~/.bash_profile</div><div class="line"></div><div class="line"><!--修改ruby包源到ruby-china上面,以提高网络速度--></div><div class="line">echo "ruby_url=https://cache.ruby-china.org/pub/ruby" > ~/.rvm/user/db</div></pre></td></tr></table></figure>
<p>安装ruby: <code>rvm install 2.2.0</code></p>
<p>选择ruby版本: <code>rvm use 2.2.0</code></p>
<p>将命令行中的ruby换成rvm的: <code>rvm use 2.2.0 --default</code></p>
<p>查询所有的ruby版本: <code>rvm list</code></p>
<p>卸载已安装版本: <code>rvm remove 1.8.7</code></p>
<h1 id="brew-使用-Homebrew-安装-Apple-没有预装但-你需要的东西。"><a href="#brew-使用-Homebrew-安装-Apple-没有预装但-你需要的东西。" class="headerlink" title="brew 使用 Homebrew 安装 Apple 没有预装但 你需要的东西。"></a>brew 使用 Homebrew 安装 Apple 没有预装但 你需要的东西。</h1><p>官网: <a href="https://brew.sh/index_zh-cn" target="_blank" rel="noopener">brew.sh</a></p>
<h2 id="常用命令-2"><a href="#常用命令-2" class="headerlink" title="常用命令"></a>常用命令</h2><p>安装 Homebrew: <code>/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"</code> </p>
<p>安装包: <code>brew install</code><br>卸载包: <code>brew uninstall</code></p>
<h2 id="常见问题及解决"><a href="#常见问题及解决" class="headerlink" title="常见问题及解决"></a>常见问题及解决</h2><h1 id="gem-ruby-package管理工具"><a href="#gem-ruby-package管理工具" class="headerlink" title="gem ruby package管理工具"></a>gem ruby package管理工具</h1><p>官网: <a href="https://rubygems.org/?locale=zh-CN" target="_blank" rel="noopener">rubygems.org</a></p>
<h2 id="常用命令-3"><a href="#常用命令-3" class="headerlink" title="常用命令"></a>常用命令</h2><p>安装包: <code>gem install</code><br>卸载包: <code>gem uninstall</code></p>
<h2 id="常见问题及解决-1"><a href="#常见问题及解决-1" class="headerlink" title="常见问题及解决"></a>常见问题及解决</h2>]]></content>
<summary type="html">
<p>工作中用到了很多的包管理工具,这里做个总结和维护,以便快速查找和记录踩过的坑</p>
<h1 id="npm-管理js-package"><a href="#npm-管理js-package" class="headerlink" title="npm 管理js packa
</summary>
<category term="tools" scheme="https://github.com/njafei/njafei.github.io/categories/tools/"/>
<category term="tools" scheme="https://github.com/njafei/njafei.github.io/tags/tools/"/>
</entry>
<entry>
<title>URL协议详解</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/23/URL/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/23/URL/</id>
<published>2018-03-23T08:31:14.000Z</published>
<updated>2018-03-23T08:35:49.468Z</updated>
<content type="html"><![CDATA[<p>我们目前使用的诸如:<code>http://www.baidu.com</code>或者<code>ftp://192.168.1.1</code>等链接,都是URL标准的一个实例。URL全称Uniform Resource Locators(通用资源定位器),标注名称为<a href="http://www.ietf.org/rfc/rfc1738.txt" target="_blank" rel="noopener">rfc1738</a>。</p>
<h1 id="URL的组成部分"><a href="#URL的组成部分" class="headerlink" title="URL的组成部分"></a>URL的组成部分</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><scheme>:<scheme-specific-part></div></pre></td></tr></table></figure>
<p>URL被冒号分成了两部分,前面是scheme(方案),后面是scheme的描述部分</p>
<h2 id="scheme"><a href="#scheme" class="headerlink" title="scheme"></a>scheme</h2><p>scheme只能由以下元素组成:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">a-z(不区分大小写)</div><div class="line">数字</div><div class="line">字符: 加号+ 句号. 连字符-</div></pre></td></tr></table></figure>
<p>shceme目前有这些类型:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div></pre></td><td class="code"><pre><div class="line"> ftp File Transfer protocol(文件传输协议)</div><div class="line"> http Hypertext Transfer Protocol(超文本传输协议)</div><div class="line"> gopher The Gopher protocol(Gopher协议)</div><div class="line"> mailto Electronic mail address(电子邮件地址)</div><div class="line"> news USENET news(USENET新闻)</div><div class="line"> nntp USENET news using NNTP access</div><div class="line"> (使用NNTP访问的USENET新闻)</div><div class="line"> telnet Reference to interactive sessions</div><div class="line">(交互式会话访问)</div><div class="line"> wais Wide Area Information Servers(广域信息服务系统)</div><div class="line"> file Host-specific file names(特殊主机文件名)</div><div class="line"> prospero Prospero Directory Service(prospero目录服务)</div></pre></td></tr></table></figure>
<p>当然,上述的scheme大部分是应用非常广泛的,我们可以自定义scheme,只要解析器支持即可,比如我们公司就根据URL设计了一条App跳转专用的URL,方便服务器分发跳转路径。</p>
<h2 id="scheme描述部分"><a href="#scheme描述部分" class="headerlink" title="scheme描述部分"></a>scheme描述部分</h2><p>scheme的描述部分也可以拆解成以下的结构:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><user>:<password>@<host>:<port>/<url-path></div></pre></td></tr></table></figure>
<p>根据方案的不同,这些部分是可以部分省略的,比如<code>http</code>通常不需要user和password</p>
<p>我们来依次看这些组成部分:</p>
<h3 id="user"><a href="#user" class="headerlink" title="user"></a>user</h3><p>这个比较好理解,就是用户名,没有则省略</p>
<h3 id="password"><a href="#password" class="headerlink" title="password"></a>password</h3><p>用户密码</p>
<h3 id="host"><a href="#host" class="headerlink" title="host"></a>host</h3><p>host(主机)是一个IP地址,由四组十进制数字以<code>.</code>分割组合而成。现在很多对客的地址都是用了域名,然后再通过DNS解析.DNS可以理解为将难以记住的ip地址和单词等组成的域名做映射,从而方便用户的使用。记住<code>https://www.baidu.com</code>可比记住<code>192.292.22.22</code>容易多了。很多的公司在使用的时候也会做DNS优化,即客户端等发出请求的时候,直接向对应的ip发送,从而节省DNS解析的时间,加快用户访问速度,有兴趣的可以网络搜索下。</p>
<h3 id="port"><a href="#port" class="headerlink" title="port"></a>port</h3><p>port(端口)是用来区分不同的网络服务的,从而实现在一个IP的基础上,提供多个网络服务。</p>
<h3 id="url-path"><a href="#url-path" class="headerlink" title="url-path"></a>url-path</h3><p>url路径提供了如何对特定资源访问的详细信息,一般都是文件夹的路径</p>
<h1 id="URL编码"><a href="#URL编码" class="headerlink" title="URL编码"></a>URL编码</h1><p>URL会使用编码,因为很多字符是不安全的,比如空格,在被转录或者排版的时候,可能会被忽略。”<”和”>”也是不安全的,因为他们被用来作为URL在文本中的分割符。<code>#</code>,<code>/</code>,<code>~</code>,<code>[</code>,<code>]</code>等也都会在不同的场景下给URL的解析带来问题,所以要对不安全的字符进行编码。通常是进行ecode。</p>
<p>总结,本文介绍了URL的组成部分,各部分的用法和使用场景,URL的编码原因等知识。</p>
<h1 id="参考文档:"><a href="#参考文档:" class="headerlink" title="参考文档:"></a>参考文档:</h1><ul>
<li><a href="http://www.ietf.org/rfc/rfc1738.txt" target="_blank" rel="noopener">rfc1738</a></li>
<li><a href="http://www.cppblog.com/woaidongmao/archive/2008/12/28/70569.html" target="_blank" rel="noopener">RFC1738 - 统一资源定位器URL</a></li>
</ul>
]]></content>
<summary type="html">
<p>我们目前使用的诸如:<code>http://www.baidu.com</code>或者<code>ftp://192.168.1.1</code>等链接,都是URL标准的一个实例。URL全称Uniform Resource Locators(通用资源定位器),标注名称为
</summary>
<category term="底层协议知识" scheme="https://github.com/njafei/njafei.github.io/categories/%E5%BA%95%E5%B1%82%E5%8D%8F%E8%AE%AE%E7%9F%A5%E8%AF%86/"/>
<category term="URL" scheme="https://github.com/njafei/njafei.github.io/tags/URL/"/>
</entry>
<entry>
<title>Node学习 Lesson1 使用express</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/23/NodeLearnLession1/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/23/NodeLearnLession1/</id>
<published>2018-03-23T06:54:43.000Z</published>
<updated>2018-03-23T06:55:36.633Z</updated>
<content type="html"><![CDATA[<p>初始化项目:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm init</div></pre></td></tr></table></figure>
<p>安装express,安装有两个方法</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">npm install express --save //方法一:直接安装,并将配置保存在package.json</div></pre></td></tr></table></figure>
<p>或者</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">修改package.josn,增加express及版本信息</div><div class="line">npm install</div></pre></td></tr></table></figure>
<p>创建文件app.js</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">touch app.js</div><div class="line">open app.js</div></pre></td></tr></table></figure>
<p>写入代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">var express = require('express');</div><div class="line"></div><div class="line">var app = express();</div><div class="line"></div><div class="line">app.get('/',function(req, res){</div><div class="line"> res.send('hello world');</div><div class="line">});</div><div class="line"></div><div class="line">app.listen(3000, function (){</div><div class="line"> console.log('app is listening ar port 3000');</div><div class="line">})</div></pre></td></tr></table></figure>
<p>执行node:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">node app.js</div></pre></td></tr></table></figure>
<p>在浏览器输入<code>localhost:3000</code>,看到上面有打印:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">hello,world</div></pre></td></tr></table></figure>
<p>课程地址:<a href="https://github.com/alsotang/node-lessons/tree/master/lesson1">《一个最简单的 express 应用》</a></p>
]]></content>
<summary type="html">
<p>初始化项目:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><
</summary>
<category term="NodeLearn" scheme="https://github.com/njafei/njafei.github.io/categories/NodeLearn/"/>
<category term="Node" scheme="https://github.com/njafei/njafei.github.io/tags/Node/"/>
<category term="NodeLearn" scheme="https://github.com/njafei/njafei.github.io/tags/NodeLearn/"/>
</entry>
<entry>
<title>nvm安装、解决nvm command not found问题、卸载</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/23/nvm-install/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/23/nvm-install/</id>
<published>2018-03-23T03:15:14.000Z</published>
<updated>2018-03-23T03:24:13.553Z</updated>
<content type="html"><![CDATA[<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><p>nvm是node的包版本管理工具,github地址如下:<a href="https://github.com/creationix/nvm">nvm</a></p>
<p>安装命令</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">curl -o- https://raw.githubusercontent.com/creationix/nvm/v0.33.8/install.sh | bash</div></pre></td></tr></table></figure>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">nvm //检查nvm是否安装成功</div><div class="line"></div><div class="line">-bash: nvm: command not found //boom,失败了</div></pre></td></tr></table></figure>
<h1 id="解决nvm-command-not-found问题"><a href="#解决nvm-command-not-found问题" class="headerlink" title="解决nvm command not found问题"></a>解决<code>nvm command not found</code>问题</h1><p>进入<code>.nvm</code>文件夹,新建<code>.bash_profile</code>:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">touch .bash_profile //新建文件</div><div class="line">open .bash_profile //打开文件</div></pre></td></tr></table></figure>
<p>在里面copy如下内容:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">export NVM_DIR="$HOME/.nvm"</div><div class="line">[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm</div></pre></td></tr></table></figure>
<p>关闭文件,然后执行这个文件:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">source .bash_profile</div></pre></td></tr></table></figure>
<p>执行完毕,我们再看看是否安装成功:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">nvm --version</div></pre></td></tr></table></figure>
<p>输出:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">0.33.8</div></pre></td></tr></table></figure>
<p>安装成功。</p>
<h1 id="卸载:"><a href="#卸载:" class="headerlink" title="卸载:"></a>卸载:</h1><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">$ nvm use system</div><div class="line">$ npm uninstall -g a_module</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h1 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h1><p>nvm是node的包版本管理工具,github地址如下:<a href="https://github.com/creationix/nvm
</summary>
<category term="node" scheme="https://github.com/njafei/njafei.github.io/categories/node/"/>
<category term="nvm" scheme="https://github.com/njafei/njafei.github.io/tags/nvm/"/>
<category term="node" scheme="https://github.com/njafei/njafei.github.io/tags/node/"/>
</entry>
<entry>
<title>JS module输出原理</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/22/nodeJS-module/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/22/nodeJS-module/</id>
<published>2018-03-22T10:04:20.000Z</published>
<updated>2018-03-22T10:08:00.869Z</updated>
<content type="html"><![CDATA[<h1 id="module输出写法"><a href="#module输出写法" class="headerlink" title="module输出写法"></a>module输出写法</h1><p>为了能够复用一些基本功能,我们通常会将其封装在module中。类似这样子:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">//greet.js</div><div class="line">'use strict';</div><div class="line"></div><div class="line">var s = 'Hello';</div><div class="line"></div><div class="line">function greet(name) {</div><div class="line"> console.log(s + ', ' + name + '!');</div><div class="line">}</div><div class="line"></div><div class="line">module.exports = greet;</div></pre></td></tr></table></figure>
<p>我们也会看到其他的写法,这样的:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">'use strict';</div><div class="line"></div><div class="line">exports.foo = 'foo';</div></pre></td></tr></table></figure>
<p>直接通过exports的属性来输出的,这样看起来其实很迷惑人,比如我第一次就有疑惑:</p>
<ul>
<li>module不是模块的意思吗? 为什么要使用module.exports的赋值来输出?</li>
<li>为什么有时候是exports来赋值输出</li>
<li>其他的地方,是如何使用我的模块呢?</li>
<li>为什么export有些可以输出多个值呢? 比如: modelu.exports = {foo: foo, tool: tool}</li>
</ul>
<h1 id="原理"><a href="#原理" class="headerlink" title="原理"></a>原理</h1><p>这里其实是module在默默地做了一些事情,它会在代码中植入类似这样的代码:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">var module = {</div><div class="line"> id: 'greet',</div><div class="line"> exports: {},</div><div class="line">}</div><div class="line"></div><div class="line">exports = moduel.exports;</div><div class="line"></div><div class="line">load(module.exports, module);</div></pre></td></tr></table></figure>
<p>这样,我们就知道了为什么<code>exports</code>和<code>module.exports</code>都可以作为模块的输出。</p>
<p>这里还要一个要注意的,之所以可以用<code>exports</code>来输出,是因为它指向了<code>module.exports</code>. 所以当<code>exports</code>的指针被修改的时候,这样用就会出问题了。</p>
<p>如果输出是一个数组,或者函数,则两者是不等价的,看下面的例子:</p>
<p>例子一: 等价的输出</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">module.exports = {foo: 'foo'};</div><div class="line"></div><div class="line">exports.foo = 'foo';</div></pre></td></tr></table></figure>
<p>例子而: 不等价输出</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">module.exports = function greet(){console.log('greet')};//这时 module.exports指向greet函数</div><div class="line"></div><div class="line">exports = function greet(){console.log('greet')};//这时,exoprts的指针指向greet函数,而module.exports仍然指向{}</div><div class="line"></div><div class="line"></div><div class="line">这里两种方式不等价,主要是因为原来都指向同一个变量,而如果赋值是数据,函数等,则export会改变指针的值。然后最终输出的还是module.exports, 所以在这里,模块的输出就不可以用export</div></pre></td></tr></table></figure>
<p>廖雪峰的博客说是建议全部使用<code>module.exports</code>,这样可以避免混淆。我的建议是大家最好搞清楚原理,这样使用起来才知其所以然。</p>
<p>ok,总结这篇文章,主要是讲了模块(module)的输出原理,为什么有两种写法,为什么每种写法都可以work,两种写法的区别和使用时机。</p>
<h1 id="引用"><a href="#引用" class="headerlink" title="引用"></a>引用</h1><ul>
<li><a href="https://www.liaoxuefeng.com/wiki/001434446689867b27157e896e74d51a89c25cc8b43bdb3000/001434502419592fd80bbb0613a42118ccab9435af408fd000" target="_blank" rel="noopener">廖雪峰 JS教程-模块</a></li>
<li><a href="http://cnodejs.org/topic/5231a630101e574521e45ef8" target="_blank" rel="noopener">exports 和 module.exports 的区别</a></li>
</ul>
]]></content>
<summary type="html">
<h1 id="module输出写法"><a href="#module输出写法" class="headerlink" title="module输出写法"></a>module输出写法</h1><p>为了能够复用一些基本功能,我们通常会将其封装在module中。类似这样子:<
</summary>
<category term="Node" scheme="https://github.com/njafei/njafei.github.io/categories/Node/"/>
<category term="Node" scheme="https://github.com/njafei/njafei.github.io/tags/Node/"/>
<category term="module" scheme="https://github.com/njafei/njafei.github.io/tags/module/"/>
</entry>
<entry>
<title>JS 静态类型检查 PropTypes</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/20/JS-PropTypes/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/20/JS-PropTypes/</id>
<published>2018-03-20T06:17:26.000Z</published>
<updated>2018-03-20T06:18:10.501Z</updated>
<content type="html"><![CDATA[<h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2><p><code>JS</code>作为弱类型语言,有时候也会带来很多麻烦,比如需要的参数是<code>Number</code>,而使用方传了<code>String</code>,虽然我们可以通过参数的检查来return掉,但是这样也带来了很多繁琐的步骤。为了避免类似的问题,<code>JS</code>提供了类型检查工具PropTypes,在<code>React V15.5</code>之前,可以直接使用<code>React.PropTypes</code>助手函数,之后则提供了npm包<code>prop-types</code>。</p>
<p>注意,处于性能原因,<code>propTypes</code>仅会在dev模式中work,如果发现类型和定义的不一致,会通过warning来提示。</p>
<p>以下是官网的例子,基本上够用了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div><div class="line">57</div><div class="line">58</div><div class="line">59</div><div class="line">60</div><div class="line">61</div><div class="line">62</div><div class="line">63</div><div class="line">64</div><div class="line">65</div><div class="line">66</div><div class="line">67</div><div class="line">68</div><div class="line">69</div><div class="line">70</div><div class="line">71</div><div class="line">72</div><div class="line">73</div><div class="line">74</div></pre></td><td class="code"><pre><div class="line">import PropTypes from 'prop-types';</div><div class="line"></div><div class="line">MyComponent.propTypes = {</div><div class="line"> // 你可以声明一个 prop 是一个特定的 JS 原始类型。 </div><div class="line"> // 默认情况下,这些都是可选的。</div><div class="line"> optionalArray: PropTypes.array,</div><div class="line"> optionalBool: PropTypes.bool,</div><div class="line"> optionalFunc: PropTypes.func,</div><div class="line"> optionalNumber: PropTypes.number,</div><div class="line"> optionalObject: PropTypes.object,</div><div class="line"> optionalString: PropTypes.string,</div><div class="line"> optionalSymbol: PropTypes.symbol,</div><div class="line"></div><div class="line"> // 任何东西都可以被渲染:numbers, strings, elements,或者是包含这些类型的数组(或者是片段)。</div><div class="line"> optionalNode: PropTypes.node,</div><div class="line"></div><div class="line"> // 一个 React 元素。</div><div class="line"> optionalElement: PropTypes.element,</div><div class="line"></div><div class="line"> // 你也可以声明一个 prop 是类的一个实例。 </div><div class="line"> // 使用 JS 的 instanceof 运算符。</div><div class="line"> optionalMessage: PropTypes.instanceOf(Message),</div><div class="line"></div><div class="line"> // 你可以声明 prop 是特定的值,类似于枚举</div><div class="line"> optionalEnum: PropTypes.oneOf(['News', 'Photos']),</div><div class="line"></div><div class="line"> // 一个对象可以是多种类型其中之一</div><div class="line"> optionalUnion: PropTypes.oneOfType([</div><div class="line"> PropTypes.string,</div><div class="line"> PropTypes.number,</div><div class="line"> PropTypes.instanceOf(Message)</div><div class="line"> ]),</div><div class="line"></div><div class="line"> // 一个某种类型的数组</div><div class="line"> optionalArrayOf: PropTypes.arrayOf(PropTypes.number),</div><div class="line"></div><div class="line"> // 属性值为某种类型的对象</div><div class="line"> optionalObjectOf: PropTypes.objectOf(PropTypes.number),</div><div class="line"></div><div class="line"> // 一个特定形式的对象</div><div class="line"> optionalObjectWithShape: PropTypes.shape({</div><div class="line"> color: PropTypes.string,</div><div class="line"> fontSize: PropTypes.number</div><div class="line"> }),</div><div class="line"></div><div class="line"> // 你可以使用 `isRequired' 链接上述任何一个,以确保在没有提供 prop 的情况下显示警告。</div><div class="line"> requiredFunc: PropTypes.func.isRequired,</div><div class="line"></div><div class="line"> // 任何数据类型的值</div><div class="line"> requiredAny: PropTypes.any.isRequired,</div><div class="line"></div><div class="line"> // 你也可以声明自定义的验证器。如果验证失败返回 Error 对象。不要使用 `console.warn` 或者 throw ,</div><div class="line"> // 因为这不会在 `oneOfType` 类型的验证器中起作用。</div><div class="line"> customProp: function(props, propName, componentName) {</div><div class="line"> if (!/matchme/.test(props[propName])) {</div><div class="line"> return new Error(</div><div class="line"> 'Invalid prop `' + propName + '` supplied to' +</div><div class="line"> ' `' + componentName + '`. Validation failed.'</div><div class="line"> );</div><div class="line"> }</div><div class="line"> },</div><div class="line"></div><div class="line"> // 也可以声明`arrayOf`和`objectOf`类型的验证器,如果验证失败需要返回Error对象。</div><div class="line"> // 会在数组或者对象的每一个元素上调用验证器。验证器的前两个参数分别是数组或者对象本身,</div><div class="line"> // 以及当前元素的键值。</div><div class="line"> customArrayProp: PropTypes.arrayOf(function(propValue, key, componentName, location, propFullName) {</div><div class="line"> if (!/matchme/.test(propValue[key])) {</div><div class="line"> return new Error(</div><div class="line"> 'Invalid prop `' + propFullName + '` supplied to' +</div><div class="line"> ' `' + componentName + '`. Validation failed.'</div><div class="line"> );</div><div class="line"> }</div><div class="line"> })</div><div class="line">};</div></pre></td></tr></table></figure>
<h2 id="默认值指定"><a href="#默认值指定" class="headerlink" title="默认值指定"></a>默认值指定</h2><p>除了能指定参数的类型,还可以指定参数的默认值:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div></pre></td><td class="code"><pre><div class="line">class Greeting extends React.Component {</div><div class="line"> render() {</div><div class="line"> return (</div><div class="line"> <h1>Hello, {this.props.name}</h1></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">// 指定 props 的默认值:</div><div class="line">Greeting.defaultProps = {</div><div class="line"> name: 'Stranger'</div><div class="line">};</div><div class="line"></div><div class="line">// 渲染为 "Hello, Stranger":</div><div class="line">ReactDOM.render(</div><div class="line"> <Greeting />,</div><div class="line"> document.getElementById('example')</div><div class="line">);</div></pre></td></tr></table></figure>
<p>只有当指定的props为undefined的时候,才会使用默认值,如果参数有,但是不符合类型检查,也会使用该参数,而不是使用默认值。</p>
<p>propTypes 的类型检测是在defaultProps 解析之后发生的,因此也会对默认属性 defaultProps 进行类型检测。</p>
<h2 id="整体流程"><a href="#整体流程" class="headerlink" title="整体流程"></a>整体流程</h2><p>整体是这样的流程:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div></pre></td><td class="code"><pre><div class="line">//step1 check defaultProps</div><div class="line">if (参数为undefined){</div><div class="line"> if (有默认值){</div><div class="line"> 使用默认参数</div><div class="line"> } else {</div><div class="line"> 使用undefined</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">//step2 check type</div><div class="line">if (不符合类型检查){</div><div class="line"> 警告()</div><div class="line"> }</div><div class="line"> 使用该参数值</div><div class="line">}</div></pre></td></tr></table></figure>
<p>如果需要更强大的拓展,可以使用<code>Flow</code> 或者 <code>TypeScript</code>。</p>
<h2 id="参考"><a href="#参考" class="headerlink" title="参考"></a>参考</h2><ul>
<li><a href="http://www.css88.com/react/docs/typechecking-with-proptypes.html" target="_blank" rel="noopener">使用 PropTypes 进行类型检查
</a></li>
<li><a href="https://www.npmjs.com/package/prop-types" target="_blank" rel="noopener">prop-types </a></li>
<li><a href="https://flow.org/en/docs/types/" target="_blank" rel="noopener">Flow</a></li>
<li><a href="https://www.typescriptlang.org/" target="_blank" rel="noopener">TypeScript</a></li>
</ul>
]]></content>
<summary type="html">
<h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2><p><code>JS</code>作为弱类型语言,有时候也会带来很多麻烦,比如需要的参数是<code>Number</code>
</summary>
<category term="JS" scheme="https://github.com/njafei/njafei.github.io/categories/JS/"/>
<category term="JS" scheme="https://github.com/njafei/njafei.github.io/tags/JS/"/>
<category term="PropTypes" scheme="https://github.com/njafei/njafei.github.io/tags/PropTypes/"/>
</entry>
<entry>
<title>JS Event-Loop</title>
<link href="https://github.com/njafei/njafei.github.io/2018/03/19/JS-EventLoop/"/>
<id>https://github.com/njafei/njafei.github.io/2018/03/19/JS-EventLoop/</id>
<published>2018-03-19T09:44:39.000Z</published>
<updated>2018-03-19T09:51:16.670Z</updated>
<content type="html"><![CDATA[<p>最近在看<code>JavaScript</code>的<code>Generator</code>、<code>Promise</code>等实现异步的方法。对于一直用<code>Objective-C</code>编程的我来说,是有些云里雾里。故查了些资料,了解相关的内容,遂有此文。</p>
<h2 id="为什么JavaScript是单线程的语言"><a href="#为什么JavaScript是单线程的语言" class="headerlink" title="为什么JavaScript是单线程的语言"></a>为什么<code>JavaScript</code>是单线程的语言</h2><p>首先,我们要解释下,<code>JS</code>是单线程的,并不是说<code>JS</code>中只有一个线程,而是在<code>JS</code>引擎中负责解释和执行<code>JS</code>语句的线程只有一个,我们称之为主线程。</p>
<p>在<code>JS</code>中,还是有很多其他线程存在的,比如:处理DOM事件的线程,定时器线程,IO线程等。</p>
<p>为什么<code>JS</code>中只有一个线程呢? 这里要说下<code>JS</code>诞生的背景,它是浏览器中执行脚本的语言,主要用途是和用户交互,为了提升体验,避免多线程带来的同步等问题,简化整个模型,采用了单线程。(<code>Objective-C</code>也是为了交互而生,但是并没有采用单线程,而是支持多线程,我个人觉得这个更多是设计思想的选择,而<code>JS</code>这样设计确实带来了更简单的模式)</p>
<blockquote>
<p>在HTML5中,提出了Web Worker标准,允许JS创建多个线程,但是规定子线程受主线程控制,且不能操作DOM。</p>
</blockquote>
<h2 id="JS任务管理"><a href="#JS任务管理" class="headerlink" title="JS任务管理"></a><code>JS</code>任务管理</h2><p>我们先看一张图,了解下<code>JS</code>中是如何管理和执行任务的:</p>
<p><img src="https://developer.mozilla.org/files/4617/default.svg" alt=""></p>
<p>这里分为三个区域:</p>
<ul>
<li>执行栈(主线程)</li>
<li>任务队列(其他线程)</li>
<li>堆 (对象)</li>
</ul>
<h3 id="主线程任务执行栈"><a href="#主线程任务执行栈" class="headerlink" title="主线程任务执行栈"></a>主线程任务执行栈</h3><p> 主线程的任务使用栈来实现任务执行和调用,这里用栈是因为栈的结构方便实现函数调用顺序的管理。<br> 这里简单举个例子:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line"> let funcA = (x) => {</div><div class="line"> return x * 2;</div><div class="line">}</div><div class="line"></div><div class="line">let funcB = (x) => {</div><div class="line"> return x + 2;</div><div class="line">}</div><div class="line"></div><div class="line"></div><div class="line">console.log(funcB(funcA(2)));</div></pre></td></tr></table></figure>
<p> 栈中执行顺序就是</p>
<ul>
<li>funcA</li>
<li>funcB</li>
<li>console.log</li>
</ul>
<h3 id="任务队列"><a href="#任务队列" class="headerlink" title="任务队列"></a>任务队列</h3><p>每当遇到需要异步执行的任务(请求网络、IO等),JS不会阻塞等待结果,而是往下面执行,当异步的任务执行了之后,系统发送通知出来,然后等待主线程任务栈执行完毕后,执行这些异步回调。</p>
<p>流程如图,就是所谓的<code>Event Loop</code></p>
<p><img src="http://on0hv7n2x.bkt.clouddn.com/JS%20eventLoop%E6%89%A7%E8%A1%8C%E6%B5%81%E7%A8%8B.png" alt=""></p>
<p>注意,这里即使异步的任务很快执行完毕,也会等到执行栈中的任务执行完毕后,才执行,这里用<code>setTimeout</code>来举个例子:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">console.log(1);</div><div class="line">setTimeout(()=>{console.log(2)},0)</div><div class="line">console.log(3);</div></pre></td></tr></table></figure>
<p>这里虽然延迟是0ms,但是输出依然是:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">1</div><div class="line">3</div><div class="line">2</div></pre></td></tr></table></figure>
<p>所以setTimeout等的延迟时间,其实是不准确的,如果主线程一直很忙,是不会执行到的。</p>
<h3 id="堆"><a href="#堆" class="headerlink" title="堆"></a>堆</h3><p>这里堆就是用来管理系统中的对象,大部分的语言应该都是使用堆来管理对象(我知道的),这里就不多解释。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>这篇文章主要是介绍了<code>JS</code>为什么采用了单线程的思想,然后介绍了<code>JS</code>中是如何任务管理是如何通过主线程+任务队列的方式来实现,从而实现了<code>Event Loop</code>。</p>
<h2 id="参考内容:"><a href="#参考内容:" class="headerlink" title="参考内容:"></a>参考内容:</h2><ul>
<li><a href="https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/EventLoop#%E4%BA%8B%E4%BB%B6%E5%BE%AA%E7%8E%AF" target="_blank" rel="noopener">并发模型与事件循环</a></li>
<li><a href="http://www.ruanyifeng.com/blog/2014/10/event-loop.html" target="_blank" rel="noopener">JavaScript 运行机制详解:再谈Event Loop
</a></li>
<li><a href="https://segmentfault.com/a/1190000004322358" target="_blank" rel="noopener">JavaScript:彻底理解同步、异步和事件循环(Event Loop)</a></li>
</ul>
]]></content>
<summary type="html">
<p>最近在看<code>JavaScript</code>的<code>Generator</code>、<code>Promise</code>等实现异步的方法。对于一直用<code>Objective-C</code>编程的我来说,是有些云里雾里。故查了些资料,了解相关的内
</summary>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/categories/ES6/"/>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/tags/ES6/"/>
</entry>
<entry>
<title>ES6之Map</title>
<link href="https://github.com/njafei/njafei.github.io/2018/02/28/ES6-Map/"/>
<id>https://github.com/njafei/njafei.github.io/2018/02/28/ES6-Map/</id>
<published>2018-02-28T06:09:25.000Z</published>
<updated>2018-02-28T06:12:15.015Z</updated>
<content type="html"><![CDATA[<h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2><p>在js中,对象(Object)的本质是键值对,但是它的键只能是字符串,这带来了很多不便。为了解决这个问题,ES6中提供了新的数据结构,Map。 Map和Object最大的区别就是它的key可以是对象。</p>
<p>看下初始化:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">let map = new Map();</div><div class="line">let obj = ["a", "b"];</div><div class="line">map.set(obj, "content");</div><div class="line">map.get(obj);// "content"</div></pre></td></tr></table></figure>
<p>map还可以用数组为参数直接初始化,要求数组中的元素都是标志键值对的数组</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">let map = new Map([["key1", "value1"],[["key2"], "value2"]]);</div></pre></td></tr></table></figure>
<h2 id="注意事项"><a href="#注意事项" class="headerlink" title="注意事项"></a>注意事项</h2><p>因为map和object是如此的相似,本文不再介绍set、get、has、遍历等方法,需要的话直接看下官网API即可。这里提下注意事项,因为key可以是对象了,所以如果key是对象,map寻找key依据key的内存地址,如果是简单类型,还是根据值。</p>
<p>看下例子: </p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">let map = new Map();</div><div class="line">let key3 = ["a"];</div><div class="line"></div><div class="line">map.set("a",1);</div><div class="line">map.set(["a"],1);</div><div class="line">map.set(key3,1);</div><div class="line"></div><div class="line">console.log(map.get("a")); // 1 </div><div class="line">console.log(map.get(["a"])); // undefined</div><div class="line">console.log(map.get(key3)); // 1</div></pre></td></tr></table></figure>
<h2 id="weakMap"><a href="#weakMap" class="headerlink" title="weakMap"></a>weakMap</h2><p>weakMap和weakSet有相似之处,都是为了防止内存泄露而设计,key必须是对象,也只对元素弱引用。</p>
]]></content>
<summary type="html">
<h2 id="基本用法"><a href="#基本用法" class="headerlink" title="基本用法"></a>基本用法</h2><p>在js中,对象(Object)的本质是键值对,但是它的键只能是字符串,这带来了很多不便。为了解决这个问题,ES6中提供了新的
</summary>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/categories/ES6/"/>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/tags/ES6/"/>
<category term="Map" scheme="https://github.com/njafei/njafei.github.io/tags/Map/"/>
</entry>
<entry>
<title>ES6之Set</title>
<link href="https://github.com/njafei/njafei.github.io/2018/02/28/ES6-Set/"/>
<id>https://github.com/njafei/njafei.github.io/2018/02/28/ES6-Set/</id>
<published>2018-02-28T05:44:04.000Z</published>
<updated>2018-02-28T05:44:56.416Z</updated>
<content type="html"><![CDATA[<p>在ES6中,提供了新的数据结构set,set类似于数组,但是其中的元素是不能重复的。我们先来看个例子:</p>
<h2 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h2><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">let set = new Set();</div><div class="line"></div><div class="line">[1,2,2,3,4,5,5,5].map(x => set.add(x));</div><div class="line"></div><div class="line">console.log(set.size); //5</div></pre></td></tr></table></figure>
<p>初始化也可以接受数组为入参</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">let set = new Set([1,2,3,4]);</div></pre></td></tr></table></figure>
<p>刚才讲到,set中所有的元素是不能重复的,那么,怎么定义重复呢?set使用的方法类似于 ===,只有一个特例,NaN, 精确相等任务NaN不等于自身,而set中认为是相等的。</p>
<p>看几个例子,就一目了然了:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">let set = new Set(["a", "a"]);</div><div class="line">console.log(set.size); //1</div><div class="line"></div><div class="line">let set = new Set([{},{}]);</div><div class="line">console.log(set.size); //2</div><div class="line"></div><div class="line">let a = {key: 'a'};</div><div class="line">let b = {key: 'b'};</div><div class="line">let set = new Set([a,a]);</div><div class="line">console.log(set.size); //1</div><div class="line"></div><div class="line">let set = new Set([a,{a}]);</div><div class="line">console.log(set.size); //2</div></pre></td></tr></table></figure>
<h2 id="属性"><a href="#属性" class="headerlink" title="属性"></a>属性</h2><p>属性有两个:</p>
<ol>
<li>construct 默认是Set函数</li>
<li>size, 即set中元素的数量</li>
</ol>
<h2 id="方法"><a href="#方法" class="headerlink" title="方法"></a>方法</h2><p>set有四个操作方法:</p>
<ol>
<li>add() 返回set本身 </li>
<li>delete() 返回删除是否成功的布尔值</li>
<li>has() 返回set中是否某个元素</li>
<li>clear() 情况set,没有返回值</li>
</ol>
<p>这里要注意的是,delete和has中判断两个元素是否相同的方法和初始化中的方法一致,所以尤其要注意对象的判断,基本类型基本不会有问题。</p>
<p>set有四个遍历方法:</p>
<ol>
<li>keys(): 返回所有的key</li>
<li>values(): 返回所有的value</li>
<li>entries(): 返回所有的键值对</li>
<li>forEach(): 回调函数遍历, 和数组用法一致</li>
</ol>
<p>因为set中本身没有key,value之分,所key和value都是set中元素本身,我们来看下例子:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">let set = new Set(["key1", "key2", "key3"]);</div><div class="line">console.log(set.keys());//["key1", "key2", "key3"]</div><div class="line"></div><div class="line">console.log(set.values());//["key1", "key2", "key3"]</div><div class="line"></div><div class="line">console.log(set.entries());//[{"key1": "key1"},{"key2": "key2"},{"key3": "key3"}]</div></pre></td></tr></table></figure>
<h2 id="weakSet"><a href="#weakSet" class="headerlink" title="weakSet"></a>weakSet</h2><p>weakSet和set类似,只有两个区别:</p>
<ol>
<li>元素只能是对象</li>
<li>对象都是弱引用,即js的垃圾回收机制不会考虑weakSet中的引用</li>
</ol>
<p>因为第一个特点,所以weakSet不能使用遍历、size等方法和属性(因为遍历的时候可能对象已经被释放了)</p>
<h2 id="用法"><a href="#用法" class="headerlink" title="用法"></a>用法</h2><p>set可以用来实现去重等功能,比如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">let array = [1,2,2,3];</div><div class="line">let set = Set(array);</div><div class="line"></div><div class="line">let newArray = Array.from(set);</div><div class="line">console.log(newArray); //[1,2,3]</div></pre></td></tr></table></figure>
<p>weakSet在互联网上找到的内容不多,阮一峰的《ES6标准入门》中提到,可以用来保存DOM节点,而不用担心节点移除时引起内存泄露。</p>
]]></content>
<summary type="html">
<p>在ES6中,提供了新的数据结构set,set类似于数组,但是其中的元素是不能重复的。我们先来看个例子:</p>
<h2 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h2><figure cl
</summary>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/categories/ES6/"/>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/tags/ES6/"/>
<category term="Set" scheme="https://github.com/njafei/njafei.github.io/tags/Set/"/>
</entry>
<entry>
<title>ipa包逆向重新签名打包</title>
<link href="https://github.com/njafei/njafei.github.io/2018/02/27/ipa-recodesign/"/>
<id>https://github.com/njafei/njafei.github.io/2018/02/27/ipa-recodesign/</id>
<published>2018-02-27T05:43:28.000Z</published>
<updated>2018-02-27T05:44:31.380Z</updated>
<content type="html"><![CDATA[<p>在使用了rn之后,我们很少会修改原生代码,但是如果想要打包,还是重新打包原生代码,挺花时间的,那么,只修改rn的代码情况下,有没有快速出包的方法? 答案就是今天这篇文章的主题,ipa逆向,然后替换资源,重新打包,这样只需要替换掉js的bundle包,省去了重新archive的时间了。</p>
<p>过程如下:</p>
<ol>
<li>test.ipa 改名为 test.zip</li>
<li>解开zip,打开Payload,显示test资源包,右键显示报内容,就会看到ipa中所有的资源等内容</li>
<li>找到并复制entitlements.plist文件,这个文件是重新签名的时候要使用的,其中记录的是app需要申请的权限</li>
<li>替换你想替换的资源,这里就是rn的bundle.js</li>
<li>确定你想使用的签名证书的名称,通常是:‘iPhone Distribution: Test Company’</li>
<li>重签名 <code>codesign -f -s "iPhone Distribution: Test Company" --entitlements entitlements.plist Payload/Test.app</code></li>
<li>将签名之后的文件夹压缩 <code>zip -r Test.ipa Payload</code></li>
</ol>
<p>其他用法:</p>
<p>这里讲的替换资源是重签名打包的一种用法,我在网上搜索的时候,发现其他人也生出了很多好的思路,比如: 为了防止提供AppStore的包出问题,直接使用最后的测试包,重新签名,然后上传,这里用到的就是重签名替换证书的功能。</p>
<p>综上,重签名打包,可以替换资源和证书,从而达到在不需要重新编译的情况下,替换资源的功能。</p>
]]></content>
<summary type="html">
<p>在使用了rn之后,我们很少会修改原生代码,但是如果想要打包,还是重新打包原生代码,挺花时间的,那么,只修改rn的代码情况下,有没有快速出包的方法? 答案就是今天这篇文章的主题,ipa逆向,然后替换资源,重新打包,这样只需要替换掉js的bundle包,省去了重新archive
</summary>
<category term="iOS" scheme="https://github.com/njafei/njafei.github.io/categories/iOS/"/>
<category term="iOS" scheme="https://github.com/njafei/njafei.github.io/tags/iOS/"/>
</entry>
<entry>
<title>ES6-Proxy</title>
<link href="https://github.com/njafei/njafei.github.io/2018/02/23/ES6-Proxy/"/>
<id>https://github.com/njafei/njafei.github.io/2018/02/23/ES6-Proxy/</id>
<published>2018-02-23T09:37:04.000Z</published>
<updated>2018-02-23T09:37:34.039Z</updated>
<content type="html"><![CDATA[<h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><p>proxy的作用是代理对象的某些行为,其 实质 是重载对象的方法。</p>
<p>举个例子,我们想要读取一个对象的某个属性</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">let obj = {a: 'a' , b: 'b'};</div><div class="line">console.log(obj.a); // 'a'</div></pre></td></tr></table></figure>
<p>而使用了proxy之后:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">let obj = {a: 'a', b: 'b'};</div><div class="line">let handler = {</div><div class="line"> get: fucntion(target, propoty){</div><div class="line"> return 35;</div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line">var proxy = new Proxy(obj, handler);</div><div class="line">console.log(proxy.a);// 35</div></pre></td></tr></table></figure>
<p>相当于 proxy通过handler里面的指令,代理了obj的行为。</p>
<p>proxy能代理的,并不仅仅是get函数,还有如下列表,具体的可以网上查阅下文档。</p>
<ul>
<li>get</li>
<li>set</li>
<li>has</li>
<li>deleteProperty</li>
<li>enumerate</li>
<li>hasOwn</li>
<li>ownKeys</li>
<li>getOwnPropertyDescritor</li>
<li>defineProperty</li>
<li>preventExtensions</li>
<li>getPrototypeOf</li>
<li>isExtensible</li>
<li>setPrototypeOf</li>
<li>apply</li>
<li>construct</li>
</ul>
<h2 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h2><p>看完了proxy的文档,感觉模模糊糊有了印象,但是还是对它的使用场景没有概念,网上搜索了下,发下一篇非常好的介绍使用场景的文章,地址如下: <a href="https://segmentfault.com/a/1190000006035363" target="_blank" rel="noopener">[译] 实例解析 ES6 Proxy 使用场景</a>,下面我也会使用这篇文章的例子,来介绍proxy到底怎么用。</p>
<h4 id="校验模块"><a href="#校验模块" class="headerlink" title="校验模块"></a>校验模块</h4><p>这里是重写set函数,实现校验功能</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div></pre></td><td class="code"><pre><div class="line">let numericDataStore = { </div><div class="line"> count: 0,</div><div class="line"> amount: 1234,</div><div class="line"> total: 14</div><div class="line">};</div><div class="line"></div><div class="line">numericDataStore = new Proxy(numericDataStore, { </div><div class="line"> set(target, key, value, proxy) {</div><div class="line"> if (typeof value !== 'number') {</div><div class="line"> throw Error("Properties in numericDataStore can only be numbers");</div><div class="line"> }</div><div class="line"> return Reflect.set(target, key, value, proxy);</div><div class="line"> }</div><div class="line">});</div><div class="line"></div><div class="line">// 抛出错误,因为 "foo" 不是数值</div><div class="line">numericDataStore.count = "foo";</div><div class="line"></div><div class="line">// 赋值成功</div><div class="line">numericDataStore.count = 333;</div></pre></td></tr></table></figure>
<h4 id="私有属性"><a href="#私有属性" class="headerlink" title="私有属性"></a>私有属性</h4><p>js中的私有属性,约定俗成是通过前面加下划线的方式来实现,这里可以用proxy来实现,并且做到了强制私有</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div></pre></td><td class="code"><pre><div class="line">var api = { </div><div class="line"> _apiKey: '123abc456def',</div><div class="line"> /* mock methods that use this._apiKey */</div><div class="line"> getUsers: function(){}, </div><div class="line"> getUser: function(userId){}, </div><div class="line"> setUser: function(userId, config){}</div><div class="line">};</div><div class="line"></div><div class="line">// logs '123abc456def';</div><div class="line">console.log("An apiKey we want to keep private", api._apiKey);</div><div class="line"></div><div class="line">// get and mutate _apiKeys as desired</div><div class="line">var apiKey = api._apiKey; </div><div class="line">api._apiKey = '987654321';</div></pre></td></tr></table></figure>
<h4 id="访问日志"><a href="#访问日志" class="headerlink" title="访问日志"></a>访问日志</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line">let api = { </div><div class="line"> _apiKey: '123abc456def',</div><div class="line"> getUsers: function() { /* ... */ },</div><div class="line"> getUser: function(userId) { /* ... */ },</div><div class="line"> setUser: function(userId, config) { /* ... */ }</div><div class="line">};</div><div class="line"></div><div class="line">function logMethodAsync(timestamp, method) { </div><div class="line"> setTimeout(function() {</div><div class="line"> console.log(`${timestamp} - Logging ${method} request asynchronously.`);</div><div class="line"> }, 0)</div><div class="line">}</div><div class="line"></div><div class="line">api = new Proxy(api, { </div><div class="line"> get: function(target, key, proxy) {</div><div class="line"> var value = target[key];</div><div class="line"> return function(...arguments) {</div><div class="line"> logMethodAsync(new Date(), key);</div><div class="line"> return Reflect.apply(value, target, arguments);</div><div class="line"> };</div><div class="line"> }</div><div class="line">});</div><div class="line"></div><div class="line">api.getUsers();</div></pre></td></tr></table></figure>
<h4 id="预警和拦截"><a href="#预警和拦截" class="headerlink" title="预警和拦截"></a>预警和拦截</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div></pre></td><td class="code"><pre><div class="line">let dataStore = { </div><div class="line"> noDelete: 1235,</div><div class="line"> oldMethod: function() {/*...*/ },</div><div class="line"> doNotChange: "tried and true"</div><div class="line">};</div><div class="line"></div><div class="line">const NODELETE = ['noDelete']; </div><div class="line">const NOCHANGE = ['doNotChange'];</div><div class="line">const DEPRECATED = ['oldMethod']; </div><div class="line"></div><div class="line">dataStore = new Proxy(dataStore, { </div><div class="line"> set(target, key, value, proxy) {</div><div class="line"> if (NOCHANGE.includes(key)) {</div><div class="line"> throw Error(`Error! ${key} is immutable.`);</div><div class="line"> }</div><div class="line"> return Reflect.set(target, key, value, proxy);</div><div class="line"> },</div><div class="line"> deleteProperty(target, key) {</div><div class="line"> if (NODELETE.includes(key)) {</div><div class="line"> throw Error(`Error! ${key} cannot be deleted.`);</div><div class="line"> }</div><div class="line"> return Reflect.deleteProperty(target, key);</div><div class="line"></div><div class="line"> },</div><div class="line"> get(target, key, proxy) {</div><div class="line"> if (DEPRECATED.includes(key)) {</div><div class="line"> console.warn(`Warning! ${key} is deprecated.`);</div><div class="line"> }</div><div class="line"> var val = target[key];</div><div class="line"></div><div class="line"> return typeof val === 'function' ?</div><div class="line"> function(...args) {</div><div class="line"> Reflect.apply(target[key], target, args);</div><div class="line"> } :</div><div class="line"> val;</div><div class="line"> }</div><div class="line">});</div><div class="line"></div><div class="line">// these will throw errors or log warnings, respectively</div><div class="line">dataStore.doNotChange = "foo"; </div><div class="line">delete dataStore.noDelete; </div><div class="line">dataStore.oldMethod();</div></pre></td></tr></table></figure>
<h4 id="过滤操作"><a href="#过滤操作" class="headerlink" title="过滤操作"></a>过滤操作</h4><p>通过特征来过滤不必要的操作,例如下载中,解析中等中间过程,减少响应的压力</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div></pre></td><td class="code"><pre><div class="line">let obj = { </div><div class="line"> getGiantFile: function(fileId) {/*...*/ }</div><div class="line">};</div><div class="line"></div><div class="line">obj = new Proxy(obj, { </div><div class="line"> get(target, key, proxy) {</div><div class="line"> return function(...args) {</div><div class="line"> const id = args[0];</div><div class="line"> let isEnroute = checkEnroute(id);</div><div class="line"> let isDownloading = checkStatus(id); </div><div class="line"> let cached = getCached(id);</div><div class="line"></div><div class="line"> if (isEnroute || isDownloading) {</div><div class="line"> return false;</div><div class="line"> }</div><div class="line"> if (cached) {</div><div class="line"> return cached;</div><div class="line"> }</div><div class="line"> return Reflect.apply(target[key], target, args);</div><div class="line"> }</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>]]></content>
<summary type="html">
<h2 id="语法"><a href="#语法" class="headerlink" title="语法"></a>语法</h2><p>proxy的作用是代理对象的某些行为,其 实质 是重载对象的方法。</p>
<p>举个例子,我们想要读取一个对象的某个属性</p>
<fig
</summary>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/categories/ES6/"/>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/tags/ES6/"/>
</entry>
<entry>
<title>ES6-Symbols</title>
<link href="https://github.com/njafei/njafei.github.io/2018/02/22/ES6-Symbols/"/>
<id>https://github.com/njafei/njafei.github.io/2018/02/22/ES6-Symbols/</id>
<published>2018-02-22T05:57:42.000Z</published>
<updated>2018-02-22T06:00:11.628Z</updated>
<content type="html"><![CDATA[<p>#背景<br>ES5中,属性都可以通过点方法来获取和赋值。这也带来了一个问题,当我想给一个类增加属性的时候,除非我看完这个类的所有细节,否则我很难知道一个属性名是否已经被占用。而ES6为了解决这个问题,引入了一个新的原始类型Symbol,它表示独一无二的值,来避免重复的命名。</p>
<p>加入Symbol之后,JS中的基础类型共有:</p>
<ul>
<li>undefined</li>
<li>Null</li>
<li>Boolean</li>
<li>String</li>
<li>Number</li>
<li>Object</li>
<li>Symbol</li>
</ul>
<p>#使用</p>
<h3 id="初始化"><a href="#初始化" class="headerlink" title="初始化"></a>初始化</h3><p>Symbol的初始化方法很简单,但是要注意,它不是对象(Object),所以不能用new初始化</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">let s = Symbol();</div></pre></td></tr></table></figure>
<p>Symbol没有属性,是一个类似于string的数据类型,我的理解,Symbol其实就是独一无二的string。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">s.name = 'symbol'; //error</div></pre></td></tr></table></figure>
<p>Symbol可以通过加参数的形式来区分不同的Symbol,如下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">let s = Symbol('foo');</div></pre></td></tr></table></figure>
<p>注意,即使使用相同的参数来初始化,得到的实例也是不同的,比如:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div></pre></td><td class="code"><pre><div class="line">let a = Symbol('foo');</div><div class="line">let b = Symbol('foo');</div><div class="line"></div><div class="line">let same = a === b;</div><div class="line"></div><div class="line">console.log(same); //false</div></pre></td></tr></table></figure>
<p>如果想要获取以某个string为参数的Symbol,在之后会讲到Symbol.for()函数。</p>
<h3 id="赋值"><a href="#赋值" class="headerlink" title="赋值"></a>赋值</h3><p>使用Symbol给属性赋值,用法和string属性赋值基本相似,但是不可以用<code>.</code>,要用<code>[]</code>。以下是三种赋值方法:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div></pre></td><td class="code"><pre><div class="line">let s = Symbol();</div><div class="line"></div><div class="line"></div><div class="line">let a = {};</div><div class="line"></div><div class="line">// method 1</div><div class="line">a[s] = 'hello';</div><div class="line"></div><div class="line">//method 2</div><div class="line">a = {</div><div class="line"> [s] = 'hello';</div><div class="line">}</div><div class="line"></div><div class="line">//method 3</div><div class="line"></div><div class="line">Object.defineProperty(a, s, {value: 'hello'});</div></pre></td></tr></table></figure>
<h3 id="使用场景"><a href="#使用场景" class="headerlink" title="使用场景"></a>使用场景</h3><p>Symbol的一个场景是上述提到的,给一个类增加属性,而避免属性重复。</p>
<p>第二是使用在常量中,<code>switch case</code>来保证唯一性。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div></pre></td><td class="code"><pre><div class="line">let CASE1 = Symbol();</div><div class="line">let CASE2 = Symbol();</div><div class="line"></div><div class="line">switch (case) {</div><div class="line"> case CASE1 :{</div><div class="line"> return 1;</div><div class="line"> }</div><div class="line"> case CASE2 :{</div><div class="line"> return 2;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h1 id="注意事项(和string属性不同的地方)"><a href="#注意事项(和string属性不同的地方)" class="headerlink" title="注意事项(和string属性不同的地方)"></a>注意事项(和string属性不同的地方)</h1><p>最主要的区别,使用 <code>for ... in</code>、 <code>for ... of</code> 或者<code>Object.keys()</code>、 <code>Object.getOwnPropertyNames()</code>不会返回Symbol</p>
<p>如果想获取对象的所有Symbols,使用函数<code>getOwnPropertySymbols</code></p>
<p>有个新的API,<code>Reflect.ownKyes</code>,会返回所有的String属性和Symbol属性</p>
<h1 id="Symbol-keyFor和Symbol-for"><a href="#Symbol-keyFor和Symbol-for" class="headerlink" title="Symbol.keyFor和Symbol.for"></a>Symbol.keyFor和Symbol.for</h1><h3 id="Symbol-for"><a href="#Symbol-for" class="headerlink" title="Symbol.for"></a>Symbol.for</h3><p>Symbol会返回一个新的实例,无论参数是什么,上面已经将到了。</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">let a = Symbol('foo');</div><div class="line">let b = Symbol('foo');</div><div class="line"></div><div class="line">let same = a === b; //false</div></pre></td></tr></table></figure>
<p>而Symbol.for则会先搜索是否有以这个参数初始化的Symbol,如果有则放回这个Symbol,否则初始化一个新的,实现逻辑如下</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div></pre></td><td class="code"><pre><div class="line">Symbol.for(params) = {</div><div class="line"> if (searchSymbolInitWithParams){</div><div class="line"> return found_symbol;</div><div class="line"> } else {</div><div class="line"> return Symbol(params);</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>和第一个例子对比看下:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">let a = Symbol.for('foo');</div><div class="line">let b = Symbol.for('foo');</div><div class="line"></div><div class="line">let same = a === b; //true</div></pre></td></tr></table></figure>
<p>如果Symbol和Symbol.for混用呢?</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">let a = Symbol('foo');</div><div class="line">let b = Symbol.for('foo');</div><div class="line"></div><div class="line">console.log(a===b); //false</div></pre></td></tr></table></figure>
<p>这说明两者是没有打通的,即Symbol初始化的实例无法被Symbol.for找到</p>
<h3 id="Symbol-keyFor"><a href="#Symbol-keyFor" class="headerlink" title="Symbol.keyFor"></a>Symbol.keyFor</h3><p>用Symbol.for来初始化的Symbol,如何知道它的初始化参数呢,这里提供了一个方法: Symbol.keyFor</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">let a = Symbol.for('foo');</div><div class="line">let b = Symbol.Keyfor(a);// 'foo'</div></pre></td></tr></table></figure>
<p>注意,Symbol初始化不能用这个函数</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">let a = Symbol('foo');</div><div class="line">let b = Symbol.Keyfor(a);// undefined</div></pre></td></tr></table></figure>
<p>注意:Symbol.for 为Symbol值登记的是全局的,这意味着在不同的iframe都可以取到同一个值。</p>
]]></content>
<summary type="html">
<p>#背景<br>ES5中,属性都可以通过点方法来获取和赋值。这也带来了一个问题,当我想给一个类增加属性的时候,除非我看完这个类的所有细节,否则我很难知道一个属性名是否已经被占用。而ES6为了解决这个问题,引入了一个新的原始类型Symbol,它表示独一无二的值,来避免重复的命名
</summary>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/categories/ES6/"/>
<category term="ES6" scheme="https://github.com/njafei/njafei.github.io/tags/ES6/"/>
</entry>
<entry>
<title>React Native性能优化</title>
<link href="https://github.com/njafei/njafei.github.io/2017/10/25/RNpreformance/"/>
<id>https://github.com/njafei/njafei.github.io/2017/10/25/RNpreformance/</id>
<published>2017-10-25T03:00:45.000Z</published>
<updated>2017-10-25T03:03:48.201Z</updated>
<content type="html"><![CDATA[<h2 id="关于帧"><a href="#关于帧" class="headerlink" title="关于帧"></a>关于帧</h2><p>在讲述如何优化性能之前,我们要先了解一下性能的概念。对一个从未接触过相关概念的人来说,所谓的性能就是当你去使用App的时候很流畅,点击、跳转等交互效果反应很快,而且很顺滑。这是感性的角度,那么理性的角度或者数据的角度看待呢?</p>
<p>这里要介绍一个概念: 帧率。我们知道,所谓的动画或者电影,其实归根结底就是在一秒内快速闪过多张不同的图片,如果快到一定的程度,肉眼会误以为里面的动画都是连贯的。在iOS等设备,标准是每秒60帧(即每秒连续展示60张图片),这个标准足以保证用户的体验。</p>
<p><img src="https://gss0.baidu.com/94o3dSag_xI4khGko9WTAnF6hhy/zhidao/pic/item/d1160924ab18972be457999ee2cd7b899e510a70.jpg" alt="动画示意"></p>
<p>系统会每16.6毫秒询问你下一帧的数据,如果你正在处理比较复杂的任务,则系统会默认这一帧内容保持不变,即出现了丢帧的现象。如果丢的帧比较多,则界面会看起来卡顿,比如用户点击了按钮,但是没有反馈。</p>
<p>对<code>React Native</code>来说,帧分为两种: <code>JavaScript</code>帧和主线程帧(<code>UI</code>帧)</p>
<h3 id="JavaScript帧"><a href="#JavaScript帧" class="headerlink" title="JavaScript帧"></a>JavaScript帧</h3><p><code>React Native</code>大部分的业务处理,都是在<code>JavaScript</code>帧中进行,包括API调用和触摸等交互的处理。那么当处理比较复杂的任务,比如<code>setState</code>然后<code>render</code>,则很可能会丢帧。或者做由JS处理的动画时,也极容易出现丢帧卡顿。</p>
<h3 id="主线程帧(UI帧)"><a href="#主线程帧(UI帧)" class="headerlink" title="主线程帧(UI帧)"></a>主线程帧(<code>UI</code>帧)</h3><p>iOS的主线程是UI线程,所以在iOS的UI效果基本是非常出色的,这也是为什么<code>NavigatorIOS</code>比<code>Navigator</code>的性能好很多的原因(<code>NavigatorIOS</code>是主线程处理,而<code>Navigator</code>是JS线程处理)</p>
<h3 id="如何查看帧数据"><a href="#如何查看帧数据" class="headerlink" title="如何查看帧数据"></a>如何查看帧数据</h3><p>我们可以通过打开<code>RN</code>的<code>Debug</code>菜单,然后选择<code>Show Perf Monitor</code>来查看当前页面的<code>JS</code>帧和主线程帧。</p>
<p>好了,性能的定义和我们评估性能标准已经知道了,下面我们来看下影响性能的因素。</p>
<h2 id="影响性能的因素和提升方案"><a href="#影响性能的因素和提升方案" class="headerlink" title="影响性能的因素和提升方案"></a>影响性能的因素和提升方案</h2><h3 id="宽泛的原因"><a href="#宽泛的原因" class="headerlink" title="宽泛的原因"></a>宽泛的原因</h3><p>我们先抛开RN或者iOS的前端框架,看下对一台带屏幕的设备来说,影响性能的原因有哪些。</p>
<p>我们用金字塔模式来看,首先最明显的有两个</p>
<ul>
<li>设备性能</li>
<li>程序设计</li>
</ul>
<p>从根本上来说,设备性能是最大的瓶颈,不过这个我们程序员暂时无能为力。而程序设计可能引起性能差的有哪些呢?判断程序设计的一个标准就是复杂度, 而复杂度又分为两个:</p>
<ul>
<li>时间复杂度</li>
<li>空间复杂度</li>
</ul>
<p>这时我们的性能因素树是:</p>
<ul>
<li>设备性能</li>
<li>程序设计<ul>
<li>时间复杂度</li>
<li>空间复杂度</li>
</ul>
</li>
</ul>
<p>在设备空间充足的情况下,主要的影响就是时间复杂度,而时间复杂度高的原因有几个:</p>
<ul>
<li>前端方案不合理,UI层次或者顺序设计不合理,浪费性能</li>
<li>使用的算法过于复杂</li>
</ul>
<p>我们挨个说下这两个个问题,前端方案是最容易造成性能不好的原因,比如我们有屏幕上有几个区域,互相之间没有影响,而由于不合理的设计,在一个区域变化的时候,要刷新这个界面,就会出现卡顿。一个合理的前端方案,应该是尽可能减少页面的刷新频率和刷新范围,保证每帧的计算是相对小的。</p>
<p>而算法过于复杂,则是算法消耗的时间太长,影响了UI的渲染。比如使用了圈复杂度非常高的算法,或者有大量的数据要不停地计算。</p>
<p>这样我们的性能因素树变成了:</p>
<ul>
<li>设备性能</li>
<li>程序设计<ul>
<li>空间复杂度</li>
<li>时间复杂度<ul>
<li>前端方案不合理,UI层次或者顺序设计不合理,浪费性能<ul>
<li>使用的算法过于复杂</li>
</ul>
</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>可能还会有人觉得网络等原因会造成卡顿,但是我觉得如果交互设计良好,网络状况不好的话,只会影响数据出现的时间长,而不会造成页面的卡顿。</p>
<p>看完了比较普遍宽泛的原因,我们看下针对RN的</p>
<h3 id="RN的特有原因"><a href="#RN的特有原因" class="headerlink" title="RN的特有原因"></a>RN的特有原因</h3><h4 id="JS-Bridge的效率"><a href="#JS-Bridge的效率" class="headerlink" title="JS Bridge的效率"></a>JS Bridge的效率</h4><p>虽然官方的文章里没有写,但是从我测试看到的数据来看,虽然RN的性能比较接近Native,但是因为JS是运行在子线程中的,所以处理大量数据或者动画的时候,JS的帧数会比较少。</p>
<p>这个我们暂时无能为力</p>
<h4 id="动画和Touchable组件在JS线程中运行"><a href="#动画和Touchable组件在JS线程中运行" class="headerlink" title="动画和Touchable组件在JS线程中运行"></a>动画和Touchable组件在JS线程中运行</h4><p>Animated和Touchable系列组件都是在JS中运行,所以在处理复杂动画或者复杂操作的时候,会出现卡顿。</p>
<p>这里给几个建议:</p>
<ul>
<li><p>关于导航</p>
<ol>
<li>iOS上使用<code>NavigatorIOS</code>替换<code>Navigator</code>,同时,react也推出了新的<a href="https://reactnavigation.org/" target="_blank" rel="noopener">Navigation库</a>希望解决导航卡顿的问题</li>
<li>push的新界面的动画,使用<code>InteractionManager</code>,就是在导航动画结束后执行新的动画,而不是同时执行</li>
</ol>
</li>
<li><p>关于动画</p>
<ol>
<li>如果<code>Animated</code>的效果不能接受,使用<code>LayoutAnimation</code>,它是基于<code>Core Animation</code></li>
<li>android上面尽量少用动画(真的很卡0_0)</li>
</ol>
</li>
</ul>
<h4 id="其他建议"><a href="#其他建议" class="headerlink" title="其他建议"></a>其他建议</h4><ul>
<li>使用<a href="">PureComponent</a></li>
<li>使用 <code>shouldComponentUpdate</code>函数,这个函数默认返回true,但是我们可以通过自定义来优化重新绘制的逻辑</li>
<li>release 去除console.log()</li>
</ul>
<figure class="highlight plain"><figcaption><span>(!__DEV__) {</span></figcaption><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line"> global.console = {</div><div class="line"> info: () => {},</div><div class="line"> log: () => {},</div><div class="line"> warn: () => {},</div><div class="line"> debug: () => {},</div><div class="line"> error: () => {},</div><div class="line"> };</div><div class="line">}</div></pre></td></tr></table></figure>
<h4 id="最重要的建议"><a href="#最重要的建议" class="headerlink" title="最重要的建议"></a>最重要的建议</h4><ol>
<li>仔细考虑UI的设计</li>
<li>仔细考虑UI的设计</li>
<li>仔细考虑UI的设计</li>
</ol>
<p>一个好的UI设计方案,是可以抵过上面所有的建议加起来的效果的,所以一定要仔细考虑再动手。</p>
<p>参考:</p>
<ul>
<li><a href="http://reactnative.cn/docs/0.49/performance.html#content" target="_blank" rel="noopener">官方文档:性能</a></li>
</ul>
]]></content>
<summary type="html">
<h2 id="关于帧"><a href="#关于帧" class="headerlink" title="关于帧"></a>关于帧</h2><p>在讲述如何优化性能之前,我们要先了解一下性能的概念。对一个从未接触过相关概念的人来说,所谓的性能就是当你去使用App的时候很流畅,点
</summary>
<category term="React-Native" scheme="https://github.com/njafei/njafei.github.io/categories/React-Native/"/>
<category term="React-Native" scheme="https://github.com/njafei/njafei.github.io/tags/React-Native/"/>
<category term="performance" scheme="https://github.com/njafei/njafei.github.io/tags/performance/"/>
</entry>
<entry>
<title>Next增加搜索localSearch</title>
<link href="https://github.com/njafei/njafei.github.io/2017/10/19/Next-add-localSearch/"/>
<id>https://github.com/njafei/njafei.github.io/2017/10/19/Next-add-localSearch/</id>
<published>2017-10-19T08:06:51.000Z</published>
<updated>2017-10-19T08:12:28.931Z</updated>
<content type="html"><![CDATA[<p>Next本身增加localSearch很简单,三步即可:</p>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>安装 hexo-generator-searchdb,在站点的根目录下执行以下命令:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">$ npm install hexo-generator-searchdb --save</div></pre></td></tr></table></figure>
<p>编辑 站点配置文件,新增以下内容到任意位置:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div></pre></td><td class="code"><pre><div class="line">search:</div><div class="line"> path: search.xml</div><div class="line"> field: post</div><div class="line"> format: html</div><div class="line"> limit: 10000</div></pre></td></tr></table></figure>
<p>编辑 主题配置文件,启用本地搜索功能:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line"># Local search</div><div class="line">local_search:</div><div class="line"> enable: true</div></pre></td></tr></table></figure>
<h2 id="遇到问题,一直loading"><a href="#遇到问题,一直loading" class="headerlink" title="遇到问题,一直loading"></a>遇到问题,一直loading</h2><p>发布了之后,点击搜索一直在loading,查了其他人的文章<a href="https://www.v2ex.com/amp/t/298727" target="_blank" rel="noopener">Hexo next 主题的 local search 功能失效,点击搜索链接无法弹出叠加层
</a>,发现是<code>search.xml</code>出现了问题。</p>
<p>排查问题步骤:</p>
<ol>
<li>command + option + J 打开调试器</li>
<li>点击NetWork,发现卡在了<code>search.xml</code>上面</li>
<li>尝试debug <code>serach.xml</code>文件,打开 <a href="https://njafei.github.io/search.xml" target="_blank" rel="noopener">https://njafei.github.io/search.xml</a></li>
<li>看到警告: <code>error on line 92 at column 35: Input is not proper UTF-8, indicate encoding ! 0x10 0xE6 0x88 0x96</code></li>
<li>编码问题,找到对应的文章,先对其进行<code>utf-8</code>编码,然后放到sublimeText中,我发现中间多了一个类似于<code>DEL</code>的乱码,删除即可</li>
<li>重新发布,搜索可用</li>
</ol>
<h2 id="缓存问题"><a href="#缓存问题" class="headerlink" title="缓存问题"></a>缓存问题</h2><p>search有时候会有缓存,这时候,可以打开对应的<code>search.xml</code>文件<a href="https://njafei.github.io/search.xml" target="_blank" rel="noopener">https://njafei.github.io/search.xml</a>,刷新几下即可</p>
<blockquote>
<p>PS: 如果你想刷新你的文件,把前面的链接替换即可。</p>
</blockquote>
]]></content>
<summary type="html">
<p>Next本身增加localSearch很简单,三步即可:</p>
<h2 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h2><p>安装 hexo-generator-searchdb,在站点的根目录
</summary>
<category term="博客搭建和功能增强" scheme="https://github.com/njafei/njafei.github.io/tags/%E5%8D%9A%E5%AE%A2%E6%90%AD%E5%BB%BA%E5%92%8C%E5%8A%9F%E8%83%BD%E5%A2%9E%E5%BC%BA/"/>
</entry>
<entry>
<title>ImageOptm 自动化无损优化图片</title>
<link href="https://github.com/njafei/njafei.github.io/2017/09/26/ImageOptm/"/>
<id>https://github.com/njafei/njafei.github.io/2017/09/26/ImageOptm/</id>
<published>2017-09-26T03:01:58.000Z</published>
<updated>2017-10-19T03:37:16.720Z</updated>
<content type="html"><![CDATA[<p>iOS控制包的大小对于公司来说,是个非常重要的事情。因为苹果公司对于包的大小超过100M的,不会允许用户使用移动网络来下载,这很可能造成商业上的损失。</p>
<p>而在控制包的大小中,一个很重要的原则就是禁止大图片。之前研究其他公司的ipa包的时候,就出现过一个icon高达1M的事故。而平时,虽然程序员们百般小心,难免被设计师暗算给张大图(玩笑),所以靠人终究不是一个可持续的保证质量的方法。</p>
<p>平时用的无损压缩最多的工具是<code>ImageOptm</code>,它可以无损压缩图片(即用户看起来感官无变化,而图片尽可能小),而且它是提供命令行的,<a href="https://imageoptim.com/command-line.html" target="_blank" rel="noopener">ImageOptm command line</a>.</p>
<p>我们就可以通过Jenkins的定时任务,每天凌晨去优化所有的图片,然后再自动commit上传。</p>
<p>具体任务就很简单了,大概几个步骤:</p>
<p>1、Jenkins开个定时任务,更新代码</p>
<p>2、执行ImageOptm脚本</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">/Applications/ImageOptim.app/Contents/MacOS/ImageOptim $WORKSPACE/</div></pre></td></tr></table></figure>
<p>3、上传代码</p>
]]></content>
<summary type="html">
<p>iOS控制包的大小对于公司来说,是个非常重要的事情。因为苹果公司对于包的大小超过100M的,不会允许用户使用移动网络来下载,这很可能造成商业上的损失。</p>
<p>而在控制包的大小中,一个很重要的原则就是禁止大图片。之前研究其他公司的ipa包的时候,就出现过一个icon高
</summary>
<category term="自动化" scheme="https://github.com/njafei/njafei.github.io/tags/%E8%87%AA%E5%8A%A8%E5%8C%96/"/>
</entry>
<entry>
<title>Java对空格的encode格式问题</title>
<link href="https://github.com/njafei/njafei.github.io/2017/09/22/Java-space-encode/"/>
<id>https://github.com/njafei/njafei.github.io/2017/09/22/Java-space-encode/</id>
<published>2017-09-22T08:57:41.000Z</published>
<updated>2017-10-19T03:37:16.721Z</updated>
<content type="html"><![CDATA[<p>今天发现了一个非常奇怪的问题,服务端给了一个encode的url,内容是位“1 1”:</p>
<p>encode之后是</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">1+1</div></pre></td></tr></table></figure>
<p>前端decode之后是</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">1+1</div></pre></td></tr></table></figure>
<p>可是在其他的浏览器等decode之后的结果是:</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">1 1</div></pre></td></tr></table></figure>
<p>发现java的encode有个坑,在java中,encode遵循的标准是<a href="http://www.faqs.org/rfcs/rfc1738.html" target="_blank" rel="noopener">rfc1738</a>,而在iOS中,encode遵循的标准是<a href="http://www.faqs.org/rfcs/rfc2396.html" target="_blank" rel="noopener">rfc2396</a>,两者对于空格的encode不同</p>
<figure class="highlight plain"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">rfc1738 => +</div><div class="line">rfc2396 => %20</div></pre></td></tr></table></figure>
<p>所以今后和Java服务端合作的时候,如果encode之后的文案出现了+号这种莫名奇妙的bug,记得让他们检查下encode的代码,可以通过替换来解决这个问题。</p>
]]></content>
<summary type="html">
<p>今天发现了一个非常奇怪的问题,服务端给了一个encode的url,内容是位“1 1”:</p>