Remix 基础

Avatar of Brittney Postma
Brittney Postma

DigitalOcean 为您旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

您可能已经听说过很多关于框架领域的新贵 Remix 的宣传。 您可能感到惊讶的是它早在 2019 年就已问世,但最初它仅作为基于订阅的付费框架提供。 2021 年,创始人筹集了种子资金并开源了该框架,让用户可以免费使用 Remix。 大门敞开,每个人似乎都在谈论它,无论是好是坏。 让我们深入了解 Remix 的一些基础知识。

Remix 是一个以服务器“边缘”为先的 JavaScript 框架。 它使用 React(至少目前是这样)作为前端,并优先考虑在边缘进行服务器端渲染应用程序。 平台可以获取服务器端代码并将其作为无服务器或边缘函数运行,使其比传统服务器更便宜,并将服务器置于更靠近用户的位置。 Remix 的创始人喜欢称其为“中心堆栈”框架,因为它会根据运行平台调整服务器和客户端之间发出的请求和响应。

部署 Remix

由于 Remix 需要服务器,因此让我们谈谈如何部署它。 Remix 不会提供服务器本身 - 您需要提供服务器 - 这使它可以在任何Node.jsDeno 环境中运行,包括Netlify EdgeDigitalOcean 的应用程序平台。 Remix 本身是一个编译器,一个将请求转换为其运行平台的程序。 此过程使用esbuild 为发送到服务器的请求创建处理程序。 它使用的 HTTP 处理程序基于Web Fetch API,并通过为其将要部署到的平台适配它们来在服务器上运行。

Remix 堆栈

Remix 堆栈是预先配置了一些常用工具的项目。 Remix 团队维护着三个官方堆栈,它们都以音乐类型命名。 此外,还有一些社区 Remix 堆栈,包括 Netlify 模板团队创建的K-Pop 堆栈。 此堆栈功能强大,包含Supabase 数据库和身份验证、Tailwind 用于样式、Cypress 端到端测试、Prettier 代码格式化、ESLint 代码检查以及TypeScript 静态类型检查。 查看 Tara Manicsic 关于部署 K-Pop 堆栈的帖子。

缓存路由

尽管 Remix 需要服务器,但它仍然可以通过缓存路由来利用Jamstack 的优势。 静态网站或静态网站生成 (SSG) 是指在构建时渲染所有内容并保持静态,直到再次构建。 内容是预生成的,可以放在 CDN 上。 这为最终用户提供了许多优势和快速的网站加载速度。 但是,Remix 不会像其他流行的 React 框架(包括 Next.js 和 Gatsby)那样执行典型的 SSG。 要获得 SSG 的一些优势,您可以在 Remix 的headers 函数中使用本机Cache-Control HTTP 标头 来缓存特定路由,或者直接在 root.tsx 文件中进行缓存。

[[headers]]
  for = "/build/*"
  [headers.values]
    "Cache-Control" = "public, max-age=31536000, s-maxage=31536000"

然后在您想要的位置添加headers 函数。 这会缓存一小时

export function headers() {
  return {
    "Cache-Control": "public, s-maxage=360",
  };
};

Remix 路由

许多框架都倾向于基于文件系统的路由。 这是一种使用指定文件夹来定义应用程序路由的技术。 它们通常具有用于声明动态路由和端点的特殊语法。 目前 Remix 与其他流行框架之间最大的区别在于能够使用嵌套路由

每个 Remix 应用程序都从 root.tsx 文件开始。 这是渲染整个应用程序基础的地方。 您将在此处找到一些常见的 HTML 布局,例如 <html> 标签、<head> 标签,以及包含渲染应用程序所需的组件的 <body> 标签。 这里要指出的一点是 <Scripts> 组件是启用网站上 JavaScript 的组件;有些东西可以在没有它的情况下工作,但并非所有东西都可以。 root.tsx 文件充当 routes 目录内所有内容的父级布局,routes 中的所有内容都渲染在 root.tsx<Outlet/> 组件所在的位置。 这是 Remix 中嵌套路由的基础。

