渐进式增强

渐进式增强

¥Progressive Enhancement

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

¥Progressive enhancement is a strategy in web design that puts emphasis on web content first, allowing everyone to access the basic content and functionality of a web page, whilst users with additional browser features or faster Internet access receive the enhanced version instead.

- 维基百科

¥- Wikipedia

这个短语由 Steven Champeon 和 Nick Finck 于 2003 年提出,当时不同浏览器对 CSS 和 JavaScript 的支持各不相同,许多用户实际上在浏览网页时禁用了 JavaScript。

¥Coined in 2003 by Steven Champeon & Nick Finck, the phrase emerged during a time of varied CSS and JavaScript support across different browsers, with many users actually browsing the web with JavaScript disabled.

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

¥Today, we are fortunate to develop for a much more consistent web and where the majority of users have JavaScript enabled.

不过,我们仍然坚信 Remix 渐进增强的核心原则。它可以通过简单的开发工作流程实现快速且弹性的应用。

¥However, we still believe in the core principles of progressive enhancement in Remix. It leads to fast and resilient apps with simple development workflows.

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

¥Performance: While it's easy to think that only 5% of your users have slow connections, the reality is that 100% of your users have slow connections 5% of the time.

弹性:每个人都禁用了 JavaScript,直到它加载完成。

¥Resilience: Everybody has JavaScript disabled until it's loaded.

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

¥Simplicity: Building your apps in a progressively enhanced way with Remix is actually simpler than building a traditional SPA.

性能

¥Performance

与典型的单页应用 (SPA) 相比,从服务器发送 HTML 可以让你的应用并行执行更多操作,从而加快初始加载体验和后续导航速度。

¥Sending HTML from the server allows your app to do more things in parallel than a typical Single Page App (SPA), making the initial loading experience and subsequent navigations faster.

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

¥Typical SPAs send a blank document and only start doing work when JavaScript has loaded.

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

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

¥A Remix app can start doing work the moment the request hits the server and stream the response so that the browser can start downloading JavaScript, other assets, and data in parallel.

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

弹性和可访问性

¥Resilience and Accessibility

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

¥While your users probably don't browse the web with JavaScript disabled, everybody has JavaScript disabled until it has finished loading. As soon as you begin server rendering your UI, you need to account for what happens when they try to interact with your app before JavaScript has loaded.

Remix 通过在 HTML 之上构建抽象来支持渐进式增强。这意味着你可以先以无需 JavaScript 的方式构建应用,然后再添加 JavaScript 来增强体验。

¥Remix embraces progressive enhancement by building its abstraction on top of HTML. This means that you can build your app in a way that works without JavaScript and then layer on JavaScript to enhance the experience.

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

¥The simplest case is a <Link to="/account">. These render an <a href="/account"> tag that works without JavaScript. When JavaScript loads, Remix will intercept clicks and handle the navigation with client side routing. This gives you more control over the UX instead of just spinning favicons in the browser tab--but it works either way.

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

¥Now consider a simple "add to cart" button.

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 是否已加载无关紧要;此按钮会将产品添加到购物车。

¥Whether JavaScript has loaded or not doesn't matter; this button will add the product to the cart.

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

¥When JavaScript loads, Remix will intercept the form submission and handle it client side. This allows you to add your own pending UI, or other client side behavior.

简易性

¥Simplicity

当你开始依赖 HTML 和 URL 等 Web 基本功能时,你会发现自己对客户端状态和状态管理的依赖会大大减少。

¥When you start to rely on basic features of the web like HTML and URLs, you will find that you reach for client side state and state management much less.

假设之前的按钮,代码没有根本性变化;我们可以添加一些客户端行为:

¥Consider the button from before, with no fundamental change to the code; we can pepper in some client side behavior:

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 加载完成后:

¥This feature continues to work the very same as it did before when JavaScript was loading, but once JavaScript loads:

  • useFetcher 不再像 <Form> 那样触发导航,因此用户可以停留在同一页面继续购物。

    ¥useFetcher no longer causes a navigation like <Form> does, so the user can stay on the same page and keep shopping

  • 应用代码决定待处理的 UI,而不是在浏览器中旋转图标。

    ¥The app code determines the pending UI instead of spinning favicons in the browser

它不是用两种不同的方式构建它 - 一次使用 JavaScript,一次不使用 - 而是通过迭代构建它。从该功能的最简单版本开始并发布;然后迭代以增强用户体验。

¥It's not about building it two different ways–once for JavaScript and once without–it's about building it in iterations. Start with the simplest version of the feature and ship it; then iterate to an enhanced user experience.

用户不仅将获得逐步增强的体验,而且应用开发者可以在不改变该功能基本设计的情况下将 UI "逐步增强" 化。

¥Not only will the user get a progressively enhanced experience, but the app developer gets to "progressively enhance" the UI without changing the fundamental design of the feature.

另一个渐进式增强带来简化的例子是 URL。当你从 URL 开始构建时,你无需担心客户端状态管理。你可以将 URL 用作 UI 的真实来源。

¥Another example where progressive enhancement leads to simplicity is with the URL. When you start with a URL, you don't need to worry about client side state management. You can use the URL as the source of truth for the UI.

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

此组件不需要任何状态管理。它只会渲染一个提交给 /search 的表单。当 JavaScript 加载时,Remix 将拦截表单提交并在客户端进行处理。这允许你添加自己的待处理 UI 或其他客户端行为。这是下一次迭代:

¥This component doesn't need any state management. It just renders a form that submits to /search. When JavaScript loads, Remix will intercept the form submission and handle it client side. This allows you to add your own pending UI, or other client side behavior. Here's the next iteration:

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

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

¥No fundamental change in architecture, simply a progressive enhancement for both the user and the code.

另请参阅:状态管理

¥See also: State Management

Remix v2.17 中文网 - 粤ICP备13048890号