React Router v7 已发布。 查看文档
渐进增强

渐进增强

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

- 维基百科

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

今天,我们很幸运能够为更一致的 Web 进行开发,并且大多数用户都启用了 JavaScript。

然而,我们仍然相信 Remix 中渐进增强的核心原则。它可以实现快速、有弹性的应用程序,并具有简单开发工作流程。

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

弹性:在 JavaScript 加载之前,所有人的 JavaScript 都是禁用的。

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

性能

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

典型的 SPA 发送一个空白文档,只有在 JavaScript 加载后才开始工作。

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

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

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

弹性和可访问性

虽然您的用户可能不会在禁用 JavaScript 的情况下浏览网页,但在 JavaScript 加载完成之前,所有人的 JavaScript 都是禁用的。一旦您开始服务器渲染 UI,您就需要考虑在 JavaScript 加载之前他们尝试与您的应用程序交互时会发生什么。

Remix 通过在其 HTML 之上构建抽象来拥抱渐进增强。这意味着您可以构建无需 JavaScript 即可工作的应用程序,然后叠加 JavaScript 来增强体验。

最简单的例子是 <Link to="/account">。这些会渲染一个 <a href="/account"> 标签,该标签无需 JavaScript 即可工作。当 JavaScript 加载时,Remix 将拦截点击并使用客户端路由处理导航。这让您可以更好地控制用户体验,而不仅仅是在浏览器选项卡中旋转图标 - 但无论哪种方式它都可以工作。

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

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 加载时,Remix 将拦截表单提交并在客户端处理它。这允许您添加自己的待处理 UI 或其他客户端行为。

简单性

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

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

import { useFetcher } from "@remix-run/react";

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 加载时,Remix 将拦截表单提交并在客户端处理它。这允许您添加自己的待处理 UI 或其他客户端行为。这是下一个迭代

import { useNavigation } from "@remix-run/react";

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>
  );
}

架构没有根本变化,只是对用户和代码的渐进增强。

另请参阅:状态管理

文档和示例在 MIT