Remix CLI 来自 @remix-run/dev
软件包。它还包含编译器。确保它位于你的 package.json
devDependencies
中,这样它就不会被部署到你的服务器。
¥The Remix CLI comes from the @remix-run/dev
package. It also includes the compiler. Make sure it is in your package.json
devDependencies
so it doesn't get deployed to your server.
要获取可用命令和标志的完整列表,请运行:
¥To get a full list of available commands and flags, run:
npx @remix-run/dev -h
remix vite:build
使用 Remix Vite 构建生产环境的应用。此命令将把 process.env.NODE_ENV
设置为 production
,并压缩输出以便部署。
¥Builds your app for production with Remix Vite. This command will set process.env.NODE_ENV
to production
and minify the output for deployment.
remix vite:build
标志 | 描述 | 类型 | 默认 |
---|---|---|---|
--assetsInlineLimit |
静态资源 base64 内联阈值(以字节为单位) | number |
4096 |
--clearScreen |
允许/禁用登录时清屏 | boolean |
|
--config 、-c |
使用指定的配置文件 | string |
|
--emptyOutDir |
当 outDir 位于根目录之外时,强制清空。 | boolean |
|
--logLevel 、-l |
使用指定的日志级别 | "info" | "warn" | "error" | "silent" | string |
|
--minify |
启用/禁用最小化,或指定要使用的最小化程序 | boolean | "terser" | "esbuild" |
"esbuild" |
--mode 、-m |
设置环境模式 | string |
|
--profile |
启动内置 Node.js 检查器 | ||
--sourcemapClient |
客户端构建的输出源映射 | boolean | "inline" | "hidden" |
false |
--sourcemapServer |
服务器构建的输出源映射 | boolean | "inline" | "hidden" |
false |
remix vite:dev
使用 Remix Vite 以开发模式运行应用。
¥Runs your app in development mode with 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 |
设置环境模式 | string |
|
--open |
启动时打开浏览器 | boolean | string |
|
--port |
指定端口 | number |
|
--profile |
启动内置 Node.js 检查器 | ||
--strictPort |
如果指定端口已被使用,则退出 | boolean |
¥Classic Remix Compiler Commands
remix build
使用 Classic Remix 编译器 构建生产环境的应用。此命令将把 process.env.NODE_ENV
设置为 production
,并压缩输出以便部署。
¥Builds your app for production with the Classic Remix Compiler. This command will set process.env.NODE_ENV
to production
and minify the output for deployment.
remix build
¥Options
选项 | flag | config | default |
---|---|---|---|
为生产构建生成源码 | --sourcemap |
N/A | false |
remix dev
在监视模式下运行 Classic Remix 编译器 并启动应用服务器。
¥Runs the Classic Remix Compiler in watch mode and spins up your app server.
Remix 编译器将会:
¥The Remix compiler will:
将 NODE_ENV
设置为 development
¥Set NODE_ENV
to development
关注应用代码的变更并触发重建
¥Watch your app code for changes and trigger rebuilds
每次重建成功后,请重启应用服务器。
¥Restart your app server whenever rebuilds succeed
通过实时刷新 (Live Reload) 和 HMR + 热数据重新验证 (HMR) 将代码更新发送到浏览器
¥Send code updates to the browser via Live Reload and HMR + Hot Data Revalidation
🎥 有关 Remix 中 HMR 和 HDR 的介绍和深入了解,请查看我们的视频:
¥🎥 For an introduction and deep dive into HMR and HDR in Remix, check out our videos:
loader
(或 loader
所依赖的任何代码)时,Remix 都会从更改后的加载器中重新获取数据。这样,你的应用始终能够更新最新的代码更改,无论是客户端还是服务器端。要了解更多关于 HMR 和 HDR 如何协同工作的信息,请查看 Pedro 在 2023 年 Remix 大会上的演讲。
¥With a custom app server
如果你使用模板开始使用,希望它已经与 remix dev
开箱即用地集成。如果不是,你可以按照以下步骤将你的项目与 remix dev
集成:
¥If you used a template to get started, hopefully it's already integrated with remix dev
out-of-the-box.
If not, you can follow these steps to integrate your project with remix dev
:
替换 package.json
中的开发脚本,并使用 -c
指定应用服务器命令:
¥Replace your dev scripts in package.json
and use -c
to specify your app server command:
{
"scripts": {
"dev": "remix dev -c \"node ./server.js\""
}
}
确保在你的应用服务器启动并运行时调用 broadcastDevReady
:
¥Ensure broadcastDevReady
is called when your app server is up and running:
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(`👉 http://localhost:${port}`);
if (process.env.NODE_ENV === "development") {
broadcastDevReady(build);
}
});
logDevReady
而不是 broadcastDevReady
。为什么?broadcastDevReady
使用 fetch
向 Remix 编译器发送就绪消息,但 CloudFlare 不支持像 fetch
那样的异步 I/O(请求处理除外)。
¥Options
选项优先级顺序如下:1.配置,3.默认值。
¥Options priority order is: 1. flags, 2. config, 3. defaults.
选项 | flag | config | default | description |
---|---|---|---|---|
命令 | -c / --command |
command |
remix-serve <server build path> |
用于运行应用服务器的命令 |
手动 | --manual |
manual |
false |
参见 手动模式指南 |
移植 | --port |
port |
动态选择开放端口 | Remix 编译器用于热更新的内部端口 |
TLS 密钥 | --tls-key |
tlsKey |
N/A | 用于配置本地 HTTPS 的 TLS 密钥 |
TLS 证书 | --tls-cert |
tlsCert |
N/A | 用于配置本地 HTTPS 的 TLS 证书 |
例如:
¥For example:
/** @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",
},
};
¥Setting a custom port
remix dev --port
选项设置用于热更新的内部端口。它不会影响你应用运行的端口。
¥The remix dev --port
option sets the internal port used for hot updates.
It does not affect the port your app runs on.
要设置应用服务器端口,请按照你在生产环境中的常规方式进行设置。例如,你可以将它硬编码到你的 server.js
文件中。
¥To set your app server port, set it the way you normally would in production.
For example, you may have it hardcoded in your server.js
file.
如果你使用 remix-serve
作为应用服务器,则可以使用其 --port
标志来设置应用服务器端口:
¥If you are using remix-serve
as your app server, you can use its --port
flag to set the app server port:
remix dev -c "remix-serve --port 8000 ./build/index.js"
相比之下,remix dev --port
选项是需要对网络端口进行细粒度控制的用户的应急方案。大多数用户不需要使用 remix dev --port
。
¥In contrast, the remix dev --port
option is an escape-hatch for users who need fine-grain control of network ports.
Most users should not need to use remix dev --port
.
¥Manual mode
默认情况下,每当重建发生时,remix dev
都会重启你的应用服务器。如果你想让你的应用服务器在重建过程中无需重启即可保持运行,请查看我们的 手动模式指南。
¥By default, remix dev
will restart your app server whenever a rebuild occurs.
If you'd like to keep your app server running without restarts across rebuilds, check out our guide for manual mode.
你可以通过比较 remix dev
报告的时间来判断应用服务器重启是否是你项目的瓶颈:
¥You can see if app server restarts are a bottleneck for your project by comparing the times reported by remix dev
:
rebuilt (Xms)
👉 Remix 编译器重建你的应用耗时 X
毫秒。
¥rebuilt (Xms)
👉 the Remix compiler took X
milliseconds to rebuild your app
app server ready (Yms)
👉 Remix 重启了你的应用服务器,新的代码更改启动耗时 Y
毫秒。
¥app server ready (Yms)
👉 Remix restarted your app server, and it took Y
milliseconds to start with the new code changes
¥Pick up changes from other packages
如果你使用的是 monorepo,你可能希望 Remix 不仅在应用代码更改时执行热更新,而且在你更改应用依赖中的任何代码时也执行热更新。
¥If you are using a monorepo, you might want Remix to perform hot updates not only when your app code changes, but whenever you change code in any of your app's dependencies.
例如,你可以有一个 UI 库包 (packages/ui
),用于你的 Remix 应用 (packages/app
)。要获取 packages/ui
中的更改,你可以配置 watchPaths 以包含你的软件包。
¥For example, you could have a UI library package (packages/ui
) that is used within your Remix app (packages/app
).
To pick up changes in packages/ui
, you can configure watchPaths to include your packages.
¥How to set up MSW
要在开发中使用 模拟服务工作线程,你需要:
¥To use Mock Service Worker in development, you'll need to:
将 MSW 作为应用服务器的一部分运行
¥Run MSW as part of your app server
配置 MSW 使其不模拟内部 "开发就绪" 消息并发送给 Remix 编译器
¥Configure MSW to not mock internal "dev ready" messages to the Remix compiler
确保你在 -c
标志内为应用服务器设置了模拟,以便 REMIX_DEV_ORIGIN
环境变量可供你的模拟使用。例如,你可以在运行 remix-serve
时使用 NODE_OPTIONS
设置 Node 的 --require
标志:
¥Make sure that you are setting up your mocks for your app server within the -c
flag so that the REMIX_DEV_ORIGIN
environment variable is available to your mocks.
For example, you can use NODE_OPTIONS
to set Node's --require
flag when running remix-serve
:
{
"scripts": {
"dev": "remix dev -c \"npm run dev:app\"",
"dev:app": "cross-env NODE_OPTIONS=\"--require ./mocks\" remix-serve ./build"
}
}
如果你使用 ESM 作为默认模块系统,则需要设置 --import
标志而不是 --require
:
¥If you're using ESM as the default module system, you will need to set the --import
flag instead of --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
上转发内部 "开发就绪" 消息:
¥Next, you can use REMIX_DEV_ORIGIN
to let MSW forward internal "dev ready" messages on /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 ...
);
¥How to integrate with a reverse proxy
假设你的应用服务器和 Remix 编译器都在同一台机器上运行:
¥Let's say you have the app server and Remix compiler both running on the same machine:
应用服务器 👉 http://localhost:1234
¥App server 👉 http://localhost:1234
Remix 编译器 👉 http://localhost:5678
¥Remix compiler 👉 http://localhost:5678
然后,你在应用服务器前设置反向代理:
¥Then, you set up a reverse proxy in front of the app server:
反向代理 👉 https://myhost
¥Reverse proxy 👉 https://myhost
但支持热更新的内部 HTTP 和 WebSocket 连接仍会尝试访问 Remix 编译器的非代理源:
¥But the internal HTTP and WebSocket connections to support hot updates will still try to reach the Remix compiler's unproxied origin:
热更新 👉 http://localhost:5678
/ ws://localhost:5678
❌
¥Hot updates 👉 http://localhost:5678
/ ws://localhost:5678
❌
要使内部连接指向反向代理,你可以使用 REMIX_DEV_ORIGIN
环境变量:
¥To get the internal connections to point to the reverse proxy, you can use the REMIX_DEV_ORIGIN
environment variable:
REMIX_DEV_ORIGIN=https://myhost remix dev
现在,热更新将正确发送到代理:
¥Now, hot updates will be sent correctly to the proxy:
热更新 👉 https://myhost
/ wss://myhost
✅
¥Hot updates 👉 https://myhost
/ wss://myhost
✅
¥Performance tuning and debugging
¥Path imports
目前,当 Remix 重建你的应用时,编译器必须处理你的应用代码及其所有依赖。编译器会从应用中对未使用的代码进行树状优化,这样你就不会将任何未使用的代码发送到浏览器,从而尽可能精简服务器。但编译器仍然需要抓取所有代码才能知道哪些要保留,哪些要进行树形优化。
¥Currently, when Remix rebuilds your app, the compiler has to process your app code along with any of its dependencies. The compiler tree-shakes unused code from the app so that you don't ship any unused code to the browser and so that you keep your server as slim as possible. But the compiler still needs to crawl all the code to know what to keep and what to tree shake away.
简而言之,这意味着导入和导出的方式会对重建应用所需的时间产生重大影响。例如,如果你正在使用 Material UI 或 AntD 之类的库,则可以使用 路径导入 来加快构建速度:
¥In short, this means that the way you do imports and exports can have a big impact on how long it takes to rebuild your app. For example, if you are using a library like Material UI or AntD, you can likely speed up your builds by using path imports:
- import { Button, TextField } from '@mui/material';
+ import Button from '@mui/material/Button';
+ import TextField from '@mui/material/TextField';
将来,Remix 可以在开发过程中预先打包依赖,以完全避免此问题。但是今天,你可以使用路径导入来帮助编译器。
¥In the future, Remix could pre-bundle dependencies in development to avoid this problem entirely. But today, you can help the compiler out by using path imports.
¥Debugging bundles
根据你的应用及其依赖,你处理的代码量可能远超应用实际需求。查看我们的 bundle 分析指南 了解更多详情。
¥Depending on your app and dependencies, you might be processing much more code than your app needs. Check out our bundle analysis guide for more details.
¥Troubleshooting
如果你期望热更新但页面却完全重新加载,请查看我们的 关于热模块替换的讨论,了解更多关于 React 快速刷新的局限性以及常见问题的解决方法。
¥If you are expecting hot updates but getting full page reloads, check out our discussion on Hot Module Replacement to learn more about the limitations of React Fast Refresh and workarounds for common issues.
¥HDR: every code change triggers HDR
热数据重新验证通过尝试打包每个加载器,然后为每个加载器提取内容指纹来检测加载器的变化。它依靠 tree shake 来确定你的更改是否影响每个加载器。
¥Hot Data Revalidation detects loader changes by trying to bundle each loader and then fingerprinting the content for each. It relies on tree shaking to determine whether your changes affect each loader or not.
为了确保 tree shake 能够可靠地检测到加载器的更改,请务必声明应用的软件包没有副作用:
¥To ensure that tree shaking can reliably detect changes to loaders, make sure you declare that your app's package is side-effect-free:
{
"sideEffects": false
}
¥HDR: harmless console errors when loader data is removed
当你删除加载器或移除该加载器返回的部分数据时,你的应用应该能够正确热更新。但是你可能会注意到浏览器中记录了控制台错误。
¥When you delete a loader or remove some of the data being returned by that loader, your app should be hot updated correctly. But you may notice console errors logged in your browser.
应用热更新时,React 严格模式和 React Suspense 可能会导致多次渲染。大多数情况下,这些方法都能正确渲染,包括你可见的最终渲染结果。但是,中间渲染有时会将新的加载器数据与旧的 React 组件一起使用,这就是这些错误的来源。
¥React strict-mode and React Suspense can cause multiple renders when hot updates are applied. Most of these render correctly, including the final render that is visible to you. But intermediate renders can sometimes use new loader data with old React components, which is where those errors come from.
我们正在继续调查潜在的竞争条件,看看能否解决这个问题。同时,如果这些控制台错误让你感到困扰,你可以在出现错误时刷新页面。
¥We are continuing to investigate the underlying race condition to see if we can smooth that over. In the meantime, if those console errors bother you, you can refresh the page whenever they occur.
当 Remix 编译器构建(并重新构建)你的应用时,你可能会注意到速度略有下降,因为编译器需要抓取每个加载器的依赖。这样,Remix 就可以在重建时检测到加载器的变化。
¥When the Remix compiler builds (and rebuilds) your app, you may notice a slight slowdown as the compiler needs to crawl the dependencies for each loader. That way Remix can detect loader changes on rebuilds.
虽然初始构建速度减慢本质上是 HDR 的代价,但我们计划优化重建,使 HDR 重建的速度不会受到明显影响。
¥While the initial build slowdown is inherently a cost for HDR, we plan to optimize rebuilds so that there is no perceivable slowdown for HDR rebuilds.