渐进式增强
本页内容

渐进式增强



渐进式增强是网页设计中的一种策略,它首先强调网页内容,让每个人都能访问网页的基本内容和功能,而拥有额外浏览器功能或更快网速的用户则会获得增强版本。

- 维基百科

当将 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 或其他客户端行为。

简单性

当您开始依赖 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
编辑