上一章,我们学习了如何使用 Data API 加载和持久化控制器的内部状态。

有时,控制器需要追踪外部资源的状态,在外部则意味着不是在 DOM 中,也不是 Stimulus 的一部分。例如,发送 HTTP 请求,以及由于请求状态的改变我们需要产生的应答。或者我们可能想要启动一个定时器,然后在控制器断开连接后停止它。这一章,我们将学习如何实现这两个场景。

异步加载 HTML

学习如何在页面中填充远程异步载入的 HTML 片段。我们在 Basecamp 中使用此技术是初始页面加载更快,保存非用户特定内容的视图使其更有效地缓存。

我们将构建一个通用的内容加载控制器,它从服务器获取 HTML 来填充页面。然后,使用它来加载未读消息,像收件箱那样。

从在 public/index.html 起草收件箱开始:

<div data-controller="content-loader"
     data-content-loader-url="/messages.html"></div>

然后新建 public/messages.html 文件,作为消息列表:

<ol>
  <li>New Message: Stimulus Launch Party</li>
  <li>Overdue: Finish Stimulus 1.0</li>
</ol>

(在真实的应用程序中你应该在服务器上动态地生成 HTML,作为演示,这里我们用静态的就行了。)

实现控制器:

// src/controllers/content_loader_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    this.load()
  }

  load() {
    fetch(this.data.get("url"))
      .then(response => response.text())
      .then(html => {
        this.element.innerHTML = html
      })
  }
}

当控制器取得连接,便开始用 Fetch 请求元素的 data-content-loader-url 指定 URL。然后,加载返回的 HTML 将其指派到元素的 innerHTML 属性。

打开控制台的 “network” 选项卡,刷新页面。你会看到指向 index.html 的初始页面请求,紧接着是控制器发出的指向 messages.html 的请求。

使用定时器自动刷新

改进控制器。通过定期刷新保证收件箱总是最新的。

使用 data-content-loader-refresh-interval 属性指定控制器应该多久重新加载一次内容,以毫秒为单位:

<div data-controller="content-loader"
     data-content-loader-url="/messages.html"
     data-content-loader-refresh-interval="5000"></div>

现在,修改控制器,使其读取刷新时间间隔,如果存在,开启刷新定时器:

connect() {
    this.load()

    if (this.data.has("refreshInterval")) {
      this.startRefreshing()
    }
  }

  startRefreshing() {
    setInterval(() => {
      this.load()
    }, this.data.get("refreshInterval"))
  }
}

刷新页面,在控制台观察每5秒发出一次的请求。然后修改一下 public/messages.html 并等待它出现在收件箱。

释放被追踪的资源

控制器取得连接时开启了定时器,但我们从未使其停下。这就意味着,如果控制器元素消失了,控制器也会继续发送 HTTP 请求。

修改 startRefreshing() 方法,使其保存定时器应用,就能解决问题。然后,在 disconnect()方法中,可以退出。

  disconnect() {
    this.stopRefreshing()
  }

  startRefreshing() {
    this.refreshTimer = setInterval(() => {
      this.load()
    }, this.data.get("refreshInterval"))
  }

  stopRefreshing() {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer)
    }
  }
}

现在便能确保内容加载控制器只会在它连接到 DOM 时才会发送 HTTP 请求了。

看一下最终的控制器类:

// src/controllers/content_loader_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    this.load()

    if (this.data.has("refreshInterval")) {
      this.startRefreshing()
    }
  }

  disconnect() {
    this.stopRefreshing()
  }

  load() {
    fetch(this.data.get("url"))
      .then(response => response.text())
      .then(html => {
        this.element.innerHTML = html
      })
  }

  startRefreshing() {
    this.refreshTimer = setInterval(() => {
      this.load()
    }, this.data.get("refreshInterval"))
  }

  stopRefreshing() {
    if (this.refreshTimer) {
      clearInterval(this.refreshTimer)
    }
  }
}

总结和下一步

在本章,学习了如何使用 Stimulus 的生命周期回调函数来获取和发布外部资源。

下一步,我们将学习何如在应用程序中安装和配置 Stimulus。

Next: Installing Stimulus in Your Application

很多框架怂恿你在 JavaScript 中一直保存着状态。它们将 DOM 看作“只写”的渲染目标,由客户端模板根据来自服务端的 JSON 进行调解。

