更多内容请访问 rubyonrails.org:

在 Rails 中使用 JavaScript

本指南涵盖了将 JavaScript 功能集成到 Rails 应用程序中的选项,包括使用外部 JavaScript 包的选项以及如何在 Rails 中使用 Turbo。

阅读本指南后,您将了解

  • 如何在无需 Node.js、Yarn 或 JavaScript 打包器的情况下使用 Rails。
  • 如何使用 import maps、Bun、esbuild、Rollup 或 Webpack 创建新的 Rails 应用程序来打包您的 JavaScript。
  • 什么是 Turbo,以及如何使用它。
  • 如何使用 Rails 提供的 Turbo HTML 助手。

1. 导入映射 (Import Maps)

导入映射 (Import maps) 允许您使用逻辑名称直接从浏览器导入 JavaScript 模块,这些名称映射到版本化的文件。导入映射从 Rails 7 开始是默认的,允许任何人使用大多数 npm 包构建现代 JavaScript 应用程序,而无需转译或打包。

使用导入映射的应用程序不需要 Node.jsYarn 即可运行。如果您计划将 Rails 与 importmap-rails 一起使用来管理 JavaScript 依赖项,则无需安装 Node.js 或 Yarn。

使用导入映射时,不需要单独的构建过程,只需使用 bin/rails server 启动服务器即可。

1.1. 安装 importmap-rails

Rails 的 Importmap 会自动包含在 Rails 7+ 的新应用程序中,但您也可以在现有应用程序中手动安装它。

$ bundle add importmap-rails

运行安装任务

$ bin/rails importmap:install

1.2. 使用 importmap-rails 添加 npm 包

要将新包添加到由 import map 支持的应用程序中,请从终端运行 bin/importmap pin 命令

$ bin/importmap pin react react-dom

然后,像往常一样将包导入 application.js

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

2. 使用 JavaScript 打包器添加 npm 包

Import maps 是新 Rails 应用程序的默认设置,但如果您更喜欢传统的 JavaScript 打包,您可以使用您选择的 BunesbuildWebpackRollup.js 创建新的 Rails 应用程序。

要在新的 Rails 应用程序中使用打包器而不是 import maps,请将 --javascript-j 选项传递给 rails new

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

这些打包选项都通过 jsbundling-rails gem 提供了简单的配置和与资产管道的集成。

使用打包选项时,请使用 bin/dev 启动 Rails 服务器并为开发构建 JavaScript。

2.1. 安装 JavaScript 运行时

如果您在 Rails 应用程序中使用 esbuild、Rollup.js 或 Webpack 来打包 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 打包器

创建新的 Rails 应用程序时,您需要在 import maps 和 JavaScript 打包解决方案之间进行选择。每个应用程序都有不同的要求,在选择 JavaScript 选项之前,您应该仔细考虑您的要求,因为对于大型复杂应用程序来说,从一个选项迁移到另一个选项可能会耗费大量时间。

Import maps 是默认选项,因为 Rails 团队相信 import maps 在降低复杂性、改善开发人员体验和提高性能方面的潜力。

对于许多应用程序,尤其是那些主要依赖 Hotwire 堆栈来满足其 JavaScript 需求的应用程序,import maps 将是长期的正确选择。您可以在此处阅读更多关于 Rails 7 将 import maps 设置为默认选项的原因。

其他应用程序可能仍然需要传统的 JavaScript 打包器。表明您应该选择传统打包器的要求包括

  • 如果您的代码需要转译步骤,例如 JSX 或 TypeScript。
  • 如果您需要使用包含 CSS 或依赖 Webpack 加载器的 JavaScript 库。
  • 如果您绝对确定需要 tree-shaking
  • 如果您将通过 cssbundling-rails gem 安装 Bootstrap、Bulma、PostCSS 或 Dart CSS。如果在使用 rails new 时未指定不同的选项,此 gem 提供的所有选项(除了 Tailwind 和 Sass)都会自动为您安装 esbuild

4. Turbo

无论您选择 import maps 还是传统打包器,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 通过 turbo-rails gem 提供 HTML 助手来简化 Turbo Frames 的使用。

使用此 gem,您可以使用 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 将页面更改作为 HTML 片段传递,这些片段包裹在自执行的 <turbo-stream> 元素中。Turbo Streams 允许您通过 WebSockets 广播其他用户所做的更改,并在表单提交后更新页面片段,而无需重新加载整个页面。

Rails 通过 turbo-rails gem 提供 HTML 和服务器端助手来简化 Turbo Streams 的使用。

使用此 gem,您可以从控制器操作渲染 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 (Unobtrusive 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>

更改链接方法(使用 data-turbo-method)的替代方法是使用 Rails 的 button_to 助手。出于可访问性原因,对于任何非 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 类并实例化它,传递请求方法、URL、选项,然后调用 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


回到顶部