Rails 8 默认 JavaScript 框架 Stimulus 高级用法

1. 访问浏览器系统 API 实现剪贴板

app/views/posts/_post.html.erb

    ...
    <div data-controller="clipboard">
      <input data-clipboard-target="source" type="text" value="<%= post.title %>" readonly>
      <button data-action="clipboard#copy">Copy to Clipboard</button>
    </div>

遵循“渐进式增强”理念,首先实现 HTML,然后添加 Stimulus 三要素:controller,action,target。 data-controller 属性与 js 文件名对应,data-action 属性与 js 里的方法名对应,target 为需要进行操作的目标。

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "source" ]
  copy(event) {
    navigator.clipboard.writeText(this.sourceTarget.value)
    alert('success')
  }
}

navigator.clipboard.writeText 是现代浏览器标准 js 剪贴板 API

每个 elements 都有默认的 events,所以上面data-action="click->clipboard#copy"实际上省略了 click->

ElementDefault Event
aclick
buttonclick
detailstoggle
formsubmit
inputinput
input type=submitclick
selectchange
textareainput

2. 通过状态管理实现幻灯片

大多数当代框架都鼓励始终将状态保存在 JavaScript 中。它们将 DOM 视为只可写入的渲染目标,通过客户端模板从服务器上消费 JSON 来调节。

Stimulus 采用了不同的方法。Stimulus 应用程序的状态以属性的形式存在于 DOM 中;而控制器本身在很大程度上是无状态的。通过这种方法,我们可以在任何地方(初始文档、Ajax 请求、Turbo 访问,甚至是另一个 JavaScript 库)处理修改 HTML,并且无需任何显式初始化步骤,相关控制器就会自动启动。

app/views/posts/index.html.erb

<div data-controller="slideshow" data-slideshow-index-value="1">
  <button data-action="slideshow#previous"></button>
  <button data-action="slideshow#next"></button>

  <div data-slideshow-target="slide">🐵</div>
  <div data-slideshow-target="slide">🙈</div>
  <div data-slideshow-target="slide">🙉</div>
  <div data-slideshow-target="slide">🙊</div>
</div>
  • controller: 一般为最外层 div DOM
  • action: 有触发事件(例如:click)的 div DOM
  • target: 需要被修改的 div DOM
  • value: 一般放在需要调用值的外层 div

app/javascript/controllers/slideshow_controller.js

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
  static targets = [ "slide" ]
  static values = { index: Number }

  next() {
    if (this.indexValue < 3) {
      this.indexValue++
    }
    else {
      this.indexValue -= 3;
    }
  }

  previous() {
    if (this.indexValue > 0)
    {
      this.indexValue--
    }
    else {
      this.indexValue += 3
    }
  }

  indexValueChanged() {
    this.showCurrentSlide()
  }

  showCurrentSlide() {
    this.slideTargets.forEach((element, index) => {
      element.hidden = index !== this.indexValue
    })
  }
}

2.1 生命周期

控制器定义了一个方法 showCurrentSlide() ,该方法在每个幻灯片目标上循环,如果索引匹配,则切换 hidden 属性。 显示第一张幻灯片来初始化控制器,而 next() 和 previous() 操作方法则前进和后退当前幻灯片。 initialize() 方法有什么作用?它与我们之前使用过的 connect() 方法有何不同?

MethodInvoked by Stimulus…
initialize()一旦控制器首次实例化
connect()控制器连接到 DOM 的任何时候
disconnect()控制器与 DOM 断开连接时

这些都是 Stimulus 生命周期回调方法,当控制器进入或离开 DOM 时,它们可以用来设置或删除相关状态。 这里我们使用 initialize() 设置幻灯片的初始状态。

2.2 使用 Stimulus 的值 values

可以理解为values为Stimulus中另外一个有同的关键字,类似targets

Stimulus 控制器支持自动映射到数据属性的类型值属性。当我们在控制器类的顶部添加一个值定义时:

  static values = { index: Number }

Stimulus 将创建一个与 data-slideshow-index-value 属性相关联的 this.indexValue 控制器属性,并为我们处理数值转换。 在我们的 HTML 中添加相关的数据属性:

<div data-controller="slideshow" data-slideshow-index-value="1">

这样我们就能在 js 中使用 this.indexValue 操纵数据。假如我们需要加入别的值:

  static values = { index: Number, other: Number }

2.3 设置默认值

还可以在静态定义中设置默认值:

  static values = { index: { type: Number, default: 2 } }

如果控制器元素上没有定义 data-slideshow-index-value 属性,索引将从 2 开始。如果你有其他值,你可以混合并匹配哪些需要默认值,哪些不需要:

  static values = { index: Number, effect: { type: String, default: "kenburns" } }

源码:https://github.com/memorycancel/rails-8-demo/compare/stimulus


回到最上

Copyright © Tommy(memorycancel). 朝花夕拾 2025