鼬~~哩賀,我是寫程式的山姆老弟,昨天跟大家一起看了 RailsGuide 的 AssetPipeline,今天來做點實驗,試試看完全用 AssetPipeline 來打包各種資源檔吧,夠夠~
因為 Rails 6 開始就改成預設使用 webpacker 打包 js 了,我們就時間倒帶,回到 Rails 5 的時代來做這個實驗吧,是說 Rails 5 要用 ruby 2.x 版本才行,麻煩的是 M1 Macbook 已經不是 Intel 平台,而是 ARM 平台,而 ARM 平台要安裝 ruby 2.x 版本需要一點技巧,這邊先不講,總之為了安裝舊版本的 ruby 也是撞了不少牆,幸好有成功破牆,才有這一篇 XD,如果你不是 M1 macbook 的話,恭喜你,這篇很簡單
我這邊使用的是 Rails 5.2.8.1 + ruby 2.6.5
$ rails new test_asset_pipeline
$ bundle install
這邊我也遇到安裝 sqlite3 和 pg gem 失敗的問題,總之後來我改在本地安裝舊版 postgres 才搞定,我把 workaround 記錄下來,給大家參考
把 sqlite3 改成 pg
# Gemfile
gem 'sqlite3' # 這行不要
gem 'pg'
$ bundle install,安裝到 pg 時報錯
安裝舊版 postgres: $ brew install postgresql
ps. 新版 postgres 是 $ brew install postgresql@14
$ rails db:create (我是連我本地的 postgres container)
$ rails db:migrate
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認基本頁面有沒有出來

$ rails g controller home index
把 home_controller 的 index action 改成首頁
Rails.application.routes.draw do
  root 'home#index'
end
檢查 app/views/layouts/application.html.erb 的引用 tag 是否為 stylesheet_link_tag 和 javascript_include_tag
<!DOCTYPE html>
<html>
  <head>
    <title>TestAssetPipeline</title>
    <%= csrf_meta_tags %>
    <%= csp_meta_tag %>
    <%= stylesheet_link_tag    'application', media: 'all', 'data-turbolinks-track': 'reload' %>
    <%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
  </head>
  <body>
    <%= yield %>
  </body>
</html>
重新命名副檔名 app/assets/stylesheets/application.css 為 app/assets/stylesheets/application.scss
然後在剛剛生成的 app/assets/stylesheets/home.scss,新增一點樣式
// app/assets/stylesheets/home.scss
h1 {
    background-color: green;
}
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認 h1 有沒有變綠色背景
成功~

把 Gemfile 的 coffee script 拿掉,只是因為我不喜歡 coffee script
# gem 'coffee-rails', '~> 4.2'
將一開始生成的 app/assets/javascripts/home.coffee 重新命名改成 app/assets/javascripts/home.js
在 app/assets/javascripts/home.js 新增一點簡單功能
console.log('home.js loaded')
function clicked() {
  console.log("Clicked in home.js")
}
在 app/views/home/index.html.erb 呼叫看看 clicked()
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<%= button_tag '按我', onclick: 'clicked()' %>
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認按鈕有沒有用
成功~

因為我很好奇,這樣只能在 home.html.erb 呼叫 home.js 裡的程式嗎?所以我做了下面的實驗
除了原本的 app/assets/javascripts/home.js 之外,再新增兩個 js 檔,並且都取名一樣的 function,想測試看看各個檔案之間的聯通、衝突
app/assets/javascripts/other.js
console.log('other.js loaded')
function clicked() {
  console.log("Clicked in other.js")
}
app/assets/javascripts/special/special.js
console.log('special/special.js loaded')
function clicked() {
  console.log("Clicked in special/special.js")
}
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認按鈕是呼叫到哪一個
如果是取名一樣的 function,答案最後一個被 load 的 function 會被執行

看來用 AssetPipeline 打包後的 JS,不管放在哪個檔案裡,裡面的內容都可以被 view 使用到的,這樣的特性跟我們前兩天用 webpacker 測出來的結果,真的是差很多,完全是不同的使用情境
將一張圖片 test-img.jpeg 放到 app/assets/images 資料夾底下
到 app/views/home/index.html.erb 使用剛剛那張圖片
<!-- app/views/home/index.html.erb -->
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<%= button_tag '按我', onclick: 'clicked()' %>
<%= image_tag 'test-img.jpeg', width: 300 %>
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認圖片有沒有顯示出來
成功~

