忘了 webpacker，舊版 Rails app 升級 jsbundling-rails + Hotwire
Tags: chinese, rails, hotwire, webpacker
Published: 2021-09-22 11:12:33

## 本篇升級方式的好處
1. 降低了現存的 Rails 4 項目升級門檻，不需要 webpacker 就能開心的使用 node packages。
2. 若想放棄 jQuery，新頁面將不需要再導入相關依賴。
3. 享受 Hotwire + Turbo 帶來的效率及速度優化。
希望能幫到像我一樣正在穩定開發但又想升級項目的朋友們！

### 先升級至 Rails 6
因為 [jsbundling-rails](https://github.com/rails/jsbundling-rails) 及 [hotwire-rails](https://github.com/hotwired/hotwire-rails) 本身就限制至少 Rails 6，因此必須升級，但**不需要安裝 webpacker，因此不會動到 JS & CSS 大幅降低了升級工作量，以我個人而言，從「太難了我放棄」到「兩天完成升級」🚀**。
Rails 的升級步驟可參考 t27duck 撰寫的 [How I upgrade Ruby on Rails](http://www.t27duck.com/posts/8-how-i-upgrade-ruby-on-rails)，其中特別注意 **Step 6 Update the Default Configuration and Framework Files**。

---

本篇文章的 [Sample Code 在此](https://github.com/hungmi/rails_6_with_jsbundling_rails)，建議可以看我分開 commit 的結果比較清楚✌️，也可使用 git checkout 回溯測試看看！

首先我 `rails new rails_6_with_jsbundling_rails --skip-javascript` 新建一個沒有任何 js 的[新項目](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/42ba98f9c7b253e7ad0e794f83eb1a99e87d4d8a)，當然，也沒裝 webpacker。

然後[加入一些 JS](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/655b22782dd6b4b16c8fe9851948efe116833efc) 以及 [Admin 區塊](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/85d9fe39a8ce74936c82e3dc5b2e4580a7243b36)用來代表項目中已存在的功能。

接著[很單純的安裝 jsbundling-rails](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/8ee77211104a41bef58c90e67c622ee42a631d3a)：
```bash
bundle add jsbundling-rails
rails javascript:install:esbuild
```
可以看到我已經有了 package.json，表示我可以透過 `yarn add` 安裝所有 node packages👍。

### 重點
1. 上面安裝 jsbundling-rails 的同時也裝了 foreman，因此請改用 `foreman start -f Procfile.dev` 開啟 Rails server + yarn build。
2. 進到 http://localhost:3000/ 會發現舊的 JS 已經全炸了💥，而且layout 裡面載入了兩次 application.js 
```rb
<head>
...
<%= javascript_include_tag 'application' %>
<%= javascript_include_tag "application", "data-turbo-track": "reload", defer: true %>
...
</head>
```
但 `application-thisisalonghash.js` 原始碼卻是空白的，這是**因為現在的 JS entry point 已經改用 `app/javascript/application.js` 了。**

而我們需要做的就是[將舊的 application.js 改名，並且微調 manifest.js 及相關的 `javascript_include_tag`](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/981d4850591a4a1d8a417d4a39b9131267686cad)，就可以讓新舊程式同時運作。

到這邊，我已經可以[安裝 hotwire-rails](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/c6767db1370f0416b40fdaaf3fe1f16a12c9f2d4)
```
bundle add hotwire-rails
rails hotwire:install
```

然後[測試一下 Turbo & Stimulus](https://github.com/hungmi/rails_6_with_jsbundling_rails/commit/99d9955e6406b320d4c8b350a5e3ad32bf57340a) 都跟舊的 JS 一起運作。

以上。

---

## 其他

### 雖然升級很難，但不用 webpacker 好嗎？
jsbundling-rails 也支援 webpack，[而且更好](https://github.com/rails/jsbundling-rails/blob/main/docs/comparison_with_webpacker.md)。
另外，未來 Rails 的主軸將會擺在 [importmap](https://github.com/rails/importmap-rails) 及 [jsbundling-rails](https://github.com/rails/jsbundling-rails)，[DHH 也詳述了背後的原因](https://world.hey.com/dhh/rails-7-will-have-three-great-answers-to-javascript-in-2021-8d68191b)。

### 幹嘛要用 Hotwire？
其實繼續使用 Rails < 6 的開發方式並沒有太大的問題，例如 Stimulus 也有提供[不需編譯的安裝方式](https://stimulus.hotwired.dev/handbook/installing#using-without-a-build-system)，安裝之後就可以避免掉許多 SJR 替換 HTML 後需要重新初始化 JS 功能的情境。

雖然我對前端生態並不熟悉，甚至不知道將 JS 模組化的好處，但在目前的開發流程中，我發現比起 Stimulus 搭配 SJR 的組合技，使用 Hotwire 不只減少了請求數，並且維護性更佳。

### 幹嘛不用 CDN 安裝 hotwire 而要用 hotwire-rails gem？
雖然 Hotwire 的 Custom HTML Element `<turbo-frame>` 只是 plain HTML，但不使用 gem 的話，Rails controller 無法 `respond_to format.turbo_stream`，甚至在 `*.turbo_stream.erb` 的使用也會受限。

### 為何不用 importmap-rails？
其實我有安裝過 importmap-rails 了，也不難使用，但因為要等 Rails 7 才能使用 ActiveStorage/ActionText/ActionCable ，而我的項目暫時無法升級，所以只好放棄。
等未來 Rails 7 正式釋出時，我會考慮升級並轉換到 importmap-rails。