-
Notifications
You must be signed in to change notification settings - Fork 0
/
search.xml
1050 lines (921 loc) · 184 KB
/
search.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
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"?>
<search>
<entry>
<title><![CDATA[JavaScript引擎]]></title>
<url>https://leowang721.github.io/2017/03/21/js/javascript-engine/</url>
<content type="html"><![CDATA[<p>JavaScript引擎是能够将JavaScript代码处理并执行的运行环境,不要把它跟浏览器内核搞混了!内核(如Webkit、Trident、Gecko等)主要是将页面转变成可视化的图像结果,即渲染出来,所以通常也被称为渲染引擎。</p>
<p>渲染引擎和JavaScript引擎是浏览器处理网页过程的两个核心部分,它们是相互合作的关系。</p>
<p>一般来说,渲染引擎根据JavaScript提供的桥接接口提供给JavaScript访问DOM的能力,很多HTML5也是通过桥接接口实现的。</p>
<p>桥接接口复杂而不高效往往是一个性能瓶颈,所以才说DOM的频繁使用会导致性能问题。</p>
<h2 id="常见的JavaScript引擎"><a href="#常见的JavaScript引擎" class="headerlink" title="常见的JavaScript引擎"></a>常见的JavaScript引擎</h2><p>排名不分先后。</p>
<h3 id="v8"><a href="#v8" class="headerlink" title="v8"></a>v8</h3><p>Google开源的使用C++编写的引擎,有其他语言(如Java)的封装,<code>BSD</code>协议,自体还有其他协议。</p>
<a id="more"></a>
<p><a href="https://github.com/v8/v8-git-mirror" target="_blank" rel="external">Github</a> <a href="https://github.com/v8/v8/wiki" target="_blank" rel="external">Wiki</a></p>
<p><code>Chrome</code>、<code>Chromium</code>都是使用v8的,另外<code>Android</code>上的浏览器默认也是使用v8作为js引擎,当然还有<code>Node.js</code>。</p>
<p>还有个有趣的: <a href="https://github.com/sdesalas/trifleJS" target="_blank" rel="external">TrifleJS</a>,一个使用 .NET WebBrowser Class 及 v8引擎的IE浏览器。</p>
<h3 id="JavaScriptCore"><a href="#JavaScriptCore" class="headerlink" title="JavaScriptCore"></a>JavaScriptCore</h3><p>是WebKit的默认JavaScript引擎,它其实是跟着 Webkit 一起发布的,源码在Source/JavaScript,东家是苹果,<code>BSD</code>/<code>LGPL</code>。</p>
<p>它有不少别名: <code>SquirrelFish</code>,<code>SquirrelFish Extreme</code>,在<code>Safari</code>中还有<code>Nitro</code>,<code>Nitro Extreme</code>,但其实项目、库的名字始终都是JavaScriptCore。</p>
<p><a href="http://trac.webkit.org/browser/trunk/Source/JavaScriptCore" target="_blank" rel="external">Code</a> <a href="http://trac.webkit.org/wiki/JavaScriptCore" target="_blank" rel="external">Wiki</a></p>
<p><code>Safari</code>使用JavaScriptCore,无论是PC还是M。<code>PhantomJS</code>是基于<code>Webkit</code>搞出来的,也是使用的JavaScriptCore。</p>
<p>iOS的开发能够直接用 JavaScriptCore 的,一家的。</p>
<h3 id="Chakra"><a href="#Chakra" class="headerlink" title="Chakra"></a>Chakra</h3><p>微软系的引擎,微软以<code>ChakraCore</code>为名在Github上开放了源代码,<code>MIT</code>协议。</p>
<p>IE9+浏览器及Edge都是使用Chakra,Win10的应用也是使用了它。</p>
<h3 id="SpiderMonkey-OdinMonkey"><a href="#SpiderMonkey-OdinMonkey" class="headerlink" title="SpiderMonkey / OdinMonkey"></a>SpiderMonkey / OdinMonkey</h3><p>Mozilla系的就是多,标题取了第一个和最新的一个……</p>
<p>SpiderMonkey,第一款JavaScript引擎,由Brendan Eich在Netscape Communications时编写,用于Mozilla Firefox 1.0~3.0版本。</p>
<p>Rhino,由Mozilla基金会管理,开放源代码,完全以Java编写。</p>
<p>TraceMonkey,基于实时编译的引擎,其中部份代码取自Tamarin引擎,用于Mozilla Firefox 3.5~3.6版本。</p>
<p>JaegerMonkey,德文Jäger原意为猎人,结合追踪和组合码技术大幅提高性能,部分技术借凿了V8、JavaScriptCore、WebKit,用于Mozilla Firefox 4.0以上版本。</p>
<p>IonMonkey,可以对JavaScript编译后的结果进行优化,用于Mozilla Firefox 18.0以上版本。</p>
<p>OdinMonkey,可以对asm.js进行优化,用于Mozilla Firefox 22.0以上版本。</p>
<h3 id="Carakan"><a href="#Carakan" class="headerlink" title="Carakan"></a>Carakan</h3><p>自Opera10.50版本开始使用。</p>
<h2 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h2><h3 id="JerryScript"><a href="#JerryScript" class="headerlink" title="JerryScript"></a>JerryScript</h3><p>三星搞出的一个用于物联网的引擎,可以运行在受限制的设备上,例如微控制器:</p>
<p>只有几 KB RAM 能运行引擎的设备(<64 KB RAM)</p>
<p>只能为代码引擎提供有限 ROM 空间的设备(<200 KB ROM)</p>
<p>该引擎支持设备上编译,提供从 JavaScript 到外设的访问</p>
]]></content>
<categories>
<category> 前端 </category>
</categories>
<tags>
<tag> 引擎 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[高性能的JavaScript开发]]></title>
<url>https://leowang721.github.io/2017/03/21/js/effective-coding/</url>
<content type="html"><![CDATA[<p>引擎暂时只是v8, JavaScriptCore、SpiderMonkey、Chakra、Carakan等等这些回头再看看做做对比啥的吧……</p>
<p>其他语言是以C/C++为例。</p>
<p>测试环境为Mac,并列出浏览器 or 环境的版本</p>
<ul>
<li>Chrome: 56.0.2924.87 (64-bit)</li>
<li>Firefox: 51.0.1 (64 位)</li>
<li>Safari: 10.0.3 (12602.4.8)</li>
<li>Node.js: v7.7.2</li>
</ul>
<p>持续更新中… 只是个持续更新的笔记而已,只是希望能说明白罢了!!</p>
<h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>C的访问机制,基本是编译确定了位置,偏移信息共享,使用时直接使用偏移量,所以非常高效。而js则不然,它在执行阶段才能确定结构,而且还能够增减对象的属性,在查找值时需要匹配属性名才能找到正确的值,这是很浪费时间的。</p>
<p>不过引擎们做了很多努力,已经在逐步接近其他语言的性能了,例如隐藏类。</p>
<p>这里说到的东西目的主要是为了避免让引擎的优化被浪费,甚至是倒退。</p>
<p>其实呢,对于WebGL这类的貌似更有用一些,对于一般开发倒是意义不大。</p>
<a id="more"></a>
<h2 id="数据类型"><a href="#数据类型" class="headerlink" title="数据类型"></a>数据类型</h2><p>在v8中,除了基础类型 <code>Boolean</code>、<code>Number</code>、<code>String</code>、<code>Null</code>和<code>Undefined</code>以外,其他都是对象。</p>
<p>在v8中,数据以两种形式来表示:</p>
<ul>
<li>值:例如 String、对象等,它们是变长的,而且内容的类型也不一样。</li>
<li>句柄:大小是固定的,句柄中包含了指向数据的指针</li>
</ul>
<p>v8实际上将所有的数据交由GC管理的,被定义在内部,我们不能直接操作,只能通过<code>Handle</code>(句柄类)来进行操作,所以访问的时候都是先找到<code>Handle</code>,再通过指针去访问实际的值,修改的时候也是修改<code>Handle</code>中的指针。</p>
<blockquote>
<p>除了极少数的数据例如整型,其他的内容都是从堆里申请内存来存储的。</p>
</blockquote>
<p><code>Handle</code>本身能够存放特定数据,这也就使得这些数据不需要再从堆中分配,这也就减少了内存的使用并增加了访问速度。</p>
<p>在v8中,<code>Handle</code>对象的大小是4/8字节(32/64位机器),在JavaScriptCore中是8字节。JavaScript的数字尽管按照标准都应是64位浮点格式来表示,但是对于整数实际的操作是基于32位整数,这也就意味着可以在<code>Handle</code>中直接存储整数以达到快速访问的目的。</p>
<p>但针对着32位的<code>Handle</code>对象而言,它至少要区分存储的数据是整数还是指针,而指针本身的最后两位都是00,其实是不需要的,所以,就用这两位来表示句柄中包含的数据的类型,最后一位如果为0表示是整数,如果是1则表示是其他类型。</p>
<p>所以v8中实际能够直接快速使用的整数是<code>小整数</code>,31位的有符号整数。</p>
<p>v8中的<code>Handle</code>数据表示:<br><img src="/images/learning/v8/tagged-values.png" width="300" alt="tagged values"></p>
<p>超过31位的数字,则被转换为double,放到一个Object中,再用指针指过去。</p>
<h3 id="开发指引"><a href="#开发指引" class="headerlink" title="开发指引"></a>开发指引</h3><ol>
<li>尽可能的使用31位有符号的整数,它很快![-1073741825, 1073741824]</li>
</ol>
<h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p>我们都知道<code>数组</code>这种数据结构,它的经典实现就是:内存中一段连续的位置分配来存储数据的线性集合。在其他语言中,它是需要在声明时固定类型、指定长度,以便在<code>栈区</code>分配内存,并且长度是不可变的。</p>
<blockquote>
<p>数组能够高效的访问,其实就在于内存的“固定”分配机制,元素类型固定,每次偏移的长度也是固定的,那么对于访问而言,仅需要计算地址位移信息而已,仅需要几个机器语言指令就可以存、取及使用。</p>
</blockquote>
<p>js的数组本质是个对象,但在v8中,以非负整数为key的元素被称为Element,每个对象都有一个指向Element数组的指针,其存放和其他属性是分开的,这其实也是针对数组的优化。</p>
<p>那么<code>Element</code>,其实也就是数组元素,它有两种类型:</p>
<ul>
<li>Fast Elements: C语言模式的数组内存分配</li>
<li>Dictionary Elements:hash table</li>
</ul>
<p>毫无疑问,<code>Dictionary Elements</code>要比<code>Fast Elements</code>慢很多,所以从性能角度而言,我们要做的事情是避免<code>Dictionary Elements</code>的出现。</p>
<p>什么状况下会导致<code>Dictionary Elements</code>的出现呢?</p>
<ol>
<li>过大的预分配数组,通常阈值是 64K(PagesPerChunk的大小限制?)</li>
<li>在非预分配的数组中,过多的超越当前数组长度的下标赋值,这个阈值由kMaxGap决定,通常是1024</li>
<li>删除元素也有可能会导致</li>
</ol>
<p>至于过多的“洞”出现在数组中会不会导致转换为<code>Dictionary Elements</code>暂时还没有明确的结论,不过不建议这样的用法。</p>
<p>示例:<br><figure class="highlight javascript"><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"><span class="comment">// for 1</span></div><div class="line"><span class="keyword">let</span> arr = <span class="keyword">new</span> <span class="built_in">Array</span>(<span class="number">65000</span>);</div><div class="line"><span class="comment">// for 2</span></div><div class="line"><span class="keyword">let</span> arr = [];</div><div class="line">arr[<span class="number">1024</span>] = <span class="number">1024</span>;</div></pre></td></tr></table></figure></p>
<p>再来看<code>Fast Elements</code>,它其实也有三种类型:</p>
<ol>
<li>Fast integer values</li>
<li>Fast doubles</li>
<li>Fast values</li>
</ol>
<p>这里其实是由元素类型导致的不同类型,所以可以再看一下上面的<code>数据类型</code>这部分</p>
<p>首先是整型,v8为了提高效率,31位有符号整数是由<code>Handle</code>保存的,所以处理起来是极快的。<br>再来是 doubles,常见的触发条件就是在整数数组中增加了一个浮点数,这会导致整个数组都被展开转换为double来存储,通过增加一个隐藏类实现的<br>最后是 values,例如再加入了一个<code>true|undefined|null</code>,这时候就要再次增加隐藏类,指明要存储的是Object</p>
<p>如果涉及到了这些转化,都会是性能的消耗,所以我们要尽可能的避免这种情况的出现。</p>
<p>然而如果一开始就初始化如:<code>let arr = [1, 2, 1.5, true]</code>,并没有这样的转换问题,所以复杂类型的字面直接初始化是一个好主意,但是更好的主意是:尽量的在数组中存储相同类型的元素。</p>
<p>在Google IO 2012年的v8讲解中有这方面的讲解,清晰易懂,建议查看一下:<a href="http://www.tudou.com/programs/view/bqxvrifP4mk/" target="_blank" rel="external">视频</a> <a href="http://v8-io12.appspot.com/#47" target="_blank" rel="external">PPT</a></p>
<p>上面的说到的东西谈及了一个概念:<code>隐藏类</code>,这个会在后面的部分再细说,在这里只需要知道这个就行了:数组的<code>Fast Elements</code>模式默认是<code>小整数</code>,随着不同类型值的进入,会导致数组类型发生转换,都是通过<code>隐藏类</code>来实现的,而这个转换往往是一种不必要的消耗。</p>
<p>下面的代码注释部分就是在说明<code>隐藏类</code></p>
<figure class="highlight javascript"><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"><span class="keyword">var</span> a = <span class="keyword">new</span> <span class="built_in">Array</span>();</div><div class="line">a[<span class="number">0</span>] = <span class="number">77</span>; <span class="comment">// integer 无洞</span></div><div class="line">a[<span class="number">1</span>] = <span class="number">88</span>;</div><div class="line">a[<span class="number">2</span>] = <span class="number">0.5</span>; <span class="comment">// double 无洞</span></div><div class="line">a[<span class="number">3</span>] = <span class="literal">true</span>; <span class="comment">// Object 无洞</span></div></pre></td></tr></table></figure>
<p>可以看出,有洞、无洞也会是<code>隐藏类</code>的一个标记,所以洞的出现不可避免的会造成性能的下降。</p>
<h3 id="实验"><a href="#实验" class="headerlink" title="实验"></a>实验</h3><h4 id="测试预分配与动态分配"><a href="#测试预分配与动态分配" class="headerlink" title="测试预分配与动态分配"></a>测试预分配与动态分配</h4><figure class="highlight plain"><table><tr><td class="gutter"><pre><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">var length = 10000000;</div><div class="line">function a(){</div><div class="line"> var arr = [];</div><div class="line"> var i = 0;</div><div class="line"> for (i = 0; i < length; i++) {</div><div class="line"> arr[i] = i;</div><div class="line"> }</div><div class="line">}</div><div class="line">function b(){</div><div class="line"> var arr = new Array(length);</div><div class="line"> var i = 0;</div><div class="line"> for (i = 0; i < length; i++) {</div><div class="line"> arr[i] = i;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>计算10次运行的平均值,结果依次为 a 和 b,Safari不支持performance.mark,所以都用了new Date()计算:</p>
<p>1000W次运算的结果</p>
<ul>
<li>Chrome: 295.8, 72.3</li>
<li>Node.js: 290.4, 65.7 (它也是v8)</li>
<li>Firefox: 77.1, 73.1</li>
<li>Safari: 53.2, 13.4</li>
</ul>
<p>100W:</p>
<ul>
<li>Chrome: 24.8, 4.9</li>
<li>Node.js: 24.1, 5.5 (它也是v8)</li>
<li>Firefox: 6.4, 5.9</li>
<li>Safari: 5.7, 1.5</li>
</ul>
<p>10W及以下几可无视差别。</p>
<p>其实还尝试了小额内存预分配(<64K)且试探性的内存延展(1024),不过从实验结果上来看由于引入了额外的计算,反而时间较内存自动分配有所增加。</p>
<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></pre></td><td class="code"><pre><div class="line">var length = 10000000;</div><div class="line">function a(){</div><div class="line"> var arr = new Array(length);</div><div class="line"> var i = 0;</div><div class="line"> for (; i < length; i++) {</div><div class="line"> arr[i] = i;</div><div class="line"> }</div><div class="line">}</div><div class="line">function b(){</div><div class="line"> var arr = new Array(length);</div><div class="line"> var i = length - 1;</div><div class="line"> for (; i >= 0; i--) {</div><div class="line"> arr[i] = i;</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>测试结果几近相同,b的表现偶尔会变坏一点点,但是不知是不是触发什么bug了,Firefox直接在逆向赋值崩掉了,刷出了<code>61.3, 5782</code>这样令人崩溃的结果。</p>
<p>anyway,不要逆向赋值了!!</p>
<h3 id="开发指引-1"><a href="#开发指引-1" class="headerlink" title="开发指引"></a>开发指引</h3><ol>
<li>使用非负整数作为数组下标,不要使用负数、浮点数、字符串等会被认为是一般性的Object的key</li>
<li>在数组中存储同一类型的元素</li>
<li>尽量的避免使用不连续的索引值,而且从0开始</li>
<li>如果非要存不同类型的元素,那么使用字面直接量初始化而不是一个一个的存入</li>
<li>预先分配数组大小,这在大多数状况下都有较大的性能提升,可以忽略掉64K的限制,但是小于万量级的话差别几可无视</li>
<li>不要逆向赋值!!不要逆向赋值!!不要逆向赋值!!</li>
<li>最好不要随便删除数组元素,这可能会导致转为<code>Dictionary Elements</code>,据说洞变少可能会被v8优化回紧凑结构,但是这是不可依赖的行为</li>
<li>先赋值,再访问,避免使用 <code>arr[100] == null</code> 或者隐式转换的判断性访问。</li>
</ol>
<h2 id="对象(隐藏类)"><a href="#对象(隐藏类)" class="headerlink" title="对象(隐藏类)"></a>对象(隐藏类)</h2><p>之前说过,js中除了那五种基础类型,其他的都是对象。而js又是个弱类型的语言,为了能够达到优化的目的(还记得与C类语言的特点么?就在<code>前言</code>部分。),即提升读取性能,v8利用动态创建隐藏内部类的方式动态地将属性的内存地址记录在对象内,从而提升整体的属性访问速度。</p>
<p>避免了通过字符串匹配的方式来查找属性值。</p>
<p><code>隐藏类</code>是为Object服务的,相同结构的Object会共享<code>隐藏类</code>,当结构发生了改变,对应的<code>隐藏类</code>也会发生改变,要么复用,要么新增。</p>
<p>而且会将使用过的<code>隐藏类</code>结构通过内嵌缓存(inline cache)缓存起来,以便复用时可以快速的访问偏移值。</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></pre></td><td class="code"><pre><div class="line">function Point(x, y) {</div><div class="line"> this.x = x;</div><div class="line"> this.y = y;</div><div class="line">}</div><div class="line">var p1 = new Point(1, 2);</div><div class="line">var p2 = new Point(3, 4); // 复用隐藏类</div><div class="line">p2.z = 5; // p2使用了不同的隐藏类!!很可能触发了一个新增处理</div></pre></td></tr></table></figure>
<p>需要注意的是,<code>隐藏类</code>将属性这些东西解析为树,所以顺序不同的初始化会导致<code>隐藏类</code>的解析结果也不同。</p>
<p>所有的Object类型都是这样。</p>
<h3 id="开发指引-2"><a href="#开发指引-2" class="headerlink" title="开发指引"></a>开发指引</h3><ol>
<li>一次性的初始化所有的属性,而不是后续的动态增加</li>
<li>属性初始化的顺序应当一致,以便保证能够复用隐藏类。</li>
<li><code>delete</code> 会触发隐藏类的改变,如果是为了内存回收,设置为null是更好的选择</li>
</ol>
<h2 id="函数"><a href="#函数" class="headerlink" title="函数"></a>函数</h2><h3 id="避免触发优化回滚"><a href="#避免触发优化回滚" class="headerlink" title="避免触发优化回滚"></a>避免触发优化回滚</h3><p>v8有一个优化行为,针对于热点函数,会使用<code>Crankshaft</code>编译器去乐观且猜测性的生成高效的本地代码,这通常是建立在变量类型不改变的前提下,如果发现类型变化了,那么v8就会使用优化回滚(Deoptimization)机制来回滚到之前的一个没有经过特别优化的代码。</p>
<p>例如:<br><figure class="highlight javascript"><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"><span class="keyword">var</span> counter = <span class="number">0</span>;</div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">waitThenGo</span>(<span class="params"></span>) </span>{</div><div class="line"> counter++;</div><div class="line"> <span class="keyword">if</span> (counter < <span class="number">10000000</span>) {</div><div class="line"> <span class="keyword">return</span> counter;</div><div class="line"> }</div><div class="line"> <span class="keyword">var</span> now = <span class="keyword">new</span> <span class="built_in">Date</span>();</div><div class="line"> <span class="built_in">console</span>.log(now);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>或许目的只是为了等一段时间之后打印当前时间,但是对于v8来说,waitThenGo执行了很多次之后,就可能会触发<code>Crankshaft</code>编译器来生成优化的代码了,它认为已经知道了所有的信息了,例如类型。</p>
<p>但是实际上,没运行到 now 这一行之前,我们都不知道 now 的类型,所以当运行到这一行时,v8就别无选择的只能回滚到一个通用的状态了。</p>
<h3 id="开发指引-3"><a href="#开发指引-3" class="headerlink" title="开发指引"></a>开发指引</h3><ol>
<li>随用随声明是好的,但是如果前面的代码会触发大量的执行return,那么提前声明后面的变量是能够在一定程度上起到优化效果的</li>
</ol>
]]></content>
<categories>
<category> 前端 </category>
</categories>
<tags>
<tag> 优化 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[JavaScript中的数据结构]]></title>
<url>https://leowang721.github.io/2017/03/20/js/data-structure-in-javascript/</url>
<content type="html"><![CDATA[<p>持续更新中…</p>
<h2 id="数组"><a href="#数组" class="headerlink" title="数组"></a>数组</h2><p><a href="/2017/03/21/js/javascript-engine/">JavaScript引擎</a>都在尽力的优化以接近C类语言的性能。</p>
<p>对于数组而言,可以直接使用,但最好是触发<code>Fast Elements</code>,具体可查看:<a href="/2017/03/21/js/effective-coding/#数组">《高性能的JavaScript开发》的数组部分</a></p>
<h2 id="栈、队列"><a href="#栈、队列" class="headerlink" title="栈、队列"></a>栈、队列</h2><p>使用数组就可以轻易的模拟了,最多封装个类方便使用。</p>
<p>栈: push pop<br>队列:unshift pop</p>
<h2 id="Set"><a href="#Set" class="headerlink" title="Set"></a>Set</h2><p>es6已经有了,直接使用吧!</p>
<a id="more"></a>
<h2 id="链表"><a href="#链表" class="headerlink" title="链表"></a>链表</h2><p>我一直认为 <code>链表</code> 是数组长度固定化的产物,js的数组是动态的,不觉得它还有什么存在的必要, <code>arr.splice(10, 1, newNode)</code> 这种代码不是比链表插入要优美得多?而且还有一堆方法可以直接用:shift, unshift, pop, push, forEach, map …</p>
<p>遍历也可以直接用 <code>for...of</code> 使用<code>Iterator</code>的方式</p>
<p>当然如果有不同的意见,我也可以改正。</p>
<p>如果是为了练习代码能力,那倒是不妨自己实现一个。</p>
<h2 id="HashTable、Dictionary"><a href="#HashTable、Dictionary" class="headerlink" title="HashTable、Dictionary"></a>HashTable、Dictionary</h2><p>js中的对象起到的作用已经超过了基础的数据结构 <code>Dictionary</code> 和 <code>HashTable</code>,也不必费心去实现了,而且es6还有<code>Map</code>。</p>
<p>hash算法倒是个有意思的东西,不过不在当前范畴。</p>
<h2 id="树"><a href="#树" class="headerlink" title="树"></a>树</h2><p>这里还是数学概念:</p>
<ul>
<li><code>自由树</code>是一个连通的、无回路的无向图。</li>
<li><code>有根树</code>就是多了个根节点,由此就有了“父子层级”的概念。</li>
<li><code>有序树</code>指的是子级节点有序的树。</li>
<li><code>二叉树</code>是指最多有两个子节点的树。</li>
<li><code>完全二叉树</code>是指子节点是满的,只要有就一定是两个,并且叶节点的深度一定相同(就是说左右子树的层数相等)。</li>
</ul>
<p>树的最大特点是:任意两个顶点由唯一一条简单路径相连</p>
<p>二叉树 => 二叉查找树 => 红黑树 => B树 => B+树 …. ????</p>
<h3 id="二叉查找树"><a href="#二叉查找树" class="headerlink" title="二叉查找树"></a>二叉查找树</h3><p>节点存值,左子树都比它小,右子树都比它大。</p>
<p>执行基本操作的时间与树的高度成正比。n个节点的完全二叉树,操作最坏运行时间为O(lgn);如果是线性链(子级节点都跑一边去了),最坏是O(n)。</p>
<p>一颗随机构造的二叉树期望高度为O(lgn),基本操作的平均时间为O(lgn)。</p>
<p><a href="https://github.com/leowang721/k-ds/blob/master/lib/tree/BinSearchTree.js" target="_blank" rel="external">一个简陋的二叉查找树的实现</a></p>
<h2 id="图"><a href="#图" class="headerlink" title="图"></a>图</h2><p>经典的图定义是 <code>G = (V, E)</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></pre></td><td class="code"><pre><div class="line">// 至少我曾经会这么写……</div><div class="line">class Graph {</div><div class="line"> nodes = [];</div><div class="line"> edges = [];</div><div class="line">}</div></pre></td></tr></table></figure>
<p>但实际上有两种表示方法:<code>邻接表</code> or <code>邻接矩阵</code>,上面那个一般是邻接矩阵,当然要写成边集的话那也没办法~~</p>
<p>来看图示:</p>
<p><code>邻接表</code>无向图 <img src="/images/learning/ds/graph-link-table.jpg"><br><code>邻接表</code>有向图 <img src="/images/learning/ds/graph-table-direction.jpg"></p>
<p><code>邻接矩阵</code>,<a href="http://c.biancheng.net/cpp/html/991.html" target="_blank" rel="external">网上找的图</a>:</p>
<p><img src="/images/learning/ds/graph-matrix.jpg"></p>
<p>邻接表适合于稀疏图(|E|远小于|V|^2),因为表示出来比较紧凑<br>邻接矩阵则适合于稠密图,或必须很快判别两个给定顶点是否存在连接边(<code>顶点间最短路径算法</code>)</p>
<p>待续…</p>
]]></content>
<categories>
<category> 算法 </category>
</categories>
<tags>
<tag> 数据结构 </tag>
<tag> 优化 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[小结下前端的持续集成(istanbul, mocha, babel, etc)]]></title>
<url>https://leowang721.github.io/2017/03/20/test/frontend-continuous-integrationtest-with-istanbul-mocha-babel-etc/</url>
<content type="html"><![CDATA[<p>这是必要而又自然的,首先我们要保证自己的基础库的回归测试的自动化(或者少修改),其次在选择开源库的时候,有着测试100%pass的库总会让别人多一点信心不是么?!</p>
<p>一个笔记而已,不必写过多的原因及背景。</p>
<p>简而言之:我想写 es6 语法的基础库,又要确保相应的测试神马的能够正常的 cover 住,而且需要在依赖其他的 es6 语法库的时候能够让测试正常运行,做了一点工作,所以记下笔记以便后续直接用。</p>
<h2 id="库选择"><a href="#库选择" class="headerlink" title="库选择"></a>库选择</h2><h3 id="测试库mocha"><a href="#测试库mocha" class="headerlink" title="测试库mocha"></a>测试库<code>mocha</code></h3><p>很正常的选择。</p>
<p><a href="https://mochajs.org/" target="_blank" rel="external">官网</a><br><a href="https://github.com/mochajs/mocha" target="_blank" rel="external">Github</a></p>
<h3 id="断言库chai"><a href="#断言库chai" class="headerlink" title="断言库chai"></a>断言库<code>chai</code></h3><p>说实话我也没想那么多,<code>mocha</code>官网用了就直接用了而已,其实本身并不在意是<code>chai</code>还是<code>jasmine</code>……</p>
<a id="more"></a>
<p><a href="http://chaijs.com/" target="_blank" rel="external">官网</a><br><a href="https://github.com/chaijs/chai" target="_blank" rel="external">Github</a></p>
<h3 id="覆盖率nyc"><a href="#覆盖率nyc" class="headerlink" title="覆盖率nyc"></a>覆盖率<code>nyc</code></h3><p>实际上还是<code>istanbul</code>,<code>nyc</code>是做了些增强:</p>
<ul>
<li>applications that spawn subprocesses.</li>
<li>ES2015 transforms, via babel-plugin-istanbul, or source-maps.</li>
</ul>
<p><a href="https://istanbul.js.org/" target="_blank" rel="external">官网</a><br><a href="https://github.com/istanbuljs/nyc" target="_blank" rel="external">Github</a></p>
<h3 id="持续集成Travis-CI"><a href="#持续集成Travis-CI" class="headerlink" title="持续集成Travis CI"></a>持续集成<code>Travis CI</code></h3><p><a href="https://travis-ci.org/" target="_blank" rel="external">官网</a></p>
<h3 id="覆盖率持续集成coveralls"><a href="#覆盖率持续集成coveralls" class="headerlink" title="覆盖率持续集成coveralls"></a>覆盖率持续集成<code>coveralls</code></h3><p>for coveralls.io</p>
<p><a href="http://coveralls.io/" target="_blank" rel="external">Coveralls官网</a><br><a href="https://github.com/nickmerwin/node-coveralls" target="_blank" rel="external">Github</a></p>
<h2 id="项目中的使用"><a href="#项目中的使用" class="headerlink" title="项目中的使用"></a>项目中的使用</h2><h3 id="安装"><a href="#安装" class="headerlink" title="安装"></a>安装</h3><p>在进行了必要的项目初始化之后(npm init ?),就可以准备持续集成相关的东西了。</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 i --save-dev mocha chai nyc coveralls babel-cli cross-env</div></pre></td></tr></table></figure>
<p>如果需要 es6 等支持,还需要安装</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 i --save-dev babel-cli babel-register babel-plugin-istanbul babel-preset-es2015-node5</div></pre></td></tr></table></figure>
<p>cross-env用于跨平台的设置 NODE_ENV=test</p>
<p>选用 preset es2015-node5 的原因则是,测试都是在 node 下进行的,而 node 在原生类被babel编译的类继承时会抛异常,所以使用这个preset。</p>
<h3 id="babel配置"><a href="#babel配置" class="headerlink" title="babel配置"></a>babel配置</h3><p>如果安装了,则新增 .babelrc 于根目录,然后配置:</p>
<figure class="highlight javascript"><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">{</div><div class="line"> <span class="string">"presets"</span>: [</div><div class="line"> <span class="string">"es2015-node5"</span></div><div class="line"> ],</div><div class="line"> <span class="string">"env"</span>: {</div><div class="line"> <span class="string">"test"</span>: {</div><div class="line"> <span class="string">"plugins"</span>: [</div><div class="line"> <span class="string">"istanbul"</span></div><div class="line"> ]</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="测试"><a href="#测试" class="headerlink" title="测试"></a>测试</h3><p>首先,测试的文件默认都在根目录下的 test 文件夹中,源文件默认在 lib 文件夹中,这时候其实不需要做过多的配置的,只有几个注意点而已:</p>
<ol>
<li>test中的文件命名,最好保留源文件名,或用.test.js后缀,以便维护</li>
<li>如果过多了,可以考虑源文件名做文件夹,然后 [feature].test.js,.test依然可选</li>
<li>test中的文件如果是多层,mocha的参数要有 –recursive,去找更深层目录的文件</li>
</ol>
<p>修改:</p>
<figure class="highlight javascript"><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"><span class="comment">// 在 package.json 中,增加命令:</span></div><div class="line">{</div><div class="line"> <span class="string">"script"</span>: {</div><div class="line"> <span class="string">"test"</span>: <span class="string">"./node_modules/.bin/mocha --recursive"</span></div><div class="line"> }</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 执行</span></div><div class="line">npm test</div></pre></td></tr></table></figure>
<h3 id="覆盖率"><a href="#覆盖率" class="headerlink" title="覆盖率"></a>覆盖率</h3><p>修改:</p>
<p>根目录增加 .nycrc,用于配置<code>nyc</code>,这是一个json文件</p>
<figure class="highlight javascript"><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">{</div><div class="line"> <span class="string">"reporter"</span>: [<span class="string">"text"</span>, <span class="string">"text-summary"</span>, <span class="string">"lcov"</span>],</div><div class="line"> <span class="string">"include"</span>: [<span class="string">"lib/*.js"</span>],</div><div class="line"> <span class="string">"require"</span>: [<span class="string">"babel-register"</span>],</div><div class="line"> <span class="string">"sourceMap"</span>: <span class="literal">false</span>,</div><div class="line"> <span class="string">"instrument"</span>: <span class="literal">false</span>,</div><div class="line"> <span class="string">"all"</span>: <span class="literal">true</span>,</div><div class="line"> <span class="string">"report-dir"</span>: <span class="string">"./test/coverage"</span></div><div class="line">}</div></pre></td></tr></table></figure>
<p>// 执行<br>npm run cover<br><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"></div><div class="line">更新下 .gitignore</div></pre></td></tr></table></figure></p>
<h1 id="Coverage-directory-used-by-tools-like-istanbul"><a href="#Coverage-directory-used-by-tools-like-istanbul" class="headerlink" title="Coverage directory used by tools like istanbul"></a>Coverage directory used by tools like istanbul</h1><p>coverage<br>.nyc_coverage<br>.nyc_output<br><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"></div><div class="line">然后增加命令,以便执行</div><div class="line"></div><div class="line">```javascript</div><div class="line">// 在 package.json 中,增加命令:</div><div class="line">{</div><div class="line"> "script": {</div><div class="line"> "cover": "cross-env NODE_ENV=test --reporter=lcov --reporter=text mocha"</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h3 id="持续集成"><a href="#持续集成" class="headerlink" title="持续集成"></a>持续集成</h3><p>for Travis CI…</p>
<p>创建文件 .travis.yml</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">language: node_js</div><div class="line">node_js:</div><div class="line"> - 7.6</div><div class="line">install:</div><div class="line">|</div><div class="line"> npm install -g npm@latest</div><div class="line"> npm --version</div><div class="line"> npm install --registry http://registry.npmjs.org</div><div class="line">script:</div><div class="line"> - npm run cover</div><div class="line">after_script:</div><div class="line"> - npm run coveralls</div></pre></td></tr></table></figure>
<p>对了,package.json的script加一个 coveralls<br><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">// 在 package.json 中,增加命令:</div><div class="line">{</div><div class="line"> "script": {</div><div class="line"> "coveralls": "cat ./coverage/lcov.info | coveralls"</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>OK.</p>
]]></content>
<categories>
<category> 前端 </category>
</categories>
<tags>
<tag> es6 </tag>
<tag> 架构 </tag>
<tag> 测试 </tag>
<tag> 持续集成 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[babel6的新老AMD模块并存问题]]></title>
<url>https://leowang721.github.io/2017/03/16/es6/try-resolve-old-amd-modules-problem-with-babel6/</url>
<content type="html"><![CDATA[<p>近来又遇到了很恶心的事儿,那就是想要升级babel为6的时候,遇到了新老AMD模块并存的问题。</p>
<p>是的,又遇到了,尽管在babel7都WIP了,升级个6还是恶心的够呛,扫了一圈之后发现,圈里貌似还是没人去解决这个问题……</p>
<p>先列出引到的链接:</p>
<ul>
<li><a href="http://babeljs.io/" target="_blank" rel="external">babel</a></li>
<li><a href="https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-amd" target="_blank" rel="external">babel-plugin-transform-es2015-modules-amd</a></li>
<li><a href="https://www.npmjs.com/package/babel-plugin-transform-es2015-modules-simple-amd" target="_blank" rel="external">babel-plugin-transform-es2015-modules-simple-amd</a></li>
</ul>
<p>我开发的小插件: <a href="https://github.com/leowang721/babel-plugin-transform-es2015-modules-existed-amd" target="_blank" rel="external">babel-plugin-transform-es2015-modules-existed-amd</a></p>
<h2 id="问题症结"><a href="#问题症结" class="headerlink" title="问题症结"></a>问题症结</h2><p>主要是新老模块并存导致的。</p>
<a id="more"></a>
<p>简而言之,babel6的插件<code>babel-plugin-transform-es2015-modules-amd</code>在处理的时候,由于面临着这样的使用状况(示例代码):</p>
<figure class="highlight javascript"><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"><span class="keyword">import</span> _ <span class="keyword">from</span> <span class="string">'lodash'</span>;</div><div class="line"><span class="keyword">import</span> {add} <span class="keyword">from</span> <span class="string">'./mathlib'</span>;</div><div class="line"><span class="comment">// do sth</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">let</span> tested = <span class="literal">true</span>;</div><div class="line"><span class="keyword">export</span> <span class="keyword">default</span> {</div><div class="line"> <span class="attr">tested</span>: <span class="literal">false</span></div><div class="line">};</div></pre></td></tr></table></figure>
<p>可以看到export let了tested,default里面也有tested,那么应该是哪个?</p>
<p>所以babel6定义为:</p>
<figure class="highlight javascript"><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">exports.tested = <span class="literal">true</span>;</div><div class="line">exports.default = {<span class="attr">tested</span>: <span class="literal">false</span>};</div></pre></td></tr></table></figure>
<p>多了一个<code>default</code>,所以babel6翻译过的<code>import 'a'</code>都变成了<code>require('a').default</code>。然而它并不会处理旧的那些AMD的模块,而如果你“不幸”的在旧的模块中去require了新的es6的模块,就挂了……</p>
<p>关键点在于,babel6的做法没什么问题,只是不兼容旧的而已,而插件<code>babel-plugin-transform-es2015-modules-simple-amd</code>的解决方案是将babel6的AMD转换输出改成babel5的方式,去掉<code>default</code>,但是如果你用了export let,那就只能sorry了,搞不了。</p>
<p>扫了一些文章基本结论都是:ES6 module to AMD的转码逻辑变更并且暂时没有什么好的办法解决这个问题。</p>
<p>还有!</p>
<p><code>babel-plugin-transform-es2015-modules-amd</code>在处理旧的<code>AMD</code>和<code>UMD</code>,都会在外面给包一层<code>define</code>,醉了么?</p>
<h2 id="硬着头皮处理"><a href="#硬着头皮处理" class="headerlink" title="硬着头皮处理"></a>硬着头皮处理</h2><p>或者可以解决的方案:不修改babel6,而是针对旧AMD模块,然后采用插件的方式处理,将它的输出也加个<code>default</code>,require的部分也是一样。</p>
<p>先只处理简单的状况!!</p>
<p>下面都是使用了 preset es2015 和 <code>babel-plugin-transform-es2015-modules-amd</code>,所以代码会先被转换一次。</p>
<p>为啥要用?因为不想去处理 es2015模块啊,万一人家支持了,我们只要撤掉这个插件就好了。</p>
<p> 列一下要面对的问题:</p>
<ol>
<li>旧模块的定义</li>
<li>定位模块主体</li>
<li>require的处理</li>
<li>exports的处理</li>
</ol>
<h3 id="定义"><a href="#定义" class="headerlink" title="定义"></a>定义</h3><p>限制为使用<code>define</code>来定义的模块,形如:</p>
<figure class="highlight javascript"><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">define(<span class="function"><span class="keyword">function</span>(<span class="params">require</span>) </span>{</div><div class="line"><span class="meta"> 'use strict'</span>;</div><div class="line"> <span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'a'</span>);</div><div class="line"> <span class="keyword">return</span> {<span class="attr">hello</span>: <span class="string">'world'</span>};</div><div class="line">});</div></pre></td></tr></table></figure>
<h3 id="模块主体部分"><a href="#模块主体部分" class="headerlink" title="模块主体部分"></a>模块主体部分</h3><p><code>define</code>一共有四种形式(变参):</p>
<figure class="highlight javascript"><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">define(factory);</div><div class="line">define(id, factory);</div><div class="line">define(dependencies, factory);</div><div class="line">define(id, dependencies, factory);</div></pre></td></tr></table></figure>
<p>无论是用哪种,主体都是<code>factory</code>,最后一个参数。</p>
<p>不过其实要处理的是<code>babel-plugin-transform-es2015-modules-amd</code>搞进来的<code>define</code>部分。</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// after es2015 preset + transform-es2015-modules-amd</span></div><div class="line">define([], <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"><span class="meta"> 'use strict'</span>;</div><div class="line"></div><div class="line"> define(<span class="function"><span class="keyword">function</span> (<span class="params">require</span>) </span>{</div><div class="line"><span class="meta"> 'use strict'</span>;</div><div class="line"></div><div class="line"> <span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'a'</span>);</div><div class="line"> <span class="keyword">return</span> { <span class="attr">hello</span>: <span class="string">'world'</span> };</div><div class="line"> });</div><div class="line">});</div><div class="line"><span class="comment">// babel插件的ast信息</span></div><div class="line"><span class="comment">// 'use strict',这个东西不算语句,而是被定义为directives里 如node.directives[0].value.value = 'use strict';</span></div><div class="line"><span class="comment">// 怎么找 define?</span></div><div class="line">node.body.length === <span class="number">1</span> <span class="comment">// 只有一个调用</span></div><div class="line"> && node.body[<span class="number">0</span>].type === <span class="string">'ExpressionStatement'</span></div><div class="line"> && node.body[<span class="number">0</span>].expression.type === <span class="string">'CallExpression'</span></div><div class="line"> && node.body[<span class="number">0</span>].expression.callee.name === <span class="string">'define'</span>; </div><div class="line"></div><div class="line">node.body[<span class="number">0</span>]; <span class="comment">// 最外层的define</span></div><div class="line"></div><div class="line"><span class="comment">// 继续找里面的</span></div><div class="line">node.body[<span class="number">0</span>].expression.arguments[<span class="number">1</span>]; <span class="comment">// 找到了最外层的define的匿名函数参数</span></div><div class="line">node.body[<span class="number">0</span>].expression.arguments[<span class="number">1</span>].body.body[<span class="number">0</span>]; <span class="comment">// ExpressionStatement</span></div><div class="line">node.body[<span class="number">0</span>].expression.arguments[<span class="number">1</span>].body.body[<span class="number">0</span>].expression.type; <span class="comment">// CallExpression</span></div><div class="line">node.body[<span class="number">0</span>].expression.arguments[<span class="number">1</span>].body.body[<span class="number">0</span>].expression.callee.name; <span class="comment">// define</span></div><div class="line"><span class="comment">// 又找到了里层的define 即node.body[0].expression.arguments[1].body.body[0]</span></div><div class="line"></div><div class="line"><span class="comment">// 那么这时候设置换成这个结果数据是什么呢</span></div><div class="line">path.node.body = node.body[<span class="number">0</span>].expression.arguments[<span class="number">1</span>].body.body;</div><div class="line"></div><div class="line">define(<span class="function"><span class="keyword">function</span> (<span class="params">require</span>) </span>{</div><div class="line"><span class="meta"> 'use strict'</span>;</div><div class="line"></div><div class="line"> <span class="keyword">var</span> a = <span class="built_in">require</span>(<span class="string">'a'</span>);</div><div class="line"> <span class="keyword">return</span> { <span class="attr">hello</span>: <span class="string">'world'</span> };</div><div class="line">});</div><div class="line"></div><div class="line"><span class="comment">// yes,又滚回来了</span></div><div class="line"><span class="comment">// 并且 node.body[0].expression.arguments[node.body[0].expression.arguments.length - 1] 就是主体</span></div></pre></td></tr></table></figure>
<h3 id="require的处理"><a href="#require的处理" class="headerlink" title="require的处理"></a>require的处理</h3><p>我是个较懒的人,所以选择的方式是这样的。</p>
<p>找到对应的点,然后使用自带的 <code>_ref.types</code> 即 <code>babel-types</code> 的方法<code>callExpression</code>来替换原节点……</p>
<p>在 visitor.Program.exit 中 <code>path.traverse(amdVisitor, this);</code> 来遍历</p>
<p>然后就直接处理了,区分了几个使用的场景,没效率,但是架不住简单。</p>
<p>因为是包了一层调用,所以还得加个函数进去,这个简单,ast node直接用<code>babel-template</code>搞就行了。</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> <span class="title">turnRequire</span>(<span class="params">target</span>) </span>{</div><div class="line"> <span class="keyword">return</span> t.callExpression(</div><div class="line"> t.identifier(<span class="string">'_interopRequireDefault'</span>),</div><div class="line"> [target]</div><div class="line"> );</div><div class="line">}</div><div class="line"><span class="keyword">var</span> amdVisitor = {</div><div class="line"> <span class="attr">CallExpression</span>: <span class="function"><span class="keyword">function</span> <span class="title">CallExpression</span>(<span class="params">path</span>) </span>{</div><div class="line"> <span class="keyword">if</span> (isValidRequireCall(path)) {</div><div class="line"> <span class="keyword">let</span> parentKey = path.parentKey;</div><div class="line"> <span class="keyword">switch</span> (parentKey) {</div><div class="line"> <span class="keyword">case</span> <span class="string">'arguments'</span>:</div><div class="line"> <span class="keyword">case</span> <span class="string">'elements'</span>:</div><div class="line"> path.parent[path.parentKey][path.key] = turnRequire(path.parent[path.parentKey][path.key]);</div><div class="line"> path.node = path.parent[path.parentKey][path.key];</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> <span class="keyword">default</span>:</div><div class="line"> path.parent[path.parentKey] = turnRequire(path.parent[path.parentKey]);</div><div class="line"> path.node = path.parent[path.parentKey];</div><div class="line"> <span class="keyword">break</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">};</div></pre></td></tr></table></figure>
<p>没有搞 Gloabl require,形如 <code>require(['main']);</code>,先手改吧。</p>
<p>至此的效果:</p>
<figure class="highlight"><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></pre></td><td class="code"><pre><div class="line">define(function(require) {</div><div class="line"> 'use strict';</div><div class="line"> var a = require('a');</div><div class="line"> var c = require('b').c;</div><div class="line"> var d = require('e').f.g;</div><div class="line"></div><div class="line"> require('lodash');</div><div class="line"> require('wer')();</div><div class="line"></div><div class="line"> function t() {</div><div class="line"> require('sss').go();</div><div class="line"> }</div><div class="line"></div><div class="line"> require(['aaa']);</div><div class="line"></div><div class="line"> return {hello: 'world'};</div><div class="line">});</div><div class="line"></div><div class="line">// 被转换为</div><div class="line"></div><div class="line">define(function (require) {</div><div class="line"> 'use strict';</div><div class="line"></div><div class="line"> var a = _interopRequireDefault(require('a'));</div><div class="line"> var c = _interopRequireDefault(require('b')).c;</div><div class="line"> var d = _interopRequireDefault(require('e')).f.g;</div><div class="line"></div><div class="line"> _interopRequireDefault(require('lodash'));</div><div class="line"> _interopRequireDefault(require('wer'))();</div><div class="line"></div><div class="line"> function t() {</div><div class="line"> _interopRequireDefault(require('sss')).go();</div><div class="line"> }</div><div class="line"></div><div class="line"> require(['aaa']);</div><div class="line"></div><div class="line"> return {hello: 'world'};</div><div class="line"></div><div class="line"> function _interopRequireDefault(obj) {</div><div class="line"> return obj && obj.__esModule ? obj.default : obj;</div><div class="line"> }</div><div class="line">});</div><div class="line"></div><div class="line">### export的处理</div><div class="line"></div><div class="line">我想的是,直接就处理 Function 的 ReturnStatement 就好了……</div><div class="line"></div><div class="line">还是从第二步定位的模块主体部分入手。</div><div class="line"></div><div class="line">来看 `mainFunc.body.body` 就是所有的语句了,只看 `ReturnStatement`,如果没有,那可能是使用 exports 或 module.exports 定义的,我们没用这种,先无视。</div><div class="line"></div><div class="line">将`ReturnStatement`的前面插一个`VariableDeclaration`,然后让`ReturnStatement`返回的是`{default:exports}`这种,大体思路如此。</div><div class="line"></div><div class="line">```javascript</div><div class="line">// 简单的实现</div><div class="line">var mainFunc = getDefineFunction(path);</div><div class="line">var returnStatement = getReturnStatement(mainFunc);</div><div class="line">if (returnStatement && returnStatement.target) {</div><div class="line"> mainFunc.body.body.splice(</div><div class="line"> returnStatement.index, 0,</div><div class="line"> t.VariableDeclaration('var', [</div><div class="line"> t.VariableDeclarator(t.identifier('__esModuleAMDExport'), t.objectExpression(</div><div class="line"> [</div><div class="line"> t.objectProperty(t.identifier('default'), returnStatement.target.argument),</div><div class="line"> t.objectProperty(t.identifier('__esModule'), t.booleanLiteral(true))</div><div class="line"> ]</div><div class="line"> ))</div><div class="line"> ])</div><div class="line"> );</div><div class="line"> returnStatement.target.argument = t.identifier('__esModuleAMDExport');</div><div class="line">}</div></pre></td></tr></table></figure>
<p>最后,要说的是:</p>
<p>如果遇到了这样的问题,只能改代码了,因为根据babel6的设计,这样本来就是不行的:</p>
<figure class="highlight javascript"><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"><span class="comment">// 在文件a中</span></div><div class="line"><span class="keyword">export</span> <span class="keyword">let</span> b = <span class="number">1</span>;</div><div class="line"></div><div class="line"><span class="comment">// 然后你非要在其他的文件中去</span></div><div class="line"><span class="keyword">import</span> a <span class="keyword">from</span> <span class="string">'./a'</span>;</div><div class="line">a.b; <span class="comment">// 这样是不行的</span></div><div class="line"></div><div class="line"><span class="comment">// 应该是</span></div><div class="line"><span class="keyword">import</span> {b} <span class="keyword">from</span> <span class="string">'./a'</span>;</div></pre></td></tr></table></figure>
]]></content>
<categories>
<category> 前端 </category>
</categories>
<tags>
<tag> babel </tag>
<tag> 插件 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[koa的中间件机制原理及async await版本实现]]></title>
<url>https://leowang721.github.io/2017/03/13/es6/koa-middlewares-implementation-with-async-await/</url>
<content type="html"><![CDATA[<p>个人还是很喜欢这种机制,也想搞一个这般的 <a href="https://github.com/leowang721/k-core" target="_blank" rel="external">Workflow</a> 辅助类出来,不过不想用 Generator,所以折腾了一小下……</p>
<h2 id="不得不说的-yield"><a href="#不得不说的-yield" class="headerlink" title="不得不说的 yield *"></a>不得不说的 yield *</h2><p><code>yield*</code>最大的作用,就是将<code>Generator</code>嵌在另一个<code>Generator</code>的内部执行。</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> *<span class="title">a</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> b();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> *<span class="title">b</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>执行结果是<code>13</code>,是的,2不会被输出,但是如果换成:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">yield</span> *b();</div></pre></td></tr></table></figure>
<p>就等同于:<br><figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> *<span class="title">a</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出就是<code>123</code>就好了。</p>
<p>而这也是koa2的中间件核心实现机制,笑话一下自己最开始搞出来的遍历行为,傻在<code>next</code>上了,囧~~</p>
<a id="more"></a>
<h2 id="async-await模式的实现"><a href="#async-await模式的实现" class="headerlink" title="async/await模式的实现"></a>async/await模式的实现</h2><p>如果还不明白,那么就看这个:</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> *<span class="title">a</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> <span class="keyword">yield</span>* b();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">5</span>);</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> *<span class="title">b</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line"> <span class="keyword">yield</span>* c();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">4</span>);</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> *<span class="title">c</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>循环内嵌之后,你只需要遍历a的Iterator就行了,会展开嵌入后面所有的<code>Generator</code>!!</p>
<p>所以核心问题就变成了,如何定义出所需要的方法a而已。</p>
<p>koa的<code>middlewares</code>是一个数组,为了能够得到要访问的方法,其实就是middlewares[0]的封装。我们知道每个<code>async function</code>其实就是一个<code>Generator function</code>,那么我只需要把参数<code>next</code>指向下一个中间件函数,并且确保调用时使用<code>yield*</code>去调用,并且参数绑定ctx和下一个next。</p>
<p>所以我需要的可能是这样的:</p>
<figure class="highlight javascript"><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"><span class="keyword">let</span> nextc = <span class="function"><span class="keyword">function</span>* (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div><div class="line"><span class="keyword">let</span> nextb = <span class="function"><span class="keyword">function</span>* (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line"> <span class="keyword">yield</span>* nextc();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">4</span>);</div><div class="line">}</div><div class="line"><span class="keyword">let</span> nexta = <span class="function"><span class="keyword">function</span>* (<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> <span class="keyword">yield</span> * nextb();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">5</span>);</div><div class="line">}</div><div class="line"></div><div class="line">nexta().next(); <span class="comment">// 执行,输出 1 2 3 4 5</span></div></pre></td></tr></table></figure>
<p>再换一下,用async/await:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 注意这里我开始引入 next 了</span></div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params">next</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> <span class="keyword">await</span> next();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">5</span>);</div><div class="line">}</div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params">next</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line"> <span class="keyword">await</span> next();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">4</span>);</div><div class="line">}</div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">c</span>(<span class="params">next</span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 然后我们来手动定义next</span></div><div class="line"><span class="keyword">let</span> nextc = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> c.call(<span class="keyword">this</span>);</div><div class="line">};</div><div class="line"></div><div class="line"><span class="keyword">let</span> nextb = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> b.call(<span class="keyword">this</span>, nextc);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> nexta = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>{</div><div class="line"> a.call(<span class="keyword">this</span>, nextb);</div><div class="line">}</div><div class="line"><span class="comment">// 执行</span></div><div class="line">nexta();</div><div class="line"><span class="comment">// 输出了 1 2 3 4 5</span></div></pre></td></tr></table></figure>
<p>发现规律了么……</p>
<p>nexta调用的是函数a,将nextb作为next参数传入,而nextb调用b,将nextc作为参数传入,同理继续。最后一个指向空就行了,啥也不干。</p>
<p>那么:</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> <span class="title">compose</span>(<span class="params">middlewares</span>) </span>{</div><div class="line"> <span class="keyword">let</span> i = middlewares.length;</div><div class="line"> <span class="keyword">let</span> next = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{}; <span class="comment">// noop</span></div><div class="line"> <span class="comment">// 循环算出来next</span></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = middlewares.length - <span class="number">1</span>; i >=<span class="number">0</span>; i--) {</div><div class="line"> next = getCallback(middlewares[i], next);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> next;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCallback</span>(<span class="params">method, next</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> method(next);</div><div class="line"> };</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> callback = compose([a, b, c]);</div><div class="line"></div><div class="line">callback(); <span class="comment">// 输出1 2 3 4 5</span></div></pre></td></tr></table></figure>
<p>貌似大功告成?还得继续……</p>
<p>中间件是需要传入数据的,简单理解为:一个数据要从第一个中间件进入,最终从第一个中间件的after出来,核心目的是改变这个数据,因此在当前的模式下,最好不要传值,我们来看:</p>
<figure class="highlight javascript"><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">app.use(<span class="keyword">async</span> (ctx, next) => {</div><div class="line"> <span class="comment">// do sth with ctx</span></div><div class="line"> <span class="keyword">await</span> next();</div><div class="line"> <span class="comment">// do sth with ctx after</span></div><div class="line">});</div></pre></td></tr></table></figure>
<p>根据上面的调用模式,如果一直是值引用,那么就要 return 处理后的值,与代码中的<code>await next()</code>不符了,而且使用还会变得麻烦,所以koa选择使用context的概念,传入一个<code>Object</code>的引用,然后一直去修改它。</p>
<p>那么我们接下来要做的事情,其实就是支持 ctx 参数了:</p>
<figure class="highlight javascript"><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></pre></td><td class="code"><pre><div class="line"><span class="comment">// 定义中间件,这看起来就跟 koa 一样了是吧</span></div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params">ctx, next</span>) </span>{</div><div class="line"> ctx.output.push(<span class="number">1</span>);</div><div class="line"> <span class="keyword">await</span> next();</div><div class="line"> ctx.output.push(<span class="number">5</span>);</div><div class="line">}</div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params">ctx, next</span>) </span>{</div><div class="line"> ctx.output.push(<span class="number">2</span>);</div><div class="line"> <span class="keyword">await</span> next();</div><div class="line"> ctx.output.push(<span class="number">4</span>);</div><div class="line">}</div><div class="line"><span class="keyword">async</span> <span class="function"><span class="keyword">function</span> <span class="title">c</span>(<span class="params">ctx, next</span>) </span>{</div><div class="line"> ctx.output.push(<span class="number">3</span>);</div><div class="line">}</div><div class="line"></div><div class="line"><span class="comment">// 定义compose,因为参数的位置变化了,所以下面的执行也要修改一下了</span></div><div class="line"><span class="comment">// 最简单的方式,加一个参数</span></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">compose</span>(<span class="params">middlewares, ctx</span>) </span>{</div><div class="line"> <span class="keyword">let</span> i = middlewares.length;</div><div class="line"> <span class="keyword">let</span> next = <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>)</span>{}; <span class="comment">// noop</span></div><div class="line"> <span class="comment">// 循环算出来next</span></div><div class="line"> <span class="keyword">for</span> (<span class="keyword">let</span> i = middlewares.length - <span class="number">1</span>; i >=<span class="number">0</span>; i--) {</div><div class="line"> next = getCallback(middlewares[i], ctx, next);</div><div class="line"> }</div><div class="line"></div><div class="line"> <span class="keyword">return</span> next;</div><div class="line">}</div><div class="line"></div><div class="line"><span class="function"><span class="keyword">function</span> <span class="title">getCallback</span>(<span class="params">method, ctx, next</span>) </span>{</div><div class="line"> <span class="keyword">return</span> <span class="keyword">async</span> <span class="function"><span class="keyword">function</span>(<span class="params"></span>) </span>{</div><div class="line"> method(ctx, next);</div><div class="line"> };</div><div class="line">}</div><div class="line"></div><div class="line"><span class="keyword">let</span> ctx = {<span class="attr">output</span>: []};</div><div class="line"><span class="keyword">let</span> callback = compose([a, b, c], ctx);</div><div class="line"></div><div class="line"><span class="keyword">await</span> callback();</div><div class="line"></div><div class="line"><span class="built_in">console</span>.log(ctx.output); <span class="comment">// 输出1 2 3 4 5</span></div></pre></td></tr></table></figure>
<p>能不能更简便些呢?</p>
<p>好吧,反正我还是没看明白 koa 的 compose 结果,怎么被使用的,直接传一个 ctx 作为参数就达到效果了。</p>
<p>我其实怀疑是 koa-convert 搞的鬼,默认都是 Generator,都被转换成 async 了,然后 call 了ctx 和 next。</p>
<p>应该是这样吧……</p>
]]></content>
<categories>
<category> 架构 </category>
</categories>
<tags>
<tag> es6 </tag>
<tag> es7 </tag>
<tag> 设计模式 </tag>
<tag> 架构 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[Iterator, Generator 和 Async]]></title>
<url>https://leowang721.github.io/2017/03/13/es6/iterator-generator-and-async/</url>
<content type="html"><![CDATA[<h2 id="Iterator"><a href="#Iterator" class="headerlink" title="Iterator"></a>Iterator</h2><p><code>Iterator</code>为不同的数据结构提供统一的访问机制,只要它部署Iterator接口,就可以完成遍历操作。</p>
<p>它的流程其实是跟链表的遍历很相似的:创建一个指针对象,指向当前数据结构的起始位置,然后不断的<code>next()</code>,每次返回的是一个对象数据,包含value和done两个字段供访问。</p>
<p><code>for...of</code>循环实际上会去自动找<code>Iterator</code>接口。</p>
<p>ES6规定,默认的Iterator接口部署在数据结构的<code>Symbol.iterator</code>这个key上,一个数据结构只要具有<code>Symbol.iterator</code>方法,就可以认为是“可遍历的”。</p>
<p>再次提醒,千万注意:<code>Symbol.iterator</code>里面的i是小写!!!</p>
<h3 id="为啥要用Iterator"><a href="#为啥要用Iterator" class="headerlink" title="为啥要用Iterator"></a>为啥要用Iterator</h3><p>跟其他语言一样,提供一种方法顺序访问一个聚合对象中各个元素,而又不需暴露该对象的内部表示。</p>
<h3 id="原生支持Iterator的类型"><a href="#原生支持Iterator的类型" class="headerlink" title="原生支持Iterator的类型"></a>原生支持Iterator的类型</h3><p>原生的表述数据集的类型:<code>Array</code>、<code>Object</code>、<code>Map</code>、<code>Set</code>。</p>
<p>而实际上原生支持<code>Iterator</code>的数据结构是:<code>Array</code>、<code>Map</code>、<code>Set</code>。</p>
<p><code>Object</code>默认不支持,其实非要手写支持的话,还不如直接用<code>Map</code>。</p>
<p>字符串也支持,因为它类似于<code>Array</code></p>
<h3 id="手动支持Iterator"><a href="#手动支持Iterator" class="headerlink" title="手动支持Iterator"></a>手动支持Iterator</h3><p><code>Symbol.iterator</code>是一个函数,返回的是一个遍历器,执行next(),不断的返回包含value和done两个字段的对象数据。</p>
<a id="more"></a>
<h4 id="类似于Array的Object"><a href="#类似于Array的Object" class="headerlink" title="类似于Array的Object"></a>类似于Array的Object</h4><p>例如<code>arguments</code>,对于这种,其实我们可以直接使用<code>Array</code>的<code>Iterator</code>接口:</p>
<figure class="highlight javascript"><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"><span class="keyword">let</span> obj = {</div><div class="line"> <span class="number">0</span>: <span class="string">'a'</span>,</div><div class="line"> <span class="number">1</span>: <span class="string">'b'</span>,</div><div class="line"> <span class="number">2</span>: <span class="string">'c'</span>,</div><div class="line"> <span class="attr">length</span>: <span class="number">3</span>,</div><div class="line"> [<span class="built_in">Symbol</span>.iterator]: <span class="built_in">Array</span>.prototype[<span class="built_in">Symbol</span>.iterator]</div><div class="line">};</div></pre></td></tr></table></figure>
<p>如之前所述,能否支持遍历,仅是检查<code>Symbol.iterator</code>这个key而已,所以普通的对象也能支持<code>Iterator</code>,单还不如用<code>Set</code>呢</p>
<p>普通对象部署<code>Iterator</code></p>
<figure class="highlight javascript"><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"><span class="keyword">let</span> obj = {</div><div class="line"> <span class="attr">a</span>: <span class="string">'a'</span>,</div><div class="line"> <span class="attr">b</span>: <span class="string">'b'</span>,</div><div class="line"> [<span class="built_in">Symbol</span>.iterator]() {</div><div class="line"> <span class="keyword">const</span> self = <span class="keyword">this</span>;</div><div class="line"> <span class="keyword">const</span> keys = <span class="built_in">Object</span>.keys(self); <span class="comment">// 举个例子而已</span></div><div class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> next() {</div><div class="line"> i++;</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">done</span>: i >= keys.length,</div><div class="line"> <span class="attr">value</span>: self[keys[i]]</div><div class="line"> };</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">};</div></pre></td></tr></table></figure>
<p>需要注意的是,<code>Iterator</code>的作用是使得数据结构的成员能够按某种次序排列,所以偶尔蛋疼的这么设定,可能仅仅是为了定义一个复杂的访问顺序,但是使用的时候仅仅<code>for...of</code>就好了。</p>
<p>因此也不能强说这么做是不对的。</p>
<p>不过,对于这种,用类不是更好一点么?例如:</p>
<figure class="highlight javascript"><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"><span class="class"><span class="keyword">class</span> <span class="title">ListIteratorByAge</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(list) {</div><div class="line"> <span class="keyword">this</span>.list = list;</div><div class="line"> }</div><div class="line"></div><div class="line"> [<span class="built_in">Symbol</span>.iterator]() {</div><div class="line"> <span class="keyword">const</span> self = <span class="keyword">this</span>;</div><div class="line"> <span class="keyword">const</span> arr = self.list.slice(<span class="number">0</span>).sort(<span class="function">(<span class="params">a, b</span>) =></span> a.age >= b.age);</div><div class="line"> <span class="keyword">let</span> i = <span class="number">-1</span>;</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> next() {</div><div class="line"> i++;</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">done</span>: i >= arr.length,</div><div class="line"> <span class="attr">value</span>: arr[i]</div><div class="line"> };</div><div class="line"> }</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>注意这里的<code>Symbol.iterator</code>的定义,能不能有更简便的方法呢:</p>
<figure class="highlight javascript"><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"><span class="class"><span class="keyword">class</span> <span class="title">ListIteratorByAge</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(list) {</div><div class="line"> <span class="keyword">this</span>.list = list;</div><div class="line"> }</div><div class="line"> [<span class="built_in">Symbol</span>.iterator]() {</div><div class="line"> <span class="keyword">const</span> arr = self.list.slice(<span class="number">0</span>).sort(<span class="function">(<span class="params">a, b</span>) =></span> a.age >= b.age);</div><div class="line"> <span class="keyword">return</span> arr[<span class="built_in">Symbol</span>.iterator]();</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>再次强调,只是在定义<code>某种次序的访问</code>,没规定要怎么访问!!只是在默认支持的数据结构中按顺序访问了而已。</p>
<h3 id="Iterator的使用"><a href="#Iterator的使用" class="headerlink" title="Iterator的使用"></a>Iterator的使用</h3><p>一个经典的硬性使用Iterator的示例:</p>
<figure class="highlight javascript"><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"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">10</span>];</div><div class="line"><span class="keyword">let</span> ite = arr[<span class="built_in">Symbol</span>.iterator]();</div><div class="line"><span class="keyword">let</span> eachResult = ite.next(); <span class="comment">// 调用一次访问到第一个元素</span></div><div class="line"><span class="keyword">while</span>(!eachResult.done) {</div><div class="line"> <span class="comment">// do sth with eachResult.value</span></div><div class="line"> eachResult = ite.next();</div><div class="line">}</div></pre></td></tr></table></figure>
<p>其实直接用<code>for...of</code>更简便:</p>
<figure class="highlight javascript"><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"><span class="keyword">let</span> arr = [<span class="number">1</span>, <span class="number">4</span>, <span class="number">2</span>, <span class="number">10</span>];</div><div class="line"><span class="keyword">for</span> (value <span class="keyword">of</span> arr) {</div><div class="line"> <span class="comment">// do sth with value</span></div><div class="line">}</div></pre></td></tr></table></figure>
<h3 id="几个默认使用-Iterator-的场景"><a href="#几个默认使用-Iterator-的场景" class="headerlink" title="几个默认使用 Iterator 的场景"></a>几个默认使用 Iterator 的场景</h3><p>总结自 <a href="http://es6.ruanyifeng.com/#docs/iterator" target="_blank" rel="external">ECMAScript6 入门</a></p>
<ol>
<li>解构赋值,例如<code>let [x,y] = set</code></li>
<li>扩展运算符,例如<code>['a', ...arr, 'd']</code></li>
<li>yield<em>,例如`yield</em> [2,3,4];`</li>
<li>由于数组的遍历会调用遍历器接口,所以任何接受数组作为参数的场合都符合条件<ul>
<li>for…of</li>
<li>Array.from()</li>
<li>Map(), Set(), WeakMap(), WeakSet()(比如new Map([[‘a’,1],[‘b’,2]]))</li>
<li>Promise.all()</li>
<li>Promise.race()</li>
</ul>
</li>
</ol>
<h2 id="Generator"><a href="#Generator" class="headerlink" title="Generator"></a>Generator</h2><p>简而言之,结合着上面的<code>Iterator</code>,可以理解<code>Generator</code>实际上是生成了一个可遍历的结果数据集,数据集的元素有哪些,由<code>yield</code>和<code>return</code>定义,函数执行的结果就是数据集的<code>Iterator</code>。</p>
<p>例如:</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span>* <span class="title">testGenerator</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">yield</span> <span class="number">1</span>;</div><div class="line"> <span class="keyword">yield</span> <span class="number">2</span>;</div><div class="line"> <span class="keyword">return</span> <span class="number">3</span>;</div><div class="line">}</div><div class="line"><span class="keyword">let</span> ite = testGenerator();</div></pre></td></tr></table></figure>
<p>那么不断执行<code>ite.next()</code>并以<code>!ite.next().done</code>为判断条件的执行结果为:<code>1,2,3</code>。</p>
<p><code>next</code>可以传入值,作为执行时上一个<code>yield</code>的执行返回结果:<br><figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span>* <span class="title">counterGen</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="keyword">let</span> i = <span class="number">0</span>;</div><div class="line"> <span class="keyword">while</span>(<span class="number">1</span>) {</div><div class="line"> <span class="keyword">let</span> signal = <span class="keyword">yield</span> i++;</div><div class="line"> <span class="comment">// console.log('now is ' + i);</span></div><div class="line"> <span class="keyword">if</span> (signal) {</div><div class="line"> i = <span class="number">0</span>;</div><div class="line"> }</div><div class="line"> }</div><div class="line">}</div><div class="line"><span class="keyword">let</span> counter = counterGen();</div><div class="line"></div><div class="line">counter.next(); <span class="comment">// {value: 0, done: false}</span></div><div class="line">...</div><div class="line">counter.next(<span class="literal">true</span>); <span class="comment">// {value: 0, done: false} 重置了</span></div></pre></td></tr></table></figure></p>
<p>需要注意的是,在执行next(true)时,counterGen 中那个注释掉的console语句中,i其实是6,后面置的0。<br>执行的最终结果中,value是0。<br>也就是说,yield的结果其实是可以被后面的语句给改掉的,或者可以将例子中while语句{}包起来的部分认为是一个函数,最后返回i是最终的结果。<br>也可以这么理解:执行到下一个<code>yield</code>时,才返回了之前的结果,大概、或许是这样吧……</p>
<p>总结一小下:</p>
<ol>
<li>定义时,function 后面跟着一个<code>*</code></li>
<li>内部用<code>yield</code>定义一个可被遍历到的元素(状态)</li>
<li><code>return</code>也会被认为是一个可被遍历到的元素(状态),如果没有<code>return</code>或没跟返回值,value会是<code>undefined</code>。</li>
<li>状态会被后面的同步代码所改变,直到下一次<code>yield</code>之前都可以</li>
</ol>
<h3 id="不得不说的-yield"><a href="#不得不说的-yield" class="headerlink" title="不得不说的 yield *"></a>不得不说的 yield *</h3><p>本来不想说这个,感觉要变成基础知识集了,不过研究中间件偏偏遇到了,不得不说……</p>
<p><code>yield*</code>最大的作用,就是将<code>Generator</code>嵌在另一个<code>Generator</code>的内部执行。</p>
<figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> *<span class="title">a</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> b();</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div><div class="line"><span class="function"><span class="keyword">function</span> *<span class="title">b</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line">}</div></pre></td></tr></table></figure>
<p>执行结果是<code>13</code>,是的,2不会被输出,但是如果换成:</p>
<figure class="highlight javascript"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">yield</span> *b();</div></pre></td></tr></table></figure>
<p>就等同于:<br><figure class="highlight javascript"><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"><span class="function"><span class="keyword">function</span> *<span class="title">a</span>(<span class="params"></span>) </span>{</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">1</span>);</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">2</span>);</div><div class="line"> <span class="built_in">console</span>.log(<span class="number">3</span>);</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>输出就是<code>123</code>就好了。</p>
<p>而这也是koa2的中间件核心实现机制,笑话一下自己搞出来的遍历行为,然后傻在<code>next</code>的调用上了,囧~~</p>
<p>可以查看</p>
<h3 id="Generator更多的用法"><a href="#Generator更多的用法" class="headerlink" title="Generator更多的用法"></a>Generator更多的用法</h3><p>建议查看 <a href="http://es6.ruanyifeng.com/#docs/generator" target="_blank" rel="external">ECMAScript6 入门</a></p>
<h2 id="Async"><a href="#Async" class="headerlink" title="Async"></a>Async</h2><p>语法糖,是对<code>Generator</code>的改进。</p>
<p>返回的结果是一个<code>Promise</code>。</p>
<h3 id="错误机制"><a href="#错误机制" class="headerlink" title="错误机制"></a>错误机制</h3><p>只需注意几点就行了。</p>
<ol>
<li>返回结果是<code>Promise</code>,所以按<code>Promise</code>的错误处理就行了</li>
<li><code>async</code>内部的<code>await</code>指定的状态,是<code>同步</code>的,全都成功整体状态才会成功,任何一个失败了,整体也就失败了(后面的不执行了),除非用try…catch包起来或者用.catch处理一下</li>
</ol>
<h3 id="for-await-of"><a href="#for-await-of" class="headerlink" title="for await of"></a>for await of</h3><figure class="highlight javascript"><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"><span class="keyword">let</span> body = <span class="string">''</span>;</div><div class="line"><span class="keyword">for</span> <span class="keyword">await</span>(<span class="keyword">const</span> data <span class="keyword">of</span> req) body += data;</div><div class="line"><span class="keyword">const</span> parsed = <span class="built_in">JSON</span>.parse(body);</div><div class="line"><span class="built_in">console</span>.log(<span class="string">'got'</span>, parsed);</div></pre></td></tr></table></figure>
]]></content>
<categories>
<category> 前端 </category>
</categories>
<tags>
<tag> es6 </tag>
<tag> es7 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[React Native 浅入门 —— 绘图篇]]></title>
<url>https://leowang721.github.io/2015/08/24/learning/react-native/drawing/</url>
<content type="html"><![CDATA[<blockquote>
<p>权当一个笔记,再写写或许更明白点</p>
</blockquote>
<p>原本搞了个模拟的,就使用元素来模拟绘图,例如直线……</p>
<p>不过确实是很麻烦…… 不过再去扫了扫 Issues,果然发现有人问过,且 facebook 也有相应的方案:</p>
<p>绘图可使用:ReactART,位置:Libraries/ART/ReactNativeART.js<br><figure class="highlight jsx"><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"><span class="keyword">var</span> ReactART = {</div><div class="line"> <span class="attr">LinearGradient</span>: LinearGradient,</div><div class="line"> <span class="attr">RadialGradient</span>: RadialGradient,</div><div class="line"> <span class="attr">Pattern</span>: Pattern,</div><div class="line"> <span class="attr">Transform</span>: Transform,</div><div class="line"> <span class="attr">Path</span>: Path,</div><div class="line"> <span class="attr">Surface</span>: Surface,</div><div class="line"> <span class="attr">Group</span>: Group,</div><div class="line"> <span class="attr">ClippingRectangle</span>: ClippingRectangle,</div><div class="line"> <span class="attr">Shape</span>: Shape,</div><div class="line"> <span class="attr">Text</span>: Text,</div><div class="line">};</div></pre></td></tr></table></figure></p>
<p>例子可暂时扫扫 <a href="https://github.com/reactjs/react-page/blob/art/" target="_blank" rel="external">https://github.com/reactjs/react-page/blob/art/</a></p>
<p>继续记笔记:</p>
<h2 id="依赖"><a href="#依赖" class="headerlink" title="依赖"></a>依赖</h2><p>使用这个之前,安装 art 库:<br><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 i art --save</div></pre></td></tr></table></figure></p>
<p>并且需要在项目中添加依赖:</p>
<ol>
<li>右键点击项目 -> ‘Add Files to <code>ProjectName</code> -> 选择 node_modules/react-native/React/Libraries/ART/ART.xcodeproj’</li>
<li>将 <code>libART.a</code> 添加到 <code>Linked Frameworks and Libraries</code></li>
</ol>
<a id="more"></a>
<h2 id="基础元素"><a href="#基础元素" class="headerlink" title="基础元素"></a>基础元素</h2><ul>
<li>Surface - 长方形的容器,可渲染的区域,是其他元素的容器!</li>
<li>Group - 可容纳形状、文本和其他的分组</li>
<li>Shape - 一个向量路径定义的形状,可填充</li>
<li>Text - 文本</li>
</ul>
<p>即一个图形,必须是这样的:最外层是一个 Surface,去定义容器的大小,就像是 Canvas 元素一样,然后其子元素由 Group 进行组装,使用 Shape 和 Text 确定内容</p>
<h3 id="公共的-props"><a href="#公共的-props" class="headerlink" title="公共的 props"></a>公共的 props</h3><ul>
<li>x,y: 坐标</li>
<li>originX, originY: 原始坐标,这个给 scale 和 rotate 使用的</li>
<li>transform: {Array} 变形相关</li>
<li>visible: {boolean} 决定了 opacity 的值是 0|1</li>
<li>opacity: {number} opacity 的值</li>
<li>scale, scaleX, scaleY {number} 缩放</li>
<li>rotation: {string} 旋转 0deg</li>
</ul>
<p>说明:<br>x, y 每个都可有,它指定了元素的位置。<br>originX, originY则是一个相对位置,它主要是给 scale 和 rotate 使用的,默认是0, 0,这样就意味着,默认是以起点为中心旋转,或者从起点开始缩放!</p>
<h3 id="Surface"><a href="#Surface" class="headerlink" title="Surface"></a>Surface</h3><p>容器,接受的 props:</p>
<ul>
<li>width: {number}</li>
<li>height: {number}</li>
</ul>
<h3 id="Group"><a href="#Group" class="headerlink" title="Group"></a>Group</h3><p>定义分组,它是一个 <code>Component</code>,实际上使用了 <code>NativeGroup</code>,可接受的 props:</p>
<ul>
<li>onMouseDown</li>
<li>onMouseUp</li>
</ul>
<h3 id="Shape"><a href="#Shape" class="headerlink" title="Shape"></a>Shape</h3><p>形状,与 ART 或者 ReactART 不同的是,当前不支持 width 和 height,可接受的 props:</p>
<ul>
<li>fill: 定义笔刷,有一个用法已废弃,不多述,主要是生成颜色:new Color(colorOrBrush),值可为:<ul>
<li>{string} 预定义颜色:maroon: ‘#800000’, red: ‘#ff0000’, orange: ‘#ffA500’, yellow: ‘#ffff00’, olive: ‘#808000’, purple: ‘#800080’, fuchsia: “#ff00ff”, white: ‘#ffffff’, lime: ‘#00ff00’, green: ‘#008000’, navy: ‘#000080’, blue: ‘#0000ff’, aqua: ‘#00ffff’, teal: ‘#008080’, black: ‘#000000’, silver: ‘#c0c0c0’, gray: ‘#808080’</li>
<li>rgb/rgba 颜色:rgba(0,0,0,0.1)</li>
<li>hex #0000FF</li>
<li>hsb</li>
<li>hsl</li>
</ul>
</li>
<li>stroke: 定义颜色,跟 fill 其实用法一样</li>
<li>strokeDash: {Object} 画虚线<ul>
<li>strokeDash.count {number} array 的个数,必须有此值才能生效,必须=array.length</li>
<li>strokeDash.array:{Array} 虚线是如何交替绘制<ul>
<li>10,10 表示先绘制10个点,再跳过10个点,如此反复</li>
<li>10, 20, 10 先绘制10个点,跳过20个点,绘制10个点,跳过10个点,再绘制20个点,如此反复</li>
</ul>
</li>
</ul>
</li>
<li>strokeCap: {string} 设置线条终点形状,可取值:butt(0) , square(2) 有个默认值 round(1)</li>
<li>strokeJoin: {string} 设置线条连接点的风格,该属性支持如下三个值 可取值:miter(0), bevel(2) 有个默认值 round(1)</li>
<li>strokeWidth: {number} 线宽 默认1</li>
</ul>
<h3 id="Text"><a href="#Text" class="headerlink" title="Text"></a>Text</h3><p>文字</p>
<ul>
<li>path 文字的路径</li>
<li>font 字体相关<ul>
<li>{string} 例如 ‘normal|bold|italic 13px 字体名称’ 可选单位如 px、pt、em 等</li>
<li>{Object}<ul>
<li>font.fontFamily {string} 字体,可以使用逗号分隔</li>
<li>fontSize {number}</li>
<li>fontWeight {string} bold|normal</li>
<li>fontStyle {string} italic|normal</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>其余几个跟 Shape 一致的</p>
<h2 id="Transform"><a href="#Transform" class="headerlink" title="Transform"></a>Transform</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></pre></td><td class="code"><pre><div class="line">var t = new Transform();</div><div class="line">t.transformTo(1, 0, 0, 1, 0, 0)</div><div class="line"> .move(props.x || 0, props.y || 0)</div><div class="line"> .rotate(props.rotation || 0, props.originX, props.originY)</div><div class="line"> .scale(scaleX, scaleY, props.originX, props.originY);</div></pre></td></tr></table></figure>
<h3 id="transform-变形矩阵"><a href="#transform-变形矩阵" class="headerlink" title="transform 变形矩阵"></a>transform 变形矩阵</h3><p>参数为(xx, yx, xy, yy[, x, y]),即 arguments<br><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"> result current arguments</div><div class="line">xx xy x xx xy x xx xy x</div><div class="line">yx yy y = yx yy y * yx yy y</div><div class="line">0 0 1 0 0 1 0 0 1</div></pre></td></tr></table></figure></p>
<p>xx - (number) 沿 X 轴的缩放 (default: 1)<br>yx - (number) 旋转? multiplier to skew the x axis (default: 0)<br>xy - (number) 旋转? multiplier to skew the y axis (default: 0)<br>yy - (number) 沿 Y 轴的缩放 (default: 1)<br>x - (number) 水平移动 (default: 0)<br>y - (number) 竖直移动 (default: 0)</p>
<h3 id="transformTo"><a href="#transformTo" class="headerlink" title="transformTo"></a>transformTo</h3><p>重置矩阵为某个值,其实就是重置 current,跟构造函数参数一致</p>
<h3 id="translate-amp-move-amp-moveTo"><a href="#translate-amp-move-amp-moveTo" class="headerlink" title="translate & move & moveTo"></a>translate & move & moveTo</h3><p>translate(x, y)<br>move(x, y) 水平位移和竖直位移<br>moveTo(x, y) 移动到</p>
<h3 id="rotate-amp-rotateTo"><a href="#rotate-amp-rotateTo" class="headerlink" title="rotate & rotateTo"></a>rotate & rotateTo</h3><p>(deg, x, y)</p>
<h3 id="scale-amp-scaleTo"><a href="#scale-amp-scaleTo" class="headerlink" title="scale & scaleTo"></a>scale & scaleTo</h3><p>(x, y)<br>缩放</p>
<h3 id="resizeTo"><a href="#resizeTo" class="headerlink" title="resizeTo"></a>resizeTo</h3><p>resizeTo(width, height)</p>
<h3 id="inversePoint-amp-point"><a href="#inversePoint-amp-point" class="headerlink" title="inversePoint & point"></a>inversePoint & point</h3><p>好像是点,不过没确认作用</p>
<h2 id="Path"><a href="#Path" class="headerlink" title="Path"></a>Path</h2><p>是在 art/core/path的基础上扩展了一小下的 path,做了一点处理,使用跟 path 一样:</p>
<p>所有的起点都是当前位置,往往是所在 Group 的x, y</p>
<h3 id="基础"><a href="#基础" class="headerlink" title="基础"></a>基础</h3><p>move<br>moveTo<br>line<br>lineTo<br>reset<br>close<br>toJSON</p>
<h3 id="高级"><a href="#高级" class="headerlink" title="高级"></a>高级</h3><ul>
<li>curve 曲线,三阶贝塞尔曲线,参数为 c1x, c1y, c2x, c2y, ex, ey<ul>
<li>(c1x, c1y): 第一个控制点坐标</li>
<li>(c2x, c2y): 第二个控制点坐标</li>
<li>(ex, ey): 曲线终点,取值是相对于起点的距离</li>
</ul>
</li>
<li>curveTo 曲线,三阶贝塞尔曲线,跟 curve 唯一的区别是 (ex, ey) 是坐标值</li>
<li>arc 弧,参数x, y, rx, ry, outer, counterClockwise, rotation<ul>
<li>(x, y) 终点坐标举例起点坐标的相对距离</li>
<li>(rx, ry) 这个还没搞清楚是啥,会影响弧度</li>
<li>outer 外侧圆弧么??</li>
<li>counterClockwise 顺时针方向么?</li>
<li>rotation 角度???<br>arcTo 跟 curve 唯一的区别是 (x, y) 是坐标值<br>counterArc<br>counterArcTo</li>
</ul>
</li>
</ul>
<h2 id="Pattern-url-width-height-left-top"><a href="#Pattern-url-width-height-left-top" class="headerlink" title="Pattern(url, width, height, left, top)"></a>Pattern(url, width, height, left, top)</h2><p>主要是笔刷</p>
<h2 id="ClippingRectangle"><a href="#ClippingRectangle" class="headerlink" title="ClippingRectangle"></a>ClippingRectangle</h2><p>其实还是个 Group,不过似乎是有路径规划了还是神马,没确定完呢<br>props</p>
<ul>
<li>x, y</li>
<li>width, height</li>
</ul>
<h2 id="LinearGradient-和-RadialGradient"><a href="#LinearGradient-和-RadialGradient" class="headerlink" title="LinearGradient 和 RadialGradient"></a>LinearGradient 和 RadialGradient</h2><p>似乎是笔刷定义,暂没确认</p>
<p>to be continued</p>
]]></content>
<categories>
<category> React Native </category>
</categories>
<tags>
<tag> React Native </tag>
<tag> 全端 </tag>
<tag> 绘图 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[React Native 浅入门 —— 变形、动画篇]]></title>
<url>https://leowang721.github.io/2015/08/13/learning/react-native/animation/</url>
<content type="html"><![CDATA[<blockquote>
<p>权当一个笔记,再写写或许更明白点</p>
</blockquote>
<p>本不想这么快就写这个的,不过用到了,顺便记录一下。</p>
<h2 id="变形(2D-或-3D-转换)"><a href="#变形(2D-或-3D-转换)" class="headerlink" title="变形(2D 或 3D 转换)"></a>变形(2D 或 3D 转换)</h2><p>支持的东西在变化,请查看 <code>/Libraries/StyleSheet/TransformPropTypes.js</code> 确定当前支持的属性。</p>
<p>查看现有官方文档中 View 的 style 支持,会发现这么几个: <code>rotation</code> <code>scaleX</code> <code>scaleY</code> <code>transformMatrix</code> <code>translateX</code> <code>translateY</code>。</p>
<p>但是很不幸滴,如果去看 <code>TransformPropTypes.js</code> 就会发现,这几个已经被标成了:<code>DEPRECATED</code>。</p>
<p>甭这么用了,就算现在能用也是一样。</p>
<p>不过官方还给了这么一段:<br><figure class="highlight javascript"><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">transform: ReactPropTypes.arrayOf(</div><div class="line"> ReactPropTypes.oneOfType([</div><div class="line"> ReactPropTypes.shape({<span class="attr">perspective</span>: ReactPropTypes.number}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">rotate</span>: ReactPropTypes.string}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">rotateX</span>: ReactPropTypes.string}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">rotateY</span>: ReactPropTypes.string}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">rotateZ</span>: ReactPropTypes.string}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">scale</span>: ReactPropTypes.number}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">scaleX</span>: ReactPropTypes.number}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">scaleY</span>: ReactPropTypes.number}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">translateX</span>: ReactPropTypes.number}),</div><div class="line"> ReactPropTypes.shape({<span class="attr">translateY</span>: ReactPropTypes.number})</div><div class="line"> ])</div><div class="line"> )</div></pre></td></tr></table></figure></p>
<p>所以实际上已经整体将变形、动画相关的切到了 <code>transform</code> 上。</p>
<p>来看一个简单的例子,看了再对比上面那段,就知道怎么用了:</p>
<a id="more"></a>
<figure class="highlight jsx"><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"><span class="keyword">var</span> styles = StyleSheet.create({</div><div class="line"> <span class="attr">line</span>: {</div><div class="line"> <span class="attr">transform</span>: [</div><div class="line"> {<span class="attr">rotate</span>: <span class="string">'45deg'</span>} <span class="comment">// 旋转45度</span></div><div class="line"> ]</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>
<p>这些属性都能在 css3 中找到,所以就分类看下好了。</p>
<h3 id="perspective(0-9-0-stable开始支持)"><a href="#perspective(0-9-0-stable开始支持)" class="headerlink" title="perspective(0.9.0-stable开始支持)"></a>perspective(0.9.0-stable开始支持)</h3><p>这个说明来自于:<a href="http://www.w3school.com.cn/cssref/pr_perspective.asp" target="_blank" rel="external">W3school</a><br>perspective 属性定义 3D 元素距视图的距离,以像素计。该属性允许您改变 3D 元素查看 3D 元素的视图。<br>当为元素定义 perspective 属性时,其子元素会获得透视效果,而不是元素本身。<br>注意:perspective 属性只影响 3D 转换元素。</p>
<h3 id="旋转"><a href="#旋转" class="headerlink" title="旋转"></a>旋转</h3><p>值类型为字符串,例如 ‘9deg’,表示9度。</p>
<ul>
<li>rotate:2D旋转,绕着中心顺时针旋转多少度。</li>
<li>rotateX, rotateY, rotateZ:3D旋转,分别是绕着 X 轴、Y 轴和 Z 轴。(0.9.0-stable开始支持)</li>
</ul>
<p><img src="https://cloud.githubusercontent.com/assets/90494/9020087/82598b70-37af-11e5-8a72-b8b0d8d47521.gif"></p>
<h3 id="缩放"><a href="#缩放" class="headerlink" title="缩放"></a>缩放</h3><p>值类型为数字,表示倍数。</p>
<ul>
<li>scale:X、Y 同时缩放</li>
<li>scaleX:仅 X 轴方向缩放</li>
<li>scaleY:仅 Y 轴方向缩放</li>
</ul>
<h3 id="移动"><a href="#移动" class="headerlink" title="移动"></a>移动</h3><p>值行为数字:单位应该是坐标轴单位</p>
<ul>
<li>translateX:X 轴方向移动</li>
<li>translateY:Y 周方向移动</li>
</ul>
<h2 id="动画"><a href="#动画" class="headerlink" title="动画"></a>动画</h2><p>上面的都是设置了 style,达到一个变形的效果,那么能不能让它们动起来呢?</p>
<p>简单的 View,再设置 state 的做法是不行的,就是一个跳跃的效果。</p>
<p>现存的翻译过的文档都没有详细些的内容,这也是因为<code>动画</code>部分官方正在开发中,不过已经放出来了,而且E文的文档已经有更新,就翻译一些过来,顺便做点实验:</p>
<p>Animated 库被设计用来简便的实现风格多样的动画行为,同时又有较高的性能。</p>
<p>Animated 主要是通过声明初始/目标值,并且配置过渡的动画方式,然后通过简单的 start/stop 方法来控制动画的执行。</p>
<p>按官方说法要比设置 state 和 prerender 快很多滴。</p>
<h3 id="支持元素"><a href="#支持元素" class="headerlink" title="支持元素"></a>支持元素</h3><p>如例子中的 Animated.Image,默认支持:Image、Text和 View。</p>
<p>不过可以通过 Animated.createAnimatedComponent 方法来创造你想要的 Component。</p>
<h3 id="动画效果(速度算法)"><a href="#动画效果(速度算法)" class="headerlink" title="动画效果(速度算法)"></a>动画效果(速度算法)</h3><ul>
<li>sprint:【弹性】简单的单弹簧物理模型,弹性的动画效果,效果符合 <a href="https://facebook.github.io/origami/" target="_blank" rel="external">Origami</a><ul>
<li>friction: 摩擦力…… 默认 7</li>
<li>tension: 张力 默认 40</li>
</ul>
</li>
<li>decay: 【渐缓】以一个初始速度开始,并逐渐减慢直至停止<ul>
<li>velocity: 初始速度,必须</li>
<li>deceleration: 减速率. 默认 0.997</li>
</ul>
</li>
<li>timing: 指定时间长度的平缓动画<ul>
<li>duration: 时间长度,单位 ms, 默认 500</li>
<li>easing: 定义曲线的平缓函数. 可查看 Easing module,其中已预定义了一些函数. iOS 默认是 Easing.inOut(Easing.ease).</li>
<li>delay: 延迟多少 ms 执行动画,默认 0</li>
</ul>
</li>
</ul>
<h2 id="动画的执行(APIs)"><a href="#动画的执行(APIs)" class="headerlink" title="动画的执行(APIs)"></a>动画的执行(APIs)</h2><p>执行 <code>start</code> 方法开始动画效果,start 可以接受一个 <code>callback</code> 参数,在动画完成时触发执行。<br>如果动画正常执行结束,callback 会接收到一个参数 <code>{finished: true}</code>,如果是非正常结束,例如被手势或其他动画打断了,finished 则为 false。</p>
<h3 id="一个-Component-展现时自动触发动画"><a href="#一个-Component-展现时自动触发动画" class="headerlink" title="一个 Component 展现时自动触发动画"></a>一个 Component 展现时自动触发动画</h3><p>主要是使用 <code>componentDidMount</code><br><figure class="highlight javascript"><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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">SampleAnimation</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> <span class="keyword">constructor</span>(props: any) {</div><div class="line"> <span class="keyword">super</span>(props);</div><div class="line"> <span class="keyword">this</span>.state = {</div><div class="line"> <span class="attr">bounceValue</span>: <span class="keyword">new</span> Animated.Value(<span class="number">0</span>)</div><div class="line"> };</div><div class="line"> }</div><div class="line"> render(): ReactElement {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <Animated.View // 支持: Image, Text, View</div><div class="line"> style={</div><div class="line"> {</div><div class="line"> flex: 1,</div><div class="line"> transform: [</div><div class="line"> {scale: this.state.bounceValue}</div><div class="line"> ]</div><div class="line"> }</div><div class="line"> }</div><div class="line"> /></div><div class="line"> );</div><div class="line"> }</div><div class="line"> componentDidMount() {</div><div class="line"> this.state.bounceValue.setValue(1.5); // 目标值</div><div class="line"> Animated.spring( // 支持: spring, decay, timing,过渡的动画方式</div><div class="line"> this.state.bounceValue,</div><div class="line"> {</div><div class="line"> toValue: 0.8, // 目标值</div><div class="line"> friction: 1 // 动画方式的参数</div><div class="line"> }</div><div class="line"> ).start(); // 开始</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<h3 id="动画的组合"><a href="#动画的组合" class="headerlink" title="动画的组合"></a>动画的组合</h3><p>可以使用以下方法来进行组合:</p>
<ul>
<li>parallel 并行</li>
<li>sequence 顺序执行</li>
<li>stagger 交错??</li>
<li>delay 延迟执行</li>
</ul>
<p>这四个方法都接受数组参数,元素内容是动画效果。</p>
<p>如果任何一个动画被 stoped,则其他动画也会终止。<br>Parallel有一个参数:stopTogether,如果被设置为 false,则不会执行这种终止的效果。</p>
<figure class="highlight jsx"><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">Animated.sequence([ <span class="comment">// spring to start and twirl after decay finishes</span></div><div class="line"> Animated.decay(position, { <span class="comment">// coast to a stop</span></div><div class="line"> velocity: {<span class="attr">x</span>: gestureState.vx, <span class="attr">y</span>: gestureState.vy}, <span class="comment">// velocity from gesture release</span></div><div class="line"> deceleration: <span class="number">0.997</span>,</div><div class="line"> }),</div><div class="line"> Animated.parallel([ <span class="comment">// after decay, in parallel:</span></div><div class="line"> Animated.spring(position, {</div><div class="line"> <span class="attr">toValue</span>: {<span class="attr">x</span>: <span class="number">0</span>, <span class="attr">y</span>: <span class="number">0</span>} <span class="comment">// return to start</span></div><div class="line"> }),</div><div class="line"> Animated.timing(twirl, { <span class="comment">// and twirl</span></div><div class="line"> toValue: <span class="number">360</span>,</div><div class="line"> }),</div><div class="line"> ]),</div><div class="line">]).start(); <span class="comment">// start the sequence group</span></div></pre></td></tr></table></figure>
<h3 id="改变输入输出值域映射:interpolate"><a href="#改变输入输出值域映射:interpolate" class="headerlink" title="改变输入输出值域映射:interpolate"></a>改变输入输出值域映射:<code>interpolate</code></h3><p>例如:[0, 1]映射[0, 100]为:</p>
<figure class="highlight jsx"><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">value.interpolate({</div><div class="line"> <span class="attr">inputRange</span>: [<span class="number">0</span>, <span class="number">1</span>],</div><div class="line"> <span class="attr">outputRange</span>: [<span class="number">0</span>, <span class="number">100</span>],</div><div class="line">});</div></pre></td></tr></table></figure>
<p>同样支持多重值域:<br><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">value.interpolate({</div><div class="line"> inputRange: [-300, -100, 0, 100, 101],</div><div class="line"> outputRange: [300, 0, 1, 0, 0],</div><div class="line">});</div></pre></td></tr></table></figure></p>
<p>这意味着:</p>
<p>-300 => 300<br>-200 150<br>-100 0<br>-50 0.5<br>0 1<br>50 0.5<br>100 0<br>101 0</p>
<p>这几个很容易理解,主要是边界值:<br>最左侧的[-300, -100]对应着[300, 0],这意味着,这种映射会一直向负数方向延伸下去,因此 -400 => 450<br>最右侧是[100, 101]对应这[0, 0],这意味着此后的数目都对应0,即 200 => 0</p>
<p><code>interpolate</code> 支持动画计算函数,很多已经在 <code>Libraries/Animation/Animated/Easing.js</code> 中定义了。</p>
<p><code>interpolate</code>可配置,默认是 <code>extend</code>,不过 <code>clamp</code> 在防止输出超出值域上同样十分有用。</p>
<h3 id="toValue-可以接受另一个-Animated-Value-作为参数"><a href="#toValue-可以接受另一个-Animated-Value-作为参数" class="headerlink" title="toValue 可以接受另一个 Animated.Value 作为参数"></a>toValue 可以接受另一个 Animated.Value 作为参数</h3><h3 id="Animated-events"><a href="#Animated-events" class="headerlink" title="Animated.events"></a>Animated.events</h3><p>输入事件,可以让手势或其他事件来直接对应动画的值,这当然需要一个结构化的数据才能支持:<br>第一层是数组,元素是对象:</p>
<p>scrollX 对应着 event.nativeEvent.contentOffset.x<br>pan.x and pan.y 对应着 gestureState.dx 和 gestureState.dy<br><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">onScroll={Animated.event(</div><div class="line"> [{nativeEvent: {contentOffset: {x: scrollX}}}] // scrollX = e.nativeEvent.contentOffset.x</div><div class="line">)}</div><div class="line">onPanResponderMove={Animated.event([</div><div class="line"> null, // ignore the native event</div><div class="line"> {dx: pan.x, dy: pan.y} // extract dx and dy from gestureState</div><div class="line">]);</div></pre></td></tr></table></figure></p>
<h3 id="spring-stopAnimation-callback-终止动画"><a href="#spring-stopAnimation-callback-终止动画" class="headerlink" title="spring.stopAnimation(callback) 终止动画"></a>spring.stopAnimation(callback) 终止动画</h3><h3 id="spring-addListener-callback-增加一个-listener,在动画执行时"><a href="#spring-addListener-callback-增加一个-listener,在动画执行时" class="headerlink" title="spring.addListener(callback) 增加一个 listener,在动画执行时"></a>spring.addListener(callback) 增加一个 listener,在动画执行时</h3><h2 id="LayoutAnimation"><a href="#LayoutAnimation" class="headerlink" title="LayoutAnimation"></a>LayoutAnimation</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><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></pre></td><td class="code"><pre><div class="line">var App = React.createClass({</div><div class="line"> componentWillMount() {</div><div class="line"> // Animate creation</div><div class="line"> LayoutAnimation.spring();</div><div class="line"> },</div><div class="line"></div><div class="line"> getInitialState() {</div><div class="line"> return { w: 100, h: 100 }</div><div class="line"> },</div><div class="line"></div><div class="line"> _onPress() {</div><div class="line"> // Animate the update</div><div class="line"> LayoutAnimation.spring();</div><div class="line"> this.setState({w: this.state.w + 15, h: this.state.h + 15})</div><div class="line"> },</div><div class="line"></div><div class="line"> render: function() {</div><div class="line"> return (</div><div class="line"> <View style={styles.container}></div><div class="line"> <View style={[styles.box, {width: this.state.w, height: this.state.h}]} /></div><div class="line"> <TouchableOpacity onPress={this._onPress}></div><div class="line"> <View style={styles.button}></div><div class="line"> <Text style={styles.buttonText}>Press me!</Text></div><div class="line"> </View></div><div class="line"> </TouchableOpacity></div><div class="line"> </View></div><div class="line"> );</div><div class="line"> }</div><div class="line">});</div></pre></td></tr></table></figure>
<h2 id="requestAnimationFrame"><a href="#requestAnimationFrame" class="headerlink" title="requestAnimationFrame"></a>requestAnimationFrame</h2><p>一般来说用不到这个 来自于 browser 的 API,Animations 基本封装好了。</p>
<p>不过,我还是来个简单的例子吧,一个超级简陋的时钟秒针的动画(这结合了绘图篇的知识):<br><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></pre></td><td class="code"><pre><div class="line">class Timing extends Component {</div><div class="line"> constructor(props) {</div><div class="line"> super(props);</div><div class="line"> this.state = {</div><div class="line"> secondRotation: (new Date()).getSeconds() * 6;</div><div class="line"> };</div><div class="line"></div><div class="line"> }</div><div class="line"></div><div class="line"> animate() {</div><div class="line"> var secondRotation = (new Date()).getSeconds() * 6;</div><div class="line"> this.setState({secondRotation});</div><div class="line"> requestAnimationFrame(this.animate.bind(this));</div><div class="line"> }</div><div class="line"></div><div class="line"> componentDidMount() {</div><div class="line"> requestAnimationFrame(this.animate.bind(this));</div><div class="line"> }</div><div class="line"></div><div class="line"> render() {</div><div class="line"> return (</div><div class="line"> <View></div><div class="line"> <Surface</div><div class="line"> width={300}</div><div class="line"> height={400}</div><div class="line"> style={</div><div class="line"> {backgroundColor: 'blue'}</div><div class="line"> }></div><div class="line"> <Group x={150} y={150}></div><div class="line"> <Shape stroke="#000000" strokeWidth={4} d={line} rotation={this.state.secondRotation} /></div><div class="line"> </Group></div><div class="line"> </Surface></div><div class="line"> </View></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>to be continued.</p>
]]></content>
<categories>
<category> React Native </category>
</categories>
<tags>
<tag> React Native </tag>
<tag> 全端 </tag>
<tag> 动画 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[React Native 浅入门 —— 交互篇]]></title>
<url>https://leowang721.github.io/2015/08/12/learning/react-native/interactive/</url>
<content type="html"><![CDATA[<blockquote>
<p>权当一个笔记,再写写或许更明白点</p>
</blockquote>
<p>习惯了前端世界的交互模式之后(其实就是 DOM 事件),在这个入门的过程中感觉 React Native 的交互处理就是个不适应。</p>
<p>如果要做 React Native 的交互,首先至少要知道这样几个东西:</p>
<ul>
<li>Gesture Responder System <a href="http://facebook.github.io/react-native/docs/gesture-responder-system.html" target="_blank" rel="external">E文</a> <a href="http://wiki.jikexueyuan.com/project/react-native/gesture-responder-system.html" target="_blank" rel="external">中文</a></li>
<li>TouchableHighlight <a href="http://facebook.github.io/react-native/docs/touchablehighlight.html#content" target="_blank" rel="external">E文</a> <a href="http://wiki.jikexueyuan.com/project/react-native/touchable-highlight.html" target="_blank" rel="external">中文</a></li>
<li>TouchableOpacity <a href="http://facebook.github.io/react-native/docs/touchableopacity.html#content" target="_blank" rel="external">E文</a> <a href="http://wiki.jikexueyuan.com/project/react-native/touchable-opacity.html" target="_blank" rel="external">中文</a></li>
<li>TouchableWithoutFeedback <a href="http://facebook.github.io/react-native/docs/touchablewithoutfeedback.html#content" target="_blank" rel="external">E文</a> <a href="http://wiki.jikexueyuan.com/project/react-native/touchable-without-feedback.html" target="_blank" rel="external">中文</a></li>
<li>PanResponder <a href="http://facebook.github.io/react-native/docs/panresponder.html" target="_blank" rel="external">E文</a> <a href="http://wiki.jikexueyuan.com/project/react-native/pan-responder.html" target="_blank" rel="external">中文</a></li>
</ul>
<p>不过干读这几个文档的话,基本就是一头雾水……</p>
<p>还是一点一点来看罢:</p>
<h2 id="普通行为"><a href="#普通行为" class="headerlink" title="普通行为"></a>普通行为</h2><p>TouchableHighlight、TouchableOpacity、TouchableWithoutFeedback 这几个很好弄,官方贴心的直接封装了最基础的 Touch 行为,在任何需要点击的 View 外面直接包上这样的标签就行了。</p>
<p>TouchableHighlight 在点击时表现为高亮<br>TouchableOpacity 在点击时表现为透明<br>TouchableWithoutFeedback 在点击时无反馈</p>
<p>这几个效果都是封装好了的,无需开发者操心。</p>
<p>Sample Code<br><a id="more"></a><br><figure class="highlight jsx"><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"><TouchableOpacity</div><div class="line"> onPressIn={<span class="keyword">this</span>._onPressInCircle.bind(<span class="keyword">this</span>)}</div><div class="line"> onPressOut={<span class="keyword">this</span>._onPressOutCircle.bind(<span class="keyword">this</span>)}></div><div class="line"> <View style={styles.gridItem}></View></div><div class="line"><<span class="regexp">/TouchableOpacity></span></div></pre></td></tr></table></figure></p>
<p>然而这些只适用于按钮系……</p>
<p>支持事件:</p>
<ul>
<li>onPress</li>
<li>onPressIn</li>
<li>onPressOut</li>
<li>onLongPress</li>
</ul>
<p>支持参数:</p>
<ul>
<li>delayLongPress {number} 单位ms</li>
<li>delayPressIn {number} 单位ms</li>
<li>delayPressOut {number} 单位ms</li>
</ul>
<p>再特殊点的行为,例如划过,就不用想用这几个货直接实现了。</p>
<h2 id="Gesture-Responder-System"><a href="#Gesture-Responder-System" class="headerlink" title="Gesture Responder System"></a>Gesture Responder System</h2><p>中文翻译叫:手势应答系统。</p>
<p>主要就是搞手势识别处理的,那其实也就是复杂点的触摸:例如一边摸一遍动啊,摸着还动出花样画个 L 啥的的那种。</p>
<h3 id="事件"><a href="#事件" class="headerlink" title="事件"></a>事件</h3><h4 id="决定是否成为处理器"><a href="#决定是否成为处理器" class="headerlink" title="决定是否成为处理器"></a>决定是否成为处理器</h4><p>冒泡的:</p>
<ul>
<li>onStartShouldSetResponder <code>touchStart</code>/<code>mouseDown</code>行为发生,是否当前的元素成为处理器</li>
<li>onMoveShouldSetResponder <code>touchMove</code>/<code>mouseMove</code>行为发生,是否当前行为成为处理器</li>
</ul>
<p>不冒泡/未来不冒泡的:</p>
<ul>
<li>onScrollShouldSetResponder 滚动行为发生了,是否当前的元素成为处理器</li>
<li>onSelectionChangeShouldSetResponder 选择行为发生了,是否当前元素成为处理器</li>
</ul>
<p>是否接管成为处理器(因为冒泡是从最深处开始,可以在父级的元素使用此类方法接管):</p>
<ul>
<li>onStartShouldSetResponderCapture <code>touchStart</code>/<code>mouseDown</code>行为发生,是否当前的元素代替最深层的子元素成为处理器</li>
<li>onMoveShouldSetResponderCapture <code>touchStart</code>/<code>mouseDown</code>行为发生,是否当前的元素代替最深层的子元素成为处理器</li>
</ul>
<h4 id="开始处理了"><a href="#开始处理了" class="headerlink" title="开始处理了"></a>开始处理了</h4><ul>
<li>onResponderStart 当前处理开始</li>
<li>onResponderGrant 现在正在响应触摸事件</li>
<li>onResponderMove 用户正移动他们的手指</li>
<li>onResponderEnd 当前处理结束</li>
<li>onResponderRelease 在触摸最后被引发,即<code>touchUp</code></li>
</ul>
<h4 id="跟拦截相关的(当前应答器的身份)"><a href="#跟拦截相关的(当前应答器的身份)" class="headerlink" title="跟拦截相关的(当前应答器的身份)"></a>跟拦截相关的(当前应答器的身份)</h4><ul>
<li>onResponderReject 当前视图的应答器不是“我”了,并且还不释放让我来当。</li>
<li>onResponderTerminationRequest 其他的东西想成为应答器。应该释放应答吗?返回 true 就是允许释放</li>
<li>onResponderTerminate 应答器已经转交给别人担当了。可能在调用onResponderTerminationRequest 之后被其他视图获取,也可能是被操作系统在没有请求的情况下获取了(发生在 iOS 的 control center/notification center)</li>
</ul>
<blockquote>
<p>以上都是在ResponderEventPlugin.js里面实现的,我们直接使用视图 View 配置</p>
</blockquote>
<h4 id="行为生命周期"><a href="#行为生命周期" class="headerlink" title="行为生命周期"></a>行为生命周期</h4><p><img src="/images/learning/react-native/react-native-interactive-lifecycle-single.png" alt="单个元素的行为生命周期"></p>
<p>这个图画的我头晕啊……</p>
<h3 id="几个特性"><a href="#几个特性" class="headerlink" title="几个特性"></a>几个特性</h3><h4 id="冒泡"><a href="#冒泡" class="headerlink" title="冒泡"></a>冒泡</h4><p>之前说到,有两个东西是冒泡的:</p>
<ul>
<li>onScrollShouldSetResponder</li>
<li>onSelectionChangeShouldSetResponder</li>
</ul>
<p>然则默认是触发最深的那个元素,也就是子级元素,如果父级要拦截作为处理器,则需要处理:</p>
<ul>
<li>onStartShouldSetResponderCapture</li>
<li>onMoveShouldSetResponderCapture</li>
</ul>
<p>这两个事件的触发顺序是从父级开始的,所以如果父级设置了返回 true,则会执行父级的处理。</p>
<p>但是如果任一返回了 false,则依然使用子级元素作为处理器。</p>
<p>不过如果父级的 <code>onStartShouldSetResponder</code> 如果返回 false,干脆不会触发父级的验证,<code>onStartShouldSetResponderCapture</code> 返回 true 也没用,Move 也是同理。</p>
<h4 id="拦截"><a href="#拦截" class="headerlink" title="拦截"></a>拦截</h4><ul>
<li>onResponderReject 当前视图的应答器不是“我”了,并且还不释放让我来当。</li>
<li>onResponderTerminationRequest 其他的东西想成为应答器。应该释放应答吗?返回 true 就是允许释放</li>
<li>onResponderTerminate 应答器已经转交给别人担当了。可能在调用onResponderTerminationRequest 之后被其他视图获取,也可能是被操作系统在没有请求的情况下获取了(发生在 iOS 的 control center/notification center)</li>
</ul>
<p>话说这个还没搞明白怎么用……</p>
<h3 id="简单用法"><a href="#简单用法" class="headerlink" title="简单用法"></a>简单用法</h3><p>直接写属性,作为 prop:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><View onResponderStart={(evt) => <span class="literal">true</span>} /></div></pre></td></tr></table></figure></p>
<p>也可以使用…运算符:<br><figure class="highlight jsx"><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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">GridItem</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> get touchProps() {</div><div class="line"> <span class="keyword">return</span> {</div><div class="line"> <span class="attr">onStartShouldSetResponder</span>: <span class="function">(<span class="params">evt</span>) =></span> <span class="literal">true</span>,</div><div class="line"> <span class="attr">onResponderGrant</span>: <span class="function">(<span class="params">evt</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'child'</span>);</div><div class="line"> <span class="built_in">console</span>.log(evt);</div><div class="line"> },</div><div class="line"> <span class="attr">onResponderTerminationRequest</span>: <span class="function">(<span class="params">evt</span>) =></span> <span class="literal">true</span></div><div class="line"> };</div><div class="line"> }</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View {...this.touchProps}></View></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>也可以使用 <code>PanResponder</code>(这个会在实际处理的事件前加个 Pan,输出时又会去掉,而且会增加一个参数 <code>gestureState</code>):</p>
<figure class="highlight jsx"><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"><span class="class"><span class="keyword">class</span> <span class="title">GridItem</span> <span class="keyword">extends</span> <span class="title">Component</span> </span>{</div><div class="line"> componentWillMount() {</div><div class="line"> <span class="keyword">this</span>._panGesture = PanResponder.create({</div><div class="line"> <span class="attr">onStartShouldSetResponder</span>: <span class="function">(<span class="params">evt, gestureState</span>) =></span> <span class="literal">true</span>,</div><div class="line"> <span class="attr">onPanResponderGrant</span>: <span class="function">(<span class="params">evt, gestureState</span>) =></span> {</div><div class="line"> <span class="built_in">console</span>.log(<span class="string">'child'</span>);</div><div class="line"> <span class="built_in">console</span>.log(evt);</div><div class="line"> <span class="built_in">console</span>.log(gestureState);</div><div class="line"> },</div><div class="line"> <span class="attr">onPanResponderTerminationRequest</span>: <span class="function">(<span class="params">evt, gestureState</span>) =></span> <span class="literal">true</span></div><div class="line"> });</div><div class="line"> }</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <View {...this._panResponder.panHandlers}></View></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><h3 id="划走切换的效果"><a href="#划走切换的效果" class="headerlink" title="划走切换的效果"></a>划走切换的效果</h3><p>可以参考:<a href="http://www.terlici.com/2015/04/06/simle-slide-menu-react-native.html" target="_blank" rel="external">http://www.terlici.com/2015/04/06/simle-slide-menu-react-native.html</a><br>就是使用 PanResponder + Animation做的。</p>
<p>这个回头我自己再搞个出来。</p>
<h3 id="手势解锁"><a href="#手势解锁" class="headerlink" title="手势解锁"></a>手势解锁</h3><p>因为不想用 WebView 做,所以这里都是从纯 React Native 的角度去考虑的。</p>
<p>因为生命周期中,TouchIn 是起点,所以如果在外面按住了划过元素,元素是不会有反应的……</p>
<p>单纯的子级接管作为处理器然后释放也是没用的,如果同时设置 Capture,父级的优先级大……</p>
<p>那么是否可以这样呢?父级判断碰撞,然后释放处理权?但是拦截的判断只在最开始触发的时候能搞,所以似乎还是行不通的。而且都碰撞到了,如果能直接处理子元素不是更简便么?</p>
<p>没那么简单,需要看看这三个方法:</p>
<h4 id="子元素的获取"><a href="#子元素的获取" class="headerlink" title="子元素的获取"></a>子元素的获取</h4><p>使用 refs:<br><figure class="highlight jsx"><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></pre></td><td class="code"><pre><div class="line"><View style={styles.gridView} {...this._panResponder.panHandlers} ></div><div class="line"> <View style={styles.gridLine}></div><div class="line"> <GridItem ref="item1" /></div><div class="line"> <GridItem ref="item2" /></div><div class="line"> <GridItem ref="item3" /></div><div class="line"> </View></div><div class="line"> <View style={styles.gridLine}></div><div class="line"> <GridItem ref="item4" /></div><div class="line"> <GridItem ref="item5" /></div><div class="line"> <GridItem ref="item6" /></div><div class="line"> </View></div><div class="line"> <View style={styles.gridLine}></div><div class="line"> <GridItem ref="item7" /></div><div class="line"> <GridItem ref="item8" /></div><div class="line"> <GridItem ref="item9" /></div><div class="line"> </View></div><div class="line"></View></div></pre></td></tr></table></figure></p>
<p>这样就可以直接通过 <code>this.refs[name]</code> 获取到子元素了。</p>
<h4 id="获取子元素的位置"><a href="#获取子元素的位置" class="headerlink" title="获取子元素的位置"></a>获取子元素的位置</h4><p>这是从 React Native 的 <a href="https://github.com/facebook/react-native/issues/1374#issuecomment-104954196" target="_blank" rel="external">Issue 1374</a> 拿到的方法:<br><figure class="highlight jsx"><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"><span class="keyword">var</span> RCTUIManager = <span class="built_in">require</span>(<span class="string">'NativeModules'</span>).UIManager;</div><div class="line"><span class="keyword">var</span> view = <span class="keyword">this</span>.refs[name];</div><div class="line"><span class="keyword">var</span> handle = React.findNodeHandle(view);</div><div class="line">RCTUIManager.measure(handle, (x, y, width, height, pageX, pageY) => {</div><div class="line"> <span class="comment">// x,y 似乎是当前container的坐标</span></div><div class="line"> <span class="comment">// width, height 是宽高</span></div><div class="line"> <span class="comment">// pageX, pageY 是在屏幕中的坐标(起始坐标)</span></div><div class="line">})</div></pre></td></tr></table></figure></p>
<p>所以,元素在屏幕中的范围是:pageX ~ pageX + width, pageY ~ pageY + height</p>
<p>至少是个简单的正方形,如果是其他形状例如圆形,可能还需要计算圆心和半径的大小。</p>
<h4 id="获取当前-touch-的坐标"><a href="#获取当前-touch-的坐标" class="headerlink" title="获取当前 touch 的坐标"></a>获取当前 touch 的坐标</h4><p>之前说过 PanResponder 会给事件方法增加一个参数 <code>gestureState</code>:</p>
<p>一个 gestureState 对象有以下属性:</p>
<ul>
<li>stateID:gestureState 的ID-在屏幕上保持至少一个触发动作的时间</li>
<li>moveX:最近动态触发的最新的屏幕坐标</li>
<li>x0:应答器横向的屏幕坐标</li>
<li>y0:应答器纵向的屏幕坐标</li>
<li>dx:触发开始后累积的横向动作距离</li>
<li>dy:触发开始后累积的纵向动作距离</li>
<li>vx:当前手势的横向速度</li>
<li>vy:当前手势的纵向速度</li>
<li>numberActiveTouch:屏幕上当前触发的数量</li>
</ul>
<p>那么 touch 位置的坐标可以这么获得:[x0 + dx, y0 + dy]</p>
<p>当然也可以使用都有的 evt 参数:</p>
<p>changedTouches - Array of all touch events that have changed since the last event<br>identifier - The ID of the touch<br>locationX - The X position of the touch, relative to the element<br>locationY - The Y position of the touch, relative to the element<br>pageX - The X position of the touch, relative to the screen<br>pageY - The Y position of the touch, relative to the screen<br>target - The node id of the element receiving the touch event<br>timestamp - A time identifier for the touch, useful for velocity calculation<br>touches - Array of all current touches on the screen</p>
<p>直接用[pageX, pageY] 就行了。</p>
<p>这样就可以进行简单的碰撞计算了,计算位置是否在某个子元素的范围内就行了。</p>
<p>实际上至此手势解锁的几个关键问题已经解决,正在写一个手势解锁的组件:<a href="https://github.com/leowang721/k-react-native-swipe-unlock" target="_blank" rel="external">k-react-native-swipe-unlock</a> 玩耍。</p>
<p>To Be Continued.</p>
]]></content>
<categories>
<category> React Native </category>
</categories>
<tags>
<tag> React Native </tag>
<tag> 全端 </tag>
</tags>
</entry>
<entry>
<title><![CDATA[React Native 浅入门 —— 结构篇]]></title>
<url>https://leowang721.github.io/2015/08/10/learning/react-native/structure/</url>
<content type="html"><![CDATA[<blockquote>
<p>权当一个笔记,再写写或许更明白点</p>
</blockquote>
<h2 id="结构布局"><a href="#结构布局" class="headerlink" title="结构布局"></a>结构布局</h2><p>说到布局,首先作为一个前端同学,我想到的是:div、p、span、section … 以及 article、header、footer…</p>
<p>这些非语义化的&语义化的标签,在前端的世界中,是用于按层级、按语义去实现一个可读的结构。</p>
<p>一个经典的 HTML 结构:<br><figure class="highlight html"><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></pre></td><td class="code"><pre><div class="line"><span class="tag"><<span class="name">header</span>></span></div><div class="line"> <span class="tag"><<span class="name">nav</span>></span></div><div class="line"> <span class="tag"><<span class="name">ul</span>></span></div><div class="line"> <span class="tag"><<span class="name">li</span>></span>首页<span class="tag"></<span class="name">li</span>></span></div><div class="line"> <span class="tag"><<span class="name">li</span>></span>文章列表<span class="tag"></<span class="name">li</span>></span></div><div class="line"> <span class="tag"></<span class="name">ul</span>></span></div><div class="line"> <span class="tag"></<span class="name">nav</span>></span></div><div class="line"><span class="tag"></<span class="name">header</span>></span></div><div class="line"><span class="tag"><<span class="name">section</span> <span class="attr">id</span>=<span class="string">"article-list"</span>></span></div><div class="line"> <span class="tag"><<span class="name">h1</span>></span>文章列表<span class="tag"></<span class="name">h1</span>></span></div><div class="line"> <span class="tag"><<span class="name">article</span>></span></div><div class="line"> <span class="tag"><<span class="name">header</span>></span></div><div class="line"> <span class="tag"><<span class="name">hgroup</span>></span></div><div class="line"> <span class="tag"><<span class="name">h1</span>></span>title<span class="tag"></<span class="name">h1</span>></span></div><div class="line"> <span class="tag"><<span class="name">h2</span>></span>作者<span class="tag"></<span class="name">h2</span>></span></div><div class="line"> <span class="tag"></<span class="name">hgroup</span>></span></div><div class="line"> <span class="tag"></<span class="name">header</span>></span></div><div class="line"> <span class="tag"><<span class="name">section</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span>第一段<span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"><<span class="name">p</span>></span>第二段<span class="tag"></<span class="name">p</span>></span></div><div class="line"> <span class="tag"></<span class="name">section</span>></span></div><div class="line"> <span class="tag"><<span class="name">aside</span>></span></div><div class="line"> <span class="tag"><<span class="name">ul</span>></span></div><div class="line"> <span class="tag"><<span class="name">li</span>></span>tag1<span class="tag"></<span class="name">li</span>></span></div><div class="line"> <span class="tag"><<span class="name">li</span>></span>tag2<span class="tag"></<span class="name">li</span>></span></div><div class="line"> <span class="tag"></<span class="name">ul</span>></span></div><div class="line"> <span class="tag"></<span class="name">aside</span>></span></div><div class="line"> <span class="tag"></<span class="name">article</span>></span></div><div class="line"> ...</div><div class="line"><span class="tag"></<span class="name">section</span>></span></div><div class="line"><span class="tag"><<span class="name">footer</span>></span></div><div class="line"> CopyRight</div><div class="line"><span class="tag"></<span class="name">footer</span>></span></div></pre></td></tr></table></figure></p>
<p>写 HTML 的时候,我们往往是直接就设计完了这样的一个结构,一个 big picture,当然我们还是先按大块设计,再去设计细节!不过在设计结构的时候,基本一直都能看到完整的东西。</p>
<p>而在 React Native 中,讲究的是 Component 的复用,是不断的分层/块去编写 Component,但是只有在那个 Component 中才能看到再进一步的结构,因为往往是“你只需要使用我就好了,不需要关心我怎么显示的。” </p>
<p>React 也是一样,不信去看最终 React 展现到浏览器中的样子吧,还是那么一堆 div 标签,干读基本读晕。</p>
<p>话题转回来,我们要这么展现文章列表:<br><a id="more"></a><br><figure class="highlight jsx"><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">...</div><div class="line"><ArticleList /></div><div class="line">...</div></pre></td></tr></table></figure></p>
<p>所以,我们需要设计个列表页:<br><figure class="highlight jsx"><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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">ArticleList</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> ...</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">Text</span>></span>文章列表<span class="tag"></<span class="name">Text</span>></span></span></div><div class="line"> <ListView</div><div class="line"> dataSource={<span class="keyword">this</span>.state.datasource}</div><div class="line"> renderRow={<span class="keyword">this</span>._renderArticle /></div><div class="line"> );</div><div class="line"> }</div><div class="line"> _renderArticle(rowData: string, <span class="attr">sectionID</span>: number, <span class="attr">rowID</span>: number) {</div><div class="line"> ...</div><div class="line"> return (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">TouchableHighlight</span> <span class="attr">onPress</span>=<span class="string">{()</span> =></span> this._pressArticle(rowID)}></span></div><div class="line"> <span class="tag"><<span class="name">Article</span> <span class="attr">id</span>=<span class="string">"{articleId}"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">TouchableHighlight</span>></span></div><div class="line"> )</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure></p>
<p>在这里确定了:我要以 ListView 的方式展现列表,点击的时候,使用 Article 的视图展现内容。</p>
<p>所以还需要写个 Article(在写的时候直接按全屏搞的,导航什么的留给 Navigator 搞定,我才不管。)</p>
<figure class="highlight jsx"><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></pre></td><td class="code"><pre><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Article</span> <span class="keyword">extends</span> <span class="title">React</span>.<span class="title">Component</span> </span>{</div><div class="line"> render() {</div><div class="line"> <span class="keyword">return</span> (</div><div class="line"> <span class="xml"><span class="tag"><<span class="name">View</span> <span class="attr">style</span>=<span class="string">"{articleStyles.header}"</span>></span></span></div><div class="line"> <span class="tag"><<span class="name">View</span> <span class="attr">styles</span>=<span class="string">"{articleStyles.title}"</span>></span></div><div class="line"> <span class="tag"><<span class="name">Text</span>></span>title<span class="tag"></<span class="name">Text</span>></span></div><div class="line"> <span class="tag"></<span class="name">View</span>></span></div><div class="line"> <span class="tag"><<span class="name">ArticleAuthor</span> <span class="attr">name</span>=<span class="string">"Leo"</span> /></span></div><div class="line"> <span class="tag"></<span class="name">View</span>></span></div><div class="line"> <span class="tag"><<span class="name">View</span> <span class="attr">style</span>=<span class="string">"{articleStyles.content}"</span>></span></div><div class="line"> <span class="tag"><<span class="name">Text</span>></span>第一段<span class="tag"></<span class="name">Text</span>></span></div><div class="line"> <span class="tag"><<span class="name">Text</span>></span>第二段<span class="tag"></<span class="name">Text</span>></span></div><div class="line"> <span class="tag"></<span class="name">View</span>></span></div><div class="line"> <span class="tag"><<span class="name">View</span> <span class="attr">style</span>=<span class="string">"{articleStyles.aside}"</span>></span></div><div class="line"> <span class="tag"><<span class="name">ArticleTags</span> /></span></div><div class="line"> <span class="tag"></<span class="name">View</span>></span></div><div class="line"> );</div><div class="line"> }</div><div class="line">}</div></pre></td></tr></table></figure>
<p>这样的过程,导致我们不能对当前的视图有一个像 HTML 那样的总览,看到的永远是块,而不是结构。而且由于 App 的场景专注性是极强的,所以它实际上更多的是 Navigator 式的浏览,而不是像 Web 大多数时候都要展现一个完整的 Nav 放在那里。</p>
<p>所以在文章列表后,点击展现 Article 的时候,我们使用 navigator 提供了一个返回按钮(最多再提供上一篇、下一篇),不需要再提供 Nav 了,而且这些都跟 Article 没啥关系,Article 只关注自己要展现的东西,而且以全屏的方式展现。</p>
<p>事实上很多 Web 站点也开始使用这种方式了。</p>
<p>这样的好处是,独立性很强,复用性也很强。坏处是:如果你“查看元素”,只会吐槽什么鬼东西!</p>
<p>不过思路是 OK 的,不过说实话我更喜欢 webcomponents,这样的结构看起来不是更好么?基本也是一个玩路</p>
<p><img src="/images/learning/react-native/webcomponents-structure.png" alt="webcomponents 理念下的一个 DOM 结构" width="500"></p>
<h2 id="标签映射"><a href="#标签映射" class="headerlink" title="标签映射"></a>标签映射</h2><h3 id="已经存在的基础映射"><a href="#已经存在的基础映射" class="headerlink" title="已经存在的基础映射"></a>已经存在的基础映射</h3><p>如图所示:<img src="/images/learning/react-native/structure.png" alt="当前 React Native 提供的跟基础 HTML 元素相关的Component(默认0.8-stable)"></p>
<p>缺了那么多的标签,让被惯坏了的前端怎么玩……</p>
<p>别的还好,View 和 Text 这两个东西,基本就要冒充很多标签的行为了:</p>
<h3 id="先说Text"><a href="#先说Text" class="headerlink" title="先说Text"></a>先说Text</h3><p>在 React Native 的世界中,只有一种文本标记:Text。只要你要显示文字,就必须放在 Text 标签中,没的商量。</p>
<p>所以 Text 对于前端同学来说,就约等于 p、div、h1~h6、section、span 等等标签,先吐会儿……</p>
<p>标题基本就是:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><Text style=<span class="string">"{某个styles.title}"</span>>标题内容<<span class="regexp">/Text></span></div></pre></td></tr></table></figure></p>
<p>段落可能就是:<br><figure class="highlight jsx"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line"><Text style=<span class="string">"{某个styles.paragraph}"</span>>标题内容<<span class="regexp">/Text></span></div></pre></td></tr></table></figure></p>
<p>我们还是更细一些,在 Web 的世界中,文本可能为(不会出现在移动领域的就不提了,例如鼠标悬浮的 title):</p>
<p><img src="/images/learning/react-native/text-types.png" alt="Text Types" width="500"></p>
<p>不是简单的一一对应的关系,可能需要不断的组合。并且在 Web 中重新实现 UI 的方式,在这里依然存在,所以有可能对应的就是某个你自行实现的或者引用自外部库的 Component。</p>
<p>在 React Native 中,文本是信息,所以一个 Text 就够了,将文本的组合,或许引入交互,或许引入图片等等 视为 Component,转到组件的世界里去玩,而不是平铺的画板上直接画内容。</p>
<h3 id="再说View"><a href="#再说View" class="headerlink" title="再说View"></a>再说View</h3><p>在 React Native 中,View 是最基本的视图:<br><code>View is a container that supports layout with flexbox, style, some touch handling, and accessibility controls, and is designed to be nested inside other views and to have 0 to many children of any type</code></p>
<p>所以其实所有的 视图Component 最终都是一个 View 而已。</p>
<p>所以从基础元素的角度来说:View 当前对于我们来说,约等于 div、section 等等所有跟布局相关的元素。</p>
<p>尼玛要用盒模型?要用 flexbox?用 View 吧,还支持无限嵌套的。同时作为容器,全局的行为神马的都往 View 身上扔吧,我就是新的 div,那个爆炸扩散嵌套的 div!</p>
<h4 id="UI-控件"><a href="#UI-控件" class="headerlink" title="UI 控件"></a>UI 控件</h4><p>这时候,来说一下 UI 控件,就是在 Web 届很喜欢搞的东东,先看已经提供了的:<br><img src="/images/learning/react-native/ui.png" alt="当前 React Native 提供的 ui Component(默认0.8-stable)"></p>
<p>当然我们可以把基础的 View 和 Text 砍掉后剩下的东西也视为 ui 控件。</p>
<p>Web 届的 UI 种类如此之多,但我们可以庆幸的是,在 APP 中,解决了一个大问题:控件交互的各异性。APP 中是普适性的 UI 交互,就这一种,都这么用,不用去实现什么3、4、5、6种日历控件神马的了。</p>
<p>在 React Native 中,需要不断的根据需求进行可复用性评估,然后不断的开发出 ui Component、业务 Component、通用 Component 等等,因为我的 APP 就是用这些 Component 拼出来的视图!</p>
<p>而这么多自行定义的 Component,你说你不搞各异性体验的 UI??</p>
<h4 id="还有呢"><a href="#还有呢" class="headerlink" title="还有呢"></a>还有呢</h4><ol>
<li>怎么画图?Canvas 木有了,怎么搞手势解锁呢?<br>直接 View 拼接?直接调用 Native 的组件?还是像 lwansbrough/react-native-canvas 现在占位用 WebView 内嵌个 Canvas?还是自行实现一个 Canvas To UIView 绘图的东西?<br>我想要的是一个通用的东西,写一个 SwipeUnlocker 出来,是期望 Web、M 站、APP 上都直接可用的,哪个解决方案能满足呢?</li>
</ol>
<p><code>2015-08-24 更新</code><br>绘图可使用:ReactART,位置:Libraries/ART/ReactNativeART.js<br><figure class="highlight jsx"><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"><span class="keyword">var</span> ReactART = {</div><div class="line"> <span class="attr">LinearGradient</span>: LinearGradient,</div><div class="line"> <span class="attr">RadialGradient</span>: RadialGradient,</div><div class="line"> <span class="attr">Pattern</span>: Pattern,</div><div class="line"> <span class="attr">Transform</span>: Transform,</div><div class="line"> <span class="attr">Path</span>: Path,</div><div class="line"> <span class="attr">Surface</span>: Surface,</div><div class="line"> <span class="attr">Group</span>: Group,</div><div class="line"> <span class="attr">ClippingRectangle</span>: ClippingRectangle,</div><div class="line"> <span class="attr">Shape</span>: Shape,</div><div class="line"> <span class="attr">Text</span>: Text,</div><div class="line">};</div></pre></td></tr></table></figure></p>
<p>例子可暂时查看 <a href="https://github.com/reactjs/react-page/blob/art/" target="_blank" rel="external">https://github.com/reactjs/react-page/blob/art/</a></p>
<p>关于绘图,再单独记录一篇笔记罢:[React Native 浅入门 —— 绘图篇]</p>
<ol>
<li><p>Form 没有了,不过还有 tcomb-form-native,不过这东西的使用模式需要适应下……</p>
</li>
<li><p>WebView 有多大的性能问题呢?</p>
</li>
<li><p>当前来说 OC <-> JS 的 Component 映射是不是还是主流模式?</p>
</li>
</ol>