使用 Quicklink 加快 React 中的导航速度

使用快捷链接自动预加载 React 单页面应用中的视口内链接。

Addy Osmani
Addy Osmani
Anton Karlovskiy
Anton Karlovskiy
Demián Renzulli
Demián Renzulli

预提取是一种技术,通过提前下载下一页的资源来加快导航速度。快捷链接是一个库,可让您通过在链接进入视野时自动预加载链接,大规模实现此技术。

在多页面应用中,该库会预加载视口内链接的文档(例如 /article.html),以便在用户点击这些链接时,从 HTTP 缓存中提取它们。

单页应用通常使用一种称为基于路由的代码拆分的技术。这样,网站便会仅在用户导航到给定路由时加载相应路由的代码。这些文件(JS、CSS)通常称为“区块”。

话虽如此,在这些网站中,与预提取文档相比,在网页需要数据块之前预先抓取这些数据块可以带来最大的性能提升。

实现这一点存在一些挑战:

  • 在到达给定路由(例如 /article)之前,确定哪些分块(例如 article.chunk.js)与该路由相关联并非易事。
  • 这些块的最终到达网址名称无法预测,因为现代模块打包器通常使用长期哈希技术进行版本控制(例如 article.chunk.46e51.js)。

本指南介绍了 Quicklink 如何解决这些问题,以及如何在 React 单页应用中实现大规模预加载。

确定与每个路由相关联的分块

quicklink 的核心组件之一是 webpack-route-manifest,这是一个 webpack 插件,可让您生成包含路由和分块的 JSON 字典。这样,库便可知道应用的每个路由需要哪些文件,并在路由进入视野时预加载这些文件。

将插件与项目集成后,它会生成一个 JSON 清单文件,将每个路线与其对应的分块相关联:

{
  '/about': [
    {
      type: 'style',
      href: '/static/css/about.f6fd7d80.chunk.css',
    },
    {
      type: 'script',
      href: '/static/js/about.1cdfef3b.chunk.js',
    },
  ],
  '/blog': [
    {
      type: 'style',
      href: '/static/css/blog.85e80e75.chunk.css',
    },
    {
      type: 'script',
      href: '/static/js/blog.35421503.chunk.js',
    },
  ],
}

您可以通过以下两种方式请求此清单文件:

  • 通过网址,例如 https://site_url/rmanifest.json
  • 通过窗口对象,在 window.__rmanifest 处。

针对视口内路由的预提取数据块

清单文件可用后,下一步是运行 npm install quicklink 来安装 Quicklink。

然后,可以使用高阶组件 (HOC) withQuicklink() 指明在链接进入视图时应预提取给定路由。

以下代码属于 React 应用的 App 组件,用于渲染包含四个链接的顶部菜单:

const App = () => (
  <div className={style.app}>
    <Hero />
    <main className={style.wrapper}>
      <Suspense fallback={<div>Loading</div>}>
        <Route path="/" exact component={Home} />
        <Route path="/blog" exact component={Blog} />
        <Route path="/blog/:title" component={Article} />
        <Route path="/about" exact component={About} />
      </Suspense>
    </main>
    <Footer />
  </div>
);

如需告知 Quicklink 应在这些路线进入视野时预提取它们,请执行以下操作:

  1. 在组件开头导入 quicklink HOC。
  2. 使用 withQuicklink() HOC 封装每个路由,并将页面组件和选项参数传递给它。
const options = {
  origins: [],
};
const App = () => (
  <div className={style.app}>
    <Hero />
    <main className={style.wrapper}>
      <Suspense fallback={<div>Loading…</div>}>
        <Route path="/" exact component={withQuicklink(Home, options)} />
        <Route path="/blog" exact component={withQuicklink(Blog, options)} />
        <Route
          path="/blog/:title"
          component={withQuicklink(Article, options)}
        />
        <Route path="/about" exact component={withQuicklink(About, options)} />
      </Suspense>
    </main>
    <Footer />
  </div>
);

withQuicklink() HOC 使用路线路径作为键,从 rmanifest.json 获取与其关联的分块。在后台,当链接进入视野时,该库会在页面中为每个分块注入 <link rel="prefetch"> 标记,以便预提取它们。浏览器会以最低优先级请求预提取的资源,并将其保留在 HTTP 缓存中 5 分钟,之后系统会应用资源的 cache-control 规则。因此,当用户点击链接并移至给定路线时,系统会从缓存中检索分块,从而大大缩短呈现该路线所需的时间。

总结

预先抓取可以极大地缩短未来导航的加载时间。在 React 单页应用中,这可以通过在用户到达每条路线之前加载与路线关联的区块来实现。Quicklink 的 React SPA 解决方案使用 webpack-route-manifest 创建路线和分块映射,以便在链接进入视图时确定要预提取哪些文件。在整个网站中实现此技术可以显著改善导航,使其看起来几乎是即时的。