5.2 Sass 和 Asset Pipeline
最后更新于:2022-04-01 22:28:46
# 5.2 Sass 和 Asset Pipeline
在新版 Rails 中,最大的变化是增加了 Asset Pipeline,这个功能明显提升了 CSS、JavaScript 和图片等静态资源文件的生成效率,而且还能降低管理成本。本节我们先大致介绍一下 Asset Pipeline,然后说明如何使用强大的 CSS 编写工具 Sass。
## 5.2.1 Asset Pipeline
Asset Pipeline 对 Rails 做了很多改动,但对 Rails 开发者来说只有三个特性需要了解:静态资源文件夹,清单文件,以及预处理器引擎。[[11](#fn-11)]下面一一介绍。
### 静态资源文件夹
在 Rails 3.0 及之前的版本,静态文件放在 `public/` 文件夹中,并且按照下面的方式组织:
* `public/stylesheets`
* `public/javascripts`
* `public/images`
这些文件夹中的文件通过请求 [http://example.com/stylesheets](http://example.com/stylesheets) 等地址直接发送给浏览器。(Rails 3.0 之后的版本也会这么做。)
在最新版 Rails 中,静态文件可以放在三个标准文件夹中,而且各有各的用途:
* `app/assets`:当前应用的资源文件;
* `lib/assets`:开发团队自己开发的代码库使用的资源文件;
* `vendor/assets`:第三方代码库使用的资源文件;
你可能猜到了,这几个文件夹中都有针对不同资源类型的子文件夹,例如:
```
$ ls app/assets/
http://railstutorial-china.org/book/images/ javascripts/ stylesheets/
```
现在我们知道 [5.1.2 节](#bootstrap-and-custom-css)中 `custom.css.scss` 存放位置的用意了:因为 `custom.css.scss` 只在应用中使用,所以把它存放在 `app/assets/stylesheets` 文件夹中。
### 清单文件
把资源文件放在适当的文件夹中之后,要通过清单文件告诉 Rails 怎么把它们合并成一个文件(通过 [Sprockets](https://github.com/sstephenson/sprockets) gem 实现,而且只能合并 CSS 和 JavaScript 文件,不会合并图片)。举个例子,我们来看一下应用默认的样式表清单文件,如[代码清单 5.14](#listing-app-css-manifest) 所示。
##### 代码清单 5.14:应用的样式表清单文件
app/assets/stylesheets/application.css
```
/*
* This is a manifest file that'll be compiled into application.css, which
* will include all the files listed below.
*
* Any CSS and SCSS file within this directory, lib/assets/stylesheets,
* vendor/assets/stylesheets, or vendor/assets/stylesheets of plugins, if any,
* can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear
* at the bottom of the compiled file so the styles you add here take
* precedence over styles defined in any styles defined in the other CSS/SCSS
* files in this directory. It is generally better to create a new file per
* style scope.
*
*= require_tree .
*= require_self
*/
```
这里关键的代码是几行 CSS 注释,Sprockets 通过这些注释引入相应的文件:
```
/*
.
.
.
*= require_tree .
*= require_self
*/
```
其中
```
*= require_tree .
```
会把 `app/assets/stylesheets` 文件夹中的所有 CSS 文件(包含子文件夹中的文件)都引入应用的 CSS 。
下面这行:
```
*= require_self
```
会把 `application.css` 这个文件中的 CSS 也加载进来。
Rails 提供的默认清单文件可以满足我们的需求,所以本书不会对其做任何修改。Rails 指南中有一篇专门[介绍 Asset Pipeline 的文章](http://guides.rubyonrails.org/asset_pipeline.html),说得更详细。
### 预处理器引擎
准备好资源文件后,Rails 会使用一些预处理器引擎来处理它们,并通过清单文件将其合并,然后发送给浏览器。我们通过扩展名告诉 Rails 使用哪个预处理器。三个最常用的扩展名是:Sass 文件的 `.scss`,CoffeeScript 文件的 `.coffee`,ERb 文件的 `.erb`。我们在 [3.4.3 节](chapter3.html#layouts-and-embedded-ruby)介绍过 ERb,[5.2.2 节](#syntactically-awesome-stylesheets)会介绍 Sass。本书不会使用 CoffeeScript,这是一个很小巧的语言,可以编译成 JavaScript。(RailsCast 中[关于 CoffeeScript 的视频](http://railscasts.com/episodes/267-coffeescript-basics)是很好的入门教程。)
预处理器引擎可以连接在一起使用,因此
```
foobar.js.coffee
```
只会使用 CoffeeScript 处理器,而
```
foobar.js.erb.coffee
```
会使用 CoffeeScript 和 ERb 处理器(按照扩展名的顺序从右向左处理,所以 CoffeeScript 处理器先执行)。
### 在生产环境中的效率问题
Asset Pipeline 带来的好处之一是,能自动优化资源文件,在生产环境中使用效果极佳。CSS 和 JavaScript 的传统组织方式是,把不同功能的代码放在不同的文件中,而且排版良好(有很多缩进)。这么做对编程人员很友好,但在生产环境中使用却效率低下——加载大量的文件会明显增加页面的加载时间,这是影响用户体验最主要的因素之一。使用 Asset Pipeline,生产环境中应用所有的样式都会集中到一个 CSS 文件中(`application.css`),所有 JavaScript 代码都会集中到一个 JavaScript 文件中(`application.js`),而且还会压缩这些文件,删除不必要的空格,减小文件大小。这样我们就最好地平衡了两方面的需求,开发方便,线上高效。
## 5.2.2 句法强大的样式表
Sass 是一种编写 CSS 的语言,从多方面增强了 CSS 的功能。本节我们要介绍两个最主要的功能:嵌套和变量。(还有一个功能是“混入”,[7.1.1 节](chapter7.html#debug-and-rails-environments)再介绍。)
[5.1.2 节](#bootstrap-and-custom-css)说过,Sass 支持一种名为 SCSS 的格式(扩展名为 `.scss`),这是 CSS 句法的一个扩展集。SCSS 只为 CSS 添加了一些功能,而没有定义全新的句法。[[12](#fn-12)]也就是说,所有有效的 CSS 文件都是有效的 SCSS 文件,这对已经定义了样式的项目来说是件好事。在我们的应用中,因为要使用 Bootstrap,所以从一开始就使用了 SCSS。Rails 的 Asset Pipeline 会自动使用 Sass 预处理器处理扩展名为 `.scss` 的文件,所以 `custom.css.scss` 文件会首先经由 Sass 预处理器处理,然后引入应用的样式表中,再发送给浏览器。
### 嵌套
样式表中经常会定义嵌套元素的样式,例如,在[代码清单 5.5](#listing-universal-css) 中,定义了 `.center` 和 `.center h1` 两个样式:
```
.center {
text-align: center;
}
.center h1 {
margin-bottom: 10px;
}
```
使用 Sass 可将其改写成
```
.center {
text-align: center;
h1 { margin-bottom: 10px;
}
}
```
内层的 `h1` 会自动放入 `.center` 上下文中。
嵌套还有一种形式,句法稍有不同。在[代码清单 5.7](#listing-logo-css) 中,有如下的代码
```
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
}
#logo:hover {
color: #fff;
text-decoration: none;
}
```
其中 LOGO 的 ID `#logo` 出现了两次,一次单独出现,另一次和 `hover` 伪类一起出现(鼠标悬停其上时的样式)。如果要嵌套第二组规则,我们要引用父级元素 `#logo`,在 SCSS 中,使用 `&` 符号实现:
```
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: #fff;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
&:hover { color: #fff;
text-decoration: none;
}
}
```
把 SCSS 转换成 CSS 时,Sass 会把 `&:hover` 编译成 `#logo:hover`。
这两种嵌套方式都可以用在[代码清单 5.13](#listing-footer-css) 中的底部样式上,改写成:
```
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid #eaeaea;
color: #777;
a {
color: #555;
&:hover {
color: #222;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 15px;
}
}
}
```
自己动手改写[代码清单 5.13](#listing-footer-css) 是个不错的练习,改完后应该验证一下 CSS 是否还能正常使用。
### 变量
Sass 允许我们自定义变量来避免重复,这样也可以写出更具表现力的代码。例如,[代码清单 5.6](#listing-typography-css) 和[代码清单 5.13](#listing-footer-css) 中都重复使用了同一个颜色代码:
```
h2 {
.
.
.
color: #777; }
.
.
.
footer {
.
.
.
color: #777; }
```
上面代码中的 `#777` 是淡灰色,我们可以把它定义成一个变量:
```
$light-gray: #777;
```
然后我们可以这样写 SCSS:
```
$light-gray: #777;
.
.
.
h2 {
.
.
.
color: $light-gray;
}
.
.
.
footer {
.
.
.
color: $light-gray;
}
```
因为像 `$light-gray` 这样的变量名比 `#777` 意思更明确,所以把不重复使用的值定义成变量往往也是很有用的。其实,Bootstrap 框架定义了很多颜色变量,[Bootstrap 文档中有这些变量的 Less 形式](http://getbootstrap.com/customize/#less-variables)。这个页面中的变量使用 Less 句法,而不是 Sass,不过 `bootstrap-sass` gem 为我们提供了对应的 Sass 形式。二者之间的对应关系也不难猜测,Less 使用 `@` 符号定义变量,而 Sass 使用 `$` 符号。在 Bootstrap 文档中我们看到已经为淡灰色定义了变量:
```
@gray-light: #777;
```
也就是说,在 `bootstrap-sass` gem 中有一个对应的 SCSS 变量 `$gray-light`。我们可以用它换掉自己定义的 `$light-gray` 变量:
```
h2 {
.
.
.
color: $gray-light;
}
.
.
.
footer {
.
.
.
color: $gray-light;
}
```
使用 Sass 提供的嵌套和变量功能改写应用的整个样式表后得到的代码如[代码清单 5.15](#listing-refactored-scss) 所示。这段代码使用了 Sass 变量(参照 Bootstrap Less 变量页面)和内置的颜色名称(例如,`white` 代表 `#fff`)。特别注意一下 `footer` 标签样式的明显改进。
##### 代码清单 5.15:使用嵌套和变量改写后的 SCSS 文件
app/assets/stylesheets/custom.css.scss
```
@import "bootstrap-sprockets";
@import "bootstrap";
/* mixins, variables, etc. */
$gray-medium-light: #eaeaea;
/* universal */
body {
padding-top: 60px;
}
section {
overflow: auto;
}
textarea {
resize: vertical;
}
.center {
text-align: center;
h1 {
margin-bottom: 10px;
}
}
/* typography */
h1, h2, h3, h4, h5, h6 {
line-height: 1;
}
h1 {
font-size: 3em;
letter-spacing: -2px;
margin-bottom: 30px;
text-align: center;
}
h2 {
font-size: 1.2em;
letter-spacing: -1px;
margin-bottom: 30px;
text-align: center;
font-weight: normal;
color: $gray-light;
}
p {
font-size: 1.1em;
line-height: 1.7em;
}
/* header */
#logo {
float: left;
margin-right: 10px;
font-size: 1.7em;
color: white;
text-transform: uppercase;
letter-spacing: -1px;
padding-top: 9px;
font-weight: bold;
&:hover {
color: white;
text-decoration: none;
}
}
/* footer */
footer {
margin-top: 45px;
padding-top: 5px;
border-top: 1px solid $gray-medium-light;
color: $gray-light;
a {
color: $gray;
&:hover {
color: $gray-darker;
}
}
small {
float: left;
}
ul {
float: right;
list-style: none;
li {
float: left;
margin-left: 15px;
}
}
}
```
Sass 提供了很多简化样式表的功能,[代码清单 5.15](#listing-refactored-scss) 只用到了最主要的功能,这是个好的开始。更多功能请查看 [Sass 的网站](http://sass-lang.com/)。
';