[Rails] 靜態檔案處理(The AssetPipeline)
keywords: rails
, assets
, public
, static
, manifest
Sprockets
透過 Rails 的 pipeline 可以對檔案(例如,SCSS, CoffeeScript, ERB)進行前處理(pre-process),這些是透過 sprockets-rails
這個 gem 完成的。
Sprockets 會打包(編譯、最小化、壓縮)所有的 .js
, .css
檔案成為一支 .js
和 .css
檔。在開發環境下,所有的檔名都會加入一段 SHA256 的編碼,若檔案沒有變更,則使用 cache 的資料,減少伺服器的負擔;一旦檔案有變更,這個編碼就會改變,以此避免 cache 問題。
# 檔名
global-908e25f4bf641868d8683022a5b62f54.css
設定打包方式
一般來說我們會在 ./config/application.rb
中看到
# ./config/application.rb
require "sprockets/railtie"
我們可以在 ./config/environments/production.rb
中設定要打包的方法:
# ./config/environments/production.rb
# 如果有安裝 sass-rails 這個 gem,則這會預設作為 css_compressor 的社竟
config.assets.js_compressor = :uglifier
config.assets.css_compressor = :sass
assets 建立與管理
這些要打包的檔案會放在 ./app/assets/
這個資料夾中;如果是其 他不需要打包的檔案,則可以放在 ./public
這個資料夾中,這些檔案會被當作靜態檔案(config.public_file_server.enabled = true
)
# ./config/environments/production.rb
# 設定能否以靜態檔案的方式存取 public 中的檔案
config.public_file_server.enabled = true
在開發環境下, Rails 預設會將打包好的檔案放到 ./public/assets
中,接著會以靜態檔案的方式存取,因此在開發環境下,是無法直接透過 server 讀取道 app/assets
中的檔案。
Scaffold
在預設的情況下,當我們使用產生器(generate )來建立檔案時,例如,建立 ProjectsController
,Rails 會自動在 ./app/assets
產生 JS 檔和 SCSS 檔,這些檔案將可以透過 require_tree
來載入:
# 如果有安裝 coffee-rails 和 sass-rails 的 gem
./app/assets/javascripts/projects.coffee
./app/assets/stylesheets/projects.scss
你也可以在特定的頁面中指定特定要載入的 stylesheets 和 JS 檔,但要留意,當這樣使用時,不要使用 require_tree
,否則同樣的 assets 可能會多次載入:
<%= javascript_include_tag params[:controller] %> or <%= stylesheet_link_tag
params[:controller] %>
當只在特定的頁面載入 JS 或 CSS 時,要留意不要使用
require_tree
,否則同樣的 assets 可能會多次載入。
避免自動產生 JS 和 CSS 檔
如果不希望在使用產生器時自動產生 JS 和 CSS 檔,可以在 application.rb
中設定:
# ./config/application.rb
#
module Passer
class Application < Rails::Application
config.generators do |g|
# g.stylesheets false
# g.javascripts false
g.assets false
end
end
end
Assets 檔案位置
Pipeline assets 的檔案可以放在下面三個位置 app/assets
, lib/assets
或 vendor/assets
:
app/assets
:通常放應用程式自身的檔案,例如自己寫的圖檔、JS 或 CSS。lib/assets
:通常放的是自己寫的函式庫(library),通常不限於在這個應用程式中使用。vendor/assets
:通常放的第三方的函式庫。
當我們透過 manifest(安裝設定檔)或 helper 要使用 asset 檔案時,Sprockets 會從這三個預設的位置搜尋檔案,任何在 assets/*
裡面的檔案都將被搜尋。舉例來說,當 manifest 檔案要載入下述檔案時:
//= require home
//= require moovinator
//= require slider
//= require phonebox
//= require sub/something
require 也可以直接載入 npm(如果有安裝 webpacker)的套件。 安裝的 gem 會自動被 require,不用重複寫(因此不要安裝用不到的 gem)。
下面這些檔案都會被處理:
# 在 app/assets, lib/assets, vendor/assets 裡面的檔案
app/assets/javascripts/home.js
lib/assets/javascripts/moovinator.js
vendor/assets/javascripts/slider.js
vendor/assets/somepackage/phonebox.js
# assets/* 的檔案都可以被處理到
app/assets/javascripts/sub/something.js
# gems/* 的檔案也會自動被 require 到
可以在下面的檔案中,看到會搜尋的路徑有哪些:
# ./config/initializers/assets.rb
Rails.application.config.assets.paths << Rails.root.join('node_modules')
如果不確定有檔案載入的順序或有哪些資料夾被載入,可以
//= require xxx
一個不存在的檔案讓它噴錯,就可以看到載入的路徑:
使用 index 檔名的檔案
Sprockets 會自動以名為 index
的檔案當作 manifest 檔來加載其他檔案,舉例來說,如果你的 jQuery 函式庫包含許多的模組,儲存在 lib/assets/javascripts/library_name
中,則 lib/assets/javascripts/library_name/index.js
這支檔案會被當作 manifest 檔案,並根據它在加載其他檔案。
透過 Helper 使用 Assets 檔案
透過 pipeline 使用的檔案會由 Sprockets 提供;如果是將檔案放在 public/assets/rails.png
則會是由伺服器直接提供:
javascript_include_tag
stylesheet_link_tag
image_tag
asset_path
asset_data_uri
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => "reload" %>
<%= javascript_include_tag "application", "data-turbolinks-track" => "reload" %>
<!-- ./app/assets/images -->
<%= image_tag "rails.png" %>
<style>
.class { background-image: url(<%= asset_path 'image.png' %>) }
.logo { background: url(<%= asset_data_uri 'logo.png' %>) }
</style>
asset-url("rails.png") <!-- url(/assets/rails.png) -->
asset-path("rails.png") <!-- "/assets/rails.png" -->
安裝設定檔(manifest)
Sprockets 使用安裝設定檔(manifest files)來決定要載入哪些 assets。
JavaScript
./app/assets/javascripts/application.js
在 Sprockets 的設定檔中,會使用 //=
作為開頭,並且使用一些指令:
require
: 告訴 Sprockets 要載入哪支檔案require_tree
: 以疊代的方式(recursive)載入特定資料夾內的所有檔案require_directory
: 載入特定資料夾內的檔案,但不會在以疊代的方式進入該資料夾中的其他資料夾。
這些指令會從上往下開始執行,但是如果是透過 require_tree
載入的檔案則不會依循任何順序;另外,這些指令不會載到同一之檔案兩次。
CSS
./app/assets/stylesheets/application.css
@import 'bootstrap'; // 載入特定檔案
@import '*';
@import '**/*';
For more information: sass-rails
在開發模式(development)或沒有沒有啟用 asset pipeline 的情況下,這些檔案會透過 coffee-script 和 rails-sass 的 gems 回傳到瀏覽器;當有啟用 asset pipeline 時,這些檔案會先被前處理(preprocessed)然後放到 public/asset
的資料夾中。
如果一支檔案要經過多次不同的前處理,可以使用多個副檔名,會以副檔名 ++由右至左++ 的順序加以編譯,例如,projects.scss.erb
會先經過 ERB
再來是 SCSS
最後以 CSS
檔給瀏覽器;如果是 projects.coffee.erb
會先經過 ERB
再來是 CoffeeScript
最後以 JS
檔給瀏覽器。
開發模式(Development Mode)
在開發模式下,所有的 assets 會根據安裝設定檔(manifest)中所定義的順序,以獨立的檔案呈現:
// app/assets/javascripts/application.js
//= require core
//= require projects
//= require tickets
這樣的檔案在 HTML 獨立呈現:
<script src="/assets/core.js?body=1"></script>
<script src="/assets/projects.js?body=1"></script>
<script src="/assets/tickets.js?body=1"></script>
發佈模式(Production Mode)
在發佈模式下,Rails 會假設 assets 已經被編譯好,並且以靜態檔案的方式由伺服器提供。
<%= javascript_include_tag "application" %>
<%= stylesheet_link_tag "application" %>
在 HTML 中:
<script src="/assets/application-908e25f4bf641868d8683022a5b62f54.js"></script>
<link
href="/assets/application-4dd5b109ee3439da54f5bdfd78a80473.css"
media="screen"
rel="stylesheet"
/>
參考資料
- The Asset Pipeline @ RailsGuides
- Asset Pipeline @ Ruby on Rails 實戰聖經