Stimulus 采用了不同的方式。Stimulus 应用程序的状态以 DOM 属性的方式存在;控制器本身很大程度上是无状态的。这种方式可以用于任意位置初始化的 HTML 文档,Ajax 请求、Turbolinks 访问,甚至另一个 JavaScript 库等,而且不需要任何显式步骤就能自动让控制器焕发活力。

构建幻灯片

在上一章,我们学习了 Stimulus 控制器可以通过添加 class 类名来维持简单的状态。但是在我们需要存储一个值也不是简单的标记时,应该怎么做呢?

我们将通过构建一个幻灯片控制器来研究这个问题,在幻灯片中,需要在属性里保存当前选中的滑块的索引。

同样,从 HTML 开始:

<div data-controller="slideshow">
  <button data-action="slideshow#previous">←</button>
  <button data-action="slideshow#next">→</button>

  <div data-target="slideshow.slide" class="slide">A</div>
  <div data-target="slideshow.slide" class="slide">B</div>
  <div data-target="slideshow.slide" class="slide">C</div>
  <div data-target="slideshow.slide" class="slide">D</div>
</div>

每个滑块目标代表幻灯片里的单个滑块。控制器将负责同一时间只有一个滑块被显示。

默认地,使用 CSS 隐藏所有滑块,仅在 slide--current class 被应用时才显示:

.slide {
  display: none;
}

.slide.slide--current {
  display: block;
}

现在,起草我们的控制器。新建文件 src/controllers/slideshow_controller.js

// src/controllers/slideshow_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.showSlide(0)
  }

  next() {
    this.showSlide(this.index + 1)
  }

  previous() {
    this.showSlide(this.index - 1)
  }

  showSlide(index) {
    this.index = index
    this.slideTargets.forEach((el, i) => {
      el.classList.toggle("slide--current", index == i)
    })
  }
}

控制器定义了一个方法 showSlide(),它将循环每个滑块目标,在滑块被匹配到时切换slide--current class。

通过显示第一个滑块来初始化控制器,next()previous() 操作方法用于向前或向后翻页。

生命周期回调函数解释

initialize() 方法有何作用?与之前所用的connect()方法有何不同?

有一些 Stimulus 回调方法,它们用于在控制器进入或离开文档时创建或销毁相关状态。

方法何时被 Stimulus 调用
initialize()仅一次,控制器被首次实例化时
connect()控制器连接到 DOM 时
disconnect()控制器与 DOM 断开连接时

刷新页面,确认 NEXT 按钮可以跳到下一个滑块。

从 DOM 读取初始状态

注意控制器如何追踪它的状态——当前被选中的滑块——在 this.index 属性 中。

现在,我们想要让第二个滑块替代第一个显示出来。我们应该如何对起始索引编码?

一种方式是从 HTML data 属性中加载初始索引。例如,可以为控制器元素添加 data-slideshow-index 属性:

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

然后,在 initialize()方法中,读取该属性,将它转换为整型,然后传递给 showSlide()

initialize() {
    const index = parseInt(this.element.getAttribute("data-slideshow-index"))
    this.showSlide(index)
  }

在控制器元素中使用 data 属性是很常见的方式,Stimulus 为此提供了 API。不同于直接读取属性的值,使用 this.data.get()更方便:

  initialize() {
    const index = parseInt(this.data.get("index"))
    this.showSlide(index)
  }

Data API 的解释 每个 Stimulus 控制器都有一个 this.data 对象和 has(), get(),和 set() 方法。这些方法为访问控制器元素的 dara 属性带来方便,有控制器标识符限定了作用域。

例如,在上面的控制器中:

  • this.data.has("index") 如果控制器元素拥有 data-slideshow-index 属性,则返回 true
  • this.data.get("index") 以字符串形式返回元素的 data-slideshow-index 属性的值
  • this.data.set("index", index) 将元素的 data-slideshow-index 属性的值设置为 index 的字符串值

如果你的属性名由一个以上单词注册,在 JavaScript 中以驼峰形式表示,在 HTML 中以属性形式表示。例如,读取data-slideshow-current-class-name属性可以使用this.data.get("currentClassName")

添加 data-slideshow-index属性到控制器元素,刷新页面,确认幻灯片从指定的滑块开始滚动。

