React Router v7 已发布。 查看文档
@remix-run/dev CLI
本页内容

Remix CLI

Remix CLI 来自 @remix-run/dev 包。它还包括编译器。请确保它在你的 package.jsondevDependencies 中,这样它就不会部署到你的服务器。

要获取可用命令和标志的完整列表,请运行

npx @remix-run/dev -h

remix vite:build

使用 Remix Vite 构建你的应用以进行生产。此命令会将 process.env.NODE_ENV 设置为 production 并缩小输出以进行部署。

remix vite:build
标志 描述 类型 默认值
--assetsInlineLimit 静态资源 base64 内联阈值(以字节为单位) number 4096
--clearScreen 允许/禁用日志记录时清除屏幕 boolean
--config, -c 使用指定的配置文件 string
--emptyOutDir 当 outDir 在根目录之外时强制清空 outDir boolean
--logLevel, -l 使用指定的日志级别 "info" | "warn" | "error" | "silent" | string
--minify 启用/禁用缩小,或指定要使用的缩小器 boolean | "terser" | "esbuild" "esbuild"
--mode, -m 设置 env 模式 string
--profile 启动内置的 Node.js 检查器
--sourcemapClient 为客户端构建输出源映射 boolean | "inline" | "hidden" false
--sourcemapServer 为服务器构建输出源映射 boolean | "inline" | "hidden" false

remix vite:dev

使用 Remix Vite 在开发模式下运行你的应用。

remix vite:dev
标志 描述 类型 默认值
--clearScreen 允许/禁用日志记录时清除屏幕 boolean
--config, -c 使用指定的配置文件 string
--cors 启用 CORS boolean
--force 强制优化器忽略缓存并重新打包 boolean
--host 指定主机名 string
--logLevel, -l 使用指定的日志级别 "info" | "warn" | "error" | "silent" | string
--mode, -m 设置 env 模式 string
--open 在启动时打开浏览器 boolean | string
--port 指定端口 number
--profile 启动内置的 Node.js 检查器
--strictPort 如果指定的端口已被占用则退出 boolean

经典 Remix 编译器命令

仅当使用经典 Remix 编译器时,此文档才相关。

remix build

使用 经典 Remix 编译器 构建你的生产应用。此命令会将 process.env.NODE_ENV 设置为 production,并压缩输出以进行部署。

remix build

选项

选项 标记 配置 默认值
为生产构建生成 sourcemaps --sourcemap N/A false

remix dev

在观察模式下运行 经典 Remix 编译器,并启动你的应用服务器。

Remix 编译器将:

  1. NODE_ENV 设置为 development
  2. 监视你的应用代码更改并触发重新构建
  3. 在重新构建成功时重新启动你的应用服务器
  4. 通过 Live Reload 和 HMR + 热数据重新验证向浏览器发送代码更新

🎥 有关 Remix 中 HMR 和 HDR 的介绍和深入探讨,请查看我们的视频

什么是“热数据重新验证”?

与 HMR 类似,HDR 是一种在无需刷新页面的情况下热更新你的应用的方法。这样,你可以在你的应用中应用编辑时保持你的应用状态。HMR 处理客户端代码更新,例如当你更改应用中的组件、标记或样式时。同样,HDR 处理服务器端代码更新。

这意味着,每当你在当前页面上更改 loader (或你的 loader 依赖的任何代码)时,Remix 将从你更改的 loader 重新获取数据。这样,你的应用就始终与最新的代码更改保持同步,无论是客户端还是服务器端。

要了解有关 HMR 和 HDR 如何协同工作的更多信息,请查看 Pedro 在 Remix Conf 2023 上的演讲

使用自定义应用服务器

