使用外部资源
在上一章中,我们学习了如何使用值加载和持久化控制器的内部状态。
有时,我们的控制器需要跟踪外部资源的状态,其中外部是指 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。