部署Remix的详细指南(附代码)

1,159 阅读8分钟

你可能已经听到了很多关于框架领域最新的孩子之一Remix的炒作。可能令人惊讶的是,它早在2019年就开始了,但它最初只作为一个基于订阅的高级框架。2021年,创始人筹集了种子资金,并开放了框架的源代码,让用户开始免费使用Remix。闸门打开了,每个人似乎都在谈论它,无论是好是坏。让我们深入了解,看看Remix的一些基本情况。

Remix是一个服务器 "边缘 "第一的JavaScript框架。它在前端使用React,至少目前是这样,并优先考虑服务器端渲染边缘的应用程序。平台可以将服务器端的代码作为无服务器或边缘功能运行,使其比传统的服务器更便宜,并使其更接近用户。Remix的创始人喜欢称其为 "中心堆栈 "框架,因为它能适应服务器和客户端之间的请求和响应,以适应其运行的平台。

部署Remix

由于Remix需要一个服务器,我们来谈谈如何部署它。Remix不提供服务器本身--你带来了服务器--允许它在任何Node.jsDeno环境中运行,包括Netlify Edge和[DigitalOcean的App Platform](www.digitalocean.com/products/ap… _acq&utm_source=css-tricks&utm_campaign=global_do_fto&utm_content=the_basics_of_remix)。Remix本身是一个编译器,一个为它所运行的平台翻译请求的程序。这个过程使用esbuild来创建对服务器请求的处理程序。它所使用的HTTP处理程序是建立在Web Fetch API上的,并在服务器上运行,为它们将被部署到的平台进行调整

混搭堆栈

Remix stacks是拥有一些常用工具的项目,这些工具为你预先配置。有三个官方堆栈由Remix团队维护,它们都以音乐流派命名。还有一些社区混音堆栈,包括由Netlify的模板团队创建的K-Pop堆栈。这个堆栈是一个强大的系统,包括Supabase数据库和认证,Tailwind用于造型,Cypress端到端测试,Prettier代码格式化,ESLint提示,以及TypeScript静态类型。请看Tara Manicsic关于部署K-Pop Stack的帖子。

缓存路由

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

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

然后在你想要的地方添加你的头信息函数。这可以缓存一个小时:

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

混搭路由

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

每个Remix应用程序都从root.tsx 文件开始。这是应用的整个基础渲染的地方。你会在这里找到一些常见的HTML布局,如<html> 标签、<head> 标签,然后是<body> 标签,以及渲染应用程序所需的组件。这里需要指出的一点是,<Scripts> 组件是在网站上启用JavaScript的东西;没有它,有些东西可以工作,但不是所有东西。root.tsx 文件作为routes 目录内所有内容的父布局,路由中的所有内容都在<Outlet/> 组件所在的root.tsx 中呈现。这就是Remix中嵌套路由的基础。

嵌套式路由

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

嵌套路由与传统路由不同。一个新的路由会把用户带到一个新的页面,而每个嵌套路由是同一个页面的一个独立部分。它通过保持业务逻辑只与需要它的文件相关联来实现关注点的分离。Remix能够处理只针对嵌套路由所在页面的部分的错误。页面上的其他路由仍然可以使用,而且出错的路由可以为错误提供相关的上下文,而不会使整个页面崩溃。

app/routes 中的根文件与基础文件中的文件目录命名相同时,Remix就会这样做。根文件通过使用<Outlet /> 组件来告诉Remix在哪里加载其他路由,从而成为该目录中文件的布局

出口组件

<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.tsx Outlet 组件在 的标记中。app/routes/about.tsx

动态路由

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

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 函数允许您使用标准的WebFormData API向表单动作添加额外功能。

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

头信息函数

任何HTTP标准头都可以放在headers 函数中。因为每个路由都可以有一个头,为了避免与嵌套的路由冲突,最深的路由--或者有最多正斜杠的URL (/) --获胜。你也可以通过,actionHeaders,loaderHeaders, 或parentHeaders

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

元函数

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

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

链接函数

HTMLlink 元素住在HTML文档的<head> 标签中,它们会导入CSS,以及其他东西。links 功能,不要与<Link /> 组件混淆,它允许你只在需要它们的路由中导入东西。因此,例如,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提供了一个组件,用于在应用程序中的不同路由之间进行连接,称为<Link/> 。要获得客户端路由,请使用<Link to="">Name</Link> ,而不是<a href="">Name</a><Link /> 组件也需要一个prefetch ,默认接受none ,如果Remix检测到用户悬停或聚焦链接,intent 来预取数据,或者render ,这将在链接呈现时立即获取路由的数据。

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的日子,喜欢它对网络标准的关注,喜欢它回归到基础。现在轮到你开始创作了。