沿用上次 webpacker 實驗用的開源字型,ChenYuluoyan-Thin.ttf,手動建立 app/assets/fonts 資料夾,再把字體放到這資料夾底下
在 app/assets/stylesheets/home.scss,對 p tag 新增字型的 css
// app/assets/stylesheets/home.scss
@font-face {
    font-family: 'ChenYuluoyan';
    src: asset_path('fonts/ChenYuluoyan-Thin.ttf')
}
p {
    font-family: 'ChenYuluoyan';
}
啟動 $ rails s,打開瀏覽器 127.0.0.1:3000,確認字型有沒有出現
成功~

後記:後來發現其實 css 中的@font-face 是多餘的 …,我原本是好奇 asset_path 和 asset_url 的差別,後來直接刪掉整個 @font-face,竟然也能正常運作
$ RAILS_ENV=production rails assets:precompile
Yarn executable was not detected in the system.
Download Yarn at https://yarnpkg.com/en/docs/install
I, [2022-09-20T22:07:55.736385 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/ChenYuluoyan-Thin-1f04002cc0906d2bda376fb133d4a8160805db0113e92baf6da54aeafde88bd7.ttf
I, [2022-09-20T22:07:55.739546 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/ChenYuluoyan-Thin-1f04002cc0906d2bda376fb133d4a8160805db0113e92baf6da54aeafde88bd7.ttf.gz
I, [2022-09-20T22:07:55.742626 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/test-img-783a001450a67aed38acd2b8cadbe0631b4f0280839de1126d49b91478a8e883.jpg
I, [2022-09-20T22:07:55.767670 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/application-87e3832199ffcf55124fefb14df62d5524cdb2342b18e4c5639c38002711b5fa.js
I, [2022-09-20T22:07:55.767891 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/application-87e3832199ffcf55124fefb14df62d5524cdb2342b18e4c5639c38002711b5fa.js.gz
I, [2022-09-20T22:07:55.774531 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/application-013fdab431408ce5410b4b64a703ede8dd70c5977fcc1afdc47a8a08385ac518.css
I, [2022-09-20T22:07:55.774602 #5884]  INFO -- : Writing /Users/unclesam/Projects/fullstack/test_asset_pipeline/public/assets/application-013fdab431408ce5410b4b64a703ede8dd70c5977fcc1afdc47a8a08385ac518.css.gz
從打包的 log 看起來,有看到 js, css, font, image 的身影,應該是都有包進去
ps. 注意這邊打包出來是 jpg,而我放進去的是 jpeg,這一個小小的細節,將會造成接下來實驗發生的錯誤
到 public/ 裡的各個資料夾檢查一下,是不是該包的都有在該放的位置

在 public/assets 底下有 css、js、圖片、字型檔,看起來好像 OK~
因為有上次 webpacker 實驗的經驗,以 production 環境啟動,要多帶一個環境變數 $ RAILS_ENV=production RAILS_SERVE_STATIC_FILES=true rails s,打開 127.0.0.1:3000,檢查畫面

結果這時報錯了,回去檢查 log 怎麼寫,$ tail -f -n 50 log/production.log

驚奇的發現,怎麼會沒有 test-img.jpeg 這個圖片檔呢!? 回去看了 public/assets 底下打包後的圖片檔,就看到了打包後自動變成了 test-img-xxx.jpg 的檔案,竟然擅自幫我改副檔名,嘖嘖嘖,所以就只好改成用 jpg 囉
<!-- app/views/home/index.html.erb -->
<h1>Home#index</h1>
<p>Find me in app/views/home/index.html.erb</p>
<%= button_tag '按我', onclick: 'clicked()' %>
<%= image_tag 'test-img.jpg', width: 300 %>
這時候重新啟動 $ RAILS_ENV=production RAILS_SERVE_STATIC_FILES=true rails s,整個畫面就正常了!真是太神奇了山姆

會發現 production 環境使用打包後的 header,就只剩下引用 application-xxx.css 和 application-xxx.js 了,這就是打包後的樣子
該怎麼說呢,這次的實驗還是有點讓我意料之外 XD,第一點是各個 JS 檔案之間竟然是互通的,第二點是圖片的副檔名在打包後竟然會變,下次在部署 production 專案的時候需要再多注意一點,第三點是字型的引用竟然不需要設置 @font-face,做完實驗之後,我還是不懂為什麼可以不用加 @font-face ?,之後再多留點心眼在打包上吧,總之就先這樣拉,我們明天見~