Rails的靜態資源管理(二)—— 如何使用 Asset Pipeline


官方文檔:http://guides.ruby-china.org/asset_pipeline.html

http://guides.rubyonrails.org/asset_pipeline.html

 

在 Rails 的早期版本中,所有靜態資源文件都放在 public 文件夾的子文件夾中,例如 imagesjavascripts 和 stylesheets 子文件夾。當 Rails 開始使用 Asset Pipeline 后,就推薦把靜態資源文件放在 app/assets 文件夾中,並使用 Sprockets 中間件處理這些文件。

當然,靜態資源文件仍然可以放在 public 文件夾及其子文件夾中。只要把 config.public_file_server.enabled 選項設置為 true,Rails 應用或 Web 服務器就會處理 public 文件夾及其子文件夾中的所有靜態資源文件。但對於需要預處理的文件,都應該放在 app/assets 文件夾中。

在生產環境中,Rails 默認會對 public/assets 文件夾中的文件進行預處理,經過預處理的靜態資源文件將由 Web 服務器直接處理,app/assets 文件夾中的文件不會直接交由 Web 服務器處理。

 

1 針對控制器的靜態資源文件

使用生成器生成控制器時,Rails 會同時為控制器生成以下文件:

  • JavaScript 文件,如果 Gemfile 中包含了 coffee-rails gem,那么生成的是 CoffeeScript 文件
  • CSS 文件,如果 Gemfile 中包含了 sass-rails gem,那么生成的是 SCSS 文件

在生成腳手架時,Rails 還會生成

  •  scaffolds.css 文件,如果 Gemfile 中包含了 sass-rails gem,那么生成的是 scaffolds.scss 文件

針對控制器的 JavaScript 文件和 CSS 文件也可以只在相應的控制器中引入:

<%= javascript_include_tag params[:controller] %> 或 <%= stylesheet_link_tag params[:controller] %>

此時,千萬不要使用 require_tree 指令,否則就會重復包含這些靜態資源文件。

在進行靜態資源文件預編譯時,請確保針對控制器的靜態文件是在按頁加載時進行預編譯的。默認情況下,Rails 不會自動對 .coffee 和 .scss 文件進行預編譯。

要使用 CoffeeScript,就必須安裝支持 ExecJS 的運行時。macOS 和 Windows 已經預裝了此類運行時。

通過在 config/application.rb 配置文件中添加下述代碼,可以禁止生成針對控制器的靜態資源文件:

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

 

 2 靜態資源文件的組織方式

應用的 Asset Pipeline 靜態資源文件可以儲存在三個位置:app/assetslib/assets 和 vendor/assets

  • app/assets 用於儲存應用自有的靜態資源文件,例如自定義圖像、JavaScript 文件和 CSS 文件。
  • lib/assets 用於儲存自有代碼庫的靜態資源文件,這些代碼庫或者不適合放在當前應用中,或者需要在多個應用間共享。
  • vendor/assets 用於儲存第三方代碼庫的靜態資源文件,例如 JavaScript 插件和 CSS 框架。如果第三方代碼庫中引用了同樣由 Asset Pipeline 處理的靜態資源文件(圖像、CSS 文件等),就必須使用 asset_path 這樣的輔助方法重新編寫相關代碼。
從 Rails 3 升級而來的用戶需要注意,通過設置應用的清單文件, 我們可以包含  lib/assets 和  vendor/assets 文件夾中的靜態資源文件,但是這兩個文件夾不再是預編譯數組的一部分。

 

2.1 搜索路徑

當清單文件或輔助方法引用了靜態資源文件時,Sprockets 會在靜態資源文件的三個默認存儲位置中進行查找。

