> For the complete documentation index, see [llms.txt](https://kelby.gitbook.io/rails-beginner-s-guide/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://kelby.gitbook.io/rails-beginner-s-guide/others/rails_assets_precompile.md).

# Rails assets precompile

## 概述

**Assets Pipeline 主要功能 & 特性**

合并、压缩、解析 css, js 文件。

合并：将多个 js 或 css 文件压缩打包成单一文件，減少 http request 的大小与数量。 \
&#x20;压缩：可以去除空格、注释等。 \
&#x20;解析：你可直接使用 SCSS 和 CoffeeScript, 它们会被解析成 css 和 js.

**主要通过 Sprockets 完成**

Assets Pipeline 的功能主要由重要的组件 Sprockets 完成。\
Sprockets 用来从你的 assets 路径打包压缩你所有的 assets 后包裝成一个文件，然后放到你目的地路径(public/assets).

通过它可以对 css, js 等静态资源进行编译、压缩。

命令行取消，不使用它：

```
rails new appname --skip-sprockets
```

这会使得原来的：

```ruby
require 'rails/all'
```

变成

```ruby
require "rails"
# Pick the frameworks you want:
require "active_model/railtie"
require "active_job/railtie"
require "active_record/railtie"
require "action_controller/railtie"
require "action_mailer/railtie"
require "action_view/railtie"
# require "sprockets/railtie" # <- 重点是这行
require "rails/test_unit/railtie"
```

sprockets 已经被取消掉。

并且，原来的：

```ruby
gem 'sass-rails'
gem 'uglifier'
gem 'coffee-rails'
```

变成：

```ruby
gem 'coffee-rails', '~> 4.1.0'
```

**默认的 3 个存放静态资源的地方**

默认 3 路径： \
&#x20;app/assets 放置我们自己所写的 js、css 或 images, 并且它们和业务关联比较紧密。实际上，这种形式用得最多。 \
&#x20;lib/assets 放置我们抽取出来的 assets, 一般来说这样的代码比较通用。实际上，这种形式用得最少。 \
&#x20;vendor/assets 是放一些我们从第三方引进的 assets，例如一些 jQuery 插件。

开发环境，你可以把你的 rails app 跑起來后，从 HTML 源代码里查看引入了那些样式或脚本，进而修改，很方便。

这 3 个目录，看似是分开的。但生产环境下，它们后会被编译、打包在一起，也就是说，它们其实是连通的。

&#x20;**.erb 使用 Asset Helper 方法**

因为 Sprockets-rails 已经引入了以下两个模块

```ruby
include ActionView::Helpers::AssetUrlHelper
include ActionView::Helpers::AssetTagHelper
```

所以，在 \*css.erb 文件里，你都可以使用以下方法：

```
stylesheet_link_tag
javascript_include_tag
```

```
image_tag
```

```
asset_path
asset_data_uri
```

同样的，使用 js.erb 后缀之后可以用 `asset_path` 等方法。

使用举例：

```ruby
audio_path("horse.wav")  # => /audios/horse.wav
audio_tag("sound")       # => <audio src="/audios/sound" />

font_path("font.ttf")    # => /fonts/font.ttf

image_path("edit.png")   # => "/images/edit.png"
image_tag("icon.png")    # => <img src="/images/icon.png" alt="Icon" />

video_path("hd.avi")     # => /videos/hd.avi
video_tag("trailer.ogg") # => <video src="/videos/trailer.ogg" />
```

**sass-rails 提供的几个 Asset Helper 方法**

由 sass-rails 提供几个以 `-url` 和 `-path` 结尾的 Asset Helpers 方法及结果：

```ruby
image-url("rails.png") # => url(/assets/rails.png)
image-path("rails.png") # => "/assets/rails.png".

asset-url("rails.png") # => url(/assets/rails.png)
asset-path("rails.png") # => "/assets/rails.png"

asset-data-url("rails.png") # => url(data:image/png;base64,iVBORw0K...)
```

注意，你可以同时使用 sass-rails 提供的 `-url` 及原生的 `url` 方法；使用 `-url` 时参数带引号，使用 `url` 时参数不带引号。

**Sprockets 提供 require 等指令**

```ruby
require # 引入某个文件。如果有重复引入，它会自动忽略，只引入一次。
require_directory # 引入某个目录下的文件，不会递归其子目录。
require_tree # 引入某个目录及递归其子目录下的所有文件，默认指的是当前目录。
require_self # 引入在自己文件里写的样式或脚本。

link

depend_on
depend_on_asset

stub
```

上述几个方法，由 Sprockets-rails 提供。

但上述几个方法，不要在 SASS/SCSS 文件里使用，而是使用 sass-rails 提供的 @import 方法。

**根据后缀名，决定编译顺序**

注意后缀名的顺序：

```ruby
app/assets/javascripts/projects.js.erb.coffee
```

从右到左一个个解析的。

**生产环境，需要注意的点**

1\) Precompiling Assets

