初探 Remix.run

2025-05-25

初探 Remix.run

今天,Remix Run Supporter Preview 终于上线了。Remix 是一个新的 React 框架。

Remix 最让我兴奋的一点是它背后的团队。Michael JacksonRyan Florence是 React 社区中最博学的工程师之一,他们构建了很多东西(比如React RouterUNPKGReach UI),这些东西在我职业生涯中、在 AWS 工作时以及在咨询过程中与客户合作时都用过无数次。

另一件令我兴奋的事情是自 React 首次发布以来过去几年发生的创新,以及他们如何在 Remix 中构建这些想法。

在这篇简短的文章中,我将向您展示 Remix 的工作原理:

  1. 创建新项目
  2. 设置凭据
  3. 配置路由
  4. 添加动态路由
  5. 动态数据获取
  6. 使用路由参数动态获取数据
  7. 使用全局 Remix 配置

您还可以在这里观看我关于 Remix 工作原理的视频演示

关于 Remix

定价

Remix 并非免费。要使用 Remix,您需要购买独立许可证(每年 250 美元)或企业许可证(每年 1,000 美元)。对此有很多不同的看法。我认为双方都有充分的理由,以下是我的看法。

我个人在刚进入咨询行业时,曾花费超过 250 美元参加 1 小时的培训,所以,为了获得一个值得信赖的团队提供的一整年支持,以及一个让我更容易构建应用程序的框架,我认为这是值得的。你可能还会考虑像 Next.js 这样的免费框架,并认为它不值得。在我看来,这两种观点都完全正确。

归根结底,我只是希望能够构建高质量的应用程序,并且尽可能快速高效地完成,而不会影响质量,而且因为时间就是金钱,我经常投资于可以让我变得更好、更快的工具(特别是那些可以利用我现有技能的工具)。

苏维埃社会主义共和国

Remix 背后的理念是一切都是 SSR。它的 API 级别也比 Next.js 低得多,暴露了整个 Request 对象,并允许你在渲染页面之前修改 headers 之类的内容。我目前还不了解它的全部功能,但乍一看,我觉得它更接近 Next.js 的精简版/更可配置版本,但没有 SSG,而且嵌套路由也有一些好处,说实话我还没有完全探索过,但感觉很有意思。

路由

Remix 与其他框架(例如 Next.js)的另一个巨大区别在于路由的工作方式。Remix 支持嵌套路由和参数,并且“这是理解 Remix 的一个关键概念”(根据文档)。

使用Outlet来自 React Router Dom 的 API,你可以构建一个嵌套路由的层次结构,并且使用非常简单的 API:

import React from "react";
import { Link, Outlet } from "react-router-dom";
import { useRouteData } from "@remix-run/react";

