-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathchapter5.html
More file actions
1909 lines (1638 loc) · 151 KB
/
chapter5.html
File metadata and controls
1909 lines (1638 loc) · 151 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="utf-8"/>
<title>Ruby on Rails 教程 - 第 5 章 完善布局</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<meta name="description" content="最好的 Ruby on Rails 入门教程"/>
<meta name="keywords" content="ruby, rails, tutorial"/>
<meta name="author" content="Michael Hartl"/>
<meta name="translator" content="安道"/>
<meta name="generator" content="persie 0.0.5.1"/>
<link rel="stylesheet" type="text/css" href="//railstutorial-china.org/assets/css/main.css"/>
<link rel="stylesheet" type="text/css" href="book.css"/>
<script type="text/javascript" src="//railstutorial-china.org/assets/js/global.js"></script>
</head>
<body class="book-page">
<nav class="navbar">
<div class="container">
<div class="clearfix">
<a class="navbar-brand hidden-sm-up" href="//railstutorial-china.org/" title="Ruby on Rails 教程">Ruby on Rails 教程</a>
<button class="navbar-toggler hidden-sm-up pull-xs-right" type="button" data-toggle="collapse" data-target="#main-nav">☰</button>
</div>
<a class="navbar-brand hidden-xs-down" href="//railstutorial-china.org/" title="Ruby on Rails 教程">Ruby on Rails 教程</a>
<div class="collapse navbar-toggleable-xs pull-sm-right" id="main-nav">
<ul class="nav navbar-nav">
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/" title="首页">首页</a></li>
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/blog/" title="博客">博客</a></li>
<li class="nav-item active"><a class="nav-link" href="//railstutorial-china.org/book/" title="阅读">阅读</a></li>
<li class="nav-item"><a class="nav-link" href="//railstutorial-china.org/#ebook" title="电子书">电子书</a></li>
</ul>
</div>
</div>
</nav>
<div class="content">
<div class="container">
<div class="row">
<div class="col-lg-offset-2 col-lg-8">
<div class="book-versions">
选择版本:
<a class="btn btn-primary" href="//railstutorial-china.org/book/" title="Ruby on Rails 教程(原书第 4 版,针对 Rails 5)">Rails 5</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails42/" title="Ruby on Rails 教程(原书第 3 版,针对 Rails 4.2)">Rails 4.2</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails4/" title="Ruby on Rails 教程(原书第 3 版,针对 Rails 4.0)">Rails 4.0</a>
<a class="btn btn-secondary" href="//railstutorial-china.org/rails3/" title="Ruby on Rails 教程(原书第 2 版,针对 Rails 3.2)">Rails 3.2</a>
</div>
<div class="alert alert-warning">
<p>在线版的内容可能落后于电子书,如果想及时获得更新,请<a href="//railstutorial-china.org/#ebook" title="购买电子书">购买电子书</a>。</p>
</div>
<article class="article">
<section data-type="chapter" id="filling-in-the-layout">
<h1><span class="title-label">第 5 章</span> 完善布局</h1>
<p><a class="xref-link" href="chapter4.html#rails-flavored-ruby">第 4 章</a>简介 Ruby 时,我们学习了如何在演示应用中引入样式表(<a class="xref-link" href="chapter4.html#motivation">4.1 节</a>),不过现在样式表中还没有内容。本章将使用一个 CSS 框架,还会自己编写样式,填充样式表。<sup>[<a id="fn-ref-1" href="#fn-1">1</a>]</sup>我们还将完善布局,添加指向各个页面的链接(例如首页和“关于”页面,参见 <a class="xref-link" href="#structure">5.1 节</a>)。在这个过程中,我们将学习局部视图、Rails 路由和 Asset Pipeline,还会介绍 Sass(<a class="xref-link" href="#sass-and-the-asset-pipeline">5.2 节</a>)。最后,我们还要向前迈出很重要的一步:允许用户在我们的网站中注册(<a class="xref-link" href="#user-signup">5.4 节</a>)。</p>
<p>本章大部分改动是添加和修改应用的布局,这些操作一般不由测试驱动,或者完全不用测试。所以我们大部分时间都在文本编辑器和浏览器中,只用 TDD 添加“联系”页面(<a class="xref-link" href="#contact-page">5.3.1 节</a>)。不过,我们将编写一种重要的测试,集成测试( integration test),检查最终完成的布局中有所需的链接(<a class="xref-link" href="#layout-link-tests">5.3.4 节</a>)。</p>
<section data-type="sect1" id="structure">
<h1><span class="title-label">5.1</span> 添加一些结构</h1>
<p>本书介绍 Web 开发而不是 Web 设计,不过在一个看起来很简陋的应用中开发会让人提不起劲,所以本节要向布局中添加一些结构,再加入一些 CSS,实现基本的样式。除了使用自定义的 CSS 规则之外,我们还会使用由 Twitter 开发的开源 Web 设计框架 <a href="http://getbootstrap.com" class="external-link">Bootstrap</a>。我们会按照一定的方式组织代码——当布局文件中的内容变多以后,再使用局部视图清理。</p>
<p>开发 Web 应用时,尽早对用户界面有个统筹安排往往会对你有所帮助。在本书后续内容中,我会经常使用网页构思图(mockup,在 Web 领域经常称之为“线框图”),展示应用最终外观的草图。<sup>[<a id="fn-ref-2" href="#fn-2">2</a>]</sup>本章大部分内容都是在开发 <a class="xref-link" href="chapter3.html#static-pages">3.2 节</a>编写的静态页面,我们要在页面中加入网站徽标、导航栏和网站页脚。在这些页面中,最重要的是“首页”,它的构思图如<a class="xref-link" href="#fig-home-page-mockup">图 5.1</a> 所示,<a class="xref-link" href="#fig-site-with-footer">图 5.9</a> 是最终实现的效果。你会发现二者之间的某些细节有所不同,例如,在最终实现的页面中我们加入了 Rails 徽标——这没什么关系,因为构思图没必要画出每个细节。</p>
<div id="fig-home-page-mockup" class="figure"><img src="images/chapter5/home_page_mockup_3rd_edition.png" alt="home page mockup 3rd edition" /><div class="figcaption"><span class="title-label">图 5.1</span>:演示应用首页的构思图</div></div>
<p>与之前一样,如果使用 Git 做版本控制,现在最好创建一个新分支:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>git checkout <span class="nt">-b</span> filling-in-layout
</code></pre></div>
</div>
<section data-type="sect2" id="site-navigation">
<h2><span class="title-label">5.1.1</span> 网站导航</h2>
<p>在应用中添加链接和样式之前,我们先来修改网站的布局文件 <code>application.html.erb</code>(上一次见到是在<a class="xref-link" href="chapter4.html#listing-application-layout-full-title">代码清单 4.3</a> 中),添加一些 HTML 结构。我们要添加一些区域,一些 CSS 类,以及导航栏。布局文件的完整内容参见<a class="xref-link" href="#listing-layout-new-structure">代码清单 5.1</a>,
对各部分的说明紧跟其后。如果你迫不及待想看到结果,请看<a class="xref-link" href="#fig-layout-no-logo-or-custom-css">图 5.2</a>。注意:结果(还)不是很让人满意。</p>
<div id="listing-layout-new-structure" data-type="listing">
<h5><span class="title-label">代码清单 5.1</span>:添加一些结构后的网站布局文件</h5>
<div class="source-file">app/views/layouts/application.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span><span class="cp"><%=</span> <span class="n">full_title</span><span class="p">(</span><span class="k">yield</span><span class="p">(</span><span class="ss">:title</span><span class="p">))</span> <span class="cp">%></span><span class="nt"></title></span>
<span class="cp"><%=</span> <span class="n">csrf_meta_tags</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="ss">media: </span><span class="s1">'all'</span><span class="p">,</span>
<span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">javascript_include_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="c"><!--[if lt IE 9]></span>
<span class="c"> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js"></span>
<span class="c"> </script></span>
<span class="c"> <![endif]--></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="nt"><header</span> <span class="na">class=</span><span class="s">"navbar navbar-fixed-top navbar-inverse"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"sample app"</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">,</span> <span class="ss">id: </span><span class="s2">"logo"</span> <span class="cp">%></span>
<span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Help"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Log in"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
<span class="nt"></div></span>
<span class="nt"></header></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="k">yield</span> <span class="cp">%></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div>
</div>
<p>我们从上往下看一下这段代码中新添加的元素。<a class="xref-link" href="chapter3.html#testing-titles">3.4.1 节</a>简单介绍过,Rails 默认使用 HTML5(如文档类型声明 <code><!DOCTYPE html></code> 所示)。因为 HTML5 标准还很新,有些浏览器(特别是旧版 Internet Explorer)还没有完全支持,所以我们加载了一些 JavaScript 代码(称作 <a href="https://github.com/aFarkas/html5shiv" class="external-link">HTML5 shim(或 shiv)</a>)<sup>[<a id="fn-ref-3" href="#fn-3">3</a>]</sup>来解决这个问题:</p>
<div data-type="listing">
<div class="highlight language-html"><pre><code><span class="c"><!--[if lt IE 9]></span>
<span class="c"> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js"></span>
<span class="c"> </script></span>
<span class="c"><![endif]--></span>
</code></pre></div>
</div>
<p>这段有点古怪的句法,只有当 IE 浏览器的版本号小于 9 时(<code>if lt IE 9</code>)才会加载其中的代码。<code><!--[if lt IE 9]></code> 这个奇怪的句法不是 Rails 提供的,其实它是 IE 浏览器为了解决兼容性问题而特别提供的。使用这个句法的好处是,只会在 IE9 以前的版本中加载 HTML5 shim,而 Firefox、Chrome 和 Safari 等其他浏览器不受影响。</p>
<p>后面的区域是一个 <code>header</code> 元素,包含网站的徽标(纯文本)、一些区域(使用 <code>div</code> 标签)和一个导航列表元素:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="nt"><header</span> <span class="na">class=</span><span class="s">"navbar navbar-fixed-top navbar-inverse"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"sample app"</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">,</span> <span class="ss">id: </span><span class="s2">"logo"</span> <span class="cp">%></span>
<span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Help"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Log in"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
<span class="nt"></div></span>
<span class="nt"></header></span>
</code></pre></div>
</div>
<p><code>header</code> 标签表明这个元素应该放在页面的顶部。我们为 <code>header</code> 标签指定了三个 CSS 类,<sup>[<a id="fn-ref-4" href="#fn-4">4</a>]</sup>分别为 <code>navbar</code>、<code>navbar-fixed-top</code> 和 <code>navbar-inverse</code>,类之间用空格分开:</p>
<div data-type="listing">
<div class="highlight language-html"><pre><code><span class="nt"><header</span> <span class="na">class=</span><span class="s">"navbar navbar-fixed-top navbar-inverse"</span><span class="nt">></span>
</code></pre></div>
</div>
<p>所有 HTML 元素都可以指定类和 ID,它们不仅仅是标注,使用 CSS 编写样式时也有用(<a class="xref-link" href="#custom-css">5.1.2 节</a>)。类和 ID 之间的主要区别是,类可以在同一个网页中多次使用,而 ID 只能使用一次。这里的三个类在 Bootstrap 框架中都有特殊的意义。我们会在 <a class="xref-link" href="#custom-css">5.1.2 节</a>安装并使用 Bootstrap。</p>
<p>在 <code>header</code> 标签中,有一个 <code>div</code> 标签:</p>
<div data-type="listing">
<div class="highlight language-html"><pre><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
</code></pre></div>
</div>
<p><code>div</code> 标签标识常规的区域,除了把文档分成不同的部分之外,没有特殊的意义。在以前的 HTML 标准中,<code>div</code> 标签用于划分网站中几乎所有的区域,但是 HTML5 增加了 <code>header</code>、<code>nav</code> 和 <code>section</code> 等元素,用于划分大多数网站中都会用到的区域。这个 <code>div</code> 标签也有一个 CSS 类,<code>container</code>。与 <code>header</code> 标签的类一样,这个类在 Bootstrap 中也有特殊意义。</p>
<p>在这个 <code>div</code> 标签中有一些 ERb 代码:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"sample app"</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">,</span> <span class="ss">id: </span><span class="s2">"logo"</span> <span class="cp">%></span>
<span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Help"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Log in"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
</code></pre></div>
</div>
<p>这里使用 Rails 提供的 <code>link_to</code> 辅助方法创建链接(<a class="xref-link" href="chapter3.html#custom-static-pages">3.2.2 节</a>是直接使用 <code>a</code> 标签创建的)。<code>link_to</code> 的第一个参数是链接文本,第二个参数是 URL。<a class="xref-link" href="#named-routes">5.3.3 节</a>会使用具名路由(named route)指定 URL,现在暂且使用 Web 开发中经常使用的占位符 <code>#</code>。第三个参数可选,是一个散列,本例使用这个参数为徽标添加一个 CSS ID——<code>logo</code>。(其他三个链接没有使用这个散列参数,没关系,因为这个参数是可选的。)Rails 辅助方法的参数经常这样使用散列,让我们仅使用 Rails 的辅助方法就能灵活添加 HTML 属性。</p>
<p><code>div</code> 标签中的第二个元素是导航链接,使用无序列表标签 <code>ul</code>,以及列表项目标签 <code>li</code> 编写:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Help"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Log in"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
</code></pre></div>
</div>
<p><code><nav></code> 标签以前是不需要的,它的目的是明确表明这些链接是导航。<code>ul</code> 标签中的 <code>nav</code>、<code>navbar-nav</code> 和 <code>navbar-right</code> 三个类在 Bootstrap 中有特殊的意义,<a class="xref-link" href="#custom-css">5.1.2 节</a>引入 Bootstrap CSS 之后会自动实现特殊的样式。在浏览器中审查导航元素,你会发现 Rails 处理布局文件并执行其中的 ERb 代码后,生成的列表如下所示:<sup>[<a id="fn-ref-5" href="#fn-5">5</a>]</sup></p>
<div data-type="listing">
<div class="highlight language-html"><pre><code><span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li><a</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">></span>Home<span class="nt"></a></li></span>
<span class="nt"><li><a</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">></span>Help<span class="nt"></a></li></span>
<span class="nt"><li><a</span> <span class="na">href=</span><span class="s">"#"</span><span class="nt">></span>Log in<span class="nt"></a></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
</code></pre></div>
</div>
<p>这就是返回给浏览器的文本。</p>
<p>布局文件的最后一部分是一个 <code>div</code> 标签,标识主内容区域:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="k">yield</span> <span class="cp">%></span>
<span class="nt"></div></span>
</code></pre></div>
</div>
<p>与之前一样,<code>container</code> 类在 Bootstrap 中有特殊意义。<a class="xref-link" href="chapter3.html#layouts-and-embedded-ruby">3.4.3 节</a>已经介绍过,<code>yield</code> 会把各个页面中的内容插入网站的布局中。</p>
<p>除了留到 <a class="xref-link" href="#partials">5.1.3 节</a>添加的网站页脚之外,布局现在完成了。访问“首页”就能看到结果。为了利用后面添加的样式,我们要在 <code>home.html.erb</code> 视图中添加一些元素,如<a class="xref-link" href="#listing-signup-button">代码清单 5.2</a> 所示。</p>
<div id="listing-signup-button" data-type="listing">
<h5><span class="title-label">代码清单 5.2</span>:“首页”视图,包含一个指向注册页面的链接</h5>
<div class="source-file">app/views/static_pages/home.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="nt"><div</span> <span class="na">class=</span><span class="s">"center jumbotron"</span><span class="nt">></span>
<span class="nt"><h1></span>Welcome to the Sample App<span class="nt"></h1></span>
<span class="nt"><h2></span>
This is the home page for the
<span class="nt"><a</span> <span class="na">href=</span><span class="s">"http://www.railstutorial.org/"</span><span class="nt">></span>Ruby on Rails Tutorial<span class="nt"></a></span>
sample application.
<span class="nt"></h2></span>
<span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Sign up now!"</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">,</span> <span class="ss">class: </span><span class="s2">"btn btn-lg btn-primary"</span> <span class="cp">%></span>
<span class="nt"></div></span>
<span class="cp"><%=</span> <span class="n">link_to</span> <span class="n">image_tag</span><span class="p">(</span><span class="s2">"rails.png"</span><span class="p">,</span> <span class="ss">alt: </span><span class="s2">"Rails logo"</span><span class="p">),</span>
<span class="s1">'http://rubyonrails.org/'</span> <span class="cp">%></span>
</code></pre></div>
</div>
<p>其中第一个 <code>link_to</code> 创建一个占位链接,指向<a class="xref-link" href="chapter7.html#sign-up">第 7 章</a>创建的用户注册页面:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="nt"><a</span> <span class="na">href=</span><span class="s">"#"</span> <span class="na">class=</span><span class="s">"btn btn-lg btn-primary"</span><span class="nt">></span>Sign up now!<span class="nt"></a></span>
</code></pre></div>
</div>
<p><code>div</code> 标签的 CSS 类 <code>jumbotron</code> 在 Bootstrap 中有特殊的意义,注册按钮的 <code>btn</code>、<code>btn-lg</code> 和 <code>btn-primary</code> 也是一样。</p>
<p>第二个 <code>link_to</code> 用到了 <code>image_tag</code> 辅助方法,它的第一个参数是图像的路径;第二个参数可选,是一个散列,本例中这个散列参数使用一个符号键设置图像的 <code>alt</code> 属性。为了能正确显示图像,应用中必须有个名为 <code>rails.png</code> 的图像。这个图像可以从本书的网站中下载,地址是 <a href="http://railstutorial-china.org/assets/images/rails.png" class="external-link">http://railstutorial-china.org/assets/images/rails.png</a>。然后把这个图片放到 <code>app/assets/images/</code> 目录中。<sup>[<a id="fn-ref-6" href="#fn-6">6</a>]</sup>如果使用云端 IDE 或 Unix 类系统,可以使用 <code>curl</code> 完成这个操作,如<a class="xref-link" href="#listing-curl-rails-png">代码清单 5.3</a> 所示。(关于 <code>curl</code> 的进一步信息,参阅《<a href="https://www.learnenough.com/command-line-tutorial#sec-downloading_a_file" class="external-link">Learn Enough Command Line to Be Dangerous</a>》。)</p>
<div id="listing-curl-rails-png" data-type="listing">
<h5><span class="title-label">代码清单 5.3</span>:下载一个图像</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>curl <span class="nt">-o</span> app/assets/images/rails.png <span class="nt">-OL</span> railstutorial-china.org/assets/images/rails.png
</code></pre></div>
</div>
<p>因为我们在<a class="xref-link" href="#listing-signup-button">代码清单 5.2</a> 中使用了 <code>image_tag</code> 辅助方法,Rails 会通过 Asset Pipeline(<a class="xref-link" href="#sass-and-the-asset-pipeline">5.2 节</a>)自动找到 <code>app/assets/images/</code> 目录中的任何图像。</p>
<p>现在终于可以看到劳动果实了,如<a class="xref-link" href="#fig-layout-no-logo-or-custom-css">图 5.2</a> 所示。</p>
<div id="fig-layout-no-logo-or-custom-css" class="figure"><img src="images/chapter5/layout_no_logo_or_custom_css_bootstrap_3rd_edition.png" alt="layout no logo or custom css bootstrap 3rd edition" /><div class="figcaption"><span class="title-label">图 5.2</span>:还没添加 CSS 的首页</div></div>
<p>为了更好地理解 <code>image_tag</code>,我们在浏览器中审查那个图像,看一下生成的 HTML:<sup>[<a id="fn-ref-7" href="#fn-7">7</a>]</sup></p>
<div data-type="listing">
<div class="highlight language-html"><pre><code><span class="nt"><img</span> <span class="na">alt=</span><span class="s">"Rails logo"</span> <span class="na">src=</span><span class="s">"/assets/rails-9308b8f92fea4c19a3a0d8385b494526.png"</span> <span class="nt">/></span>
</code></pre></div>
</div>
<p>其中,字符串 <code>9308b8f92fea4c19a3a0d8385b494526</code>(你看到的可能不一样)由 Rails 添加,目的是确保文件名的唯一性,如果文件变化了,让浏览器重新加载文件(而不是从浏览器缓存中读取)。注意,<code>src</code> 属性中并不包含 <code>images</code>,而是静态文件(图像、JavaScript、CSS 等)共用的 <code>assets</code> 目录。在服务器中,Rails 会把 <code>assets</code> 目录中的图像和 <code>app/assets/images</code> 目录中的文件对应起来。这么做是为了让浏览器觉得所有静态文件都在同一个目录中,有利于快速伺服。<code>alt</code> 属性的内容会在图像无法加载时显示,例如在针对视觉障碍人士的屏幕阅读器中。</p>
<p>你可能觉得<a class="xref-link" href="#fig-layout-no-logo-or-custom-css">图 5.2</a> 所示的页面并不是很美观。或许吧。不过也可以小小地高兴一下,因为我们为 HTML 结构指定了合适的类,可以用来添加 CSS。</p>
<h5 id="exercises-site-navigation" class="discrete">练习</h5>
<ol class="arabic">
<li>
<p>没有猫图的网页还算是网页吗!使用<a class="xref-link" href="#listing-downloading-a-cat-pic">代码清单 5.4</a> 中的命令下载<a class="xref-link" href="#fig-kitten">图 5.3</a> 所示的小猫图像。<sup>[<a id="fn-ref-8" href="#fn-8">8</a>]</sup></p>
</li>
<li>
<p>使用 <code>mv</code> 命令把 <code>kitten.jpg</code> 移到保存静态图像资源的目录中(<a class="xref-link" href="#the-asset-pipeline">5.2.1 节</a>)。</p>
</li>
<li>
<p>使用 <code>image_tag</code> 把 <code>kitten.jpg</code> 添加到首页,如<a class="xref-link" href="#fig-kitten-on-home">图 5.4</a> 所示。</p>
</li>
</ol>
<div id="fig-kitten" class="figure"><img src="images/chapter5/kitten.jpg" alt="kitten" /><div class="figcaption"><span class="title-label">图 5.3</span>:不可或缺的小猫图像</div></div>
<div id="listing-downloading-a-cat-pic" data-type="listing">
<h5><span class="title-label">代码清单 5.4</span>:从网上下载一张小猫图像</h5>
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>curl <span class="nt">-OL</span> cdn.learnenough.com/kitten.jpg
</code></pre></div>
</div>
<div id="fig-kitten-on-home" class="figure"><img src="images/chapter5/kitten_on_home.png" alt="kitten on home" /><div class="figcaption"><span class="title-label">图 5.4</span>:添加小猫图像后的首页</div></div>
</section>
<section data-type="sect2" id="custom-css">
<h2><span class="title-label">5.1.2</span> Bootstrap 和自定义的 CSS</h2>
<p>前一节为很多 HTML 元素指定了 CSS 类,这样我们就可以使用 CSS 灵活地构建布局了。如前所述,其中很多类在 Bootstrap 中有特殊的意义。Bootstrap 是 Twitter 开发的框架,可以方便地把精美的 Web 设计和用户界面元素添加到使用 HTML5 开发的应用中。本节,我们将结合 Bootstrap 和一些自定义的 CSS,为演示应用添加一些样式。值得注意的是,使用 Bootstrap 后,应用的设计就自动实现了响应式(responsive),在各种设备中都具有精美的外观。</p>
<p>首先,我们要安装 Bootstrap。在 Rails 应用中可以使用 <code>bootstrap-sass</code> 这个 gem,如<a class="xref-link" href="#listing-bootstrap-sass">代码清单 5.5</a> 所示。Bootstrap 框架本身使用 <a href="http://lesscss.org" class="external-link">Less</a> 语言编写动态样式表,而 Rails 的 Asset Pipeline 默认支持的是(非常类似的)Sass 语言。<code>bootstrap-sass</code> 会把 Less 转换成 Sass,而且让 Bootstrap 中所有必要的文件都可以在当前应用中使用。<sup>[<a id="fn-ref-9" href="#fn-9">9</a>]</sup></p>
<div id="listing-bootstrap-sass" data-type="listing">
<h5><span class="title-label">代码清单 5.5</span>:把 <code>bootstrap-sass</code> gem 添加到 <code>Gemfile</code> 文件中</h5>
<div class="highlight language-ruby"><pre><code><span class="n">source</span> <span class="s1">'https://rubygems.org'</span>
<span class="n">gem</span> <span class="s1">'rails'</span><span class="p">,</span> <span class="s1">'5.1.6'</span>
<span class="n">gem</span> <span class="s1">'sass-rails'</span><span class="p">,</span> <span class="s1">'5.0.6'</span>
<span class="p">.</span>
<span class="nf">.</span>
<span class="o">.</span>
</code></pre></div>
</div>
<p>和之前一样,运行 <code>bundle install</code> 安装 Bootstrap:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span>bundle <span class="nb">install</span>
</code></pre></div>
</div>
<p><code>rails generate</code> 命令会自动为控制器生成一个单独的 CSS 文件,但很难使用正确的顺序引入这些样式,所以简单起见,本书会把所有 CSS 都放在一个文件中。为此,我们要先新建这个 CSS 文件:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">touch </span>app/assets/stylesheets/custom.scss
</code></pre></div>
</div>
<p>(这里使用 <a class="xref-link" href="chapter3.html#green">3.3.3 节</a>用过的 <code>touch</code> 命令,此外也可以使用其他方式。)目录名和文件扩展名都很重要。<code>app/assets/stylesheets/</code> 目录是 Asset Pipeline 的一部分,其中所有的样式表都会引入 <code>application.css</code> 文件。文件名 <code>custom.scss</code> 中包含 <code>.scss</code> 扩展名,说明这是“Sassy CSS”文件,Asset Pipeline 会使用 Sass 处理其中的内容。(<a class="xref-link" href="#sass-and-the-asset-pipeline">5.2 节</a>才会使用 Sass,不过加入这个扩展名才能发挥 <code>bootstrap-sass</code> gem 的作用。)</p>
<p>在这个 CSS 文件中,我们可以使用 <code>@import</code> 函数引入 Bootstrap(以及相关的 Sprockets 代码),如<a class="xref-link" href="#listing-bootstrap-css">代码清单 5.6</a> 所示。<sup>[<a id="fn-ref-10" href="#fn-10">10</a>]</sup></p>
<div id="listing-bootstrap-css" data-type="listing">
<h5><span class="title-label">代码清单 5.6</span>:导入 Bootstrap 中的 CSS</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="k">@import</span> <span class="s2">"bootstrap-sprockets"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s2">"bootstrap"</span><span class="p">;</span>
</code></pre></div>
</div>
<p>这两行代码会引入整个 Bootstrap CSS 框架。然后,重启 Web 服务器(先按 Ctrl-C 键,然后执行 <code>rails server</code> 命令),让这些改动生效,效果如<a class="xref-link" href="#fig-sample-app-only-bootstrap">图 5.5</a> 所示。文本的位置还不合适,徽标也没有任何样式,不过颜色搭配和注册按钮看起来都不错。</p>
<p>下面我们要加入一些整站都会用到的 CSS,美化网站布局和各个页面,如<a class="xref-link" href="#listing-universal-css">代码清单 5.7</a> 所示。效果如<a class="xref-link" href="#fig-sample-app-universal">图 5.6</a> 所示。这段代码定义了很多样式规则,为了说明 CSS 规则的作用,我通常会加入一些 CSS 注释,放在 <code>/* ... */</code> 中。</p>
<div id="listing-universal-css" data-type="listing">
<h5><span class="title-label">代码清单 5.7</span>:添加全站使用的 CSS</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="k">@import</span> <span class="s2">"bootstrap-sprockets"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s2">"bootstrap"</span><span class="p">;</span>
<span class="cm">/* universal */</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">60px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">section</span> <span class="p">{</span>
<span class="nl">overflow</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">textarea</span> <span class="p">{</span>
<span class="nl">resize</span><span class="p">:</span> <span class="n">vertical</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.center</span> <span class="p">{</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.center</span> <span class="nt">h1</span> <span class="p">{</span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>注意,<a class="xref-link" href="#listing-universal-css">代码清单 5.7</a> 中的 CSS 格式都是统一的。一般来说,CSS 规则通过类、ID、HTML 标签或者三者结合在一起来指代目标,然后在后面跟着一些样式声明。例如:</p>
<div data-type="listing">
<div class="highlight language-css"><pre><code><span class="nt">body</span> <span class="p">{</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">60px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>这个规则把页面的上内边距设为 60 像素。我们在 <code>header</code> 标签上指定了 <code>navbar-fixed-top</code> 类,Bootstrap 会把这个导航条固定在页面的顶部,所以页面的上内边距会把主内容区和导航条隔开一段距离。(导航条的颜色在 Bootstrap 2.0 中变了,所以要加入 <code>navbar-inverse</code> 类,把亮色变暗。)下面的 CSS 规则:</p>
<div data-type="listing">
<div class="highlight language-css"><pre><code><span class="nc">.center</span> <span class="p">{</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>把 <code>.center</code> 类的样式定义为 <code>text-align: center;</code>。<code>.center</code> 中的点号说明这个规则是样式化一个类。(在<a class="xref-link" href="#listing-logo-css">代码清单 5.9</a> 中会看到,<code>#</code> 样式化一个 ID。)这个规则的意思是,任何类为 <code>.center</code> 的标签(例如 <code>div</code>),其中包含的内容都会在页面中居中显示。(<a class="xref-link" href="#listing-signup-button">代码清单 5.2</a> 中有用到这个类。)</p>
<p>虽然 Bootstrap 提供了很精美的文字排版样式,我们还是要为文字的外观添加一些自定义的规则,如<a class="xref-link" href="#listing-typography-css">代码清单 5.8</a> 所示。(并不是所有样式都用于“首页”,但所有规则都会在这个演示应用的某个地方用到。)效果如<a class="xref-link" href="#fig-sample-app-typography">图 5.7</a> 所示。</p>
<div id="fig-sample-app-only-bootstrap" class="figure"><img src="images/chapter5/sample_app_only_bootstrap_3rd_edition.png" alt="sample app only bootstrap 3rd edition" /><div class="figcaption"><span class="title-label">图 5.5</span>:使用 Bootstrap CSS 后的演示应用</div></div>
<div id="fig-sample-app-universal" class="figure"><img src="images/chapter5/sample_app_universal_3rd_edition.png" alt="sample app universal 3rd edition" /><div class="figcaption"><span class="title-label">图 5.6</span>:添加一些留白以及其他全局样式</div></div>
<div id="listing-typography-css" data-type="listing">
<h5><span class="title-label">代码清单 5.8</span>:添加一些精美的文字排版样式</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="k">@import</span> <span class="s2">"bootstrap-sprockets"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s2">"bootstrap"</span><span class="p">;</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="o">/*</span> <span class="nt">typography</span> <span class="o">*/</span>
<span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span><span class="o">,</span> <span class="nt">h3</span><span class="o">,</span> <span class="nt">h4</span><span class="o">,</span> <span class="nt">h5</span><span class="o">,</span> <span class="nt">h6</span> <span class="p">{</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">h1</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">3em</span><span class="p">;</span>
<span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">-2px</span><span class="p">;</span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">h2</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1</span><span class="mi">.2em</span><span class="p">;</span>
<span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">-1px</span><span class="p">;</span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">30px</span><span class="p">;</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="nb">normal</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">p</span> <span class="p">{</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1</span><span class="mi">.1em</span><span class="p">;</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1</span><span class="mi">.7em</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<div id="fig-sample-app-typography" class="figure"><img src="images/chapter5/sample_app_typography_3rd_edition.png" alt="sample app typography 3rd edition" /><div class="figcaption"><span class="title-label">图 5.7</span>:添加一些排版样式</div></div>
<p>最后,我们还要为只包含“sample app”文本的网站徽标添加一些样式。<a class="xref-link" href="#listing-logo-css">代码清单 5.9</a> 中的 CSS 把文字变成全大写字母,并修改字号、颜色和位置。(我们使用的是 ID,因为我们希望徽标在页面中只出现一次,不过也可以使用类。)</p>
<div id="listing-logo-css" data-type="listing">
<h5><span class="title-label">代码清单 5.9</span>:添加网站徽标的样式</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="k">@import</span> <span class="s2">"bootstrap-sprockets"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s2">"bootstrap"</span><span class="p">;</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="o">/*</span> <span class="nt">header</span> <span class="o">*/</span>
<span class="nn">#logo</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1</span><span class="mi">.7em</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
<span class="nl">text-transform</span><span class="p">:</span> <span class="nb">uppercase</span><span class="p">;</span>
<span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">-1px</span><span class="p">;</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">9px</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="p">}</span>
<span class="nn">#logo</span><span class="nd">:hover</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
<span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>其中,<code>color: #fff;</code> 把徽标文字的颜色变成白色。HTML 中的颜色代码由 3 组 16 进制数组成,分别代表三原色中的红绿蓝(就是这个顺序)。<code>#ffffff</code> 是 3 种颜色都为最大值的情况,表示纯白色。<code>#fff</code> 是 <code>#ffffff</code> 的简写形式。CSS 标准为很多常用的 <a href="https://www.w3schools.com/colors/colors_names.asp" class="external-link">HTML 颜色</a>定义了别名,例如 <code>white</code> 代表 <code>#fff</code>。添加<a class="xref-link" href="#listing-logo-css">代码清单 5.9</a> 中的样式后,效果如<a class="xref-link" href="#fig-sample-app-logo">图 5.8</a> 所示。</p>
<div id="fig-sample-app-logo" class="figure"><img src="images/chapter5/sample_app_logo_3rd_edition.png" alt="sample app logo 3rd edition" /><div class="figcaption"><span class="title-label">图 5.8</span>:为徽标添加样式后的演示应用</div></div>
<h5 id="exercises-custom-css" class="discrete">练习</h5>
<ol class="arabic">
<li>
<p>使用<a class="xref-link" href="#listing-comment-out-image">代码清单 5.10</a> 中的代码,把前一节练习中添加的猫图注释掉。使用 Web 审查工具确认页面的 HTML 源码中没有那个图像了。</p>
</li>
<li>
<p>把<a class="xref-link" href="#listing-css-hide-image">代码清单 5.11</a> 中的 CSS 添加到 <code>custom.scss</code> 文件中,隐藏应用中的所有图像(目前只有首页有一张 Rails 徽标)。使用 Web 审查工具确认图像确实不见了,但是 HTML 仍在。</p>
</li>
</ol>
<div id="listing-comment-out-image" data-type="listing">
<h5><span class="title-label">代码清单 5.10</span>:把嵌入式 Ruby 代码注释掉</h5>
<div class="highlight language-erb"><pre><code><span class="c"><%#= image_tag("kitten.jpg", alt: "Kitten") %></span>
</code></pre></div>
</div>
<div id="listing-css-hide-image" data-type="listing">
<h5><span class="title-label">代码清单 5.11</span>:隐藏图像的 CSS</h5>
<div class="highlight language-scss"><pre><code><span class="nt">img</span> <span class="p">{</span>
<span class="nl">display</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
</section>
<section data-type="sect2" id="partials">
<h2><span class="title-label">5.1.3</span> 局部视图</h2>
<p>虽然<a class="xref-link" href="#listing-layout-new-structure">代码清单 5.1</a> 中的布局达到了目的,但其中的内容看起来有点混乱。HTML shim 就占了三行,而且使用了只针对 IE 的奇怪句法,如果能把它打包放在一个单独的地方就好了。此外,页头的 HTML 自成一个逻辑单元,也可以把这部分打包放在某个地方。在 Rails 中我们可以使用局部视图(partial)实现这种想法。先来看一下定义了局部视图之后的布局文件,如<a class="xref-link" href="#listing-layout-with-partials">代码清单 5.12</a> 所示。</p>
<div id="listing-layout-with-partials" data-type="listing">
<h5><span class="title-label">代码清单 5.12</span>:把 HTML shim 和页头放到局部视图中之后的网站布局</h5>
<div class="source-file">app/views/layouts/application.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span><span class="cp"><%=</span> <span class="n">full_title</span><span class="p">(</span><span class="k">yield</span><span class="p">(</span><span class="ss">:title</span><span class="p">))</span> <span class="cp">%></span><span class="nt"></title></span>
<span class="cp"><%=</span> <span class="n">csrf_meta_tags</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="ss">media: </span><span class="s1">'all'</span><span class="p">,</span>
<span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">javascript_include_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="hll"> <span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/shim'</span> <span class="cp">%></span></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="hll"> <span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/header'</span> <span class="cp">%></span></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="k">yield</span> <span class="cp">%></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div>
</div>
<p>在这段代码中,我们把 HTML shim 删掉,换成了一行代码,调用 Rails 的辅助方法 <code>render</code>:</p>
<div data-type="listing">
<div class="highlight language-erb"><pre><code><span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/shim'</span> <span class="cp">%></span>
</code></pre></div>
</div>
<p>这行代码会寻找一个名为 <code>app/views/layouts/_shim.html.erb</code> 的文件,执行其中的代码,然后把结果插入视图。<sup>[<a id="fn-ref-11" href="#fn-11">11</a>]</sup>(回顾一下,执行 Ruby 表达式并将结果插入模板中要使用 <code><%= … %></code>。)注意,文件名 <code>_shim.html.erb</code> 的开头有个下划线,这是局部视图的命名约定,以便在目录中快速定位所有局部视图。</p>
<p>当然,若要局部视图起作用,我们要写入相应的内容。HTML shim 局部视图只包含三行代码,如<a class="xref-link" href="#listing-stylesheets-partial">代码清单 5.13</a> 所示。</p>
<div id="listing-stylesheets-partial" data-type="listing">
<h5><span class="title-label">代码清单 5.13</span>:HTML shim 局部视图</h5>
<div class="source-file">app/views/layouts/_shim.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="c"><!--[if lt IE 9]></span>
<span class="c"> <script src="//cdnjs.cloudflare.com/ajax/libs/html5shiv/r29/html5.min.js"></span>
<span class="c"> </script></span>
<span class="c"><![endif]--></span>
</code></pre></div>
</div>
<p>类似地,我们可以把页头移入局部视图,如<a class="xref-link" href="#listing-header-partial">代码清单 5.14</a> 所示,然后再次调用 <code>render</code> 把这个局部视图插入布局中。(一般都要在文本编辑器中手动创建局部视图对应的文件。)</p>
<div id="listing-header-partial" data-type="listing">
<h5><span class="title-label">代码清单 5.14</span>:网站页头的局部视图</h5>
<div class="source-file">app/views/layouts/_header.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="nt"><header</span> <span class="na">class=</span><span class="s">"navbar navbar-fixed-top navbar-inverse"</span><span class="nt">></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"sample app"</span><span class="p">,</span> <span class="s1">'#'</span><span class="p">,</span> <span class="ss">id: </span><span class="s2">"logo"</span> <span class="cp">%></span>
<span class="nt"><nav></span>
<span class="nt"><ul</span> <span class="na">class=</span><span class="s">"nav navbar-nav navbar-right"</span><span class="nt">></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Home"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Help"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Log in"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
<span class="nt"></div></span>
<span class="nt"></header></span>
</code></pre></div>
</div>
<p>现在我们已经知道怎么创建局部视图了,让我们来加入与页头对应的页脚吧。你或许已经猜到了,我们会把这个局部视图命名为 <code>_footer.html.erb</code>,放在 <code>layouts</code> 目录中,如<a class="xref-link" href="#listing-footer-partial">代码清单 5.15</a> 所示。<sup>[<a id="fn-ref-12" href="#fn-12">12</a>]</sup></p>
<div id="listing-footer-partial" data-type="listing">
<h5><span class="title-label">代码清单 5.15</span>:页脚的局部视图</h5>
<div class="source-file">app/views/layouts/_footer.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="nt"><footer</span> <span class="na">class=</span><span class="s">"footer"</span><span class="nt">></span>
<span class="nt"><small></span>
The <span class="nt"><a</span> <span class="na">href=</span><span class="s">"http://www.railstutorial.org/"</span><span class="nt">></span>Ruby on Rails Tutorial<span class="nt"></a></span>
by <span class="nt"><a</span> <span class="na">href=</span><span class="s">"http://www.michaelhartl.com/"</span><span class="nt">></span>Michael Hartl<span class="nt"></a></span>
<span class="nt"></small></span>
<span class="nt"><nav></span>
<span class="nt"><ul></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"About"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li></span><span class="cp"><%=</span> <span class="n">link_to</span> <span class="s2">"Contact"</span><span class="p">,</span> <span class="s1">'#'</span> <span class="cp">%></span><span class="nt"></li></span>
<span class="nt"><li><a</span> <span class="na">href=</span><span class="s">"http://news.railstutorial.org/"</span><span class="nt">></span>News<span class="nt"></a></li></span>
<span class="nt"></ul></span>
<span class="nt"></nav></span>
<span class="nt"></footer></span>
</code></pre></div>
</div>
<p>与页头类似,我们在页脚使用 <code>link_to</code> 创建指向“关于”页面和“联系”页面的链接,地址先使用占位符 <code>#</code>。(与 <code>header</code> 一样,<code>footer</code> 也是 HTML5 新增的标签。)</p>
<p>按照 HTML shim 和页头局部视图的方式,我们可以在布局视图中渲染页脚局部视图,如<a class="xref-link" href="#listing-layout-with-footer">代码清单 5.16</a> 所示。</p>
<div id="listing-layout-with-footer" data-type="listing">
<h5><span class="title-label">代码清单 5.16</span>:添加页脚局部视图后的网站布局</h5>
<div class="source-file">app/views/layouts/application.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span><span class="cp"><%=</span> <span class="n">full_title</span><span class="p">(</span><span class="k">yield</span><span class="p">(</span><span class="ss">:title</span><span class="p">))</span> <span class="cp">%></span><span class="nt"></title></span>
<span class="cp"><%=</span> <span class="n">csrf_meta_tags</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">stylesheet_link_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="ss">media: </span><span class="s1">'all'</span><span class="p">,</span>
<span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">javascript_include_tag</span> <span class="s1">'application'</span><span class="p">,</span> <span class="s1">'data-turbolinks-track'</span><span class="p">:</span> <span class="s1">'reload'</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/shim'</span> <span class="cp">%></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/header'</span> <span class="cp">%></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="k">yield</span> <span class="cp">%></span>
<span class="hll"> <span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/footer'</span> <span class="cp">%></span></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div>
</div>
<p>当然,如果没有样式的话,页脚还很丑。页脚的样式参见<a class="xref-link" href="#listing-footer-css">代码清单 5.17</a>,效果如<a class="xref-link" href="#fig-site-with-footer">图 5.9</a> 所示。</p>
<div id="fig-site-with-footer" class="figure"><img src="images/chapter5/site_with_footer_bootstrap_3rd_edition.png" alt="site with footer bootstrap 3rd edition" /><div class="figcaption"><span class="title-label">图 5.9</span>:添加页脚后的首页</div></div>
<div id="listing-footer-css" data-type="listing">
<h5><span class="title-label">代码清单 5.17</span>:添加网站页脚的 CSS</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="o">/*</span> <span class="nt">footer</span> <span class="o">*/</span>
<span class="nt">footer</span> <span class="p">{</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">45px</span><span class="p">;</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="nl">border-top</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="mh">#eaeaea</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">footer</span> <span class="nt">a</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#555</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">footer</span> <span class="nt">a</span><span class="nd">:hover</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#222</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">footer</span> <span class="nt">small</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">footer</span> <span class="nt">ul</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">right</span><span class="p">;</span>
<span class="nl">list-style</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">footer</span> <span class="nt">ul</span> <span class="nt">li</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="nl">margin-left</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<h5 id="exercises-partials" class="discrete">练习</h5>
<ol class="arabic">
<li>
<p>把 Rails 在 <code>head</code> 元素中插入的标签替换成<a class="xref-link" href="#listing-rails-default-head">代码清单 5.18</a> 中的 <code>render</code> 调用。提示:简单起见,不要删除,应该剪切。</p>
</li>
<li>
<p>我们还没创建<a class="xref-link" href="#listing-rails-default-head">代码清单 5.18</a> 所需的局部视图,所以测试应该失败。确认的确如此。</p>
</li>
<li>
<p>在 <code>layouts</code> 目录中创建所需的局部视图,把内容粘贴进去,然后确认测试可以通过。</p>
</li>
</ol>
<div id="listing-rails-default-head" data-type="listing">
<h5><span class="title-label">代码清单 5.18</span>:把 Rails 在 <code>head</code> 元素中插入的标签替换成 <code>render</code> 调用</h5>
<div class="source-file">app/views/layouts/application.html.erb</div>
<div class="highlight language-erb"><pre><code><span class="cp"><!DOCTYPE html></span>
<span class="nt"><html></span>
<span class="nt"><head></span>
<span class="nt"><title></span><span class="cp"><%=</span> <span class="n">full_title</span><span class="p">(</span><span class="k">yield</span><span class="p">(</span><span class="ss">:title</span><span class="p">))</span> <span class="cp">%></span><span class="nt"></title></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/rails_default'</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/shim'</span> <span class="cp">%></span>
<span class="nt"></head></span>
<span class="nt"><body></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/header'</span> <span class="cp">%></span>
<span class="nt"><div</span> <span class="na">class=</span><span class="s">"container"</span><span class="nt">></span>
<span class="cp"><%=</span> <span class="k">yield</span> <span class="cp">%></span>
<span class="cp"><%=</span> <span class="n">render</span> <span class="s1">'layouts/footer'</span> <span class="cp">%></span>
<span class="nt"></div></span>
<span class="nt"></body></span>
<span class="nt"></html></span>
</code></pre></div>
</div>
</section>
</section>
<section data-type="sect1" id="sass-and-the-asset-pipeline">
<h1><span class="title-label">5.2</span> Sass 和 Asset Pipeline</h1>
<p>Rails 中最有用的功能之一是 Asset Pipeline,它极大地简化了静态资源文件(CSS、JavaScript 和图像)的生成和管理。本节先概述 Asset Pipeline 的作用,然后说明如何使用 Sass 这个强大的 CSS 编写工具。</p>
<section data-type="sect2" id="the-asset-pipeline">
<h2><span class="title-label">5.2.1</span> Asset Pipeline</h2>
<p>Rails 开发者要理解 Asset Pipeline 的三个概念:静态资源目录,清单文件,以及预处理器引擎。<sup>[<a id="fn-ref-13" href="#fn-13">13</a>]</sup>下面一一介绍。</p>
<section data-type="sect3" id="asset-directories">
<h3>静态资源目录</h3>
<p>Rails 的 Asset Pipeline 使用三个标准的目录存放静态资源文件,各有各的作用:</p>
<ul>
<li>
<p><code>app/assets</code>:当前应用的静态资源文件;</p>
</li>
<li>
<p><code>lib/assets</code>:开发团队自己开发的代码库使用的静态资源文件;</p>
</li>
<li>
<p><code>vendor/assets</code>:第三方代码库使用的静态资源文件;</p>
</li>
</ul>
<p>这几个目录中都有针对不同静态资源类型的子目录,例如:</p>
<div data-type="listing">
<div class="highlight language-sh"><pre><code><span class="nv">$ </span><span class="nb">ls </span>app/assets/
images/ javascripts/ stylesheets/
</code></pre></div>
</div>
<p>现在我们知道 <a class="xref-link" href="#custom-css">5.1.2 节</a>中 <code>custom.scss</code> 存放位置的用意了:因为 <code>custom.scss</code> 只在应用中使用,所以把它放在 <code>app/assets/stylesheets</code> 目录中。</p>
</section>
<section data-type="sect3" id="manifest-files">
<h3>清单文件</h3>
<p>把静态资源文件放在适当的目录中之后,要通过清单文件(manifest file)告诉 Rails 怎么把它们合并成一个文件(通过 <a href="https://github.com/rails/sprockets" class="external-link">Sprockets</a> gem 实现,而且只合并 CSS 和 JavaScript 文件,不会合并图像)。举个例子,我们来看一下应用默认的样式清单文件,如<a class="xref-link" href="#listing-app-css-manifest">代码清单 5.19</a> 所示。</p>
<div id="listing-app-css-manifest" data-type="listing">
<h5><span class="title-label">代码清单 5.19</span>:应用的CSS清单文件</h5>
<div class="source-file">app/assets/stylesheets/application.css</div>
<div class="highlight language-css"><pre><code><span class="c">/*</span>
<span class="c"> * This is a manifest file that'll be compiled into application.css, which</span>
<span class="c"> * will include all the files listed below.</span>
<span class="c"> *</span>
<span class="c"> * Any CSS and SCSS file within this directory, lib/assets/stylesheets,</span>
<span class="c"> * vendor/assets/stylesheets, or vendor/assets/stylesheets of plugins, if any,</span>
<span class="c"> * can be referenced here using a relative path.</span>
<span class="c"> *</span>
<span class="c"> * You're free to add application-wide styles to this file and they'll appear</span>
<span class="c"> * at the bottom of the compiled file so the styles you add here take</span>
<span class="c"> * precedence over styles defined in any styles defined in the other CSS/SCSS</span>
<span class="c"> * files in this directory. It is generally better to create a new file per</span>
<span class="c"> * style scope.</span>
<span class="c"> *</span>
<span class="hll"><span class="c"> *= require_tree .</span></span>
<span class="hll"><span class="c"> *= require_self</span></span>
<span class="c"> */</span>
</code></pre></div>
</div>
<p>这里关键的代码是几行 CSS 注释,Sprockets 通过这些注释引入相应的文件:</p>
<div data-type="listing">
<div class="highlight language-css"><pre><code><span class="c">/*</span>
<span class="c"> .</span>
<span class="c"> .</span>
<span class="c"> .</span>
<span class="c"> *= require_tree .</span>
<span class="c"> *= require_self</span>
<span class="c">*/</span>
</code></pre></div>
</div>
<p>其中</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>*= require_tree .
</code></pre></div>
</div>
<p>会把 <code>app/assets/stylesheets</code> 目录中的所有 CSS 文件(包含子目录中的文件)都引入应用的 CSS 文件。</p>
<p>下面这行:</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>*= require_self
</code></pre></div>
</div>
<p>会把 <code>application.css</code> 这个文件中的 CSS 也加载进来。</p>
<p>Rails 提供的默认清单文件可以满足我们的需求,所以本书不会对其做任何修改。Rails 指南中有一篇专门介绍 <a href="http://guides.rubyonrails.org/asset_pipeline.html" class="external-link">Asset Pipeline</a> 的文章,说得更详细。</p>
</section>
<section data-type="sect3" id="preprocessor-engines">
<h3>预处理器引擎</h3>
<p>准备好静态资源文件后,Rails 会使用一些预处理器引擎来处理它们,并通过清单文件将其合并,然后发送给浏览器。我们通过扩展名告诉 Rails 使用哪个预处理器。三个最常用的扩展名是:Sass 文件的 <code>.scss</code>,CoffeeScript 文件的 <code>.coffee</code>,ERb 文件的 <code>.erb</code>。我们在 <a class="xref-link" href="chapter3.html#layouts-and-embedded-ruby">3.4.3 节</a>介绍过 ERb,<a class="xref-link" href="#syntactically-awesome-stylesheets">5.2.2 节</a>会介绍 Sass。本书不会使用 CoffeeScript,这是一门很小巧的语言,可以编译成浏览器中执行的 JavaScript。</p>
<p>预处理器引擎可以连接在一起使用,因此 <code>foobar.js.coffee</code> 只会使用 CoffeeScript 处理器,而 <code>foobar.js.erb.coffee</code> 会使用 CoffeeScript 和 ERb 处理器(按照扩展名的顺序从右向左处理,所以 CoffeeScript 处理器先执行)。</p>
</section>
<section data-type="sect3" id="efficiency-in-production">
<h3>在生产环境中的效率问题</h3>
<p>Asset Pipeline 带来的好处之一是,能自动优化静态资源文件,在生产环境中使用效果极佳。CSS 和 JavaScript 的传统组织方式是,把不同功能的代码放在不同的文件中,而且排版良好(有很多缩进)。这么做对编程人员很友好,但在生产环境中使用却效率低下——加载大量的文件会明显增加页面的加载时间,这是影响用户体验的最主要因素之一。使用 Asset Pipeline,生产环境中应用的所有样式都集中到一个 CSS 文件中(<code>application.css</code>),所有 JavaScript 代码都集中到一个 JavaScript 文件中(<code>application.js</code>),而且还会简化(minify)这些文件,删除不必要的空格,减小文件大小。这样我们就最好地平衡了两方面的需求——开发方便,线上高效。</p>
</section>
</section>
<section data-type="sect2" id="syntactically-awesome-stylesheets">
<h2><span class="title-label">5.2.2</span> 句法强大的样式表</h2>
<p>Sass 是一种编写 CSS 的语言,从多方面增强了 CSS 的功能。本节我们要介绍两个最主要的功能:嵌套和变量。(还有一个功能是“混入”,<a class="xref-link" href="chapter7.html#rails-environments">7.1.1 节</a>再介绍。)</p>
<p><a class="xref-link" href="#custom-css">5.1.2 节</a>简单说过,Sass 支持一种名为 SCSS 的格式(扩展名为 <code>.scss</code>),这是 CSS 的一个严格超集。SCSS 只为 CSS 添加了一些功能,而没有定义全新的句法。<sup>[<a id="fn-ref-14" href="#fn-14">14</a>]</sup>也就是说,所有有效的 CSS 文件都是有效的 SCSS 文件,这对已经定义了样式的项目来说是件好事。在我们的应用中,因为要使用 Bootstrap,所以从一开始就使用了 SCSS。Rails 的 Asset Pipeline 会自动使用 Sass 预处理器处理扩展名为 <code>.scss</code> 的文件,所以 <code>custom.scss</code> 文件会首先经由 Sass 预处理器处理,然后引入应用的样式表中,再发送给浏览器。</p>
<section data-type="sect3" id="nesting">
<h3>嵌套</h3>
<p>样式表中经常会定义嵌套元素的样式,例如,在<a class="xref-link" href="#listing-universal-css">代码清单 5.7</a> 中,我们定义了 <code>.center</code> 和 <code>.center h1</code> 两个样式:</p>
<div data-type="listing">
<div class="highlight language-css"><pre><code><span class="hll"><span class="nc">.center</span> <span class="p">{</span></span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="p">}</span>
<span class="hll"><span class="nc">.center</span> <span class="nt">h1</span> <span class="p">{</span></span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>使用 Sass 可将其改写成</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="hll"><span class="nc">.center</span> <span class="p">{</span></span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="hll"> <span class="nt">h1</span> <span class="p">{</span></span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>内层的 <code>h1</code> 会自动放入 <code>.center</code> 上下文中。</p>
<p>嵌套还有一种形式,句法稍有不同。在<a class="xref-link" href="#listing-logo-css">代码清单 5.9</a> 中,有如下的代码</p>
<div data-type="listing">
<div class="highlight language-css"><pre><code><span class="hll"><span class="nf">#logo</span> <span class="p">{</span></span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1.7em</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="m">#fff</span><span class="p">;</span>
<span class="nl">text-transform</span><span class="p">:</span> <span class="nb">uppercase</span><span class="p">;</span>
<span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">-1px</span><span class="p">;</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">9px</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="p">}</span>
<span class="hll"><span class="nf">#logo</span><span class="nd">:hover</span> <span class="p">{</span></span>
<span class="nl">color</span><span class="p">:</span> <span class="m">#fff</span><span class="p">;</span>
<span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>其中徽标的 ID <code>#logo</code> 出现了两次,一次单独出现,另一次和 <code>hover</code> 伪类一起出现(鼠标悬停其上时的样式)。如果要嵌套第二组规则,我们要引用父级元素 <code>#logo</code>。在 SCSS 中,这使用 <code>&</code> 符号实现:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="hll"><span class="nn">#logo</span> <span class="p">{</span></span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="nl">margin-right</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="nl">font-size</span><span class="p">:</span> <span class="m">1</span><span class="mi">.7em</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
<span class="nl">text-transform</span><span class="p">:</span> <span class="nb">uppercase</span><span class="p">;</span>
<span class="nl">letter-spacing</span><span class="p">:</span> <span class="m">-1px</span><span class="p">;</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">9px</span><span class="p">;</span>
<span class="nl">font-weight</span><span class="p">:</span> <span class="nb">bold</span><span class="p">;</span>
<span class="hll"> <span class="k">&</span><span class="nd">:hover</span> <span class="p">{</span></span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#fff</span><span class="p">;</span>
<span class="nl">text-decoration</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>把 SCSS 转换成 CSS 时,Sass 会把 <code>&:hover</code> 编译成 <code>#logo:hover</code>。</p>
<p>这两种嵌套方式都可以用在<a class="xref-link" href="#listing-footer-css">代码清单 5.17</a> 中的页脚样式上,将其改写成:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="nt">footer</span> <span class="p">{</span>
<span class="nl">margin-top</span><span class="p">:</span> <span class="m">45px</span><span class="p">;</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">5px</span><span class="p">;</span>
<span class="nl">border-top</span><span class="p">:</span> <span class="m">1px</span> <span class="nb">solid</span> <span class="mh">#eaeaea</span><span class="p">;</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span>
<span class="nt">a</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#555</span><span class="p">;</span>
<span class="k">&</span><span class="nd">:hover</span> <span class="p">{</span>
<span class="nl">color</span><span class="p">:</span> <span class="mh">#222</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="nt">small</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">ul</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">right</span><span class="p">;</span>
<span class="nl">list-style</span><span class="p">:</span> <span class="nb">none</span><span class="p">;</span>
<span class="nt">li</span> <span class="p">{</span>
<span class="nl">float</span><span class="p">:</span> <span class="nb">left</span><span class="p">;</span>
<span class="nl">margin-left</span><span class="p">:</span> <span class="m">15px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>自己动手改写<a class="xref-link" href="#listing-footer-css">代码清单 5.17</a> 是个不错的练习,改完后应该验证一下 CSS 是否还能正常使用。</p>
</section>
<section data-type="sect3" id="variables">
<h3>变量</h3>
<p>Sass 允许自定义变量来避免重复,这样也可以写出更具表现力的代码。例如,<a class="xref-link" href="#listing-typography-css">代码清单 5.8</a> 和<a class="xref-link" href="#listing-footer-css">代码清单 5.17</a> 中重复使用了同一个颜色代码:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="nt">h2</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="hll"><span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span></span>
<span class="p">}</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nt">footer</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="hll"><span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span></span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>上面代码中的 <code>#777</code> 是淡灰色,我们可以把它定义成一个变量:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="nv">$light-gray</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span>
</code></pre></div>
</div>
<p>然后可以这样写 SCSS:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="nv">$light-gray</span><span class="p">:</span> <span class="mh">#777</span><span class="p">;</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nt">h2</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="nv">$light-gray</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nt">footer</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="nv">$light-gray</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>因为像 <code>$light-gray</code> 这样的变量名比 <code>#777</code> 意思更明确,所以把不重复使用的值定义成变量往往也是很有用的。其实,Bootstrap 框架定义了很多颜色变量,<a href="http://getbootstrap.com/customize/#less-variables" class="external-link">Bootstrap 文档中有这些变量的 Less 形式</a>。这个页面中的变量使用 Less 句法,而不是 Sass,不过 <code>bootstrap-sass</code> gem 为我们提供了对应的 Sass 形式。二者之间的对应关系也不难猜测,Less 使用 <code>@</code> 符号定义变量,而 Sass 使用 <code>$</code> 符号。在 Bootstrap 文档中我们看到已经为淡灰色定义了变量:</p>
<div data-type="listing">
<div class="highlight language-text"><pre><code>@gray-light: #777;
</code></pre></div>
</div>
<p>也就是说,在 <code>bootstrap-sass</code> gem 中有一个对应的 SCSS 变量 <code>$gray-light</code>。我们可以用它换掉自己定义的 <code>$light-gray</code> 变量:</p>
<div data-type="listing">
<div class="highlight language-scss"><pre><code><span class="nt">h2</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="nv">$gray-light</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nc">.</span>
<span class="nt">footer</span> <span class="p">{</span>
<span class="err">.</span>
<span class="err"> .</span>
<span class="err"> .</span>
<span class="err"> </span><span class="nl">color</span><span class="p">:</span> <span class="nv">$gray-light</span><span class="p">;</span>
<span class="p">}</span>
</code></pre></div>
</div>
<p>使用 Sass 提供的嵌套和变量功能改写应用的整个样式表后得到的代码如<a class="xref-link" href="#listing-refactored-scss">代码清单 5.20</a> 所示。这段代码使用了 Sass 变量(参照 Bootstrap Less 变量页面)和内置的颜色名称(例如,<code>white</code> 代表 <code>#fff</code>)。请特别留意 <code>footer</code> 标签样式的改进有多明显。</p>
<div id="listing-refactored-scss" data-type="listing">
<h5><span class="title-label">代码清单 5.20</span>:使用嵌套和变量改写后的 SCSS 文件</h5>
<div class="source-file">app/assets/stylesheets/custom.scss</div>
<div class="highlight language-scss"><pre><code><span class="k">@import</span> <span class="s2">"bootstrap-sprockets"</span><span class="p">;</span>
<span class="k">@import</span> <span class="s2">"bootstrap"</span><span class="p">;</span>
<span class="cm">/* mixins, variables, etc. */</span>
<span class="nv">$gray-medium-light</span><span class="p">:</span> <span class="mh">#eaeaea</span><span class="p">;</span>
<span class="cm">/* universal */</span>
<span class="nt">body</span> <span class="p">{</span>
<span class="nl">padding-top</span><span class="p">:</span> <span class="m">60px</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">section</span> <span class="p">{</span>
<span class="nl">overflow</span><span class="p">:</span> <span class="nb">auto</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">textarea</span> <span class="p">{</span>
<span class="nl">resize</span><span class="p">:</span> <span class="n">vertical</span><span class="p">;</span>
<span class="p">}</span>
<span class="nc">.center</span> <span class="p">{</span>
<span class="nl">text-align</span><span class="p">:</span> <span class="nb">center</span><span class="p">;</span>
<span class="nt">h1</span> <span class="p">{</span>
<span class="nl">margin-bottom</span><span class="p">:</span> <span class="m">10px</span><span class="p">;</span>
<span class="p">}</span>
<span class="p">}</span>
<span class="cm">/* typography */</span>
<span class="nt">h1</span><span class="o">,</span> <span class="nt">h2</span><span class="o">,</span> <span class="nt">h3</span><span class="o">,</span> <span class="nt">h4</span><span class="o">,</span> <span class="nt">h5</span><span class="o">,</span> <span class="nt">h6</span> <span class="p">{</span>
<span class="nl">line-height</span><span class="p">:</span> <span class="m">1</span><span class="p">;</span>
<span class="p">}</span>
<span class="nt">h1</span> <span class="p">{</span>