```ruby
RAILS_ENV=production bin/rake assets:precompile
```

```ruby
load 'deploy/assets'
```

另，上述会生成、使用 shared/assets 目录。

预编译的文件，默认是：

```ruby
[ Proc.new { |filename, path| path =~ /app\/assets/ && !%w(.js .css).include?(File.extname(filename)) },
/application.(css|js)$/ ]
```

新增：

```ruby
Rails.application.config.assets.precompile += ['admin.js', 'admin.css', 'swfObject.js']
```

另，只使用 .js 或 .css 后缀即可。不必减少，也不必增多。

2\) Far-future Expires Header

配置 Web 服务器，更好的对待静态资源：

```ruby
location ~ ^/assets/ {
  expires 1y;
  add_header Cache-Control public;

  add_header ETag "";
  break;
}
```

**CDN 及异地静态资源**

把静态资源放到其它服务器：

```ruby
config.action_controller.asset_host = 'mycdnsubdomain.fictional-cdn.com'

config.action_controller.asset_host = ENV['CDN_HOST']
```

注意其影响：

```ruby
<%= asset_path('smile.png') %>

http://mycdnsubdomain.fictional-cdn.com/assets/smile.png

# 仅作用于指定的静态资源
<%= asset_path 'image.png', host: 'mycdnsubdomain.fictional-cdn.com' %>
```

另，如果生产环境有图片，而开发环境没有，而自己又不想导图片。可以尝试用这种方式，在开发环境也显示图片。

**两种引入方式**

1）独立引入

```ruby
config.assets.precompile += %w( site.css )
```

```ruby
stylesheet_link_tag "site"
```

2）一起引入

```ruby
# application.css

// = require_self
// = require 'site'
```

当然了，也可以直接把文件放到 public/assets/ 目录下，之后这些文件不受 Assets precompile 影响。

## sprockets-rails

**Railtie**

以 Railtie 的形式引入，继承于 Rails::Railtie （所以，Rails::Railtie 提供的方法，它是可以用的）

主要任务包括，但不限于：

* 设置 config.assets.x (这里的 x 表示众多的配置项)
* 其它众多看不见的功能

上述配置，包括但不限于：

```ruby
config.assets.precompile
config.assets.paths
config.assets.version
config.assets.prefix
config.assets.manifest
config.assets.digest
config.assets.debug
config.assets.compile
config.assets.configure
```

**打开了 Rails 下的 Engine 和 Application**

Engine 主要是加入路径：

```ruby
paths["vendor/assets"]
paths["lib/assets"]
paths["app"]
paths["app/assets"]
```

Application 主要提供以下几个 config 项：

```
:assets
:assets_manifest
:precompiled_assets
```

**如何引入**

它是 gem，所以：

```ruby
gem 'sprockets-rails', :require => 'sprockets/railtie'
```

**Rake task**

由 Task 文件完成。

```ruby
rake assets:precompile
rake assets:clean
rake assets:clobber
```

命令

```
RAILS_ENV=production bin/rake assets:precompile
```

将 app/assets 存放普通的前端资源复制到 public/assets 目录。

**Helper**

包括但不限于：

```ruby
include ActionView::Helpers::AssetUrlHelper
include ActionView::Helpers::AssetTagHelper
include Sprockets::Rails::Utils

VIEW_ACCESSORS = [:assets_environment, :assets_manifest,
                  :assets_precompile, :precompiled_assets,
                  :assets_prefix, :digest_assets, :debug_assets]
```

## 一些配置项

**rails scaffold 或 rails g controller 时不再生成默认的 js, css 文件**

```ruby
config.generators do |g|
  g.assets false
end
```

**显性配置 css, js 压缩器**

```ruby
config.assets.css_compressor = :yui
config.assets.js_compressor = :uglifier
```

```ruby
config.assets.css_compressor = :yui
```

```ruby
config.assets.css_compressor = :sass
```

JavaScript Compression

:closure, :uglifier 和 :yui

分别对应

closure-compiler, uglifier 和 yui-compressor gem.

```ruby
config.assets.js_compressor = :uglifier
```