嵌套路由

Remix 不仅由React Router 团队中的一些成员创建,它还使用 React Router。 事实上,他们正在将 Remix 中的一些优点带回 React Router。 Next.js 和 SvelteKit 的维护者目前正在努力解决的一个复杂问题是嵌套路由。

嵌套路由与传统路由不同。 传统的路由会将用户带到新页面,而每个嵌套路由都是同一页面的一个单独部分。 它允许通过将业务逻辑与仅需使用它的文件相关联来实现关注点分离。 Remix 能够处理仅限于嵌套路由所在的页面部分的错误。 页面上的其他路由仍然可以使用,而出现问题的路由可以提供与错误相关的上下文,而不会导致整个页面崩溃。

app/routes 中的根文件与将加载到基础文件中的文件目录同名时,Remix 会执行此操作。 根文件通过使用 <Outlet /> 组件来告诉 Remix 在哪里加载其他路由,从而成为目录中文件的布局

Outlet 组件

<Outlet /> 组件是告诉 Remix 应该在哪里渲染嵌套路由内容的信号。 它被放在 app/routes 目录的根目录中的文件,其名称与嵌套路由相同。 以下代码位于 app/routes/about.tsx 文件中,包含 app/routes/about 文件夹内文件的出口

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

export default function About() {
  return (
    <>
      <section>
        I am the parent layout. I will be on any page inside of my named directory.
      </section>
      { /* All of my children, the files in the named directory, will go here. */ }
      <Outlet />
    </>
  )
}

文件夹结构

app/routes/ 目录中的任何文件都将成为其名称的 URL 路由。 也可以添加一个包含 index.tsx 文件的目录。

app/
├── routes/
│   │
│   └── blog
|   |   ├── index.tsx ## The /blog route
│   └── about.tsx  ## The /about route
│   ├── index.tsx  ## The / or home route
└── root.tsx

如果路由与目录同名,则该命名文件将成为目录中文件的布局文件,并且布局文件需要Outlet 组件 来放置嵌套路由。

app/
├── routes/
│   │
│   └── about
│   │   ├── index.tsx
│   ├── about.tsx ## this is a layout for /about/index.tsx
│   ├── index.tsx
└── root.tsx

布局也可以通过在它们前面加上双下划线 (__) 来创建。

app/
├── routes/
│   │
│   └── about
│   │   ├── index.tsx
│   ├── index.tsx
│   ├── about.tsx
│   ├── __blog.tsx ## this is also a layout
└── root.tsx

https://your-url.com/about 仍然会渲染 app/routes/about.tsx 文件,但也会渲染 app/routes/about/index.tsxOutlet 组件app/routes/about.tsx 标记中的任何内容。

动态路由

动态路由是指根据 URL 中的信息而改变的路由。这可能是一个博客文章的名称或一个客户 ID,但无论是什么,在路由前面添加的 `$` 语法都表示它是动态的。除了 `$` 前缀外,名称并不重要。

app/
├── routes/
│   │
│   └── about
│   │   ├── $id.tsx
│   │   ├── index.tsx
│   ├── about.tsx ## this is a layout for /about/index.tsx
│   ├── index.tsx
└── root.tsx

获取这些数据!

由于 Remix 在服务器上渲染所有数据,因此您不会看到很多在 React 应用程序中成为标准的事物,例如 `useState()` 和 `useEffect()` 钩子,在 Remix 中。由于数据已经在服务器上进行评估,因此对客户端状态的需求更少。

您使用的用于获取数据的服务器类型也不重要。由于 Remix 位于请求和响应之间并适当地对其进行转换,因此您可以使用标准的 Web Fetch API。Remix 在一个 `loader` 函数中执行此操作,该函数 **仅** 在服务器上运行,并使用 `useLoaderData()` 钩子在组件中渲染数据。以下是一个使用 Cat as a Service API 来渲染随机猫图片的示例。

import { Outlet, useLoaderData } from '@remix-run/react'

