@remix-run/dev CLI
在本页

Remix CLI

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

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

npx @remix-run/dev -h

remix vite:build

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

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

remix vite:dev

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

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

经典 Remix 编译器命令

本文档仅在使用 经典 Remix 编译器 时相关。

remix build

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

remix build

选项

选项 标志 配置 默认值
为生产构建生成源映射 --sourcemap N/A false

remix dev

以监视模式运行 经典 Remix 编译器 并启动你的应用程序服务器。

Remix 编译器将

  1. NODE_ENV 设置为 development
  2. 监视你的应用程序代码以进行更改并触发重建
  3. 在重建成功时重新启动你的应用程序服务器
  4. 通过实时重新加载和 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 不支持像 fetch 这样的异步 I/O,除非在请求处理中。

选项

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

选项 标志 配置 默认值 描述
命令 -c / --command 命令 remix-serve <server build path> 用于运行你的应用程序服务器的命令。
手动 --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 毫秒使用新的代码更改启动。

获取来自其他包的更改

如果你使用的是单体仓库,你可能希望 Remix 不仅在你的应用程序代码更改时执行热更新,而且在你的任何应用程序依赖项的代码更改时也执行热更新。

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

如何设置 MSW

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

  1. 在你的应用程序服务器中运行 MSW。
  2. 配置 MSW 不模拟到 Remix 编译器的内部“开发就绪”消息。

确保你在 -c 标志中为你的应用程序服务器设置你的模拟,以便 REMIX_DEV_ORIGIN 环境变量对你的模拟可用。例如,你可以使用 NODE_OPTIONS 在运行 remix-serve 时设置 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

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

要确保树摇动能够可靠地检测到对 loader 的更改,请确保声明你的应用程序的包是无副作用的。

{
  "sideEffects": false
}
HDR:当删除 loader 数据时,无害的控制台错误

当你删除一个 loader 或删除该 loader 返回的一些数据时,你的应用程序应该会正确地进行热更新。但你可能会注意到浏览器中记录的控制台错误。

React 严格模式和 React Suspense 在应用热更新时可能会导致多次渲染。大多数渲染都正确,包括对你可见的最终渲染。但是,中间渲染有时可能会将新的 loader 数据与旧的 React 组件一起使用,这就是这些错误的来源。

我们正在继续调查潜在的竞争条件,看看我们是否可以解决这个问题。在此期间,如果这些控制台错误困扰你,你可以在它们出现时刷新页面。

HDR:性能

当 Remix 编译器构建(和重建)你的应用程序时,你可能会注意到轻微的减速,因为编译器需要遍历每个 loader 的依赖项。这样,Remix 就可以在重建时检测到 loader 更改。

虽然初始构建减速本质上是 HDR 的成本,但我们计划优化重建,以便 HDR 重建不会出现明显的减速。

文档和示例根据 MIT