发布于 2026-01-06 3 阅读
0

Web 组件中 API 调用的优化概述

优化 Web 组件中的 API 调用

概述

概述

构建 Web 组件时,您可能需要从外部实体获取资源。这通常被称为“API 调用”。在本篇博文中,我将探讨如何使用 JavaScript 的fetch方法以及一些其他技巧来优化 API 调用,从而获取数据。本文中,我将引用我为elmsln/lrnwebcomponentsWeb 组件 mono-repo 编写的一个元素中的代码。
该元素的完整源代码位于github-preview-source。

要点

浏览器如何加载 JavaScript

这看起来似乎是个简单的概念:你把脚本添加到 HTML 页面中,JavaScript 就加载完毕了。但实际上,浏览器做的远不止加载脚本这么简单。JavaScript 基于异步处理的概念,也就是说,浏览器在执行页面设置等其他操作的同时,也会处理 JavaScript 代码。这一切都发生得非常迅速,但都在浏览器的事件循环中完成。

事件循环会将任务放入队列中,执行每个任务并等待其完成,然后再执行队列中的下一个任务。理解这一点很重要,因为我们的 API 调用会被注册为一个任务,并由于我们将其封装在 `setTimeout` 调用中而排在脚本后面。稍后会详细介绍……

使用 fetch 发起 API 请求

这或许很简单,但我还是要讲解一下。在我的 Web 组件中,我会定义一个名为 `getData()` 的函数fetchGithubData。这个函数会接收一些参数,用于调用 GitHub 的 API,并将数据返回给一个处理方法。处理方法会将数据保存到我们的元素中,以便显示出来。

fetchGithubData(apiUrl, org, repo){
   fetch(`${apiUrl}/repos/${org}/${repo}`)
      .then((response) => {
        if (response.ok) {
          return response.json();
        }
      })
      .then((json) => {
        this.handleResponse(json);
      })
      .catch((error) => {
        this.__assetAvailable = false;
        console.error(error);
      });
}
Enter fullscreen mode Exit fullscreen mode

该函数接收一个 apiUrl(https://api.github.com)、一个 GitHub 组织或用户以及一个存储库名称,然后将该数据提供给我们的处理函数。

handleResponse(response) {
    if (response) {
      this.__assetAvailable = true;
      this.__description = response.description;
      this.repoLang = response.language;
      this.__stars = response.stargazers_count;
      this.__forks = response.forks;
    }
  }
Enter fullscreen mode Exit fullscreen mode

我们的数据处理程序首先检查是否收到响应,如果收到响应,它会设置一些属性,这些属性会在我们的 Web 组件中呈现。

以下是我们的 Web 组件示例,供您参考。 可以看到,它包含了我们在处理方法中设置的一些属性,例如标题、仓库的主要语言、分支数、描述和星标数。
替代文字

发光元素生命周期方法

由于我的元素使用了lit-element库,我们将利用生命周期方法来发起API调用。lit-element提供了几个生命周期方法,但我们将重点关注其中的两个:`on`firstUpdated和`on` updated

firstUpdated方法会在 DOM 注册元素后立即被调用。updated生命周期方法紧随firstUpdated其后,我们将在此处发起 API 调用。

我们希望在更新函数中调用 API,因为如果元素挂载并渲染后仓库或组织发生变化,我们可以响应这种变化,因为我们的元素已经“更新”了。请观看此演示视频,了解为什么我们在更新生命周期方法中使用 API 调用。

如果您有兴趣了解更多关于Lit-Elements生命周期方法的信息,请访问他们的文档:https://lit-element.polymer-project.org/guide/lifecycle

使用超时和防抖

现在我将展示为什么以及如何使用 Javascript 内置setTimeout函数来进行 API 调用。

以下是我们更新后的生命周期方法中的代码。

updated(changedProperties) {
    changedProperties.forEach((oldValue, propName) => {
      // only make the fetch after we get everything setup
      if (
        [
          "repo",
          "org",
        ].includes(propName) &&
        this[propName]
      ) {
        clearTimeout(this.__debounce);
        this.__debounce = setTimeout(() => {
          this.fetchGithubData(
            this.apiUrl,
            this.repo,
            this.org,
          );
        }, 0);
      }
    });
  }
Enter fullscreen mode Exit fullscreen mode

我们使用 forEach 循环遍历每个已更改的属性。您可能会问,那么初始设置的属​​性怎么办?这些属性仍然被视为新属性,并在组件挂载时传递给更新函数。

接下来,我们检查要考虑的属性是否属于该类。如果防抖变量中已经设置了超时时间,则将其清除。这样做是为了确保 API 调用只进行一次,这样当 forEach 循环遍历到最后一个已更改的属性时,超时时间就不会被清除,从而避免再次进行 API 调用。

我们使用 `setTimeout` 是因为浏览器会在文件中所有 JavaScript 代码处理完毕后调用它。这样可以确保在发起 API 调用之前一切就绪。`setTimeout` 回调函数会被添加到浏览器的事件循环队列中,并在文件中所有其他 JavaScript 代码解析完毕后立即被调用。

应用缓存标头

最后,我们会在请求中添加头部信息,告诉浏览器缓存我们的结果(以便稍后使用)。这样可以提高性能。当浏览器再次发出相同的请求时,它会先检查缓存,如果缓存中已有响应对象,则会使用缓存的响应,而不是发出新的请求。

我们可以在元素的构造函数中设置头部信息来实现这一点,如下所示:

constructor() {
    super();
    this.url = "https://github.com";
    this.apiUrl = "https://api.github.com";
    this.rawUrl = "https://raw.githubusercontent.com";
    this.extended = false;
    this.readMe = "README.md";
    this.branch = "master";
    this.viewMoreText = "View More";
    this.notFoundText = "Asset not found";
    this.headers = {
      cache: "force-cache",
    };
  }
Enter fullscreen mode Exit fullscreen mode

然后我们可以在 fetch 调用中使用这些标头。

fetch('https://someendpoint.com/git/', this.headers)
Enter fullscreen mode Exit fullscreen mode

结论

就是这样!如果您有兴趣了解更多关于 Web 组件以及我在本文中提到的一些内容,请查看下面的资源部分。

资源

社交媒体

LinkedIn
GitHub

文章来源:https://dev.to/collinkleest/optimizing-api-calls-in-web-components-14kn