export async function loader() {
  const response = await fetch('<https://cataas.com/cat?json=true>')
  const data = await response.json()
  return {
    data
  }
}

export default function AboutLayout() {
  const cat = useLoaderData<typeof loader>()
  return (
    <>
      <img
        src={`https://cataas.com/cat/${cat}`}
        alt="A random cat."
      />
      <Outlet />
    </>
  )
}

路由参数

在动态路由中,以 `$` 为前缀的路由需要能够访问 URL 参数来处理应渲染的这些数据。`loader` 函数可以通过 `params` 参数访问这些参数。

import { useLoaderData } from '@remix-run/react'
import type { LoaderArgs } from '@remix-run/node'

export async function loader({ params }: LoaderArgs) {
  return {
      params
  }
}

export default function AboutLayout() {
  const { params } = useLoaderData<typeof loader>()
  return <p>The url parameter is {params.tag}.</p>
}

其他 Remix 函数

Remix 有一些其他辅助函数,它们在 路由模块 API 中为正常的 HTML 元素和属性添加了额外功能。每个路由都可以定义自己的这些类型的函数。

Action 函数

一个 `action` 函数允许您使用标准的 web FormData API 为表单操作添加额外功能。

export async function action({ request }) {
  const body = await request.formData();
  const todo = await fakeCreateTodo({
      title: body.get("title"),
  });
  return redirect(`/todos/${todo.id}`);
}

Headers 函数

任何 HTTP 标准标头 都可以放在 `headers` 函数中。由于每个路由都可以拥有一个标头,为了避免与嵌套路由发生冲突,最深的路由(或具有最多正斜杠 (/) 的 URL)将获胜。您还可以获取传递的标头,`actionHeaders`、`loaderHeaders` 或 `parentHeaders`

export function headers({
  actionHeaders,
  loaderHeaders,
  parentHeaders,
}) {
  return {
"Cache-Control": loaderHeaders.get("Cache-Control"),
  };
}

Meta 函数

此函数将设置 HTML 文档的元标记。默认情况下,`root.tsx` 文件中设置了一个,但可以为每个路由更新它们。

export function meta() {
  return {
    title: "Your page title",
    description: "A new description for each route.",
  };
};

HTML `link` 元素位于 HTML 文档的 `` 标记中,它们导入 CSS,以及其他内容。`links` 函数(不要与 ` 混淆)允许您仅在需要它们的路由中导入内容。因此,例如,CSS 文件可以是作用域化的,并且仅在需要这些特定文件的路由上导入。`link` 元素从 `links()` 函数中返回,作为对象数组,可以是来自 `link` API 的 `HtmlLinkDescriptor`,也可以是 `PageLinkDescriptor`,它可以预取页面的数据。

export function links() {
  return [
    // add a favicon
    {
      rel: "icon",
      href: "/favicon.png",
      type: "image/png",
    },
    // add an external stylesheet
    {
      rel: "stylesheet",
      href: "<https://example.com/some/styles.css>",
      crossOrigin: "true",
    },
    // add a local stylesheet,
    { rel: "stylesheet", href: stylesHref },

    // prefetch a page's data
    { page: "/about/community" }
  ]
}

路由之间链接

Remix 提供了一个在应用程序中的不同路由之间跳转的组件,称为 `

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

export default function Nav() {
  return (
    <nav>
      <Link to="/">Home</Link>{" "}
      <Link to="/about">About</Link>{" "}
      <Link to="/about/community" prefetch="intent">Community</Link>
    </nav>
  );
}

下一步

现在您了解了 Remix 的基础知识,您已准备好开始实际构建应用程序,对吧?Remix 提供了 笑话应用程序博客教程 来帮助您开始实施这些基本知识。您也可以从头开始,并创建一个全新的 Remix 应用程序。或者,如果您已准备好深入研究,可以尝试 K-Pop Stack。我非常享受使用 Remix 的时光,并且喜欢它专注于 web 标准并将其回归基础。现在轮到您开始创造了!