v7.1.3.2
更多資訊請至 rubyonrails.org: 更多 Ruby on Rails

在 Rails 中使用 JavaScript

本指南涵蓋將 JavaScript 功能整合到 Rails 應用程式的選項,包括使用外部 JavaScript 套件的選項,以及如何將 Turbo 與 Rails 搭配使用。

閱讀本指南後,您將知道

1 匯入地圖

匯入地圖 讓您可以使用邏輯名稱匯入 JavaScript 模組,這些邏輯名稱會直接從瀏覽器對應到版本控制的檔案。匯入地圖是 Rails 7 的預設值,讓任何人無需轉譯或捆綁,就能使用大多數 npm 套件來建置現代的 JavaScript 應用程式。

使用匯入地圖的應用程式不需要 Node.jsYarn 才能運作。如果您計畫使用 Rails 搭配 importmap-rails 來管理您的 JavaScript 相依項,則不需要安裝 Node.js 或 Yarn。

使用匯入地圖時,不需要個別的建置程序,只要使用 bin/rails server 啟動您的伺服器即可開始使用。

1.1 安裝 importmap-rails

匯入 Rails 的地圖會自動包含在 Rails 7+ 的新應用程式中,但您也可以手動安裝在現有的應用程式中

$ bin/bundle add importmap-rails

執行安裝任務

$ bin/rails importmap:install

1.2 使用 importmap-rails 新增 npm 套件

若要將新套件新增到您的匯入地圖驅動的應用程式,請從您的終端機執行 bin/importmap pin 指令

$ bin/importmap pin react react-dom

然後,照常將套件匯入 application.js

import React from "react"
import ReactDOM from "react-dom"

2 使用 JavaScript 捆綁器新增 npm 套件

匯入地圖是新 Rails 應用程式的預設值,但如果您偏好傳統的 JavaScript 捆綁,您可以使用您選擇的 Bunesbuildwebpackrollup.js 來建立新的 Rails 應用程式。

若要在新的 Rails 應用程式中使用捆綁器來取代匯入地圖,請將 —javascript-j 選項傳遞給 rails new

$ rails new my_new_app --javascript=bun
OR
$ rails new my_new_app -j bun

這些捆綁選項都附帶一個簡單的設定檔,並透過 jsbundling-rails 寶石與資產管線整合。

使用捆綁選項時,請使用 bin/dev 來啟動 Rails 伺服器並建置 JavaScript 以進行開發。

2.1 安裝 JavaScript 執行環境

如果您使用 esbuild、rollup.js 或 Webpack 來在您的 Rails 應用程式中捆綁您的 JavaScript,則必須安裝 Node.js 和 Yarn。如果您使用 Bun,則您只需要安裝 Bun,因為它同時是 JavaScript 執行環境和捆綁器。

2.1.1 安裝 Bun

Bun 網站 尋找安裝說明,並使用以下指令驗證是否正確安裝且在您的路徑中

$ bun --version

您的 Bun 執行時期版本應會列印出來。如果顯示類似 1.0.0 的內容,表示 Bun 已正確安裝。

如果不是,您可能需要在目前目錄中重新安裝 bun 或重新啟動您的終端機。

2.1.2 安裝 Node.js 和 Yarn

如果您使用 esbuild、rollup.js 或 Webpack,您將需要 Node.js 和 Yarn。

Node.js 網站 尋找安裝說明,並使用以下指令驗證是否正確安裝

$ node --version

您的 Node.js 執行時期版本應會列印出來。請確定其大於 8.16.0

若要安裝 Yarn,請遵循 Yarn 網站 上的安裝說明。執行此指令應會列印出 Yarn 版本

$ yarn --version

如果顯示類似 1.22.0 的內容,表示 Yarn 已正確安裝。

3 在 Import Maps 和 JavaScript Bundler 之間做選擇

當您建立新的 Rails 應用程式時,您需要在 import maps 和 JavaScript bundling 解決方案之間做選擇。每個應用程式都有不同的需求,您在選擇 JavaScript 選項之前應仔細考量您的需求,因為對於大型、複雜的應用程式而言,從一個選項移轉到另一個選項可能會很耗時。