export default function Team() {
  let data = useRouteData();
  return (
    <div>
      <h2>Team</h2>
      <ul>
        {data.map((member) => (
          <li key={member.id}>
            <Link to={member.login}>{member.login}</Link>
          </li>
        ))}
      </ul>
      <hr />
      <Outlet />
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

当您使用 进行导航时Link,Outlet 将从导航路线呈现新内容。

HTTP 缓存

Remix 引入了加载器的概念,它不仅能让你返回路由数据,还能发送完整的响应,包括发送缓存控制头。通过简单的 API 来设置加载器和路由的头,你可以轻松利用浏览器(和 CDN)内置的缓存功能。

例如,如果你在响应中设置了缓存头,当用户访问相同的路由时,它甚至不会获取数据,而是会使用缓存。如果你在服务器前面放置一个 CDN,服务器很少会真正处理请求,因为 CDN 会缓存这些请求。

代码

解释得足够多了,让我们看一些代码。

创建项目

购买 Remix 许可证订阅后,您将获得其控制面板的访问权限。在此控制面板中,您可以查看许可证详情、文档和账单信息。

Remix 仪表板

这与我使用过的大多数开源框架的体验截然不同,而这一切都隐藏在付费墙后面。

从这个仪表板您可以获得开始所需的一切,包括快速入门教程。

首先,他们建议您克隆一个使用 express 作为服务器的开源启动项目:

$ git clone git@github.com:remix-run/starter-express.git my-remix-app
Enter fullscreen mode Exit fullscreen mode

它们开箱即用地支持 Firebase 使用单个命令进行部署:

firebase deploy
Enter fullscreen mode Exit fullscreen mode

未来他们还计划支持部署到这些不同的云服务提供商:

  • Firebase
  • 韦尔塞尔
  • AWS Amplify
  • 建筑师
  • Azure
  • Netlify

限制访问

你可能想知道他们是如何限制只有付费用户才能访问的。他们的方法是,为了安装node-modules应用程序运行所需的最新版本,你必须配置一个如下所示的.npmrc文件来包含你的密钥:

//npm.remix.run/:_authToken=your-unique-token

# This line tells npm where to find @remix-run packages.
@remix-run:registry=https://npm.remix.run
Enter fullscreen mode Exit fullscreen mode

配置完成后,您可以使用 npm 或 yarn 安装依赖项。

项目结构

以下是 Remix 特定项目配置

remix-app  
└───app
│   │   App.tsx
│   │   entry-browser.tsx
│   │   entry-server.tsx
│   │   global.css
│   │   tsconfig.json
│   └───routes
│       │   index.js
│       │   404.js
│       │   500.js
│       │   index.css
└───config
│   │   shared-tsconfig.json
└───loaders
│    │   global.ts
│    │   tsconfig.json
└───public
│    │   favicon.ico
└───.npmrc
└───remix.config.js
└───server.js
Enter fullscreen mode Exit fullscreen mode

入口点是App.tsx,看起来像这样:

import React from "react";
import { Meta, Scripts, Styles, Routes, useGlobalData } from "@remix-run/react";

export default function App() {
  let data = useGlobalData();

  return (
    <html lang="en">
      <head>
        <meta charSet="utf-8" />
        <Meta />
        <Styles />
      </head>
      <body>
        <Routes />
        <Scripts />
        <footer>
          <p>This page was rendered at {data.date.toLocaleString()}</p>
        </footer>
      </body>
    </html>
  );
}
Enter fullscreen mode Exit fullscreen mode

有一件有趣的事情是这一行:

let data = useGlobalData();
Enter fullscreen mode Exit fullscreen mode

loaders/global.ts中,你可以配置全局值、变量(静态和动态),或者任何你需要从服务器加载以渲染基础布局的内容。例如,假设我们想在这里定义应用程序版本并在整个应用程序中使用它,我们可以在这里定义:

import type { DataLoader } from "@remix-run/core";

let loader: DataLoader = async () => {
  return {
    date: new Date(),
    version: "V2.0"
  };
};

export = loader;
Enter fullscreen mode Exit fullscreen mode

然后像这样使用它:

let data = useGlobalData();
const version = data.version;
Enter fullscreen mode Exit fullscreen mode

路由

来自文档: remix 中的路由可以通过两种方式定义:通常在“app/routes”文件夹内定义,或者使用 remix.config.routes 手动定义。

因此,如果我创建一个名为routes/contact.js的文件,它将可以在http://myapp/contact上使用。

但正如所提到的,您也可以在remix.config.js中定义它们。

从remix.config.js中的代码注释来看

A hook for defining custom routes based on your own file
conventions. This is not required, but may be useful if
you have custom/advanced routing requirements.
Enter fullscreen mode Exit fullscreen mode

以下是样板提供的示例:

routes(defineRoutes) {
  return defineRoutes(route => {
    route(
      // The URL path for this route.
      "/pages/one",
      // The path to this route's component file, relative to `appDirectory`.
      "pages/one.tsx",
      // Options:
      {
        // The path to this route's data loader, relative to `loadersDirectory`.
        loader: "...",
        // The path to this route's styles file, relative to `appDirectory`.
        styles: "..."
      }
    );
  });
},
Enter fullscreen mode Exit fullscreen mode

在这样做时,如果您想要或需要绕过 Remix 的意见,您可以定义自定义路线配置。

数据加载

Remix 最有趣和最强大的功能之一是它加载数据的方式。

该方法将路由加载器结合起来,实现动态数据获取。

文档中提到: Remix 中的页面数据来自名为“路由数据加载器”的文件。这些加载器仅在服务器端运行,因此您可以使用任何需要的 Node 模块来加载数据,包括直接连接到数据库。

如果您将加载器命名为与路由相同,Remix 将在渲染之前自动调用它,并使该数据在您的路由中可用。

让我们看看它是如何工作的。

假设我在routes/people.ts上创建了一条路线和页面,如下所示:

// routes/people.ts
import React, { useState, useEffect } from "react";

export default function People() {
  return (
    <div>
      <h2>Star Wars Characters</h2>
      // todo, fetch & map over star wars characters from API
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

当呈现此路线时,我想要获取人员数组的数据并使其在组件中可用。

为此,我们可以在loader/routes目录中创建一个名为people.ts的新文件,并包含以下代码:

// loaders/routes/people.ts
module.exports = () => {
  return fetch(`https://swapi.dev/api/people/`);
};
Enter fullscreen mode Exit fullscreen mode

您现在可以使用useRouteDataRemix 的 API 来访问路线中的这些数据:

// routes/people.ts
import React, { useState, useEffect } from "react";
import { Link } from "react-router-dom";
import { useRouteData } from "@remix-run/react";

export default function People() {
  const data = useRouteData()
  return (
    <div>
      <h2>Star Wars Characters</h2>
      {
        data.results.map((result, index) => (
          <div key={index}>
            <Link to={`/person/${index + 1}`}>
              <h1>{result.name}</h1>            
            </Link>
          </div>
        ))
      }
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

最大的问题

为什么我们不必先等待 fetch 然后再等待 res.json?因为 Remix 会等待你的加载器,而 fetch 会解析为响应,而 Remix 期望的正是这种类型的对象。

来自文档:你可能对最后一点感到困惑。为什么我们不用通常的 await res.json() 来解包获取响应?

如果你在 Node.js 的世界里待过一段时间,就会发现“请求”和“响应”有很多不同的版本。express API req, res 可能是最常见的,但无论你在哪里,它总会有所不同。

浏览器在发布 Fetch API 时,不仅为 window.fetch 创建了规范,还为 fetch 发送和返回的内容(请求、标头和响应)创建了规范。您可以在 MDN 上阅读有关这些 API 的信息。

我们没有提出自己的 API,而是在 Web Fetch API 之上构建了 Remix。

添加缓存

让我们看看如何实现我在本文开头提到的缓存机制。

我们可以进行以下更新:

const { json } = require("@remix-run/loader");
let res = await fetch(swapi);
let data = await res.json();
return json(data, { headers: { "cache-control": "max-age=3600"}})
Enter fullscreen mode Exit fullscreen mode

在接下来的一个小时内,浏览器将不会再次请求该资源,对于下一个访问者,CDN 也不会。

结合动态路由、路由参数和数据获取

这对于动态路由如何运作?例如,如果我想深入到像/person/#person-id这样的路由并获取数据,该怎么办?

具体操作如下。我们需要两个新文件:一个用于路由,一个用于加载器。

首先,我们在loaders/person/$id.js创建一个加载器,如下所示:

// loaders/person/$id.js
module.exports = ({ params }) => {
  return fetch(`https://swapi.dev/api/people/${params.id}`)
};
Enter fullscreen mode Exit fullscreen mode

接下来我们创建路由,例如routes/person/$id.js。Remix 会解析 url 中的参数( person/$id 中的 $id 部分),并将它们传递给加载器。

// routes/person/$id.js
import React from "react";
import { useRouteData } from "@remix-run/react";

export default function Person() {
  const user = useRouteData()
  return (
    <div>
      <h2>{user.name}</h2>
      <h3>Homeworld - { user.homeworld }</h3>
      <p>Height - {user.height}</p>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

现在可以使用从 API 获取的数据useRouteData

使用 Remix.run 视频启动并运行

结论

总的来说,我非常享受使用 Remix。它是一种构建混合 React Web 应用的全新方法,它建立在我们多年来在现代和传统 Web 技术上所取得的进步之上。

我希望 Remix 随着时间的推移不断变得更好,但考虑到它不是 OSS,我不确定它会走多远或走多快。

我赞赏 Ryan 和 Michael 尝试新的付费模式,并期待看到其成果。开源软件的货币化挑战尚未完全解决,因此任何新的方案都将为任何希望让自己的开源工作现在和将来盈利且可维护的人奠定基础。

另请查看我的帖子,深入了解 Remix 和 Next.js 之间的差异。

文章来源:https://dev.to/dabit3/a-first-look-at-remix-run-449a
PREV
GraphQL 和全栈无服务器时代的基础设施即代码
NEXT
进入 DevRel 的 7 个技巧