在 DOM 中持久化状态

我们已经了解如何通过读取 data 属性来引导幻灯片的初始滑块索引。

在浏览幻灯片时,属性不会与控制器的 index 属性同步。如果在文档中科隆控制器元素,克隆的控制器会恢复到它的初始值。

可以通过为 index 属性定义代表 Data API 的 getter 和 setter 来改进控制器:

// src/controllers/slideshow_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "slide" ]

  initialize() {
    this.showCurrentSlide()
  }

  next() {
    this.index++
  }

  previous() {
    this.index--
  }

  showCurrentSlide() {
    this.slideTargets.forEach((el, i) => {
      el.classList.toggle("slide--current", this.index == i)
    })
  }

  get index() {
    return parseInt(this.data.get("index"))
  }

  set index(value) {
    this.data.set("index", value)
    this.showCurrentSlide()
  }
}

这里将 showSlide() 重命名为 showCurrentSlide()并修改使其从 this.index读取值。 get index() 以整型返回控制器元素的 data-slideshow-index 属性。set index()方法设置属性的值,然后刷新当前滑块。

支持,控制器的状态便完全保存在 DOM 中了。

总结和下一步

本章我们了解了如何使用 Stimulus 的 Data API 来加载和保存幻灯片控制器的当前索引。

从可用性角度,控制器尚未完成。考虑如何完善控制器,大概需要解决以下问题:

在查看第一个滑块时,上一页按钮什么事都不用干也显示出来了。在其内部,索引值从0降到-1。是否可以在这里点击上一页按钮跳到最后一个滑块的索引?(此问题同样存在于下一页按钮)。

如果忘了指定 data-slideshow-index 属性,get index() 方法中的 parseInt() 会返回 NaN。在此情况下,是否可以预设一个默认值 0?

下一步,我们将了解如何在 Stimulus 控制器中追踪外部资源,比如定时器和 HTTP 请求等。

Next: Working With External Resources

我们已经实现了第一个控制器,并学习了 Stimulus 是怎样将 HTML 连接到 JavaScript 的。现在,我们看一下 Basecamp 是怎样创建控制器并将它用在真实的应用程序中的。

封装 DOM 剪贴板 API

在 Basecamp 的 UI 中,有类似这样的按钮:
bc3-clipboard-ui.png
点击按钮,Basecamp 就会将文字、URL 或者邮箱地址复制到你的剪贴板中。

Web 平台有这样的系统剪贴板访问 API,但是我们并不需要其中的 HTML 元素。要实现这样的复制按钮,我们必须使用 JavaScript。

实现复制按钮

比如说,我们有一个 APP,允许我们使用生成的 PIN 来授权别人访问。如果在生成的 PIN 旁边有一个可以直接点击就能复制 PIN 的按钮,那么我们就能更方便地分享它了。

打开public/index.html并使用下面这样的按钮替换掉<body>中的内容:

<div>
  PIN: <input type="text" value="1234" readonly>
  <button>Copy to Clipboard</button>
</div>

设置控制器

下一步,新建 src/controllers/clipboard_controller.js 并添加一个空的方法 copy()

// src/controllers/clipboard_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  copy() {
  }
}

在最外层的 <div> 中添加 data-controller="clipboard"。这个属性将在元素上显示,Stimulus 将会连接一个控制器实例:

<div data-controller="clipboard">

定义目标

