遠看是 Trouble,近看是 Turbo - Turbo 介紹
遠看是 Trouble,近看是 Turbo - Turbo 介紹
前言
就讓我用這篇文章來介紹,什麼是 Turbo。
什麼是 Turbo
Turbo 是 Rails 7 裡佔有一席之地的功能。Rails 7 將 Hotwire 作為預設的前端框架,其中包誇在內的 Turbo 就是本篇主角。
Turbo 主要是想提高畫面上元素的更新效率,以提升使用者體驗,在一般透過導向方式來替換頁面的網頁設計,使用者能明顯地感受到每一次的換頁帶來的停頓,即便不到一秒,但使用 Turbo 則不會有這樣的停頓,讓使用者在操作時倍感順暢。
如何運作
假如我們現在在 A 頁面,頁面上有通往 B 頁面的連結。
在說明運作之前,我們要先知道,透過 Turbo 所進行的換頁,其實不是換頁,而是仍在同一頁 ( A頁面 ),並改變該頁面上的內容 ( HTML )。
改變的方式則是將原本的 A 頁面內容 ( HTML ) 抽掉,並放入 B 頁面的內容 ( HTML ),所以我們可以知道 Turbo 是透過一整包的 HTML 的流動來改變原頁面上的內容,而非前往一個新的頁面。當 HTML 換好後,如果是整頁換,最後會把瀏覽器上的網址換成從 A 頁面的網址換成 B 頁面的網址,如果是局部換,網址則不會改變。所以我們其實是一直在同一張紙上,不斷地更換紙上的內容。
當我們點擊 link 後,會透過 Ajax 向後端發送 request,並在收到 response,將 response 處理完後,將整包 HTML 渲染到新的頁面上。
Turbo 的種類
主要分為 turbo frame
與 turbo stream
,前者主要是透過 get
,後者則是 get
以外的 http method。
用法基本上是將 turbo frame tag
跟 turbo stream tag
掛上 id
,並將要替換的 HTML 也取上相同 id
,便能完成局部替換。
至於整頁替換,在 Rails 7 預設使用 Turbo 的情況下,每個 link 都會是以 Turbo 的方式進行所謂的換頁。
Turbo Frame
我們直接透過實作來介紹,我現在有一個文章列表的介面如下圖:
一般來說,當使用者點擊 Edit this post
按鈕時,會被導向編輯文章的表單頁面,但可以藉由 turbo 幫我做到,不需導向,直接在這個介面上顯示表單:
透過上圖可以發現,原本的文章被替換成文章編輯表單,而不是被導向到編輯的頁面: 網址沒改變、 New Post
按鈕還在,title Posts
還在。
實作
index.html.erb
: 上圖整個文章的頁面
<div>
<div>
<h1>Posts</h1>
<%= link_to "New post", new_post_path %>
</div>
<div>
<%= render @posts %>
</div>
</div>
_post.html.erb
: 在文章頁面中,呈現每篇文章,我們把要被替換掉的區塊,用 turbo_frame_tag
包起來,並給予參數 dom_id(post)
,在瀏覽器上,會轉換成 <turbo-frame id='post_1'> </turbo-frame>
。
<%= turbo_frame_tag dom_id(post) do %>
<p>
<strong>Title:</strong>
<%= post.title %>
</p>
<p>
<strong>Content:</strong>
<%= post.content %>
</p>
<% if action_name != "show" %>
<%= link_to "Show this post", post %>
<%= link_to "Edit this post", edit_post_path(post) %>
<hr>
<% end %>
<% end %>
edit.html.erb
: 文章編輯頁面,我們一樣用 turbo_frame_tag dom_id(@post)
產生一樣的 <turbo-frame id='post_1'> </turbo-frame>
。
<%= turbo_frame_tag dom_id(@post) do %>
<div>
<h2>Editing post</h2>
<%= form_with(model: @post, class: "contents") do |form| %>
<div>
<%= form.label :title, 'title:' %>
<%= form.text_field :title %>
</div>
<div>
<%= form.label :content, 'content:' %>
<%= form.text_field :content %>
</div>
<div>
<%= form.submit %>
</div>
<% end %>
<%= link_to "Show this post", @post %>
<%= link_to "Back to posts", posts_path %>
</div>
<% end %>
如此一來,當我按下, Edit this post
按鈕時,就會透過 Ajax
發出 request
,取得 edit_post_path(post)
的 HTML
後,依照 edit_post_path(post)
的 HTML
上的 turbo_frame id
,將整包 HTML
帶到相對應的 turbo_frame id
裡,執行替換,所以可以看到,我們的文章列表上的第一篇文章直接變成編輯表單。
Turbo Stream
這裡我們一樣透過實作來講解。延續剛剛的編輯文章。在一般情形,當我們點擊 Update Post
執行更新時,在更新成功時,導回文章列表頁面 redirect_to posts_path
,並看到新的資料。既然我們表單已經透過 turbo 讓操作單頁化,那送出表單後,應該也是要用 turbo,流程上才合理。
實作
首先,在 PostsController 的 update action 中表明,我們要用 turbo_stream
class PostsController < ApplicationController
before_action :set_post, only: %i[ show edit update destroy ]
def update
if @post.update(post_params)
respond_to do |format|
format.turbo_stream <---- this one
end
else
render :edit
end
end
end
接著在 views/posts
新增 update.turbo_stream.erb
,並定義 turbo_stream
以及它要操作的 action
,這裡使用 update
:
<%= turbo_stream.update dom_id(@post) do %>
<%= render 'post', post: @post %>
<% end %>
// 意思就是,我要把 block 裡的 HTML,透過 turbo_stream,以 update 方式,來更新 id 同為 `dom_id(@post)` 的 turbo-frame 內的 HTML
如此一來,當編輯表單送出後,在 update
action 成功更新後,會依照 update.turbo_stream.erb
裡的 turbo_stream
進行操作。
action
turbo_stream
總共有七個 action: Append
、 Prepend
、 Replace
、 Update
、 Remove
、 Before
、 After
。端視需求使用不同的 action。端視需求使用不同的 action。至於每個 action 在做什麼,可以參考官網說明。
結論
今天用這篇文章讓第一次接觸 Turbo 的開發者能夠清楚知道 Turbo 的運作與實作,讓我們來快速 review 一下。
Turbo 讓我們可以做到 SPA,並提升換頁的速率,其中可透過 turbo_frame
與 turbo_stream
,進行局部換頁。主要是針對相同 id
的 target 進行匹配並渲染。
希望大家可以透過本篇文章了解 Turbo 以及如何實作 Turbo。