渐进增强
本页内容

渐进增强

渐进增强是一种网页设计策略,它首先强调网页内容,允许所有人访问网页的基本内容和功能,而拥有额外浏览器功能或更快互联网访问速度的用户则可以获得增强版本。

- 维基百科

当将 React Router 与服务器端渲染(框架模式下的默认设置)结合使用时,您可以自动利用渐进增强的优势。

为什么渐进增强很重要

这个短语由 Steven Champeon 和 Nick Finck 于 2003 年创造,它出现在不同浏览器对 CSS 和 JavaScript 的支持各不相同的时期,当时许多用户实际上是在禁用 JavaScript 的情况下浏览网页的。

今天,我们很幸运能够在网络环境更加统一且大多数用户都启用 JavaScript 的情况下进行开发。

然而,我们仍然相信 React Router 中渐进增强的核心原则。它可以带来快速、弹性的应用程序以及简单化的开发工作流程。

性能:虽然很容易认为只有 5% 的用户连接速度较慢,但现实情况是 100% 的用户在 5% 的时间内连接速度较慢。

弹性:在 JavaScript 加载完成之前,所有人都是在禁用 JavaScript 的情况下使用网站的。

简洁性:使用 React Router 以渐进增强的方式构建应用程序实际上比构建传统的 SPA 更简单。

性能

服务器渲染允许你的应用比典型的 单页应用 (SPA) 并行执行更多操作,从而加快初始加载体验和后续导航速度。

典型的 SPA 发送一个空白文档,并且仅在 JavaScript 加载完成后才开始工作

HTML        |---|
JavaScript      |---------|
Data                      |---------------|
                            page rendered 👆

React Router 应用程序可以在请求到达服务器的那一刻开始工作并流式传输响应,以便浏览器可以并行开始下载 JavaScript、其他资源和数据

               👇 first byte
HTML        |---|-----------|
JavaScript      |---------|
Data        |---------------|
              page rendered 👆

弹性和可访问性

虽然你的用户可能不会在禁用 JavaScript 的情况下浏览网页,但在 JavaScript 加载完成之前,所有人都是在没有 JavaScript 的情况下使用网站的。 React Router 通过构建在 HTML 之上拥抱渐进增强,允许你以无需 JavaScript 即可工作的方式构建应用程序,然后在之上叠加 JavaScript 以增强体验。

最简单的情况是 <Link to="/account">。这些渲染一个 <a href="/account"> 标签,该标签在没有 JavaScript 的情况下也能工作。当 JavaScript 加载时,React Router 将拦截点击事件并使用客户端路由处理导航。这让你能够更好地控制用户体验,而不是仅仅在浏览器标签页中旋转网站图标——但无论哪种方式,它都能正常工作。

现在考虑一个简单的添加到购物车按钮

export function AddToCart({ id }) {
  return (
    <Form method="post" action="/add-to-cart">
      <input type="hidden" name="id" value={id} />
      <button type="submit">Add To Cart</button>
    </Form>
  );
}

JavaScript 是否已加载并不重要,此按钮都会将产品添加到购物车。

当 JavaScript 加载时,React Router 将拦截表单提交并在客户端处理它。这允许你添加自己的待定 UI 或其他客户端行为。

简洁性

当你开始依赖于 Web 的基本功能(如 HTML 和 URL)时,你会发现你对客户端状态和状态管理的需求会大大减少。

考虑之前的按钮,在代码没有根本性改变的情况下,我们可以加入一些客户端行为

import { useFetcher } from "react-router";

export function AddToCart({ id }) {
  const fetcher = useFetcher();

  return (
    <fetcher.Form method="post" action="/add-to-cart">
      <input name="id" value={id} />
      <button type="submit">
        {fetcher.state === "submitting"
          ? "Adding..."
          : "Add To Cart"}
      </button>
    </fetcher.Form>
  );
}

此功能在 JavaScript 加载时仍然像以前一样工作,但一旦 JavaScript 加载

  • useFetcher 不再像 <Form> 那样引起导航,因此用户可以停留在同一页面并继续购物
  • 应用程序代码决定待定 UI,而不是在浏览器中旋转网站图标

这不是要用两种不同的方式构建它——一种用于 JavaScript,一种用于没有 JavaScript 的情况——而是要以迭代的方式构建它。从最简单的功能版本开始并发布;然后迭代到增强的用户体验。

用户不仅将获得渐进增强的体验,而且应用程序开发人员也可以“渐进地增强”UI,而无需更改功能的基本设计。

渐进增强带来简洁性的另一个例子是 URL。当你从 URL 开始时,你无需担心客户端状态管理。你可以只使用 URL 作为 UI 的真理来源。

export function SearchBox() {
  return (
    <Form method="get" action="/search">
      <input type="search" name="query" />
      <SearchIcon />
    </Form>
  );
}

此组件不需要任何状态管理。它只是渲染一个提交到 /search 的表单。当 JavaScript 加载时,React Router 将拦截表单提交并在客户端处理它。这是下一个迭代版本

import { useNavigation } from "react-router";

export function SearchBox() {
  const navigation = useNavigation();
  const isSearching =
    navigation.location.pathname === "/search";

  return (
    <Form method="get" action="/search">
      <input type="search" name="query" />
      {isSearching ? <Spinner /> : <SearchIcon />}
    </Form>
  );
}

架构上没有根本性的改变,仅仅是对用户和代码的渐进增强。

另请参阅:状态管理

文档和示例 CC 4.0