鼬~~哩賀,我是寫程式的山姆老弟,昨天跟大家一起看了 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
?,之後再多留點心眼在打包上吧,總之就先這樣拉,我們明天見~