這三個默認存儲位置分別是 app/assets 文件夾的 imagesjavascripts 和 stylesheets 子文件夾,實際上這三個文件夾並沒有什么特別之處,所有的 app/assets/* 文件夾及其子文件夾都會被搜索。

例如,下列文件:

app/assets/javascripts/home.js
lib/assets/javascripts/moovinator.js
vendor/assets/javascripts/slider.js
vendor/assets/somepackage/phonebox.js

在清單文件中可以像下面這樣進行引用:

 

//= require home
//= require moovinator
//= require slider
//= require phonebox

這些文件夾的子文件夾中的靜態資源文件:

app/assets/javascripts/sub/something.js

可以像下面這樣進行引用:

 

//= require sub/something

 

通過在 Rails 控制台中檢查 Rails.application.config.assets.paths 變量,我們可以查看搜索路徑。

除了標准的 app/assets/* 路徑,還可以在 config/application.rb 配置文件中為 Asset Pipeline 添加其他路徑。例如:

config.assets.paths << Rails.root.join("lib", "videoplayer", "flash")
Rails 會按照路徑在搜索路徑中出現的先后順序,對路徑進行遍歷。因此,在默認情況下, app/assets  中的文件優先級最高,將會遮蓋  lib  和  vendor  文件夾中的同名文件。

千萬注意,在清單文件之外引用的靜態資源文件必須添加到預編譯數組中,否則無法在生產環境中使用。

 

2.2 使用索引文件

對於 Sprockets,名為 index(帶有相關擴展名)的文件具有特殊用途。

例如,假設應用中使用的 jQuery 庫及多個模塊儲存在 lib/assets/javascripts/library_name 文件夾中,那么 lib/assets/javascripts/library_name/index.js 文件將作為這個庫的清單文件。在這個庫的清單文件中,應該按順序列出所有需要加載的文件,或者干脆使用require_tree 指令。

在應用的清單文件中,可以把這個庫作為一個整體加載:

//= require library_name

這樣,相關代碼總是作為整體在應用中使用,降低了維護成本,並使代碼保持簡潔。

 

Sprockets 沒有為訪問靜態資源文件添加任何新方法,而是繼續使用我們熟悉的 javascript_include_tag 和 stylesheet_link_tag 輔助方法:

<%= stylesheet_link_tag "application", media: "all" %>
<%= javascript_include_tag "application" %>

如果使用了 Rails 默認包含的 turbolinks gem,並使用了 data-turbolinks-track 選項,Turbolinks 就會檢查靜態資源文件是否有更新,如果有更新就加載到頁面中:

<%= 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" %>

如果在應用中啟用了 Asset Pipeline,並且未在當前環境中禁用 Asset Pipeline,那么這個圖像文件將由 Sprockets 處理。如果圖像的位置是 public/assets/rails.png,那么將由 Web 服務器處理。

如果文件請求包含 SHA256 哈希值,例如 public/assets/rails-f90d8a84c707a8dc923fca1ca1895ae8ed0a09237f6992015fef1e11be77c023.png,處理的方式也是一樣的。

Sprockets 還會檢查 config.assets.paths 中指定的路徑,其中包括 Rails 應用的標准路徑和 Rails 引擎添加的路徑。

也可以把圖像放在子文件夾中,訪問時只需加上子文件夾的名稱即可:

<%= image_tag "icons/rails.png" %>

如果對靜態資源文件進行了預編譯,那么在頁面中鏈接到並不存在的靜態資源文件或空字符串將導致該頁面拋出異常。因此,在使用 image_tag 等輔助方法處理用戶提供的數據時一定要小心。

 

3.1 CSS 和 ERB

Asset Pipeline 會自動計算 ERB 的值。也就是說,只要給 CSS 文件添加 .erb 擴展名(例如 application.css.erb),就可以在 CSS 規則中使用 asset_path 等輔助方法。

.class { background-image: url(<%= asset_path 'image.png' %>) }

上述代碼中的 asset_path 輔助方法會返回指向圖像真實路徑的鏈接。圖像必須位於靜態文件加載路徑中,例如 app/assets/images/image.png,以便在這里引用。如果在 public/assets 文件夾中已經存在此圖像的帶指紋的版本,那么將引用這個帶指紋的版本。

要想使用 data URI(用於把圖像數據直接嵌入 CSS 文件中),可以使用 asset_data_uri 輔助方法:

#logo { background: url(<%= asset_data_uri 'logo.png' %>) }

asset_data_uri 輔助方法會把正確格式化后的 data URI 插入 CSS 源代碼中。

注意,關閉標簽不能使用 -%> 形式。

 

3.2 CSS 和 Sass

在使用 Asset Pipeline 時,靜態資源文件的路徑都必須重寫,為此 sass-rails gem 提供了 -url 和 -path 系列輔助方法(在 Sass 中使用連字符,在 Ruby 中使用下划線),用於處理圖像、字體、視頻、音頻、JavaScript 和 CSS 等類型的靜態資源文件。

  • 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"

 

3.3 JavaScript/CoffeeScript 和 ERB

只要給 JavaScript 文件添加 .erb 擴展名(例如 application.js.erb),就可以在 JavaScript 源代碼中使用 asset_path 輔助方法:

$('#logo').attr({ src: "<%= asset_path('logo.png') %>" });

上述代碼中的 asset_path 輔助方法會返回指向圖像真實路徑的鏈接。

同樣,只要給 CoffeeScript 文件添加 .erb 擴展名(例如 application.coffee.erb),就可以在 CoffeeScript 源代碼中使用 asset_path 輔助方法:

$('#logo').attr src: "<%= asset_path('logo.png') %>"

 

4 清單文件和指令

Sprockets 使用清單文件來確定需要包含和處理哪些靜態資源文件。這些清單文件中的指令會告訴 Sprockets,要想創建 CSS 或 JavaScript 文件需要加載哪些文件。通過這些指令,可以讓 Sprockets 加載指定文件,對這些文件進行必要的處理,然后把它們連接為單個文件,最后進行壓縮(壓縮方式取決於 Rails.application.config.assets.js_compressor 選項的值)。這樣在頁面中只需處理一個文件而非多個文件,減少了瀏覽器的請求次數,大大縮短了頁面的加載時間。通過壓縮還能使文件變小,使瀏覽器可以更快地下載。

4.1 javascript

在默認情況下,新建 Rails 應用的 app/assets/javascripts/application.js 文件包含下面幾行代碼:

// ...
//= require jquery
//= require jquery_ujs
//= require_tree .

在 JavaScript 文件中,Sprockets 指令以 //=. 開頭。上述代碼中使用了 require 和 require_tree 指令。require 指令用於告知 Sprockets 哪些文件需要加載。這里加載的是 Sprockets 搜索路徑中的 jquery.js 和 jquery_ujs.js 文件。我們不必顯式提供文件的擴展名,因為 Sprockets 假定在 .js 文件中加載的總是 .js 文件。

require_tree 指令告知 Sprockets 以遞歸方式包含指定文件夾中的所有 JavaScript 文件。在指定文件夾路徑時,必須使用相對於清單文件的相對路徑。也可以通過 require_directory 指令包含指定文件夾中的所有 JavaScript 文件,此時將不會采取遞歸方式。

清單文件中的指令是按照從上到下的順序處理的,但我們無法確定 require_tree 指令包含文件的順序,因此不應該依賴於這些文件的順序。如果想要確保連接文件時某些 JavaScript 文件出現在其他 JavaScript 文件之前,可以在清單文件中先行加載這些文件。注意,require 系列指令不會重復加載文件。

4.2 css

在默認情況下,新建 Rails 應用的 app/assets/stylesheets/application.css 文件包含下面幾行代碼:

/* ...
*= require_self
*= require_tree .
*/