Import maps 是預設選項,因為 Rails 團隊相信 import maps 有潛力降低複雜性、改善開發人員體驗,並提供效能提升。

對於許多應用程式而言,特別是那些主要依賴 Hotwire 堆疊來滿足其 JavaScript 需求的應用程式,import maps 將會是長期的正確選項。您可以在 這裡 閱讀更多關於將 import maps 設定為 Rails 7 預設值背後的理由。

其他應用程式可能仍需要傳統的 JavaScript 捆綁器。表示您應該選擇傳統捆綁器的需求包括

  • 如果您的程式碼需要轉譯步驟,例如 JSX 或 TypeScript。
  • 如果您需要使用包含 CSS 或依賴 Webpack 載入器 的 JavaScript 函式庫。
  • 如果您絕對確定您需要 tree-shaking
  • 如果您將透過 cssbundling-rails gem 安裝 Bootstrap、Bulma、PostCSS 或 Dart CSS。此 gem 提供的所有選項(Tailwind 和 Sass 除外)都會自動為您安裝 esbuild,如果您未在 rails new 中指定其他選項。

4 Turbo

無論您選擇匯入地圖或傳統捆綁器,Rails 都會附帶 Turbo 來加速您的應用程式,同時大幅減少您需要編寫的 JavaScript 數量。

Turbo 讓您的伺服器直接傳送 HTML,作為盛行前端架構的替代方案,這些架構將 Rails 應用程式的伺服器端簡化為僅比 JSON API 多一點的功能。

4.1 Turbo Drive

Turbo Drive 透過避免在每次導覽要求時執行全頁面拆除和重建,來加速頁面載入。Turbo Drive 是對 Turbolinks 的改善和替換。

4.2 Turbo Frames

Turbo Frames 允許在要求時更新頁面的預定義部分,而不會影響頁面內容的其餘部分。

您可以使用 Turbo Frames 建立原位編輯,而無需任何自訂 JavaScript、延遲載入內容,並輕鬆建立伺服器呈現的標籤式介面。

Rails 提供 HTML 輔助函式,透過 turbo-rails gem 簡化 Turbo Frames 的使用。

使用此寶石,您可以使用 turbo_frame_tag 輔助程式將 Turbo Frame 加入您的應用程式,如下所示

<%= turbo_frame_tag dom_id(post) do %>
  <div>
     <%= link_to post.title, post_path(post) %>
  </div>
<% end %>

4.3 Turbo Streams

Turbo Streams 將頁面變更傳送為包裝在自執行 <turbo-stream> 元素中的 HTML 片段。Turbo Streams 讓您可以透過 WebSockets 廣播其他使用者所做的變更,並在提交表單後更新頁面片段,而不需要載入完整頁面。

Rails 提供 HTML 和伺服器端輔助程式,透過 turbo-rails 寶石簡化 Turbo Streams 的使用。

使用此寶石,您可以從控制器動作呈現 Turbo Streams

def create
  @post = Post.new(post_params)

  respond_to do |format|
    if @post.save
      format.turbo_stream
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

Rails 會自動尋找 .turbo_stream.erb 檢視檔案,並在找到時呈現該檢視。

Turbo Stream 回應也可以在控制器動作中內嵌呈現

def create
  @post = Post.new(post_params)

  respond_to do |format|
    if @post.save
      format.turbo_stream { render turbo_stream: turbo_stream.prepend('posts', partial: 'post') }
    else
      format.html { render :new, status: :unprocessable_entity }
    end
  end
end

最後,Turbo Streams 可以使用內建輔助程式從模型或背景工作啟動。這些廣播可以用於透過 WebSocket 連線更新所有使用者的內容,讓頁面內容保持最新,並讓您的應用程式栩栩如生。

若要從模型廣播 Turbo Stream,請結合類似以下的模型回呼

class Post < ApplicationRecord
  after_create_commit { broadcast_append_to('posts') }
end

