渐进增强
本页内容

渐进增强

渐进增强是一种网络设计策略,它首先强调网站内容,让每个人都能访问网页的基本内容和功能,同时拥有更多浏览器特性或更快互联网连接的用户可以获得增强版本。

- 维基百科

在使用带有服务器端渲染(框架模式下的默认设置)的 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 等 Web 的基本特性时,您会发现自己对客户端状态和状态管理的需求大大减少。

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

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