鼬~~哩賀,我是寫程式的山姆老弟,昨天跟大家一起用 webpacker 安裝、打包並使用了 fontawesome
和 axios
的 JS 第三方套件,前幾天也有研究了 Asset Pipeline,可以說是把 Rails 歷代的前端工具都試用過了,今天我們來試試看最新 Rails 7 所預設的 importmap,一個不同於 webpacker 的 JS 解決方案
因為 importmap 在 RailsGuide 還沒出文件,我們就先參考 Github 上的資訊,再加上自己做點實驗來驗證概念,夠夠~
確認版本 $ rails —-version
Rails 7.0.4
我實驗用的是 Rails 7.0.4 版
$ rails new test_importmap
Gemfile
安裝的 gem 有幾個新面孔:
...
# The original asset pipeline for Rails [https://github.com/rails/sprockets-rails]
gem "sprockets-rails"
# Use JavaScript with ESM import maps [https://github.com/rails/importmap-rails]
gem "importmap-rails"
# Hotwire's SPA-like page accelerator [https://turbo.hotwired.dev]
gem "turbo-rails"
# Hotwire's modest JavaScript framework [https://stimulus.hotwired.dev]
gem "stimulus-rails"
...
sprocket-rails
: 就是 Asset Pipeline,之前都是直接埋在 rails 裡,只是這次變成預設選配,也就是你可以選擇把 Asset Pipeline 取消掉,改用別的 CSS / JS 打包方式importmap-rails
: 這就是今天的研究重點了,看看跟 webpacker 用起來有什麼不一樣turbo-rails
: 目前還沒研究到 turbo,只知道有個 hotwire 跟 turbo 好像有點關係,但不知道差別,之後要研究應該會跟舊版的 turbolinks 一起研究stimulus-rails
: 我有偷看了幾位大神在 YT 試用的影片,感覺是個類似 React 或 Vue 的使用方式,目前看起來我覺得沒有很喜歡,不是很直覺,要用這種方式,我乾脆就直接用 React 還比較好維護app/views/layouts/application.html.erb
的 js helper method,從 javascript_pack_tag
,變成了 javascript_importmap_tags
;而 CSS 依舊是預設由 Asset Pipeline 來處理,所以使用 stylesheet_link_tag
<!DOCTYPE html>
<html>
<head>
<title>TestImportmap</title>
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<%= yield %>
</body>
</html>
app/javascript
資料夾底下沒有 packs
了,改成 controllers
,這應該是 stimulus-rails
的結構
外層沒有 package.json
了,改成在 config/importmap.rb
# Pin npm packages by running ./bin/importmap
pin "application", preload: true
pin "@hotwired/turbo-rails", to: "turbo.min.js", preload: true
pin "@hotwired/stimulus", to: "stimulus.min.js", preload: true
pin "@hotwired/stimulus-loading", to: "stimulus-loading.js", preload: true
pin_all_from "app/javascript/controllers", under: "controllers"
新增個頁面 $ rails g controller home index
把新頁面變成首頁
# config/routes.rb
Rails.application.routes.draw do
root 'home#index'
end
$ ./bin/importmap pin bootstrap jquery
Pinning "bootstrap" to https://ga.jspm.io/npm:bootstrap@5.2.1/dist/js/bootstrap.esm.js
Pinning "@popperjs/core" to https://ga.jspm.io/npm:@popperjs/core@2.11.6/lib/index.js
Pinning "jquery" to https://ga.jspm.io/npm:jquery@3.6.1/dist/jquery.js
是直接使用 CDN 的方式安裝,會在 config/importmap.rb
新增以下幾行
# config/importmap.rb
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.2.1/dist/js/bootstrap.esm.js"
pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.6/lib/index.js"
pin "jquery", to: "https://ga.jspm.io/npm:jquery@3.6.1/dist/jquery.js"
在 app/javascript/application.js
引用 bootstrap
// app/javascript/application.js
...
import 'bootstrap'
import 'jquery'
import '@popperjs/core'
啟動 $ rails s
,打開 127.0.0.1:3000
,檢查一下 header,確認是有引用 bootstrap
和相依套件 popper
,這樣就算使用 importmap 來安裝 bootstrap
,是不是很簡單啊? 才怪…
剛才完成的只有 bootstrap
的 JS 部分而已,如果要使用 bootstrap
的 CSS 部分,還是要回歸 Asset Pipeline,心裡 OS: bootstrap 最重要的就是 CSS 啊,怎麼還是那麼麻煩!
所以要在 Gemfile 新增 gem 'bootstrap'
,同時把 sass 放回來,Rails 7 預設是不安裝 sass 的,並執行 $ bundle install
# Gemfile
...
# Use Sass to process CSS
gem "sassc-rails"
# Use Bootstrap
gem 'bootstrap'
為了套用 sass,把 app/assets/stylesheets/application.css
改名為 app/assets/stylesheets/application.scss
,並新增 @import 'bootstrap'
/*
* This is a manifest file that'll be compiled into application.css, which will include all the files
* listed below.
*
* Any CSS (and SCSS, if configured) file within this directory, lib/assets/stylesheets, or any plugin's
* vendor/assets/stylesheets directory can be referenced here using a relative path.
*
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
* compiled file so the styles you add here take precedence over styles defined in any other CSS
* files in this directory. Styles in this file should be added after the last require_* statement.
* It is generally better to create a new file per style scope.
*
*= require_tree .
*= require_self
*/
@import 'bootstrap';
在 app/views/home/index.html.erb
新增幾個 bootstrap 元件:nav bar、dropdown button、tooltip button,還有 bootstrap style 的 p
tag
<!-- app/views/home/index.html.erb -->
<nav class="navbar navbar-expand-lg navbar-dark bg-dark" aria-label="Ninth navbar example">
<div class="container-xl">
<a class="navbar-brand" href="#">Container XL</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarsExample07XL" aria-controls="navbarsExample07XL" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarsExample07XL">
<ul class="navbar-nav me-auto mb-2 mb-lg-0">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Link</a>
</li>
<li class="nav-item">
<a class="nav-link disabled">Disabled</a>
</li>
<li class="nav-item dropdown">
<a class="nav-link dropdown-toggle" href="#" data-bs-toggle="dropdown" aria-expanded="false">Dropdown</a>
<ul class="dropdown-menu">
<li><a class="dropdown-item" href="#">Action</a></li>
<li><a class="dropdown-item" href="#">Another action</a></li>
<li><a class="dropdown-item" href="#">Something else here</a></li>
</ul>
</li>
</ul>
<form role="search">
<input class="form-control" type="search" placeholder="Search" aria-label="Search">
</form>
</div>
</div>
</nav>
<h1>Home#index</h1>
<button type="button" class="btn btn-secondary" data-bs-toggle="tooltip" data-bs-placement="top">Tooltip on top</button>
<button class="btn btn-secondary dropdown-toggle" type="button" data-bs-toggle="dropdown" aria-expanded="false">
Dropend button
</button>
<%= content_tag(:div, class: 'container') do %>
<p class="bg-dark text-light">Find me in app/views/home/index.html.erb</p>
<% end %>
啟動 $ rails s
,打開 127.0.0.1:3000
,檢查一下,會發現 CSS 部分是正常的,但 JS 的運作有問題,只有 navbar 的展開是正常,其他像是 dropdown、tooltips 都是會報錯的
我後來看了大神的 YT 教學,終於修好了,真的是傻眼,跟我一起看一下怎麼修的吧
在 config/importmap.rb
要修一下,把 @popperjs/core
的 to 的路徑修正
pin "bootstrap", to: "https://ga.jspm.io/npm:bootstrap@5.2.1/dist/js/bootstrap.esm.js"
- pin "@popperjs/core", to: "https://ga.jspm.io/npm:@popperjs/core@2.11.6/lib/index.js"
+ pin "@popperjs/core", to: "https://unpkg.com/@popperjs/core@2.11.6/dist/esm/index.js"
我就問,到底誰會知道要這樣改!!!?
然後重新啟動、重新整理之後, navbar 的 dropdown 就好了,WTF
但下面的 tooltips 和 dropdown button 還是不能正常運作,我不懂,我真的不懂到底是發生什麼事了 Q_Q
恩… 試用 importmap
之後,讓我覺得非常傻眼,這些奇怪的 bug 遇到了都不知道怎麼修,到底誰會知道要改 library 的來源處,總之我應該之後會嘗試用別的方法,我放棄 importmap
了…,我們明天見…
Hi,
剛好最近公司的新專案是使用 Rails 7,可以透過直接下載 bootstrap 的 min.css
並且手動放進 assets/stylesheets
,之後就會被 applicaiton.html.erb
內的 stylesheet_link_tag
帶進來。
若是要把 css 整理到別的資料夾內,記得在 config/intializers/asset.rb
內 Rails.application.config.assets.paths << 加入你想要的路徑 ( 通常用會 Rails.root.join 起手 )
因為沒有了 node_mudules
所以確實一開始會有點不習慣,但這種要用什麼裝什麼的感覺其實很讚
感謝分享~ 我之後再多用幾個專案來玩玩看 importmap
tooltips 需要增加 code 啟用
import "@popperjs/core"
document.addEventListener("turbo:load", () => {
const tooltipTriggerList = document.querySelectorAll('[data-bs-toggle="tooltip"]')
const tooltipList = [...tooltipTriggerList].map(tooltipTriggerEl => new bootstrap.Tooltip(tooltipTriggerEl))
const popoverTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="popover"]'))
const popoverList = popoverTriggerList.map(popoverTriggerEl => new bootstrap.Popover(popoverTriggerEl))
})
至於 dropdown button 我沒遇到問題