如果你使用模板开始,希望它已经与 remix dev 开箱即用集成。如果不是,你可以按照以下步骤将你的项目与 remix dev 集成

  1. 替换 package.json 中的开发脚本,并使用 -c 指定你的应用服务器命令

    {
      "scripts": {
        "dev": "remix dev -c \"node ./server.js\""
      }
    }
    
  2. 确保在你的应用服务器启动并运行时调用 broadcastDevReady

    import path from "node:path";
    
    import { broadcastDevReady } from "@remix-run/node";
    import express from "express";
    
    const BUILD_DIR = path.resolve(__dirname, "build");
    const build = require(BUILD_DIR);
    
    const app = express();
    
    // ... code for setting up your express app goes here ...
    
    app.all("*", createRequestHandler({ build }));
    
    const port = 3000;
    app.listen(port, () => {
      console.log(`👉 https://127.0.0.1:${port}`);
    
      if (process.env.NODE_ENV === "development") {
        broadcastDevReady(build);
      }
    });
    

    对于 CloudFlare,请使用 logDevReady 而不是 broadcastDevReady

    为什么?broadcastDevReady 使用 fetch 向 Remix 编译器发送就绪消息,但 CloudFlare 不支持请求处理之外的异步 I/O(如 fetch)。

选项

选项优先级顺序为:1. 标记,2. 配置,3. 默认值。

选项 标记 配置 默认值 描述
命令 -c / --command 命令 remix-serve <服务器构建路径> 用于运行你的应用服务器的命令
手动 --manual 手动 false 请参阅 手动模式指南
端口 --port port 动态选择的开放端口 Remix 编译器用于热更新的内部端口
TLS 密钥 --tls-key tlsKey N/A 用于配置本地 HTTPS 的 TLS 密钥
TLS 证书 --tls-cert tlsCert N/A 用于配置本地 HTTPS 的 TLS 证书

例如

/** @type {import('@remix-run/dev').AppConfig} */
module.exports = {
  dev: {
    // ...any other options you want to set go here...
    manual: true,
    tlsKey: "./key.pem",
    tlsCert: "./cert.pem",
  },
};

设置自定义端口

remix dev --port 选项设置用于热更新的内部端口。它不会影响你的应用运行的端口。

要设置你的应用服务器端口,请像在生产环境中一样正常设置。例如,你可能在 server.js 文件中将其硬编码。

如果你使用 remix-serve 作为你的应用服务器,你可以使用其 --port 标记来设置应用服务器端口

remix dev -c "remix-serve --port 8000 ./build/index.js"

相比之下,remix dev --port 选项是为需要精细控制网络端口的用户提供的逃生舱。大多数用户不需要使用 remix dev --port

手动模式

默认情况下,remix dev 将在每次重新构建时重新启动你的应用服务器。如果你想在重新构建过程中保持你的应用服务器运行而不重新启动,请查看我们的 手动模式指南

你可以通过比较 remix dev 报告的时间来查看应用服务器重新启动是否是你的项目的瓶颈

  • rebuilt (Xms) 👉 Remix 编译器花费了 X 毫秒来重新构建你的应用
  • app server ready (Yms) 👉 Remix 重新启动了你的应用服务器,并花费了 Y 毫秒来启动新的代码更改

从其他包中获取更改

如果你正在使用 monorepo,你可能希望 Remix 不仅在你的应用代码更改时执行热更新,还希望在你更改任何应用的依赖项中的代码时执行热更新。

例如,你可以有一个 UI 库包 (packages/ui),它在你的 Remix 应用 (packages/app) 中使用。要获取 packages/ui 中的更改,你可以配置 watchPaths 以包含你的包。

如何设置 MSW

要在开发中使用 Mock Service Worker,你需要

  1. 作为你的应用服务器的一部分运行 MSW
  2. 配置 MSW 以不模拟发送给 Remix 编译器的内部“开发就绪”消息

确保你在 -c 标记中为你的应用服务器设置模拟,以便 REMIX_DEV_ORIGIN 环境变量可用于你的模拟。例如,你可以在运行 remix-serve 时使用 NODE_OPTIONS 来设置 Node 的 --require 标记

{
  "scripts": {
    "dev": "remix dev -c \"npm run dev:app\"",
    "dev:app": "cross-env NODE_OPTIONS=\"--require ./mocks\" remix-serve ./build"
  }
}

如果你使用 ESM 作为默认模块系统,则需要设置 --import 标记而不是 --require