無論新建 Rails 應用時是否使用了 --skip-sprockets 選項,Rails 都會創建 app/assets/javascripts/application.js 和 app/assets/stylesheets/application.css 文件。因此,之后想要使用 Asset Pipeline 非常容易。

我們在 JavaScript 文件中使用的指令同樣可以在 CSS 文件中使用,此時加載的是 CSS 文件而不是 JavaScript 文件。在 CSS 清單文件中,require_tree 指令的工作原理和在 JavaScript 清單文件中相同,會加載指定文件夾中的所有 CSS 文件。

上述代碼中使用了 require_self 指令,用於把當前文件中的 CSS 代碼(如果存在)插入調用這個指令的位置。

要想使用多個 Sass 文件,通常應該使用 Sass @import 規則,而不是 Sprockets 指令。如果使用 Sprockets 指令,這些 Sass 文件將擁有各自的作用域,這樣變量和混入只能在定義它們的文件中使用。

和使用 require_tree 指令相比,使用 @import "*" 和 @import "**/*" 的效果完全相同,都能加載指定文件夾中的所有文件。

我們可以根據需要使用多個清單文件。例如,可以用 admin.js 和 admin.css 清單文件分別包含應用管理后台的 JS 和 CSS 文件。

CSS 清單文件中指令的執行順序類似於前文介紹的 JavaScript 清單文件,尤其是加載的文件都會按照指定順序依次編譯。例如,我們可以像下面這樣把 3 個 CSS 文件連接在一起:

/* ...
*= require reset
*= require layout
*= require chrome
*/

 

5 預處理

靜態資源文件的擴展名決定了預處理的方式。在使用默認的 Rails gemset 生成控制器或腳手架時,會生成 CoffeeScript 和 SCSS 文件,而不是普通的 JavaScript 和 CSS 文件。在前文的例子中,生成 projects 控制器時會生成 app/assets/javascripts/projects.coffee和 app/assets/stylesheets/projects.scss 文件。

在開發環境中,或 Asset Pipeline 被禁用時,會使用 coffee-script 和 sass gem 提供的處理器分別處理相應的文件請求,並把生成的 JavaScript 和 CSS 文件發給瀏覽器。當 Asset Pipeline 可用時,會對這些文件進行預處理,然后儲存在 public/assets 文件夾中,由 Rails 應用或 Web 服務器處理。

通過添加其他擴展名,可以對文件進行更多預處理。對擴展名的解析順序是從右到左,相應的預處理順序也是從右到左。例如,對於 app/assets/stylesheets/projects.scss.erb 文件,會先處理 ERB,再處理 SCSS,最后作為 CSS 文件處理。同樣,對於app/assets/javascripts/projects.coffee.erb 文件,會先處理 ERB,再處理 CoffeeScript,最后作為 JavaScript 文件處理。

記住預處理順序很重要。例如,如果我們把文件名寫為 app/assets/javascripts/projects.erb.coffee,就會先處理 CoffeeScript,這時一旦遇到 ERB 代碼就會出錯。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM