[Hotwire]製作簡易聊天室(Turbo Frame)
該範例取自 Hotwire 官方影片教學
參考網站: turbo-rails doc, hotrails
在現今習慣前後端分離的架構底下 API 以及 Json 的溝通方式成為了主流的方法,但今天要介紹的 Hotwire 卻反其道而行
- 硬是要傳 HTML 給前端 ^ↀᴥↀ^
- 硬是要 server side render ヾ(*ΦωΦ)ノ
- 硬是要第一次快速載入網頁 ლ(=ↀωↀ=)ლ
- 硬是要很 rails (ↀДↀ)✧
Hotwire 主要由兩個 Javascript 套件整合起來
- Turbo (Core)
- Stimulus(optional)
目前可以在 Basecamp 所開發的 HEY 看到這個應用
開發者希望工程師能用最少化手動去寫 Javascript 為此開發出 Hotwire. 我在使用的過程中確實只有新增一個 js 檔案其餘的事情基本上都在 controller, model, view 裡面去完成.
Hotwire 只有在 rails 7 才有提供所以要使用的朋友要注意自己的版本
Turbo/turbo-rails
When you click an eligible link, Turbo Drive prevents the browser from following it, changes the browser’s URL using the History API, requests the new page using
fetch
, and then renders the HTML response.
基本上 turbo 做的事情就是局部的重新渲染 html,透過 turbo_frame_tag 去建立 <turbo-frame> 以及定義要重新渲染的範圍.要注意的是 turbo_frame_tag 基本上是一對的,一定會有一個 DOM 是打 request 而另一個是負責渲染
# /app/views/rooms/show.html.erb...
<%= turbo_frame_tag "room" do %>
<%= render @room %>
<p>
<%= link_to 'Edit', edit_room_path(@room) %>
<%= link_to 'Back', rooms_path, "data-turbo-frame": "_top" %>
<%= link_to 'Add chat', new_room_message_path(@room), data: { turbo_frame: "new_messages" } %>
</p>
<% end %>
...
turbo_frame_tag 後面的第一個參數會轉變為 id
當點擊 Edit 按鈕之後,客戶端會送出 GET edit 的請求,此時伺服器端會回傳 HTML 如下圖,我們可以發現其實他的回傳就是一整頁 edit 的頁面.
Turbo 會透過 <turbo-frame> 去把要重新渲染的地方抓出來
並且更改原本 <turbo-frame> 的 src
不過除了 turbo_frame_tag 這個 helper 之外,我們也可以透過掛載 data-turbor-frame 去驅動 turbo 為我們做事,下面片段程式碼中的 Add chat 就是透過這樣的方式去打 request 然後再把拿到的 HTML 渲染在下方的 <%= turbo_frame_tag “new_messages” %>
# /app/views/rooms/show.html.erb...
<%= turbo_frame_tag "room" do %>
<%= render @room %>
<p>
<%= link_to 'Edit', edit_room_path(@room) %>
<%= link_to 'Back', rooms_path, "data-turbo-frame": "_top" %>
<%= link_to 'Add chat', new_room_message_path(@room), data: { turbo_frame: "new_messages" } %>
</p>
<% end %><%= turbo_frame_tag "new_messages" %>
...
還有一種使用方式是單一節點的 turbo_frame_tag,通常這種時候都會有 src 這個屬性,然後會在頁面掛在之後再去自動打 reqeust.
<%= turbo_frame_tag "new_messages", src: new_room_message_path(@room), target: "_top", loading: "lazy" %>
而 target 管理的是取代 HTML 的範圍,target 如果是 _top,turbo 就會刷新整個頁面因為他會指向 window,反之如果是用 _self 或是其他名稱的話 turbo 只會作用在定義的 <turbo-frame>.
target
refers to another<turbo-frame>
element by ID to be navigated when a descendant<a>
is clicked. Whentarget="_top"
, navigate the window.
ref: frame targeting
# /app/views/rooms/show.html.erb...
<%= turbo_frame_tag "room" do %>
<%= render @room %>
<p>
<%= link_to 'Edit', edit_room_path(@room) %>
<%= link_to 'Back', rooms_path, "data-turbo-frame": "_top" %>
<%= link_to 'Add chat', new_room_message_path(@room), data: { turbo_frame: "new_messages" } %>
</p>
<% end %><%= turbo_frame_tag "new_messages" %>
...
除此之外你也可以透過設定 loading 去決定 <turbo-frame> 的載入方式,透過設定成 lazy 就可以等到 DOM 被看到的時候才會去打 request.
ref: Frame HTML 屬性 (還有其他屬性等著你去發現)
<%= turbo_frame_tag "new_messages", src: new_room_message_path(@room), target: "_top", loading: "lazy" %>
以上是 Turbo, Turbo frame 以及 turbo-rails 的一些簡介,下一篇會繼續分享 turbo-stream 的部分,透過 WebSocket 做到不用重新整理頁面的 realtime rendering.