我们需要引用文本域,这样我们就能在调用剪贴板 API 前选择它的内容了。在文本域元素上添加 `data-target="clipboard.source":

PIN: <input data-target="clipboard.source" type="text" value="1234" readonly>

现在,给控制器添加一个目标定义,这样我们就能使用 this.sourceTarget 访问文本域元素:

export default class extends Controller {
  static targets = [ "source" ]

  // ...
}

static targets 这行是什么? 在 Stimulus 加载控制器类时,它会在被称为目标(targets)的静态数组中寻找目标名称字符串。针对数组中的每个目标名称,Stimulus

会为你的控制器添加三个新属性。这里 "source" 这个目标名称就变成了这样三个属性:

  • this.sourceTarget 在控制器作用域内寻找到的第一个来源目标。如果没有找到,访问此属性会抛出一个错误
  • this.sourceTargets 在控制器作用域内寻找到的由所有来源目标组成的数组。
  • this.hasSourceTarget 如果找到了来源目标,返回 true,否则,返回 false。

连接操作

我们已经准备好装配这个复制按钮了。

我们想要点击这个难就能调用控制器的 copy() 方法,所以需要添加 data-action="clipboard#copy":

<button data-action="clipboard#copy">Copy to Clipboard</button>

常见的事件有一个速记符号 你可能已经注意到了我们省略了操作描述符中的 click->。那是因为 Stimulus 将点击事件设置成了操作 <button> 元素的默认事件。

当然其他元素都有默认事件。这里是完整的列表:

元素默认事件
aclick
buttonclick
formsubmit
inputchange
input type=submitclick
selectchange
textareachange

最后,在 copy() 方法中,我们可以选中输入框中的内容并调用剪贴板 API:

  copy() {
    this.sourceTarget.select()
    document.execCommand("copy")
  }

刷新页面,然后点击复制按钮。然后在文本编辑器中粘贴,你应该看到 PIN 值是1234

设计有弹性的用户界面

尽管剪贴板 API 在当前浏览器里能得到很好的支持,但可能还会有一小部分人在通过老版本浏览器使用我们的应用程序。

我们还应该意识到人民在访问应用程序时,可能会遇到一些问题。例如,网络连接问题或者 CDN 可用性问题等,都有可能影响到 JavaScript 的加载。

你可能觉得向后支持老版本浏览器是不值得去做的,或者,网络问题只是一时的,只需要刷新一下就好了。但是往往能有一些简单的方式来优雅地应对此类问题。

所谓有弹性的方式,通常被理解为渐进增强,它是构建 web 界面的一种实践,最基础的功能由 HTML 和 CSS 实现,然后再在此基础上使用 CSS 和 JavaScript 简历基础的用户体验,逐步地根据浏览器支持实现最佳效果。

PIN 字段的渐进增强

我们一起看看,如何对 PIN 字段进行渐进增强,隐藏复制按钮,除非它被浏览器支持。这样就能避免给别人展示不能用的按钮。

首先在 CSS 中隐藏复制按钮。然后测试 Stimulus 控制器中剪贴板 API 的支持情况。如果 API 被支持,我们可以控制器元素上添加一个 CSS 类名将按钮显示出来。

为按钮添加 class="clipboard-button"

<button data-action="clipboard#copy" class="clipboard-button">Copy to Clipboard</button>

然后,将下拉样式添加到 public/main.css

.clipboard-button {
  display: none;
}

.clipboard--supported .clipboard-button {
  display: initial;
}

在控制器中实现 connect() 方法,它可以在 API 被支持时添加 CSS 类名 到控制器元素上:

  connect() {
    if (document.queryCommandSupported("copy")) {
      this.element.classList.add("clipboard--supported")
    }
  }

如果愿意,你可以在浏览器中禁用 JavaScript,刷新页面,注意,复制按钮不见了。

我们对 PIN 字段进行了渐进增强:复制按钮的基线状态是隐藏,只有在 JavaScript 探测到剪贴板 API 被支持时才显示出来。

Stimulus 控制器可复用

到目前位置,我们看到了同一时间页面上有一个控制器实例时会发生什么。

在页面上同时存在多个控制器实例也不是罕见现象。例如,我们想要显示一个 PIN 的列表,每一个都有它的复制按钮。

控制器是可复用的:任意时候我们想要提供一种方式去向剪贴板复制一段文本,我们只需要页面上添加一段带着正确注解的代码。

我们来试试再在页面上添加一个 PIN。复制并粘贴<div>便有了两个完全一样的 PIN 字段,修改第二个的 value 属性:

<div data-controller="clipboard">
  PIN: <input data-target="clipboard.source" type="text" value="3737" readonly>
  <button data-action="clipboard#copy" class="clipboard-button">Copy to Clipboard</button>
</div>

刷新并确认两个按钮都能使用。

操作和目标可以用在任意元素上

再添加一个 PIN 字段。这次我们使用复制链接来替换原有的按钮:

<div data-controller="clipboard">
  PIN: <input data-target="clipboard.source" type="text" value="3737" readonly>
  <a href="#" data-action="clipboard#copy" class="clipboard-button">Copy to Clipboard</a>
</div>

Stimulus 允许我们使用任意元素,只要它拥有正确的 data-action 就行。

注意,在此时,点击链接同样会引起浏览器跳转到链接的 href 所指地址。可以在 action 中调用 event.preventDefault() 禁用默认行为:

  copy(event) {
    event.preventDefault()
    this.sourceTarget.select()
    document.execCommand("copy")
  }

同样地,来源目标不需要是 <input type="text">。控制器只期望它拥有 value 熟悉和 select() 方法。这意味着,可以使用 <textarea> 替代:

PIN: <textarea data-target="clipboard.source" readonly>3737</textarea>

总结和下一步

在本章,我们了解在 Stimulus 控制器中封装浏览器 API 的真是案例。还稍作修改让控制器针对旧版浏览器和网络问题变得有弹性。还了解了在页面上可以同时存在控制器的多个实例。最后,我们探索了操作和目标的解耦。

下一步,我们将学习 Stimulus 控制器的状态管理。

Next: Managing State

学习 Stimulus 的最简单方式就是构建一个简单的控制器。这一章我们就来学习一个。

先决条件

要往下继续,你需要运行一个 stimulus-starter ,它是一个用于探索 Stimulus 的预配置空白骨架。

推荐使用 remixing stimulus-starter on Glitch ,它能完整地在浏览器里运行,而且不需要再安装其他东西:
Remix on Glitch

或者,如果你喜欢使用自己的舒适的文本编辑器,您将需要克隆并设置stimulus-starter:

$ git clone https://github.com/stimulusjs/stimulus-starter.git
$ cd stimulus-starter
$ yarn install
$ yarn start请输入代码 

然后在浏览器中访问 http://localhost:9000/

(注意 stimulus-starter 使用 Yarn 来管理依赖,你得先安装它。)

从 HTML 起步

我们从一个简单的练习开始:带按钮的文本域。点击按钮时,让文本域的值显示在控制台。

每个 Stimulus 项目都是从 HTML 开始的,此项目也不例外。编辑 public/index.html 并在 <body> 标签后面添加以下代码:

<div>
  <input type="text">
  <button>Greet</button>
</div>

然后刷新页面,便能看到文本域和按钮了。

控制器为 HTML 带来生命力

作为其核心,Stimulus 的目标是自动将 DOM 元素连接到 JavaScript 对象。这些对象由控制器调用。

我们一起来通过拓展框架内置的控制器类创建第一个控制器。在 src/controllers/ 文件夹里创建一个hello_controller.js文件。然后放入以下代码:

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
}

使用标识符将控制器和 DOM 链接起来

下一步,我们需要告诉 Stimulus 控制器应该怎样连接到 HTML。在 <div>data-controller 属性中添加标识符即可:

<div data-controller="hello">
  <input type="text">
  <button>Greet</button>
</div>

标识符充当了元素和控制器之间的链接。在此例中,标识符 hello 告诉 Stimulus 去 hello_controller.js 创建一个控制器的实例。在第六章《安装指南》中可以了解更多关于如何动态加载控制器。

检查是否生效

刷新页面,你会看到任何改变都没有。我们应该如何知道控制器有没有生效?

一种方式是在 connect() 方法中添加一个 log 状态,控制器链接到
HTML 时 Stimulus 就会触发它。

hello_controller.js 实现 connect()方法:

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  connect() {
    console.log("Hello, Stimulus!", this.element)
  }
}

刷新页面,你就能在控制台中看到Hello, Stimulus!了。

Actions Respond to DOM Events

现在,我们看下,如何修改代码实现点击“Greet”按钮时显示 log 信息。

首先,将 connect() 重命名为 greet()

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  greet() {
    console.log("Hello, Stimulus!", this.element)
  }
}

我们想要在按钮的点击事件触发时调用 greet() 方法。在 Stimulus 中,处理事件的控制器方法叫做操作(action)方法。

将操作方法连接到按钮的点击事件。打开 public/index.html 并添加到按钮的魔法属性 data-action 上:

<div data-controller="hello">
  <input type="text">
  <button data-action="click->hello#greet">Greet</button>
</div>

操作描述符(action descriptor)解释

data-action的值 value click->hello#greet被成为操作描述符。这里的意思是:

  • click 是事件名称
  • hello 是控制器标识符
  • greet 要调用的方法名

刷新页面并打开开发者控制台。你就能在点击“Greet”按钮时看到 log 信息了。

将重要元素映射到控制器属性中

我们即将完成此次练习,修改 action 使其对我们输入到文本域里的任何名字都说 hello。

要实现它,首先我们需要在控制器中引用 input 元素。然后就能通过读取它的值获取输入的内容了。

Stimulus 让我们将重要元素标记为目标(target),然后我们就能轻松地在控制器中通过相应的属性引用这些元素了。打开public/index.html添加魔法属性data-targetinput 元素:

<div data-controller="hello">
  <input data-target="hello.name" type="text">
  <button data-action="click->hello#greet">Greet</button>
</div>

﹟目标描述符(Target Descriptors)解释
data-target的值hello.name被称为目标描述符. 这里的意思是:

  • hello 是控制器标识符
  • name 是目标的名称

在我们将 name 添加到控制器的目标描述符列表中时,Stimulus 会自动创建一个this.nameTarget属性,它会返回第一个匹配到的目标元素。我们可以使用这个属性读取元素的值并构建欢迎字符串。

我们来试试。打开hello_controller.js然后这样修改:

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name" ]

  greet() {
    const element = this.nameTarget
    const name = element.value
    console.log(`Hello, ${name}!`)
  }
}

然后刷新页面,打开控制塔。在输入框中输入你的名字,点击“Greet”按钮。Hello, world!

控制器简化重构

我们已经知道 Stimulus 控制器是 JavaScript 类的实例。该类的方法可以作为事件处理器使用。

这意味这我们拥有了标准的重构技术兵工厂。比如,我们可以通过提取name getter 来清理 greet() 方法:

// src/controllers/hello_controller.js
import { Controller } from "stimulus"

export default class extends Controller {
  static targets = [ "name" ]

  greet() {
    console.log(`Hello, ${this.name}!`)
  }

  get name() {
    return this.nameTarget.value
  }
}

总结和后续步骤

恭喜!你写出了你的第一个 Stimulus 控制器!

本文我们覆盖了该框架的核心概念:controllers, identifiers, actions, 和 targets。在下一章,我们将了解如何组件这些东西构建正儿八经的控制器,呃,像 Basecamp 那样。

Next: Building Something Real

Stimulus 1.0: 一个针对你已有的 HTML 的 JavaScript 框架

Stimulus 是一款拥有谦逊理想的 JavaScript 框架。Stimulus 不会把你的应用程序整个搬到前端,它的设计理念是通过自动将元素连接到 JavaScript 来强化你的 HTML。

将 HTML 连接到 JavaScript

Stimulus 的工作方式是不停地监控页面,等待 data-controller 这个魔法属性出现。就像 CSS 的 class属性,你可以在它里面放入多个值。不同于 CSS 类名的使用,data-controller 的值是用于与 Stimulus 控制器连接或断开连接的。

你可以这样认为:class 是连接 HTML 和 CSS 的桥梁,data-controller 是连接 HTLM 和 JavaScript 的桥梁。

在此基础上,Stimulus 增加了魔法属性data-action,它描述了页面上的事件应该怎样触发控制器里的方法;还有魔法属性 data-target,它为你提供了在控制器作用域(controller’s scope)中寻找元素的途径(handle )。

关注点分离

Stimulus 的魔法属性让你清晰地将内容与行为分离,如同你使用 CSS 将内容与形式分离那样。此外,Stimulus 的自然约定也鼓励你按名称对相关联的代码进行分组。

这样的安排能帮助你构建可复用的,类似 trait 的控制器,为你的代码带来足够使用的结构层次,使之远离 “JavaScript 乱炖汤”。

可阅读的文档

当你的 JavaScript 行为是由魔法属性映射的,你可以阅读一段 HTML 便能知道发生了什么。这是一个受欢迎的方案,比如当你六个月后返回一个模板,不记得各种东西是如何组合在一起的时候,就能明白了。

可读的代码,也意味着团队中的其他人也能通过阅读模版或者开发者控制台,来快速地追踪行为或判断问题。

水是温暖的,不坑

现在是时候来探索一下 Stimulus 的工作方式了。继续阅读,学习如何构建你的第一个控制器。