與在頁面上設定的 WebSocket 連線,該連線應接收類似以下的更新

<%= turbo_stream_from "posts" %>

5 取代 Rails/UJS 功能

Rails 6 附帶一個稱為 UJS(非侵入式 JavaScript)的工具。UJS 允許開發人員覆寫 <a> 標籤的 HTTP 請求方法,在執行動作之前加入確認對話方塊,以及更多功能。UJS 是 Rails 7 之前的預設值,但現在建議改用 Turbo。

5.1 方法

按一下連結總是會產生 HTTP GET 請求。如果您的應用程式是 RESTful,有些連結實際上是會變更伺服器上資料的動作,應使用非 GET 請求執行。data-turbo-method 屬性允許使用明確方法(例如「post」、「put」或「delete」)標記此類連結。

Turbo 會掃描應用程式中的 <a> 標籤,尋找 turbo-method 資料屬性,並在存在時使用指定的方法,覆寫預設的 GET 動作。

例如

<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete" } %>

這會產生

<a data-turbo-method="delete" href="...">Delete post</a>

變更連結方法的另一種方法是使用 Rails 的 button_to 輔助函式,而不是使用 data-turbo-method。基於易於存取的原因,對於任何非 GET 動作,實際的按鈕和表單較為理想。

5.2 確認

您可以透過在連結和表單上新增 data-turbo-confirm 屬性,要求使用者提供額外的確認。在點選連結或提交表單時,系統會向使用者顯示包含屬性文字的 JavaScript confirm() 對話方塊。如果使用者選擇取消,則不會執行動作。

例如,使用 link_to 輔助函式

<%= link_to "Delete post", post_path(post), data: { turbo_method: "delete", turbo_confirm: "Are you sure?" } %>

會產生

<a href="..." data-turbo-confirm="Are you sure?" data-turbo-method="delete">Delete post</a>

當使用者點選「刪除文章」連結時,系統會顯示「您確定嗎?」確認對話方塊。

此屬性也可以與 button_to 輔助函式一起使用,但必須將其新增到 button_to 輔助函式內部呈現的表單中

<%= button_to "Delete post", post, method: :delete, form: { data: { turbo_confirm: "Are you sure?" } } %>

5.3 Ajax 要求

從 JavaScript 進行非 GET 要求時,需要 X-CSRF-Token 標頭。沒有此標頭,Rails 將不會接受要求。

Rails 需要此令牌才能防止跨網站要求偽造 (CSRF) 攻擊。請在 安全性指南 中閱讀更多資訊。

Rails Request.JS 封裝了新增 Rails 所需要求標頭的邏輯。只要從套件匯入 FetchRequest 類別,並傳遞要求方法、網址、選項進行實例化,然後呼叫 await request.perform(),並對回應執行您需要的動作即可。

例如

import { FetchRequest } from '@rails/request.js'

....

async myMethod () {
  const request = new FetchRequest('post', 'localhost:3000/posts', {
    body: JSON.stringify({ name: 'Request.JS' })
  })
  const response = await request.perform()
  if (response.ok) {
    const body = await response.text
  }
}

當使用其他函式庫進行 Ajax 呼叫時,必須自行將安全性令牌新增為預設標頭。若要取得令牌,請查看應用程式檢視中由 csrf_meta_tags 列印的 <meta name='csrf-token' content='THE-TOKEN'> 標籤。您可以執行類似下列動作

document.head.querySelector("meta[name=csrf-token]")?.content

意見回饋

歡迎協助提升本指南的品質。

如果您發現任何錯字或事實錯誤,請協助我們修正。首先,您可以閱讀我們的 文件貢獻 區段。

您也可能會發現不完整或過時的內容。請務必為 main 新增任何遺漏的文件。請先查看 Edge Guides,確認問題是否已在主分支中修正。查看 Ruby on Rails Guides Guidelines,了解風格和慣例。

如果您發現需要修正的地方,但無法自行修補,請 開啟問題

最後,歡迎在 官方 Ruby on Rails 論壇 討論任何與 Ruby on Rails 文件相關的事項。