edge
更多信息请访问 rubyonrails.org: 更多 Ruby on Rails

在Rails中使用JavaScript

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

阅读本指南后,您将了解:

1 导入映射

导入映射允许您使用逻辑名称直接从浏览器导入JavaScript模块,这些逻辑名称与版本化文件相对应。从Rails 7开始,导入映射是默认选项,允许任何人在不需要转译或打包的情况下构建现代JavaScript应用程序。

使用导入映射的应用程序不需要Node.jsYarn。如果您计划使用importmap-rails来管理JavaScript依赖项,那么无需安装Node.js或Yarn。

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

1.1 安装importmap-rails

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

$ bin/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包

导入映射是新Rails应用程序的默认选项,但如果您更喜欢传统的JavaScript打包,可以使用您选择的esbuildwebpackrollup.js创建新的Rails应用程序。

要在新的Rails应用程序中使用打包工具而不是导入映射,请将—javascript-j选项传递给rails new

$ rails new my_new_app --javascript=webpack
$ rails new my_new_app -j webpack

这些打包选项都带有简单的配置,并通过jsbundling-rails gem与资产管道集成。

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

2.1 安装Node.js和Yarn

如果您在Rails应用程序中使用JavaScript打包工具,则必须安装Node.js和Yarn。

Node.js网站上找到安装说明,并使用以下命令验证其是否正确安装:

$ node --version

您的Node.js运行时版本应该被打印出来。确保它大于8.16.0

要安装Yarn,请按照Yarn网站上的安装说明进行操作。运行此命令应该打印出Yarn的版本:

$ yarn --version

如果它显示类似于1.22.0的内容,则Yarn已正确安装。

3 在导入映射和JavaScript打包工具之间进行选择

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

导入映射是默认选项,因为Rails团队相信导入映射在减少复杂性、改善开发人员体验和提供性能增益方面的潜力。

对于许多应用程序,特别是那些主要依赖Hotwire堆栈满足其JavaScript需求的应用程序,导入映射将是长期的正确选择。您可以在此处阅读有关在Rails 7中将导入映射设置为默认选项的原因的更多信息here

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

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

无论您选择使用导入映射还是传统的打包工具,Rails都附带了Turbo,可以加快应用程序的速度,同时大大减少您需要编写的JavaScript代码量。

Turbo使您的服务器能够直接传递HTML,作为传统前端框架的替代方案,将Rails应用程序的服务器端减少到几乎只是一个JSON API。

3.1 Turbo Drive

Turbo Drive通过避免在每次导航请求时进行完整页面的拆除和重建来加快页面加载速度。 Turbo Drive是对Turbolinks的改进和替代。

3.2 Turbo Frames

Turbo Frames允许在请求时更新页面的预定义部分,而不影响页面的其他内容。

您可以使用Turbo Frames构建无需任何自定义JavaScript的原地编辑,延迟加载内容,并轻松创建服务器渲染的选项卡界面。

Rails提供了HTML辅助程序,通过turbo-rails gem来简化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 %>

3.3 Turbo Streams

Turbo Streams将页面更改作为包装在自执行的<turbo-stream>元素中的HTML片段传递。 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" %>

4 替代Rails/UJS功能

Rails 6附带了一个名为UJS(Unobtrusive JavaScript)的工具。 UJS允许开发人员覆盖<a>标签的HTTP请求方法,在执行操作之前添加确认对话框等功能。在Rails 7之前,UJS是默认选项,但现在建议使用Turbo。

4.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操作,实际按钮和表单更可取。

4.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 "删除帖子", post, method: :delete, form: { data: { turbo_confirm: "确定要删除吗?" } } %>

4.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

反馈

欢迎您帮助改进本指南的质量。

如果您发现任何拼写错误或事实错误,请贡献您的意见。 要开始,请阅读我们的 文档贡献 部分。

您还可能会发现不完整的内容或过时的内容。 请为主要内容添加任何缺失的文档。请先检查 Edge 指南,以验证问题是否已经修复或尚未修复。 请参阅 Ruby on Rails 指南准则 以了解样式和规范。

如果您发现需要修复但无法自行修复的问题,请 提交问题

最后但同样重要的是,欢迎您在 官方 Ruby on Rails 论坛 上讨论有关 Ruby on Rails 文档的任何问题。