{
  "scripts": {
    "dev": "remix dev -c \"npm run dev:app\"",
    "dev:app": "cross-env NODE_OPTIONS=\"--import ./mocks/index.js\" remix-serve ./build/index.js"
  }
}

接下来,你可以使用 REMIX_DEV_ORIGIN 让 MSW 在 /ping 上转发内部“开发就绪”消息

import { http, passthrough } from "msw";

const REMIX_DEV_PING = new URL(
  process.env.REMIX_DEV_ORIGIN
);
REMIX_DEV_PING.pathname = "/ping";

export const server = setupServer(
  http.post(REMIX_DEV_PING.href, () => passthrough())
  // ... other request handlers go here ...
);

如何与反向代理集成

假设你的应用服务器和 Remix 编译器都在同一台机器上运行

  • 应用服务器 👉 https://127.0.0.1:1234
  • Remix 编译器 👉 https://127.0.0.1:5678

然后,你在应用服务器前面设置一个反向代理

  • 反向代理 👉 https://myhost

但是,用于支持热更新的内部 HTTP 和 WebSocket 连接仍将尝试访问 Remix 编译器未代理的源

  • 热更新 👉 https://127.0.0.1:5678 / ws://127.0.0.1:5678

要使内部连接指向反向代理,你可以使用 REMIX_DEV_ORIGIN 环境变量

REMIX_DEV_ORIGIN=https://myhost remix dev

现在,热更新将正确发送到代理

  • 热更新 👉 https://myhost / wss://myhost

性能调优和调试

路径导入

目前,当 Remix 重新构建你的应用时,编译器必须处理你的应用代码及其任何依赖项。编译器会从应用中进行摇树优化以删除未使用的代码,这样你就不会将任何未使用的代码发送到浏览器,并使你的服务器尽可能精简。但是,编译器仍然需要爬取所有代码以了解要保留什么以及要摇树优化掉什么。

简而言之,这意味着你执行导入和导出的方式可能会对重新构建你的应用所需的时间产生很大影响。例如,如果你正在使用 Material UI 或 AntD 之类的库,你很可能可以通过使用 路径导入 来加快构建速度

- import { Button, TextField } from '@mui/material';
+ import Button from '@mui/material/Button';
+ import TextField from '@mui/material/TextField';

将来,Remix 可能会在开发中预先捆绑依赖项,以完全避免此问题。但是今天,你可以通过使用路径导入来帮助编译器。

调试捆绑包

根据你的应用和依赖项,你可能正在处理比你的应用需要的代码多得多的代码。请查看我们的 捆绑包分析指南 以了解更多详细信息。

故障排除

HMR

如果你期望热更新但获得的是整页重新加载,请查看我们关于 热模块替换的讨论,以了解有关 React Fast Refresh 的限制和常见问题的解决方法。

HDR:每次代码更改都会触发 HDR

热数据重新验证通过尝试捆绑每个加载器,然后为每个加载器的内容进行指纹识别来检测加载器更改。它依赖于摇树优化来确定你的更改是否会影响每个加载器。

为确保摇树优化可以可靠地检测到加载器的更改,请确保声明你的应用的包是无副作用的

{
  "sideEffects": false
}
HDR:删除加载器数据时出现无害的控制台错误

当你删除加载器或删除该加载器返回的某些数据时,你的应用应正确热更新。但是你可能会注意到浏览器中记录的控制台错误。

应用热更新时,React 严格模式和 React Suspense 可能会导致多次渲染。其中大多数渲染是正确的,包括你看到的最终渲染。但是,中间渲染有时会使用带有旧 React 组件的新加载器数据,这就是这些错误出现的原因。

我们正在继续调查潜在的竞争条件,以查看是否可以解决该问题。同时,如果这些控制台错误困扰你,你可以在每次发生这些错误时刷新页面。

HDR:性能

当 Remix 编译器构建(和重新构建)你的应用时,你可能会注意到,随着编译器需要爬取每个加载器的依赖项,速度会稍微变慢。这样,Remix 可以在重新构建时检测到加载器更改。

虽然初始构建变慢是 HDR 的固有代价,但我们计划优化重建,以便 HDR 重建不会出现可察觉的减速。

文档和示例在以下许可下授权: MIT