企业🤖AI Agent构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
# 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/)。