跳至内容

使用外部资源

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

有时,我们的控制器需要跟踪外部资源的状态,其中外部是指 DOM 或 Stimulus 中不存在的任何内容。例如,我们可能需要发出 HTTP 请求,并在请求状态更改时做出响应。或者,我们可能希望启动一个计时器,然后在控制器不再连接时停止它。在本章中,我们将了解如何执行这两项操作。

异步加载 HTML

让我们学习如何通过加载和插入远程 HTML 片段来异步填充页面部分。我们在 Basecamp 中使用此技术来保持我们的初始页面加载速度,并使我们的视图不包含特定于用户的的内容,以便可以更有效地缓存它们。

我们将构建一个通用的内容加载器控制器,它使用从服务器获取的 HTML 填充其元素。然后,我们将使用它加载未读消息列表,就像你在电子邮件收件箱中看到的那样。

首先在 public/index.html 中绘制收件箱

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

然后创建一个新的 public/messages.html 文件,其中包含一些用于我们的消息列表的 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 "@hotwired/stimulus"

export default class extends Controller {
static values = { url: String }

connect() {
this.load()
}

load() {
fetch(this.urlValue)
.then(response => response.text())
.then(html => this.element.innerHTML = html)
}
}

当控制器连接时,我们启动一个 Fetch 请求,请求 URL 指定在元素的 data-content-loader-url-value 属性中。然后,我们将返回的 HTML 加载到我们的元素的 innerHTML 属性中。

在浏览器的开发者控制台中打开网络选项卡并重新加载页面。你将看到一个表示初始页面加载的请求,然后是我们的控制器对 messages.html 的后续请求。

使用计时器自动刷新

让我们通过定期刷新收件箱来改进我们的控制器,以便它始终保持最新状态。

我们将使用 data-content-loader-refresh-interval-value 属性来指定控制器应重新加载其内容的频率,单位为毫秒

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

现在,我们可以更新控制器以检查间隔,如果存在,则启动刷新计时器。

向控制器添加一个 static values 定义,并定义一个新方法 startRefreshing()

export default class extends Controller {
static values = { url: String, refreshInterval: Number }

startRefreshing() {
setInterval(() => {
this.load()
}, this.refreshIntervalValue)
}

// …
}

然后更新 connect() 方法,以便在存在间隔值时调用 startRefreshing()

  connect() {
this.load()

if (this.hasRefreshIntervalValue) {
this.startRefreshing()
}
}

重新加载页面,并在开发者控制台中观察每五秒一次的新请求。然后对 public/messages.html 进行更改,并等待它出现在收件箱中。

释放跟踪的资源

当控制器连接时,我们启动计时器,但我们从不停止它。这意味着如果我们的控制器的元素消失,控制器将继续在后台发出 HTTP 请求。

我们可以通过修改我们的 startRefreshing() 方法来保留对计时器的引用,从而解决此问题

  startRefreshing() {
this.refreshTimer = setInterval(() => {
this.load()
}, this.refreshIntervalValue)
}

然后,我们可以在下面添加一个相应的 stopRefreshing() 方法来取消计时器

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

最后,为了指示 Stimulus 在控制器断开连接时取消计时器,我们将添加一个 disconnect() 方法

  disconnect() {
this.stopRefreshing()
}

现在,我们可以确保内容加载器控制器仅在连接到 DOM 时发出请求。

让我们看看我们的最终控制器类

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

export default class extends Controller {
static values = { url: String, refreshInterval: Number }

connect() {
this.load()

if (this.hasRefreshIntervalValue) {
this.startRefreshing()
}
}

disconnect() {
this.stopRefreshing()
}

load() {
fetch(this.urlValue)
.then(response => response.text())
.then(html => this.element.innerHTML = html)
}

startRefreshing() {
this.refreshTimer = setInterval(() => {
this.load()
}, this.refreshIntervalValue)
}

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

使用操作参数

如果我们希望加载器与多个不同的源一起工作,我们可以使用操作参数来实现。采用此 HTML

<div data-controller="content-loader">
<a href="#" data-content-loader-url-param="/messages.html" data-action="content-loader#load">Messages</a>
<a href="#" data-content-loader-url-param="/comments.html" data-action="content-loader#load">Comments</a>
</div>

然后,我们可以通过 load 操作使用这些参数

import { Controller } from "@hotwired/stimulus"

export default class extends Controller {
load({ params }) {
fetch(params.url)
.then(response => response.text())
.then(html => this.element.innerHTML = html)
}
}

我们甚至可以解构参数以仅获取 URL 参数

  load({ params: { url } }) {
fetch(url)
.then(response => response.text())
.then(html => this.element.innerHTML = html)
}

总结和后续步骤

在本章中,我们了解了如何使用 Stimulus 生命周期回调获取和释放外部资源。

接下来,我们将了解如何在您自己的应用程序中安装和配置 Stimulus。

下一步:在您的应用程序中安装 Stimulus