forked from phodal/github
-
Notifications
You must be signed in to change notification settings - Fork 0
/
index.html
3211 lines (3186 loc) · 286 KB
/
index.html
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
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" lang="" xml:lang="">
<head>
<meta charset="utf-8" />
<meta name="generator" content="pandoc" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=yes" />
<title>GitHub 漫游指南 – github-roam</title>
<style type="text/css">
code{white-space: pre-wrap;}
span.smallcaps{font-variant: small-caps;}
span.underline{text-decoration: underline;}
div.column{display: inline-block; vertical-align: top; width: 50%;}
</style>
<style type="text/css">
a.sourceLine { display: inline-block; line-height: 1.25; }
a.sourceLine { pointer-events: none; color: inherit; text-decoration: inherit; }
a.sourceLine:empty { height: 1.2em; }
.sourceCode { overflow: visible; }
code.sourceCode { white-space: pre; position: relative; }
div.sourceCode { margin: 1em 0; }
pre.sourceCode { margin: 0; }
@media screen {
div.sourceCode { overflow: auto; }
}
@media print {
code.sourceCode { white-space: pre-wrap; }
a.sourceLine { text-indent: -1em; padding-left: 1em; }
}
pre.numberSource a.sourceLine
{ position: relative; left: -4em; }
pre.numberSource a.sourceLine::before
{ content: attr(title);
position: relative; left: -1em; text-align: right; vertical-align: baseline;
border: none; pointer-events: all; display: inline-block;
-webkit-touch-callout: none; -webkit-user-select: none;
-khtml-user-select: none; -moz-user-select: none;
-ms-user-select: none; user-select: none;
padding: 0 4px; width: 4em;
color: #aaaaaa;
}
pre.numberSource { margin-left: 3em; border-left: 1px solid #aaaaaa; padding-left: 4px; }
div.sourceCode
{ }
@media screen {
a.sourceLine::before { text-decoration: underline; }
}
code span.al { color: #ff0000; font-weight: bold; } /* Alert */
code span.an { color: #60a0b0; font-weight: bold; font-style: italic; } /* Annotation */
code span.at { color: #7d9029; } /* Attribute */
code span.bn { color: #40a070; } /* BaseN */
code span.bu { } /* BuiltIn */
code span.cf { color: #007020; font-weight: bold; } /* ControlFlow */
code span.ch { color: #4070a0; } /* Char */
code span.cn { color: #880000; } /* Constant */
code span.co { color: #60a0b0; font-style: italic; } /* Comment */
code span.cv { color: #60a0b0; font-weight: bold; font-style: italic; } /* CommentVar */
code span.do { color: #ba2121; font-style: italic; } /* Documentation */
code span.dt { color: #902000; } /* DataType */
code span.dv { color: #40a070; } /* DecVal */
code span.er { color: #ff0000; font-weight: bold; } /* Error */
code span.ex { } /* Extension */
code span.fl { color: #40a070; } /* Float */
code span.fu { color: #06287e; } /* Function */
code span.im { } /* Import */
code span.in { color: #60a0b0; font-weight: bold; font-style: italic; } /* Information */
code span.kw { color: #007020; font-weight: bold; } /* Keyword */
code span.op { color: #666666; } /* Operator */
code span.ot { color: #007020; } /* Other */
code span.pp { color: #bc7a00; } /* Preprocessor */
code span.sc { color: #4070a0; } /* SpecialChar */
code span.ss { color: #bb6688; } /* SpecialString */
code span.st { color: #4070a0; } /* String */
code span.va { color: #19177c; } /* Variable */
code span.vs { color: #4070a0; } /* VerbatimString */
code span.wa { color: #60a0b0; font-weight: bold; font-style: italic; } /* Warning */
</style>
<link rel="stylesheet" href="style.css" />
<!--[if lt IE 9]>
<script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/3.7.3/html5shiv-printshiv.min.js"></script>
<![endif]-->
<meta name="viewport" content="width=device-width">
</head>
<body>
<p>
<h1>GitHub 漫游指南</h1>
<p>项目首页: <a href="https://github.com/phodal/github-roam">GitHub 漫游指南</a></p>
<p>By <a href="https://www.phodal.com">Phodal Huang</a>(微博、知乎、GitHub、SegmentFault: @<a href="http://weibo.com/phodal">phodal</a>)
</p>
<p>我的其他电子书:</p>
<ul>
<li>《<a href="https://github.com/phodal/ideabook">Phodal's Idea实战指南</a>》</li>
<li>《<a href="https://github.com/phodal/designiot">一步步搭建物联网系统</a>》</li>
<li>《<a href="https://serverless.ink/">Serverless 应用开发指南</a>》</li>
<li>《<a href="https://github.com/phodal/repractise">RePractise</a>》</li>
<li>《<a href="https://github.com/phodal/growth-ebook">Growth: 全栈增长工程师指南</a>》</li>
<li>《<a href="https://github.com/phodal/growth-in-action">Growth: 全栈增长工程师实战</a>》</li>
<li>《<a href="https://github.com/phodal/fe">我的职业是前端工程师</a>》</li>
<li>《<a href="https://github.com/phodal/make">写给软件工程师看的硬件编程指南</a>》</li>
</ul>
<p>微信公众号</p>
<p><img src="https://articles.phodal.com/qrcode.jpg" alt=""/></p>
<p>
当前为预览版,在使用的过程中遇到任何遇到请及时与我联系。阅读过程中问题,不烦在GitHub上提出来:
<a href="https://github.com/phodal/hardware-guide/issues">Issues</a>
</p>
<p>
阅读过程中遇到语法错误、拼写错误、技术错误等等,不烦来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。
</p>
<div style="width:800px">
<iframe src="https://ghbtns.com/github-btn.html?user=phodal&repo=github-roam&type=watch&count=true"
allowtransparency="true" frameborder="0" scrolling="0" width="110px" height="20px"></iframe>
<div>
<nav id="TOC">
<ul>
<li><a href="#前言">前言</a><ul>
<li><a href="#关于作者">关于作者</a></li>
<li><a href="#我与github的故事">我与GitHub的故事</a><ul>
<li><a href="#github与收获">GitHub与收获</a></li>
<li><a href="#github与成长">GitHub与成长</a></li>
</ul></li>
<li><a href="#为什么你应该深入github">为什么你应该深入GitHub</a><ul>
<li><a href="#方便工作">方便工作</a></li>
<li><a href="#获得一份工作">获得一份工作</a></li>
<li><a href="#扩大交际">扩大交际</a></li>
</ul></li>
</ul></li>
<li><a href="#创建开源项目">创建开源项目</a><ul>
<li><a href="#取一个好的名字">取一个好的名字</a></li>
<li><a href="#挑选好-license">挑选好 LICENSE</a><ul>
<li><a href="#公有领域">公有领域</a></li>
<li><a href="#gpl">GPL</a></li>
<li><a href="#mit">MIT</a></li>
<li><a href="#creative-commons">Creative Commons</a></li>
</ul></li>
</ul></li>
<li><a href="#git基本知识与github使用">Git基本知识与GitHub使用</a><ul>
<li><a href="#git">Git</a><ul>
<li><a href="#git初入">Git初入</a></li>
</ul></li>
<li><a href="#github">GitHub</a><ul>
<li><a href="#版本管理与软件部署">版本管理与软件部署</a></li>
<li><a href="#github与git">GitHub与Git</a></li>
<li><a href="#在-github-创建项目">在 GitHub 创建项目</a></li>
</ul></li>
<li><a href="#github-流行项目分析">GitHub 流行项目分析</a></li>
<li><a href="#pull-request">Pull Request</a><ul>
<li><a href="#我的第一个pr">我的第一个PR</a></li>
<li><a href="#cla">CLA</a></li>
</ul></li>
</ul></li>
<li><a href="#构建-github-项目">构建 GitHub 项目</a><ul>
<li><a href="#如何用好-github">如何用好 GitHub</a><ul>
<li><a href="#敏捷软件开发">敏捷软件开发</a></li>
<li><a href="#测试">测试</a></li>
<li><a href="#ci">CI</a></li>
<li><a href="#代码质量">代码质量</a></li>
</ul></li>
<li><a href="#模块分离与测试">模块分离与测试</a><ul>
<li><a href="#代码模块化">代码模块化</a></li>
<li><a href="#自动化测试">自动化测试</a></li>
<li><a href="#jshint">Jshint</a></li>
<li><a href="#mocha">Mocha</a></li>
<li><a href="#测试示例">测试示例</a></li>
</ul></li>
<li><a href="#代码质量与重构">代码质量与重构</a><ul>
<li><a href="#code-climate">Code Climate</a></li>
<li><a href="#代码的坏味道">代码的坏味道</a></li>
</ul></li>
</ul></li>
<li><a href="#git-提交信息及几种不同的规范">Git 提交信息及几种不同的规范</a><ul>
<li><a href="#工作写法">工作写法</a></li>
<li><a href="#常规写法">常规写法</a></li>
<li><a href="#开源应用开源库写法">开源应用、开源库写法</a></li>
</ul></li>
<li><a href="#创建项目文档">创建项目文档</a><ul>
<li><a href="#readme">README</a></li>
<li><a href="#官方首页与在线文档">官方首页与在线文档</a></li>
<li><a href="#可用示例">可用示例</a></li>
</ul></li>
<li><a href="#改善-github-项目代码质量重构">改善 GitHub 项目代码质量:重构</a><ul>
<li><a href="#为什么重构">为什么重构?</a></li>
<li><a href="#重构umarkdown">重构uMarkdown</a><ul>
<li><a href="#代码说明">代码说明</a></li>
</ul></li>
<li><a href="#intellij-idea重构">Intellij Idea重构</a><ul>
<li><a href="#rename">Rename</a></li>
<li><a href="#extract-method">Extract Method</a></li>
<li><a href="#inline-method">Inline Method</a></li>
<li><a href="#pull-members-up">Pull Members Up</a></li>
<li><a href="#重构之以查询取代临时变量">重构之以查询取代临时变量</a></li>
</ul></li>
</ul></li>
<li><a href="#改善-github-项目代码质量测试">改善 GitHub 项目代码质量:测试</a><ul>
<li><a href="#tdd">TDD</a><ul>
<li><a href="#一次测试驱动开发">一次测试驱动开发</a></li>
<li><a href="#说说tdd">说说TDD</a></li>
<li><a href="#tdd思考">TDD思考</a></li>
</ul></li>
<li><a href="#功能测试">功能测试</a><ul>
<li><a href="#轻量级网站测试twill">轻量级网站测试TWill</a></li>
<li><a href="#twill-登陆测试">Twill 登陆测试</a></li>
<li><a href="#twill-测试脚本">Twill 测试脚本</a></li>
</ul></li>
<li><a href="#fake-server">Fake Server</a></li>
</ul></li>
<li><a href="#如何推广">如何推广</a><ul>
<li><a href="#marketing-first">Marketing First</a></li>
<li><a href="#编写一个好的-readme">编写一个好的 README</a><ul>
<li><a href="#这个项目做什么一句话文案">这个项目做什么——一句话文案</a></li>
<li><a href="#它解决了什么问题">它解决了什么问题</a></li>
<li><a href="#它有什么特性">它有什么特性</a></li>
<li><a href="#安装及hello-world-示例">安装及hello, world 示例</a></li>
</ul></li>
<li><a href="#技术文档">技术文档</a><ul>
<li><a href="#技术文档-1">技术文档</a></li>
<li><a href="#更多的示例程序">更多的示例程序</a></li>
<li><a href="#编写技术文章书籍">编写技术文章、书籍</a></li>
</ul></li>
<li><a href="#鼓励吸引贡献者">鼓励、吸引贡献者</a></li>
</ul></li>
<li><a href="#开源项目维护">开源项目维护</a><ul>
<li><a href="#release">Release</a></li>
</ul></li>
<li><a href="#git-与-github-工具推荐">Git 与 GitHub 工具推荐</a><ul>
<li><a href="#git-命令行增强">Git 命令行增强</a><ul>
<li><a href="#diff-so-fancy"><span>diff-so-fancy</span></a></li>
<li><a href="#git-extras"><span>git-extras</span></a></li>
</ul></li>
<li><a href="#intellij-idea">Intellij IDEA</a></li>
<li><a href="#gitgithub桌面增强">Git、GitHub桌面增强</a><ul>
<li><a href="#sourcetree">SourceTree</a></li>
<li><a href="#github-desktop">GitHub Desktop</a></li>
</ul></li>
<li><a href="#git-娱乐">Git 娱乐</a><ul>
<li><a href="#githug">githug</a></li>
<li><a href="#gource">Gource</a></li>
</ul></li>
</ul></li>
<li><a href="#github用户分析">GitHub用户分析</a><ul>
<li><a href="#生成图表">生成图表</a><ul>
<li><a href="#数据解析">数据解析</a></li>
<li><a href="#matplotlib">Matplotlib</a></li>
</ul></li>
<li><a href="#每周分析">每周分析</a><ul>
<li><a href="#python-github-每周情况分析">python github 每周情况分析</a></li>
<li><a href="#python-数据分析">Python 数据分析</a></li>
<li><a href="#python-matplotlib图表">Python Matplotlib图表</a></li>
</ul></li>
<li><a href="#存储到数据库中">存储到数据库中</a><ul>
<li><a href="#sqlite3">SQLite3</a></li>
<li><a href="#数据导入">数据导入</a></li>
<li><a href="#redis">Redis</a></li>
</ul></li>
<li><a href="#邻近算法与相似用户">邻近算法与相似用户</a></li>
</ul></li>
<li><a href="#如何在github寻找灵感fork">如何在GitHub“寻找灵感(fork)”</a><ul>
<li><a href="#lettuce构建过程">Lettuce构建过程</a><ul>
<li><a href="#需求">需求</a></li>
<li><a href="#计划">计划</a></li>
<li><a href="#实现第一个需求">实现第一个需求</a></li>
<li><a href="#实现第二个需求">实现第二个需求</a></li>
</ul></li>
</ul></li>
<li><a href="#如何以正确的姿势阅读开源软件代码">如何以“正确的姿势”阅读开源软件代码</a><ul>
<li><a href="#阅读过程">阅读过程</a></li>
<li><a href="#示例">示例</a></li>
</ul></li>
<li><a href="#github连击">GitHub连击</a><ul>
<li><a href="#天">100天</a><ul>
<li><a href="#天的提升">40天的提升</a></li>
<li><a href="#天的挑战">100天的挑战</a></li>
<li><a href="#天的希冀">140天的希冀</a></li>
</ul></li>
<li><a href="#天的showcase">200天的Showcase</a><ul>
<li><a href="#一些项目简述">一些项目简述</a></li>
<li><a href="#google-map-solr-polygon-搜索">google map solr polygon 搜索</a></li>
<li><a href="#技能树">技能树</a></li>
</ul></li>
<li><a href="#天-1">365天</a><ul>
<li><a href="#编程的基础能力">编程的基础能力</a></li>
<li><a href="#技术与框架设计">技术与框架设计</a></li>
<li><a href="#领域与练习">领域与练习</a></li>
<li><a href="#其他-1">其他</a></li>
</ul></li>
<li><a href="#天-2">500天</a><ul>
<li><a href="#天与10000小时">500天与10000小时</a></li>
<li><a href="#编程的情绪周期">编程的情绪周期</a></li>
<li><a href="#有意图的练习">有意图的练习</a></li>
<li><a href="#预见性练习">预见性练习</a></li>
<li><a href="#小结">小结</a></li>
</ul></li>
<li><a href="#天里">365*2-7天里</a><ul>
<li><a href="#编码的练习">编码的练习</a></li>
<li><a href="#see-you-again">See you Again</a></li>
</ul></li>
</ul></li>
<li><a href="#github-里程碑">GitHub 里程碑</a><ul>
<li><a href="#写在github-的第-19999-个-star-时">写在GitHub 的第 19999 个 star 时</a><ul>
<li><a href="#从创建开源框架说起">从创建开源框架说起</a></li>
<li><a href="#下一个开源项目">下一个开源项目</a></li>
</ul></li>
</ul></li>
<li><a href="#github-寻宝指南">GitHub 寻宝指南</a><ul>
<li><a href="#寻找-demo-节省时间">寻找 Demo 节省时间</a></li>
<li><a href="#寻找脚手架加快前期开发">寻找脚手架:加快前期开发</a></li>
<li><a href="#寻找-awesome-xxx探索可能性">寻找 awesome-xxx:探索可能性</a></li>
<li><a href="#模仿轮子的轮子"><strong>模仿轮子</strong>的轮子</a></li>
<li><a href="#学习资源">学习资源</a></li>
<li><a href="#密钥密码">密钥/密码</a></li>
<li><a href="#私有商用的-sdk-或代码">私有、商用的 SDK 或代码</a></li>
<li><a href="#数据及数据制作工具">数据及数据制作工具</a></li>
<li><a href="#结论">结论</a></li>
</ul></li>
<li><a href="#faq">FAQ</a><ul>
<li><a href="#如何看待github-项目刷star行为">如何看待github 项目刷Star行为?</a></li>
</ul></li>
</ul>
</nav>
<h1 id="前言">前言</h1>
<h2 id="关于作者">关于作者</h2>
<p>黄峰达(Phodal Huang)是一个创客、工程师、咨询师和作家。他毕业于西安文理学院电子信息工程专业,现作为一个咨询师就职于 ThoughtWorks 深圳。长期活跃于开源软件社区 GitHub,目前专注于物联网和前端领域。</p>
<p>作为一个开源软件作者,著有 Growth、Stepping、Lan、Echoesworks 等软件。其中开源学习应用 Growth,广受读者和用户好评,可在 APP Store 及各大 Android 应用商店下载。</p>
<p>作为一个技术作者,著有《自己动手设计物联网》(电子工业出版社)、《全栈应用开发:精益实践》(电子工业出版社,正在出版)。并在 GitHub 上开源有《Growth: 全栈增长工程师指南》、《GitHub 漫游指南》等七本电子书。</p>
<p>作为技术专家,他为英国 Packt 出版社审阅有物联网书籍《Learning IoT》、《Smart IoT》,前端书籍《Angular 2 Serices》、《Getting started with Angular》等技术书籍。</p>
<p>他热爱编程、写作、设计、旅行、hacking,你可以从他的个人网站:<a href="https://www.phodal.com/">https://www.phodal.com/</a> 了解到更多的内容。</p>
<p>其它相关信息:</p>
<ul>
<li>微博:<a href="http://weibo.com/phodal">http://weibo.com/phodal</a></li>
<li>GitHub: <a href="https://github.com/phodal">https://github.com/phodal</a></li>
<li>知乎:<a href="https://www.zhihu.com/people/phodal">https://www.zhihu.com/people/phodal</a></li>
<li>SegmentFault:<a href="https://segmentfault.com/u/phodal">https://segmentfault.com/u/phodal</a></li>
</ul>
<p>当前为预览版,在使用的过程中遇到任何问题请及时与我联系。阅读过程中的问题,不妨在GitHub上提出来: <a href="https://github.com/phodal/github/issues">Issues</a></p>
<p>阅读过程中遇到语法错误、拼写错误、技术错误等等,不妨来个Pull Request,这样可以帮助到其他阅读这本电子书的童鞋。</p>
<p>我的电子书:</p>
<ul>
<li>《<a href="https://github.com/phodal/github-roam">GitHub 漫游指南</a>》</li>
<li>《<a href="https://github.com/phodal/fe">我的职业是前端工程师</a>》</li>
<li>《<a href="https://github.com/phodal/serverless">Serverless 架构应用开发指南</a>》</li>
<li>《<a href="https://github.com/phodal/growth-ebook">Growth: 全栈增长工程师指南</a>》</li>
<li>《<a href="https://github.com/phodal/ideabook">Phodal’s Idea实战指南</a>》</li>
<li>《<a href="https://github.com/phodal/designiot">一步步搭建物联网系统</a>》</li>
<li>《<a href="https://github.com/phodal/repractise">RePractise</a>》</li>
<li>《<a href="https://github.com/phodal/growth-in-action">Growth: 全栈增长工程师实战</a>》</li>
</ul>
<p>我的微信公众号:</p>
<figure>
<img src="./img/wechat.jpg" alt="作者微信公众号:phodal-weixin" /><figcaption>作者微信公众号:phodal-weixin</figcaption>
</figure>
<p>支持作者,可以加入作者的小密圈:</p>
<figure>
<img src="./img/xiaomiquan.jpg" alt="小密圈" /><figcaption>小密圈</figcaption>
</figure>
<p>或者转账:</p>
<p><img src="./img/alipay.png" alt="支付宝" /> <img src="./img/wechat-pay.png" alt="微信" /></p>
<p>我的GitHub主页上写着加入的时间——<code>Joined on Nov 8, 2010</code>,那时才大一,在那之后的那么长的日子里我都没有登录过。也许是因为我学的不是计算机,到了今天——<code>2015.3.9</code>,我才发现这其实是程序员的社交网站。</p>
<p>过去,曾经有很长的一些时间我试过在GitHub上连击,也试着去了解别人是如何用好这个工具的。当然粉丝在GitHub上也是很重要的。</p>
<p>在这里,我会试着将我在GitHub上学到的东西一一分享出来。</p>
<h2 id="我与github的故事">我与GitHub的故事</h2>
<p>在我大四找工作的时候,试图去寻找一份硬件、物联网相关的工作(ps: 专业是电子信息工程)。尽管简历上写得满满的各种经历、经验,然而并没有卵用。跑了几场校园招聘会后,十份简历(ps: 事先已经有心里准备)一个也没有投出去——因为学校直接被拒。我对霸面什么的一点兴趣都没有,千里马需要伯乐。后来,我加入了<a href="https://martinfowler.com/">Martin Flower</a>所在的公司,当然这是后话了。</p>
<p>这是一个残酷的世界,在学生时代,如果你长得不帅不高的话,那么多数的附加技能都是白搭(ps: 通常富的是看不到这篇文章的)。在工作时期,如果你上家没有名气,那么将会影响你下一份工作的待遇。而,很多东西却可以改变这些,GitHub就是其中一个。</p>
<p>注册GitHub的时候大概是大一的时候,我熟悉的时候已经是大四了,现在已经毕业一年了。在过去的近两年里,我试着以几个维度在GitHub上创建项目:</p>
<ol type="1">
<li>快速上手框架来实战,即demo</li>
<li>重构别人的代码</li>
<li>创建自己可用的框架</li>
<li>快速构建大型应用</li>
<li>构建通用的框架</li>
</ol>
<h3 id="github与收获">GitHub与收获</h3>
<p>先说说<strong>与技能无关的收获</strong>吧,毕业设计做的是一个《<a href="https://github.com/phodal/iot">最小物联网系统</a>》,考虑到我们专业老师没有这方面知识,答辩时会带来问题,尽量往这方面靠拢。当我毕业后,这个项目已经有过百个star了,这样易上手的东西还是比较受欢迎的(ps: 不过这种硬件相关的项目通常受限于GitHub上硬件开发工程师比较少的困扰)。</p>
<p>毕业后一个月收到PACKT出版社的邮件(ps: 他们是在github上找到我的),内容是关于Review一本<a href="iot">物联网</a>书籍,即在《<a href="http://www.phodal.com/blog/review-it-books-with-translate-book/">从Review到翻译IT书籍</a>》中提到的《Learning Internet of Things》。作为一个四级没过的“物联网专家”,去审阅一本英文的物联网书籍。。。</p>
<p>当然,后来是审阅完了,书上有我的英文简介。</p>
<figure>
<img src="./img/phodal-intro.jpg" alt="Phodal Huang Introduction" /><figcaption>Phodal Huang Introduction</figcaption>
</figure>
<p>一个月前,收到MANNING出版社的邮件(ps: 也是在github上),关于Review一本<a href="iot">物联网</a>书籍的目录,并提出建议。</p>
<p>也因此带来了其他更多的东西,当然不是这里的主题。在这里,我们就不讨论各种骚扰邮件,或者中文合作。从没有想象过,我也可以在英语世界有一片小天地。</p>
<p>这些告诉我们,GitHub上找一个你擅长的主题,那么会有很多人找上你的。</p>
<h3 id="github与成长">GitHub与成长</h3>
<p>过去写过一篇《<a href="http://www.phodal.com/blog/use-github-grow-self/">如何通过github提升自己</a>》的文章,现在只想说三点:</p>
<ol type="1">
<li>测试</li>
<li>更多的测试</li>
<li>更多的、更多的、更多的测试</li>
</ol>
<p>没有测试的项目是很扯淡的,除非你的项目只有一个函数,然后那个函数返回<code>Hello,World</code>。</p>
<p>如果你的项目代码有上千行,如果你能保证测试覆盖率可以达到95%以的话,那么我想你的项目不会有太复杂的函数。假使有这样的函数,那么它也是被测试覆盖住的。</p>
<p>如果你在用心做这个项目,那么你看到代码写得不好也会试着改进,即重构。当有了一些,你的技能会不断提升。你开始会试着接触更多的东西,如stub,如mock,如fakeserver。</p>
<p>有一天,你会发现你离不开测试。</p>
<p>然后就会相信: <strong>那些没有写测试的项目都是在耍流氓</strong></p>
<h2 id="为什么你应该深入github">为什么你应该深入GitHub</h2>
<p>上面我们说的都是我们可以收获到的东西,我们开始尝试就意味着我们知道它可能给我们带来好处。上面已经提到很多可以提升自己的例子了,这里再说说其他的。</p>
<h3 id="方便工作">方便工作</h3>
<p>我们可以从中获取到不同的知识、内容、信息。每个人都可以从别人的代码中学习,当我们需要构建一个库的时候,我们可以在上面寻找不同的库和代码来实现我们的功能。如当我在实现一个库的时候,我会在GitHub上找到相应的组件:</p>
<ul>
<li>Promise 支持</li>
<li>Class类(ps:没有一个好的类使用的方式)</li>
<li>Template 一个简单的模板引擎</li>
<li>Router 用来控制页面的路由</li>
<li>Ajax 基本的Ajax Get/Post请求</li>
</ul>
<h3 id="获得一份工作">获得一份工作</h3>
<p>越来越多的人因为GitHub获得工作,因为他们的做的东西正好符合一些公司的要求。那么,这些公司在寻找代码的时候,就会试着邀请他们。</p>
<p>因而,在GitHub寻找合适的候选人,已经是一种趋势。</p>
<h3 id="扩大交际">扩大交际</h3>
<p>如果我们想创造出更好、强大地框架时,那么认识更多的人可能会带来更多的帮助。有时候会同上面那一点一样的效果</p>
<h1 id="创建开源项目">创建开源项目</h1>
<p>人们出于不同的目的来创建开源项目,可不论目的是什么,过程都是一样的。</p>
<ol type="1">
<li>首先,我们需要为我们的项目取一个名字。</li>
<li>然后,为我们的开源项目选择一个合适的 LICENSE</li>
<li>然后再去创建项目</li>
</ol>
<h2 id="取一个好的名字">取一个好的名字</h2>
<p>取名字,从来就不是一件容易的事。</p>
<p>因此,我就长话短说,一般就是取一个有意义的名字,当然没有意义也没有任何问题。</p>
<p>通常而言,如果自己计划有一系列的开源项目,那么我们可以保持一定的命令规则。</p>
<h2 id="挑选好-license">挑选好 LICENSE</h2>
<blockquote>
<p>在二十世纪而七十年代末和八十年代初,为了防止自己的软件被竞争对手所使用,大多数厂家停止分发其软件源代码,并开始使用版权和限制性软件许可证,来限制或者禁止软件源代码的复制或再分配。随后,Richard Matthew Stallman(Richard Matthew Stallman)发起了自由软件运动,他开创了 Copyleft 的概念:使用版权法的原则来保护使用、修改和分发自由软件的权利,并且是描述这些术语的自由软件许可证的主要作者。最为人所称道的是GPL(被z广泛使用的自由软件协议)。<a href="#fn1" class="footnote-ref" id="fnref1"><sup>1</sup></a></p>
</blockquote>
<p>(PS:关于自由软件及 RMS 的更多信息、历史,可以阅读《若为自由故:自由软件之父 - 理查德 斯托曼传》)</p>
<p>随后,便诞生了开源软件的概念,开源的要求比自由软件宽松一些<a href="#fn2" class="footnote-ref" id="fnref2"><sup>2</sup></a>。迄今发布的自由软件源代码都是开源软件,而并非所有的开源软件都是自由软件。这是因为不同的许可(协议)赋予用户不同的权利,如 GPL 协议强制要求开源修改过源码的代码,而宽松一点的 MIT 则不会有这种要求。</p>
<p>如下是不同开源许可证的市场占有率及使用情况。</p>
<figure>
<img src="./img/permissive-vs-copylift-license-2.jpg" alt="License 使用情况" /><figcaption>License 使用情况</figcaption>
</figure>
<p>又比如,在我们看到的一些外版书籍上,如果拥有代码。那么作者一般就会在前言或者类似的位置里,指明书中代码的版权所属。如:</p>
<blockquote>
<p>也许你需要在自己的程序或文档中用到本书的代码,但除非大篇幅地使用,否则不必与我们联系取得授权。例如,用本书中的几段代码编写程序无需请求许可,blabla。</p>
</blockquote>
<p>于是,选择一个合理的 LICENSE,就变成了一个有趣的话题。为此,笔者做了一个如何进行开源协议选型的流程图:</p>
<p><a href="https://github.com/phodal/licenses"><img src="./img/licenses.png" alt="如何选择 License" /></a></p>
<p>简单地来说,这些 License 之间是一些权利的区别,如当你把代码放置到公有领域,就意味着任何人可以修改,并且不需要标明出注;可如果你想要别人标明出处及作者,你就需要 MIT 协议;而你希望别人闭源的话,那么你就需要 MPL 协议等等。</p>
<p>那么,下面让我们简单地介绍一下不同的几个协议。</p>
<h3 id="公有领域">公有领域</h3>
<blockquote>
<p>WTFPL(Do What The Fuck You Want To Public License,中文译名:你他妈的想干嘛就干嘛公共许可证)是一种不太常用的、极度放任的自由软件许可证。它的条款基本等同于贡献到公有领域。<a href="#fn3" class="footnote-ref" id="fnref3"><sup>3</sup></a></p>
</blockquote>
<p>这就意味着,对于拿到这些代码的其他人,他们想怎么修改就可以怎么修改。</p>
<p>这取决于</p>
<h3 id="gpl">GPL</h3>
<p>由于 GPL 的传染性,便意味着,他人引用我们的代码时,其所写的代码也需要使用 GPL 开源。即:GPL 是有 “传染性” 的 “病毒” ,因为 GPL 条款规定演绎作品也必须是 GPL 的。</p>
<p>而如果我们只针对的是,他人可以使用库,而不开源,则可以用 LGPL。但是修改库则不适用。</p>
<h3 id="mit">MIT</h3>
<p>因此,一般而言,我使用的是 MIT 协议。至少我保留了一个署名权,即你可以修改我的代码,但是在 LICENSE 里必须加上我的名字。</p>
<p>选用 MIT 特别有意思,特别是在最近几年里,发生过:</p>
<ul>
<li><a href="https://zhuanlan.zhihu.com/p/25739512">iView “抄袭” Element UI 事件</a></li>
<li><a href="https://github.com/JackyAndroid/AndroidTVLauncher/issues/22">AndroidTVLauncher “抄袭” 事件</a></li>
</ul>
<p>等等。这告诫了我们,如果你不想要有这种经历,那么就不要用 MIT 了。</p>
<h3 id="creative-commons">Creative Commons</h3>
<p>是的,当我写 Markdown 的时候,考虑到未来会以纸质书的形式出现,便会使用 CC-BY-NC-ND 协议:</p>
<ul>
<li>CC -> Creative Commons</li>
<li>BY -> 署名(英语:Attribution,by)</li>
<li>NC -> 非商业性使用(英语:NonCommercial)</li>
<li>ND -> 禁止演绎(英语:NoDerivs)。</li>
</ul>
<p>即,任何人可以使用我写的电子书来自由复制、散布、展示及演出,但是不得用于商业用途(作者本人可以)。它可以随意地放在他的博客上,他的各个文章里。但是必须标明出自,并且不得改变、转变或更改本作品。</p>
<p>如果你不介意的话,你可以使用公有领域(Public Domain)。可是这样一来,万一有一天,别人直接拿的作品出书,你就骂爹了。</p>
<h1 id="git基本知识与github使用">Git基本知识与GitHub使用</h1>
<h2 id="git">Git</h2>
<p>从一般开发者的角度来看,git有以下功能:</p>
<ol type="1">
<li>从服务器上克隆数据库(包括代码和版本信息)到单机上。</li>
<li>在自己的机器上创建分支,修改代码。</li>
<li>在单机上自己创建的分支上提交代码。</li>
<li>在单机上合并分支。</li>
<li>新建一个分支,把服务器上最新版的代码fetch下来,然后跟自己的主分支合并。</li>
<li>生成补丁(patch),把补丁发送给主开发者。</li>
<li>看主开发者的反馈,如果主开发者发现两个一般开发者之间有冲突(他们之间可以合作解决的冲突),就会要求他们先解决冲突,然后再由其中一个人提交。如果主开发者可以自己解决,或者没有冲突,就通过。</li>
<li>一般开发者之间解决冲突的方法,开发者之间可以使用pull 命令解决冲突,解决完冲突之后再向主开发者提交补丁。</li>
</ol>
<p>从主开发者的角度(假设主开发者不用开发代码)看,git有以下功能:</p>
<ol type="1">
<li>查看邮件或者通过其它方式查看一般开发者的提交状态。</li>
<li>打上补丁,解决冲突(可以自己解决,也可以要求开发者之间解决以后再重新提交,如果是开源项目,还要决定哪些补丁有用,哪些不用)。</li>
<li>向公共服务器提交结果,然后通知所有开发人员。</li>
</ol>
<h3 id="git初入">Git初入</h3>
<p>如果是第一次使用Git,你需要设置署名和邮箱:</p>
<pre><code>$ git config --global user.name "用户名"
$ git config --global user.email "电子邮箱"</code></pre>
<p>将代码仓库clone到本地,其实就是将代码复制到你的机器里,并交由Git来管理:</p>
<pre><code>$ git clone git@github.com:someone/symfony-docs-chs.git</code></pre>
<p>你可以修改复制到本地的代码了(symfony-docs-chs项目里都是rst格式的文档)。当你觉得完成了一定的工作量,想做个阶段性的提交:</p>
<p>向这个本地的代码仓库添加当前目录的所有改动:</p>
<pre><code>$ git add .</code></pre>
<p>或者只是添加某个文件:</p>
<pre><code>$ git add -p</code></pre>
<p>我们可以输入</p>
<pre><code>$git status</code></pre>
<p>来看现在的状态,如下图是添加之前的:</p>
<figure>
<img src="./img/before-add.png" alt="Before add" /><figcaption>Before add</figcaption>
</figure>
<p>下面是添加之后 的</p>
<figure>
<img src="./img/after-add.png" alt="After add" /><figcaption>After add</figcaption>
</figure>
<p>可以看到状态的变化是从黄色到绿色,即unstage到add。</p>
<h2 id="github">GitHub</h2>
<p>Wiki百科上是这么说的</p>
<blockquote>
<p>GitHub 是一个共享虚拟主机服务,用于存放使用Git版本控制的软件代码和内容项目。它由GitHub公司(曾称Logical Awesome)的开发者Chris Wanstrath、PJ Hyett和Tom Preston-Werner 使用Ruby on Rails编写而成。</p>
</blockquote>
<p>当然让我们看看官方的介绍:</p>
<blockquote>
<p>GitHub is the best place to share code with friends, co-workers, classmates, and complete strangers. Over eight million people use GitHub to build amazing things together.</p>
</blockquote>
<p>它还是什么?</p>
<ul>
<li>网站</li>
<li>免费博客</li>
<li>管理配置文件</li>
<li>收集资料</li>
<li>简历</li>
<li>管理代码片段</li>
<li>托管编程环境</li>
<li>写作</li>
</ul>
<p>等等。看上去像是大餐,但是你还需要了解点什么?</p>
<h3 id="版本管理与软件部署">版本管理与软件部署</h3>
<p>jQuery[^jQuery]在发布版本<code>2.1.3</code>,一共有152个commit。我们可以看到如下的提交信息:</p>
<ul>
<li>Ajax: Always use script injection in globalEval … bbdfbb4</li>
<li>Effects: Reintroduce use of requestAnimationFrame … 72119e0</li>
<li>Effects: Improve raf logic … 708764f</li>
<li>Build: Move test to appropriate module fbdbb6f</li>
<li>Build: Update commitplease dev dependency</li>
<li>…</li>
</ul>
<h3 id="github与git">GitHub与Git</h3>
<blockquote>
<p>Git是一个分布式的版本控制系统,最初由Linus Torvalds编写,用作Linux内核代码的管理。在推出后,Git在其它项目中也取得了很大成功,尤其是在Ruby社区中。目前,包括Rubinius、Merb和Bitcoin在内的很多知名项目都使用了Git。Git同样可以被诸如Capistrano和Vlad the Deployer这样的部署工具所使用。</p>
</blockquote>
<blockquote>
<p>GitHub可以托管各种git库,并提供一个web界面,但与其它像 SourceForge或Google Code这样的服务不同,GitHub的独特卖点在于从另外一个项目进行分支的简易性。为一个项目贡献代码非常简单:首先点击项目站点的“fork”的按钮,然后将代码检出并将修改加入到刚才分出的代码库中,最后通过内建的“pull request”机制向项目负责人申请代码合并。已经有人将GitHub称为代码玩家的MySpace。</p>
</blockquote>
<h3 id="在-github-创建项目">在 GitHub 创建项目</h3>
<p>接着,我们试试在上面创建一个项目:</p>
<figure>
<img src="./img/github-roam-create.jpg" alt="GitHub Roam" /><figcaption>GitHub Roam</figcaption>
</figure>
<p>就会有下面的提醒:</p>
<figure>
<img src="./img/project-init.jpg" alt="GitHub Roam" /><figcaption>GitHub Roam</figcaption>
</figure>
<p>它提供多种方式的创建方法:</p>
<blockquote>
<p>…or create a new repository on the command line</p>
</blockquote>
<pre><code>echo "# github-roam" >> README.md
git init
git add README.md
git commit -m "first commit"
git remote add origin git@github.com:phodal/github-roam.git
git push -u origin master</code></pre>
<blockquote>
<p>…or push an existing repository from the command line</p>
</blockquote>
<pre><code>git remote add origin git@github.com:phodal/github-roam.git
git push -u origin master</code></pre>
<p>如果你完成了上面的步骤之后,那么我想你想知道你需要怎样的项目。</p>
<h2 id="github-流行项目分析">GitHub 流行项目分析</h2>
<p>之前曾经分析过一些GitHub的用户行为,现在我们先来说说GitHub上的Star吧。(截止: 2015年3月9日23时。)</p>
<table>
<colgroup>
<col style="width: 14%" />
<col style="width: 28%" />
<col style="width: 28%" />
<col style="width: 17%" />
<col style="width: 11%" />
</colgroup>
<thead>
<tr class="header">
<th>用户</th>
<th>项目名</th>
<th>Language</th>
<th>Star</th>
<th>Url</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>twbs</td>
<td>Bootstrap</td>
<td>CSS</td>
<td>78490</td>
<td><a href="https://github.com/twbs/bootstrap">https://github.com/twbs/bootstrap</a></td>
</tr>
<tr class="even">
<td>vhf</td>
<td>free-programming books</td>
<td>-</td>
<td>37240</td>
<td><a href="https://github.com/vhf/free-programming-books">https://github.com/vhf/free-programming-books</a></td>
</tr>
<tr class="odd">
<td>angular</td>
<td>angular.js</td>
<td>JavaScript</td>
<td>36,061</td>
<td><a href="https://github.com/angular/angular.js">https://github.com/angular/angular.js</a></td>
</tr>
<tr class="even">
<td>mbostock</td>
<td>d3</td>
<td>JavaScript</td>
<td>35,257</td>
<td><a href="https://github.com/mbostock/d3">https://github.com/mbostock/d3</a></td>
</tr>
<tr class="odd">
<td>joyent</td>
<td>node</td>
<td>JavaScript</td>
<td>35,077</td>
<td><a href="https://github.com/joyent/node">https://github.com/joyent/node</a></td>
</tr>
</tbody>
</table>
<p>上面列出来的是前5的,看看大于1万个stars的项目的分布,一共有82个:</p>
<table>
<thead>
<tr class="header">
<th>语言</th>
<th>项目数</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>JavaScript</td>
<td>37</td>
</tr>
<tr class="even">
<td>Ruby</td>
<td>6</td>
</tr>
<tr class="odd">
<td>CSS</td>
<td>6</td>
</tr>
<tr class="even">
<td>Python</td>
<td>4</td>
</tr>
<tr class="odd">
<td>HTML</td>
<td>3</td>
</tr>
<tr class="even">
<td>C++</td>
<td>3</td>
</tr>
<tr class="odd">
<td>VimL</td>
<td>2</td>
</tr>
<tr class="even">
<td>Shell</td>
<td>2</td>
</tr>
<tr class="odd">
<td>Go</td>
<td>2</td>
</tr>
<tr class="even">
<td>C</td>
<td>2</td>
</tr>
</tbody>
</table>
<p>类型分布:</p>
<ul>
<li>库和框架: 如<code>jQuery</code></li>
<li>系统: 如<code>Linux</code>、<code>hhvm</code>、<code>docker</code></li>
<li>配置集: 如<code>dotfiles</code></li>
<li>辅助工具: 如<code>oh-my-zsh</code></li>
<li>工具: 如<code>Homewbrew</code>和<code>Bower</code></li>
<li>资料收集: 如<code>free programming books</code>,<code>You-Dont-Know-JS</code>,<code>Font-Awesome</code></li>
<li>其他:简历如<code>Resume</code></li>
</ul>
<h2 id="pull-request">Pull Request</h2>
<p>除了创建项目之外,我们也可以创建Pull Request来做贡献。</p>
<h3 id="我的第一个pr">我的第一个PR</h3>
<p>我的第一个PR是给一个小的Node的CoAP相关的库的Pull Request。原因比较简单,是因为它的README.md写错了,导致我无法进行下一步。</p>
<pre><code> const dgram = require('dgram')
- , coapPacket = require('coap-packet')
+ , package = require('coap-packet')</code></pre>
<p>很简单,却又很有用的步骤,另外一个也是:</p>
<pre><code> else
cat << END
$0: error: module ngx_pagespeed requires the pagespeed optimization library.
-Look in obj/autoconf.err for more details.
+Look in objs/autoconf.err for more details.
END
exit 1
fi</code></pre>
<h3 id="cla">CLA</h3>
<p>CLA即Contributor License Agreement,在为一些大的组织、机构提交Pull Request的时候,可能需要签署这个协议。他们会在你的Pull Request里问你,只有你到他们的网站去注册并同意协议才会接受你的PR。</p>
<p>以下是我为Google提交的一个PR</p>
<figure>
<img src="./img/google-cla.png" alt="Google CLA" /><figcaption>Google CLA</figcaption>
</figure>
<p>以及Eclipse的一个PR</p>
<figure>
<img src="./img/eclipse-cla.png" alt="Eclipse CLA" /><figcaption>Eclipse CLA</figcaption>
</figure>
<p>他们都要求我签署CLA。</p>
<h1 id="构建-github-项目">构建 GitHub 项目</h1>
<h2 id="如何用好-github">如何用好 GitHub</h2>
<p>如何用好 GitHub,并实践一些敏捷软件开发是一个很有意思的事情.我们可以在上面做很多事情,从测试到CI,再到自动部署.</p>
<h3 id="敏捷软件开发">敏捷软件开发</h3>
<p>显然我是在扯淡,这和敏捷软件开发没有什么关系。不过我也不知道瀑布流是怎样的。说说我所知道的一个项目的组成吧:</p>
<ul>
<li>看板式管理应用程序(如trello,简单地说就是管理软件功能)</li>
<li>CI(持续集成)</li>
<li>测试覆盖率</li>
<li>代码质量(code smell)</li>
</ul>
<p>对于一个不是远程的团队(如只有一个人的项目) 来说,Trello、Jenkin、Jira不是必需的:</p>
<blockquote>
<p>你存在,我深深的脑海里</p>
</blockquote>
<p>当只有一个人的时候,你只需要明确知道自己想要什么就够了。我们还需要的是CI、测试,以来提升代码的质量。</p>
<h3 id="测试">测试</h3>
<p>通常我们都会找Document,如果没有的话,你会找什么?看源代码,还是看测试?</p>
<div class="sourceCode" id="cb10"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb10-1" title="1"><span class="at">it</span>(<span class="st">"specifying response when you need it"</span><span class="op">,</span> <span class="kw">function</span> (done) <span class="op">{</span></a>
<a class="sourceLine" id="cb10-2" title="2"> <span class="kw">var</span> doneFn <span class="op">=</span> <span class="va">jasmine</span>.<span class="at">createSpy</span>(<span class="st">"success"</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb10-3" title="3"></a>
<a class="sourceLine" id="cb10-4" title="4"> <span class="va">lettuce</span>.<span class="at">get</span>(<span class="st">'/some/cool/url'</span><span class="op">,</span> <span class="kw">function</span> (result) <span class="op">{</span></a>
<a class="sourceLine" id="cb10-5" title="5"> <span class="at">expect</span>(result).<span class="at">toEqual</span>(<span class="st">"awesome response"</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb10-6" title="6"> <span class="at">done</span>()<span class="op">;</span></a>
<a class="sourceLine" id="cb10-7" title="7"> <span class="op">}</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb10-8" title="8"></a>
<a class="sourceLine" id="cb10-9" title="9"> <span class="at">expect</span>(<span class="va">jasmine</span>.<span class="va">Ajax</span>.<span class="va">requests</span>.<span class="at">mostRecent</span>().<span class="at">url</span>).<span class="at">toBe</span>(<span class="st">'/some/cool/url'</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb10-10" title="10"> <span class="at">expect</span>(doneFn).<span class="va">not</span>.<span class="at">toHaveBeenCalled</span>()<span class="op">;</span></a>
<a class="sourceLine" id="cb10-11" title="11"></a>
<a class="sourceLine" id="cb10-12" title="12"> <span class="va">jasmine</span>.<span class="va">Ajax</span>.<span class="va">requests</span>.<span class="at">mostRecent</span>().<span class="at">respondWith</span>(<span class="op">{</span></a>
<a class="sourceLine" id="cb10-13" title="13"> <span class="st">"status"</span><span class="op">:</span> <span class="dv">200</span><span class="op">,</span></a>
<a class="sourceLine" id="cb10-14" title="14"> <span class="st">"contentType"</span><span class="op">:</span> <span class="st">'text/plain'</span><span class="op">,</span></a>
<a class="sourceLine" id="cb10-15" title="15"> <span class="st">"responseText"</span><span class="op">:</span> <span class="st">'awesome response'</span></a>
<a class="sourceLine" id="cb10-16" title="16"> <span class="op">}</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb10-17" title="17"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>代码来源: <a href="https://github.com/phodal/lettuce">https://github.com/phodal/lettuce</a></p>
<p>上面的测试用例,清清楚楚地写明了用法,虽然写得有点扯。</p>
<p>等等,测试是用来干什么的。那么,先说说我为什么会想去写测试吧:</p>
<ul>
<li>我不希望每次做完一个个新功能的时候,再手动地去测试一个个功能。(自动化测试)</li>
<li>我不希望在重构的时候发现破坏了原来的功能,而我还一无所知。</li>
<li>我不敢push代码,因为我没有把握。</li>
</ul>
<p>虽然,我不是TDD的死忠,测试的目的是保证功能正常,TDD没法让我们写出质量更高的代码。但是有时TDD是不错的,可以让我们写出逻辑更简单地代码。</p>
<p>也许你已经知道了<code>Selenium</code>、<code>Jasmine</code>、<code>Cucumber</code>等等的框架,看到过类似于下面的测试</p>
<pre><code> Ajax
✓ specifying response when you need it
✓ specifying html when you need it
✓ should be post to some where
Class
✓ respects instanceof
✓ inherits methods (also super)
✓ extend methods
Effect
✓ should be able fadein elements
✓ should be able fadeout elements</code></pre>
<p>代码来源: <a href="https://github.com/phodal/lettuce">https://github.com/phodal/lettuce</a></p>
<p>看上去似乎每个测试都很小,不过补完每一个测试之后我们就得到了测试覆盖率</p>
<table>
<colgroup>
<col style="width: 11%" />
<col style="width: 27%" />
<col style="width: 22%" />
<col style="width: 25%" />
<col style="width: 13%" />
</colgroup>
<thead>
<tr class="header">
<th>File</th>
<th>Statements</th>
<th>Branches</th>
<th>Functions</th>
<th>Lines</th>
</tr>
</thead>
<tbody>
<tr class="odd">
<td>lettuce.js</td>
<td>98.58% (209 / 212)</td>
<td>82.98%(78 / 94)</td>
<td>100.00% (54 / 54)</td>
<td>98.58% (209 / 212)</td>
</tr>
</tbody>
</table>
<p>本地测试都通过了,于是我们添加了<code>Travis-CI</code>来跑我们的测试</p>
<h3 id="ci">CI</h3>
<p>虽然node.js不算是一门语言,但是因为我们用的node,下面的是一个简单的<code>.travis.yml</code>示例:</p>
<div class="sourceCode" id="cb12"><pre class="sourceCode yml"><code class="sourceCode yaml"><a class="sourceLine" id="cb12-1" title="1"><span class="fu">language:</span><span class="at"> node_js</span></a>
<a class="sourceLine" id="cb12-2" title="2"><span class="fu">node_js:</span></a>
<a class="sourceLine" id="cb12-3" title="3"> <span class="kw">-</span> <span class="st">"0.10"</span></a>
<a class="sourceLine" id="cb12-4" title="4"></a>
<a class="sourceLine" id="cb12-5" title="5"><span class="fu">notifications:</span></a>
<a class="sourceLine" id="cb12-6" title="6"> <span class="fu">email:</span><span class="at"> </span><span class="ch">false</span></a>
<a class="sourceLine" id="cb12-7" title="7"></a>
<a class="sourceLine" id="cb12-8" title="8"><span class="fu">before_install:</span><span class="at"> npm install -g grunt-cli</span></a>
<a class="sourceLine" id="cb12-9" title="9"><span class="fu">install:</span><span class="at"> npm install</span></a>
<a class="sourceLine" id="cb12-10" title="10"><span class="fu">after_success:</span><span class="at"> CODECLIMATE_REPO_TOKEN=321480822fc37deb0de70a11931b4cb6a2a3cc411680e8f4569936ac8ffbb0ab codeclimate < coverage/lcov.info</span></a></code></pre></div>
<p>代码来源: <a href="https://github.com/phodal/lettuce">https://github.com/phodal/lettuce</a></p>
<p>我们把这些集成到<code>README.md</code>之后,就有了之前那张图。</p>
<p>CI对于一个开发者在不同城市开发同一项目上来说是很重要的,这意味着当你添加的部分功能有测试覆盖的时候,项目代码会更加强壮。</p>
<h3 id="代码质量">代码质量</h3>
<p>像<code>jslint</code>这类的工具,只能保证代码在语法上是正确的,但是不能保证你写了一堆bad smell的代码。</p>
<ul>
<li>重复代码</li>
<li>过长的函数</li>
<li>等等</li>
</ul>
<p><code>Code Climate</code>是一个与github集成的工具,我们不仅仅可以看到测试覆盖率,还有代码质量。</p>
<p>先看看上面的ajax类:</p>
<div class="sourceCode" id="cb13"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb13-1" title="1"><span class="va">Lettuce</span>.<span class="at">get</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> callback) <span class="op">{</span></a>
<a class="sourceLine" id="cb13-2" title="2"> <span class="va">Lettuce</span>.<span class="at">send</span>(url<span class="op">,</span> <span class="st">'GET'</span><span class="op">,</span> callback)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-3" title="3"><span class="op">};</span></a>
<a class="sourceLine" id="cb13-4" title="4"></a>
<a class="sourceLine" id="cb13-5" title="5"><span class="va">Lettuce</span>.<span class="at">send</span> <span class="op">=</span> <span class="kw">function</span> (url<span class="op">,</span> method<span class="op">,</span> callback<span class="op">,</span> data) <span class="op">{</span></a>
<a class="sourceLine" id="cb13-6" title="6"> data <span class="op">=</span> data <span class="op">||</span> <span class="kw">null</span><span class="op">;</span></a>
<a class="sourceLine" id="cb13-7" title="7"> <span class="kw">var</span> request <span class="op">=</span> <span class="kw">new</span> <span class="at">XMLHttpRequest</span>()<span class="op">;</span></a>
<a class="sourceLine" id="cb13-8" title="8"> <span class="cf">if</span> (callback <span class="kw">instanceof</span> Function) <span class="op">{</span></a>
<a class="sourceLine" id="cb13-9" title="9"> <span class="va">request</span>.<span class="at">onreadystatechange</span> <span class="op">=</span> <span class="kw">function</span> () <span class="op">{</span></a>
<a class="sourceLine" id="cb13-10" title="10"> <span class="cf">if</span> (<span class="va">request</span>.<span class="at">readyState</span> <span class="op">===</span> <span class="dv">4</span> <span class="op">&&</span> (<span class="va">request</span>.<span class="at">status</span> <span class="op">===</span> <span class="dv">200</span> <span class="op">||</span> <span class="va">request</span>.<span class="at">status</span> <span class="op">===</span> <span class="dv">0</span>)) <span class="op">{</span></a>
<a class="sourceLine" id="cb13-11" title="11"> <span class="at">callback</span>(<span class="va">request</span>.<span class="at">responseText</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-12" title="12"> <span class="op">}</span></a>
<a class="sourceLine" id="cb13-13" title="13"> <span class="op">};</span></a>
<a class="sourceLine" id="cb13-14" title="14"> <span class="op">}</span></a>
<a class="sourceLine" id="cb13-15" title="15"> <span class="va">request</span>.<span class="at">open</span>(method<span class="op">,</span> url<span class="op">,</span> <span class="kw">true</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-16" title="16"> <span class="cf">if</span> (data <span class="kw">instanceof</span> Object) <span class="op">{</span></a>
<a class="sourceLine" id="cb13-17" title="17"> data <span class="op">=</span> <span class="va">JSON</span>.<span class="at">stringify</span>(data)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-18" title="18"> <span class="va">request</span>.<span class="at">setRequestHeader</span>(<span class="st">'Content-Type'</span><span class="op">,</span> <span class="st">'application/json'</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-19" title="19"> <span class="op">}</span></a>
<a class="sourceLine" id="cb13-20" title="20"> <span class="va">request</span>.<span class="at">setRequestHeader</span>(<span class="st">'X-Requested-With'</span><span class="op">,</span> <span class="st">'XMLHttpRequest'</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-21" title="21"> <span class="va">request</span>.<span class="at">send</span>(data)<span class="op">;</span></a>
<a class="sourceLine" id="cb13-22" title="22"><span class="op">};</span></a></code></pre></div>
<p>代码来源: <a href="https://github.com/phodal/lettuce">https://github.com/phodal/lettuce</a></p>
<p>在<a href="https://codeclimate.com/github/phodal/lettuce/src/ajax.js">Code Climate</a>在出现了一堆问题</p>
<ul>
<li>Missing “use strict” statement. (Line 2)</li>
<li>Missing “use strict” statement. (Line 14)</li>
<li>‘Lettuce’ is not defined. (Line 5)</li>
</ul>
<p>而这些都是小问题啦,有时可能会有</p>
<ul>
<li>Similar code found in two :expression_statement nodes (mass = 86)</li>
</ul>
<p>这就意味着我们可以对上面的代码进行重构,他们是重复的代码。</p>
<h2 id="模块分离与测试">模块分离与测试</h2>
<p>在之前说到</p>
<blockquote>
<p>奋斗了近半个月后,将fork的代码读懂、重构、升级版本、调整,添加新功能、添加测试、添加CI、添加分享之后,终于almost finish。</p>
</blockquote>
<p>今天就来说说是怎样做的。</p>
<p>以之前造的<a href="https://github.com/phodal/lettuce">Lettuce</a>为例,里面有:</p>
<ul>
<li>代码质量(Code Climate)</li>
<li>CI状态(Travis CI)</li>
<li>测试覆盖率(96%)</li>
<li>自动化测试(npm test)</li>
<li>文档</li>
</ul>
<p>按照<a href="https://github.com/phodal/awesome-developer">Web Developer路线图</a>来说,我们还需要有:</p>
<ul>
<li>版本管理</li>
<li>自动部署</li>
</ul>
<p>等等。</p>
<h3 id="代码模块化">代码模块化</h3>
<p>在SkillTree的源码里,大致分为三部分:</p>
<ul>
<li>namespace函数: 顾名思义</li>
<li>Calculator也就是TalentTree,主要负责解析、生成url,头像,依赖等等</li>
<li>Skill 主要是tips部分。</li>
</ul>
<p>而这一些都在一个js里,对于一个库来说,是一件好事,但是对于一个项目来说,并非如此。</p>
<p>依赖的库有</p>
<ul>
<li>jQuery</li>
<li>Knockout</li>
</ul>
<p>好在Knockout可以用Require.js进行管理,于是,使用了<code>Require.js</code>进行管理:</p>
<div class="sourceCode" id="cb14"><pre class="sourceCode html"><code class="sourceCode html"><a class="sourceLine" id="cb14-1" title="1"><span class="kw"><script</span><span class="ot"> type=</span><span class="st">"text/javascript"</span><span class="ot"> data-main=</span><span class="st">"app/scripts/main.js"</span><span class="ot"> src=</span><span class="st">"app/lib/require.js"</span><span class="kw">></script></span></a></code></pre></div>
<p><code>main.js</code>配置如下:</p>
<div class="sourceCode" id="cb15"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb15-1" title="1"><span class="va">require</span>.<span class="at">config</span>(<span class="op">{</span></a>
<a class="sourceLine" id="cb15-2" title="2"> <span class="dt">baseUrl</span><span class="op">:</span> <span class="st">'app'</span><span class="op">,</span></a>
<a class="sourceLine" id="cb15-3" title="3"> <span class="dt">paths</span><span class="op">:{</span></a>
<a class="sourceLine" id="cb15-4" title="4"> <span class="dt">jquery</span><span class="op">:</span> <span class="st">'lib/jquery'</span><span class="op">,</span></a>
<a class="sourceLine" id="cb15-5" title="5"> <span class="dt">json</span><span class="op">:</span> <span class="st">'lib/json'</span><span class="op">,</span></a>
<a class="sourceLine" id="cb15-6" title="6"> <span class="dt">text</span><span class="op">:</span> <span class="st">'lib/text'</span></a>
<a class="sourceLine" id="cb15-7" title="7"> <span class="op">}</span></a>
<a class="sourceLine" id="cb15-8" title="8"><span class="op">}</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb15-9" title="9"></a>
<a class="sourceLine" id="cb15-10" title="10"><span class="at">require</span>([<span class="st">'scripts/ko-bindings'</span>])<span class="op">;</span></a>
<a class="sourceLine" id="cb15-11" title="11"></a>
<a class="sourceLine" id="cb15-12" title="12"><span class="at">require</span>([<span class="st">'lib/knockout'</span><span class="op">,</span> <span class="st">'scripts/TalentTree'</span><span class="op">,</span> <span class="st">'json!data/web.json'</span>]<span class="op">,</span> <span class="kw">function</span>(ko<span class="op">,</span> TalentTree<span class="op">,</span> TalentData) <span class="op">{</span></a>
<a class="sourceLine" id="cb15-13" title="13"> <span class="st">'use strict'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb15-14" title="14"> <span class="kw">var</span> vm <span class="op">=</span> <span class="kw">new</span> <span class="at">TalentTree</span>(TalentData)<span class="op">;</span></a>
<a class="sourceLine" id="cb15-15" title="15"> <span class="va">ko</span>.<span class="at">applyBindings</span>(vm)<span class="op">;</span></a>
<a class="sourceLine" id="cb15-16" title="16"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>text、json插件主要是用于处理web.json,即用json来处理技能,于是不同的类到了不同的js文件。</p>
<pre><code>.
|____Book.js
|____Doc.js
|____ko-bindings.js
|____Link.js
|____main.js
|____Skill.js
|____TalentTree.js
|____Utils.js</code></pre>
<p>加上了后来的推荐阅读书籍等等。而Book和Link都是继承自Doc。</p>
<div class="sourceCode" id="cb17"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb17-1" title="1"><span class="at">define</span>([<span class="st">'scripts/Doc'</span>]<span class="op">,</span> <span class="kw">function</span>(Doc) <span class="op">{</span></a>
<a class="sourceLine" id="cb17-2" title="2"> <span class="st">'use strict'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb17-3" title="3"> <span class="kw">function</span> <span class="at">Book</span>(_e) <span class="op">{</span></a>
<a class="sourceLine" id="cb17-4" title="4"> <span class="va">Doc</span>.<span class="at">apply</span>(<span class="kw">this</span><span class="op">,</span> arguments)<span class="op">;</span></a>
<a class="sourceLine" id="cb17-5" title="5"> <span class="op">}</span></a>
<a class="sourceLine" id="cb17-6" title="6"> <span class="va">Book</span>.<span class="at">prototype</span> <span class="op">=</span> <span class="kw">new</span> <span class="at">Doc</span>()<span class="op">;</span></a>
<a class="sourceLine" id="cb17-7" title="7"></a>
<a class="sourceLine" id="cb17-8" title="8"> <span class="cf">return</span> Book<span class="op">;</span></a>
<a class="sourceLine" id="cb17-9" title="9"><span class="op">}</span>)<span class="op">;</span> </a></code></pre></div>
<p>而这里便是后面对其进行重构的内容。Doc类则是Skillock中类的一个缩影</p>
<div class="sourceCode" id="cb18"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb18-1" title="1"><span class="at">define</span>([]<span class="op">,</span> <span class="kw">function</span>() <span class="op">{</span></a>
<a class="sourceLine" id="cb18-2" title="2"> <span class="st">'use strict'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb18-3" title="3"> <span class="kw">var</span> Doc <span class="op">=</span> <span class="kw">function</span> (_e) <span class="op">{</span></a>
<a class="sourceLine" id="cb18-4" title="4"> <span class="kw">var</span> e <span class="op">=</span> _e <span class="op">||</span> <span class="op">{};</span></a>
<a class="sourceLine" id="cb18-5" title="5"> <span class="kw">var</span> self <span class="op">=</span> <span class="kw">this</span><span class="op">;</span></a>
<a class="sourceLine" id="cb18-6" title="6"></a>
<a class="sourceLine" id="cb18-7" title="7"> <span class="va">self</span>.<span class="at">label</span> <span class="op">=</span> <span class="va">e</span>.<span class="at">label</span> <span class="op">||</span> (<span class="va">e</span>.<span class="at">url</span> <span class="op">||</span> <span class="st">'Learn more'</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb18-8" title="8"> <span class="va">self</span>.<span class="at">url</span> <span class="op">=</span> <span class="va">e</span>.<span class="at">url</span> <span class="op">||</span> <span class="st">'javascript:void(0)'</span><span class="op">;</span></a>
<a class="sourceLine" id="cb18-9" title="9"> <span class="op">};</span></a>
<a class="sourceLine" id="cb18-10" title="10"></a>
<a class="sourceLine" id="cb18-11" title="11"> <span class="cf">return</span> Doc<span class="op">;</span></a>
<a class="sourceLine" id="cb18-12" title="12"><span class="op">}</span>)<span class="op">;</span></a></code></pre></div>
<p>或者说这是一个AMD的Class应该有的样子。考虑到this的隐性绑定,作者用了self=this来避免这个问题。最后Return了这个对象,我们在调用的就需要new一个。大部分在代码中返回的都是对象,除了在Utils类里面返回的是函数:</p>
<div class="sourceCode" id="cb19"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb19-1" title="1"><span class="cf">return</span> <span class="op">{</span></a>
<a class="sourceLine" id="cb19-2" title="2"> <span class="dt">getSkillsByHash</span><span class="op">:</span> getSkillsByHash<span class="op">,</span></a>
<a class="sourceLine" id="cb19-3" title="3"> <span class="dt">getSkillById</span><span class="op">:</span> getSkillById<span class="op">,</span> </a>
<a class="sourceLine" id="cb19-4" title="4"> <span class="dt">prettyJoin</span><span class="op">:</span> prettyJoin</a>
<a class="sourceLine" id="cb19-5" title="5"><span class="op">};</span></a></code></pre></div>
<p>当然函数也是一个对象。</p>
<h3 id="自动化测试">自动化测试</h3>
<p>一直习惯用Travis CI,于是也继续用Travis Ci,<code>.travis.yml</code>配置如下所示:</p>
<div class="sourceCode" id="cb20"><pre class="sourceCode yml"><code class="sourceCode yaml"><a class="sourceLine" id="cb20-1" title="1"><span class="fu">language:</span><span class="at"> node_js</span></a>
<a class="sourceLine" id="cb20-2" title="2"><span class="fu">node_js:</span></a>
<a class="sourceLine" id="cb20-3" title="3"> <span class="kw">-</span> <span class="st">"0.10"</span></a>
<a class="sourceLine" id="cb20-4" title="4"></a>
<a class="sourceLine" id="cb20-5" title="5"><span class="fu">notifications:</span></a>
<a class="sourceLine" id="cb20-6" title="6"> <span class="fu">email:</span><span class="at"> </span><span class="ch">false</span></a>
<a class="sourceLine" id="cb20-7" title="7"></a>
<a class="sourceLine" id="cb20-8" title="8"><span class="fu">branches:</span></a>
<a class="sourceLine" id="cb20-9" title="9"> <span class="fu">only:</span></a>
<a class="sourceLine" id="cb20-10" title="10"> <span class="kw">-</span> gh-pages</a></code></pre></div>
<p>使用gh-pages的原因是,我们一push代码的时候,就可以自动测试、部署等等,好处一堆堆的。</p>
<p>接着我们需要在<code>package.json</code>里面添加脚本</p>
<div class="sourceCode" id="cb21"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb21-1" title="1"><span class="st">"scripts"</span><span class="op">:</span> <span class="op">{</span></a>
<a class="sourceLine" id="cb21-2" title="2"> <span class="st">"test"</span><span class="op">:</span> <span class="st">"mocha"</span></a>
<a class="sourceLine" id="cb21-3" title="3"> <span class="op">}</span></a></code></pre></div>
<p>这样当我们push代码的时候便会自动跑所有的测试。因为mocha的主要配置是用<code>mocha.opts</code>,所以我们还需要配置一下<code>mocha.opts</code></p>
<pre><code>--reporter spec
--ui bdd
--growl
--colors
test/spec </code></pre>
<p>最后的<code>test/spec</code>是指定测试的目录。</p>
<h3 id="jshint">Jshint</h3>
<blockquote>
<p>JSLint定义了一组编码约定,这比ECMA定义的语言更为严格。这些编码约定汲取了多年来的丰富编码经验,并以一条年代久远的编程原则 作为宗旨:能做并不意味着应该做。JSLint会对它认为有的编码实践加标志,另外还会指出哪些是明显的错误,从而促使你养成好的 JavaScript编码习惯。</p>
</blockquote>
<p>当我们的js写得不合理的时候,这时测试就无法通过:</p>
<pre><code>line 5 col 25 A constructor name should start with an uppercase letter.
line 21 col 62 Strings must use singlequote.</code></pre>
<p>这是一种驱动写出更规范js的方法。</p>
<h3 id="mocha">Mocha</h3>
<blockquote>
<p>Mocha 是一个优秀的JS测试框架,支持TDD/BDD,结合 should.js/expect/chai/better-assert,能轻松构建各种风格的测试用例。</p>
</blockquote>
<p>最后的效果如下所示:</p>
<pre><code>Book,Link
Book Test
✓ should return book label & url
Link Test
✓ should return link label & url</code></pre>
<h3 id="测试示例">测试示例</h3>
<p>简单地看一下Book的测试:</p>
<div class="sourceCode" id="cb25"><pre class="sourceCode javascript"><code class="sourceCode javascript"><a class="sourceLine" id="cb25-1" title="1"><span class="co">/* global describe, it */</span></a>
<a class="sourceLine" id="cb25-2" title="2"></a>
<a class="sourceLine" id="cb25-3" title="3"><span class="kw">var</span> requirejs <span class="op">=</span> <span class="at">require</span>(<span class="st">"requirejs"</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb25-4" title="4"><span class="kw">var</span> assert <span class="op">=</span> <span class="at">require</span>(<span class="st">"assert"</span>)<span class="op">;</span></a>
<a class="sourceLine" id="cb25-5" title="5"><span class="kw">var</span> should <span class="op">=</span> <span class="at">require</span>(<span class="st">"should"</span>)<span class="op">;</span></a>