渐进增强

渐进增强

渐进增强是网页设计中的一种策略,它首先强调网页内容,允许每个人访问网页的基本内容和功能,而具有更多浏览器功能或更快的互联网访问的用户则会收到增强版本。

- 维基百科

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

如今,我们很幸运能够为一个更加一致的网络开发,并且大多数用户都启用了 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 加载完成之前都处于禁用状态。一旦您开始服务器端渲染 UI,您就需要考虑在 JavaScript 加载之前用户尝试与您的应用程序交互时会发生什么。

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

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

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

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,而不是在浏览器中旋转 favicon

这并不是要以两种不同的方式构建它——一次用于 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