上一章,我们学习了如何使用 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

标签: none