**如何新增静态资源所在路径：**

如何新增：

```ruby
config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
```

**Runtime Error Checking**

```ruby
config.assets.raise_runtime_errors = false
```

没必要关掉吧。

**Turning Debugging Off**

```ruby
config.assets.debug = false
```

没必要关掉吧。

**Live Compilation**

生产环境，实时编译。

开发、测试等环境，由 coffee-script and sass 实时编译。

反正我是不推荐：

```ruby
config.assets.compile = true
```

生产环境 app/assets 下的文件已经预编译好，放到了 public/assets 下，直接使用即可。

**CDNs and the Cache-Control Header**

```ruby
config.static_cache_control = "public, max-age=31536000"
```

**Changing the assets Path**

```ruby
config.assets.prefix = "/some_other_path"
```

这也没必要吧。

**Assets Cache Store**

```ruby
config.assets.cache_store = :memory_store
```

```ruby
config.assets.cache_store = :memory_store, { size: 32.megabytes }
```

```ruby
config.assets.configure do |env|
  env.cache = ActiveSupport::Cache.lookup_store(:null_store)
end
```

一般情况下，也不会动这里哈\~

**ssets Pipeline 怎么关掉?**

你可以在 confing/application.rb 中把他关掉：

```ruby
config.assets.enabled = false
```

## 其它

**查看所有 assets 目录**

当使用第三方 gem 引入 assets 资源的时候，使用它可以让我们看到加入了哪些目录。

```
Rails.application.config.assets
```

> Note：它只管加载目录，想引入目录下面资源文件的话，还得自己或 gem 添加。

**Deploy 的小技巧**

1）本地编译 - 有好也有坏。反正我是不推荐。

2）如果 assets 沒有更新，就不要跑 precompile.

3\) 有更新就在本地 precompile，然后再上传。

**慎用 require\_tree**

```ruby
# application.css
//= require_tree

# 相当于
//= require_tree .
```

意味着加载当前目录下的文件，并且递归加载其子目录下的文件。虽然省事了，这并不是好的实践。因为我们的文件、目录是会逐渐增多的。这就容易出现下列结果：

* 加载过多，导致性能慢
* 加载过多，导致混淆（原本没有必要加载的文件也加载了）

**css & scss & sass**

后缀名 .scss 意义 Sassy CSS \
&#x20;(还有一种后缀名为 .sass 的，区别是它严格缩进)

根据原有文件后缀名，选择不同的使用方式：

```ruby
# 1 application.css
*= require font-awesome
```

```ruby
# 2 application.css.scss
@import "font-awesome";
```

```ruby
# 3 application.css.sass
@import font-awesome
```

**检测 assets 是否挂了（存在）的命令**

图片"rails.png"存在

```ruby
Rails.application.assets.find_asset 'rails.png'
=> #<Sprockets::StaticAsset:0x3fed3aa004f8 
pathname="/Users/kelby/appname/app/assets/images/rails.png",
mtime=2015-04-13 20:10:42 +0800, digest="3526faae1dacebb591431f0054e8f33e">
```

图片"not-image.png"不存在

```ruby
Rails.application.assets.find_asset 'not-image.png'
 => nil
```

**没有 JavaScript 运行时**

```ruby
group :production do
  gem 'therubyracer'
end
```

但不推荐这么做，还是安装的好。

**X-Sendfile Headers**

Web 服务器支持的话，不妨一试：

```ruby
# config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache
# config.action_dispatch.x_sendfile_header = 'X-Accel-Redirect' # for NGINX
```

当你使用 `send_file` 等方法给客户端发送文件时，如果你开启了此特性，并且硬盘里有恰好又有此文件。那么，你的 Web服务器会忽略应用的响应数据，直接从硬盘读取文件并返回，使得速度更快。

**不好的实践**

以 controller 为单位加载资源

```ruby
<%= javascript_include_tag params[:controller] %>
<%= stylesheet_link_tag params[:controller] %>
```

在我看来，上述方式并不好。

**CSS 里引入图片、字体需注意**

使用 css，特别是第三方样式的时候，注意查看是否引入了图片、字体，它们使用 url 引用，以及路径是否准确。

## Debug

```ruby
Rails.application.config.assets
```

查看资源情况。

它属于：

```ruby
Rails.application.config.assets.class
=> Sprockets::Railtie::OrderedOptions
```

我们接触比较多的是其中的 `paths` 和 `precompile` 对应的数据。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://kelby.gitbook.io/rails-beginner-s-guide/others/rails_assets_precompile.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
