Chapter 10

部署与生态

掌握 Remix 适配器体系,将应用部署到任意平台,了解测试方案与迁移路径

10.1 适配器体系

Remix 通过适配器(Adapter)机制实现平台无关性。核心业务逻辑(路由、Loader、Action)完全不变,只需安装对应平台的适配器,就能将 Remix 应用部署到 Node.js、Cloudflare Workers、Vercel、Netlify 等不同运行环境。

🟢

@remix-run/node

Node.js 标准适配器,适用于自托管 VPS、Docker 容器等传统部署方式

☁️

@remix-run/cloudflare

Cloudflare Workers 适配器,边缘计算,全球延迟极低,内置 KV/D1 存储

Vercel

官方 Vercel 适配器,零配置部署,自动 CDN 加速,边缘运行时可选

🦋

Netlify

Netlify Functions 适配器,与 Netlify 平台深度集成

🚀

Fly.io

Indie Stack 默认部署平台,全球边缘节点,对个人开发者友好

☁️

AWS Lambda

通过 @remix-run/architect 适配 AWS Lambda + API Gateway

10.2 Node.js 生产部署

# 生产构建
npm run build

# 构建产物结构:
# build/server/   — 服务端代码
# build/client/   — 客户端静态资源(CSS/JS/图片)

# 启动生产服务器(Node.js 适配器)
npm start
# 等同于: node ./build/server/index.js
SHELL

10.3 Docker 生产部署

# Dockerfile — 多阶段构建
FROM node:20-alpine AS base
WORKDIR /app
COPY package*.json ./

FROM base AS deps
RUN npm ci

FROM base AS build
COPY --from=deps /app/node_modules ./node_modules
COPY . .
RUN npm run build
RUN npm prune --production

FROM base AS production
ENV NODE_ENV=production
COPY --from=build /app/build ./build
COPY --from=build /app/node_modules ./node_modules
COPY --from=build /app/package.json ./package.json

EXPOSE 3000
CMD ["npm", "start"]
DOCKERFILE

10.4 Cloudflare Workers 部署

# 安装 Cloudflare 适配器
npm install @remix-run/cloudflare wrangler
SHELL
// vite.config.ts — Cloudflare Workers
import { vitePlugin as remix } from "@remix-run/dev";
import { cloudflareDevProxyVitePlugin } from "@remix-run/cloudflare-pages";
import { defineConfig } from "vite";

export default defineConfig({
  plugins: [
    cloudflareDevProxyVitePlugin(),
    remix({
      future: { v3_fetcherPersist: true },
    }),
  ],
});
TS
# 部署到 Cloudflare Pages
npx wrangler pages deploy build/client
SHELL

10.5 与 Shopify Hydrogen 的关系

Shopify Hydrogen 是 Shopify 官方的自定义店铺前端框架,底层完全基于 Remix 构建。学习 Remix 就是学习 Hydrogen 的基础。两者的关系可以类比为 Next.js 与 Vercel 的商业生态关系。

层次RemixHydrogen
定位通用全栈框架电商专用框架
路由/数据Loader/Action基于 Remix 扩展
数据源任意数据库Shopify Storefront API
缓存HTTP 标准缓存Hydrogen Cache API
部署多平台Shopify Oxygen(边缘)

10.6 测试策略 — Vitest + Testing Library

npm install -D vitest @testing-library/react @testing-library/user-event jsdom
SHELL
// app/routes/login.test.tsx
import { render, screen } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { describe, it, expect, vi } from "vitest";

describe("LoginPage", () => {
  it("renders login form", () => {
    render(<LoginPage />);
    expect(screen.getByLabelText(/邮箱/)).toBeInTheDocument();
    expect(screen.getByLabelText(/密码/)).toBeInTheDocument();
  });
});
TSX
// loader 单元测试
import { loader } from "~/routes/blog._index";
import { describe, it, expect, vi } from "vitest";

describe("blog loader", () => {
  it("returns posts", async () => {
    const request = new Request("http://localhost/blog");
    const response = await loader({ request, params: {}, context: {} });
    const data = await response.json();
    expect(data.posts).toBeDefined();
    expect(Array.isArray(data.posts)).toBe(true);
  });
});
TSX

10.7 状态管理 — Loader 即状态

Remix 应用通常不需要 Redux、Zustand 等全局状态管理库。大多数"状态"本质上是服务端数据,通过 Loader 提供;URL 是另一种状态(分页、筛选、排序);只有少量纯 UI 状态(模态框、下拉菜单)才需要 useState

不需要全局状态的场景

  • 用户信息 → root loader
  • 文章列表 → 路由 loader
  • 搜索关键词 → URL searchParams
  • 当前页码 → URL searchParams
  • 筛选条件 → URL searchParams
  • 表单错误 → useActionData

需要 useState 的场景

  • 模态框 open/close
  • 下拉菜单展开状态
  • 图片轮播当前索引
  • 主题切换(dark/light)
  • 复杂富文本编辑器状态
  • WebSocket 实时消息

10.8 从 Next.js 迁移指南

如果你已有 Next.js 项目,以下映射表可以帮助理解概念迁移方向。

Next.jsRemix 等价说明
getServerSidePropsloader服务端数据加载
getStaticPropsloader + Cache-Control用 HTTP 缓存代替构建时静态化
Server Actionsaction数据变更(更接近 HTML 语义)
app/layout.tsx布局路由 + Outlet嵌套布局
page.tsx路由默认导出组件页面组件
route.ts资源路由API 端点
middleware.tsloader 守卫 / 适配器层请求中间件
next/link prefetchLink prefetch="intent"链接预获取
useRouter.pushuseNavigate编程式导航
loading.tsxuseNavigation + UI加载状态
💡

迁移建议不建议一次性迁移整个 Next.js 项目。可以先在 Remix 中重建核心页面,验证团队对 Loader/Action 模式的掌握程度,再逐步迁移其余页面。两者在路由概念上差异较大,团队需要重新建立心智模型。

10.9 完整学习路径回顾

ℹ️

课程总结Remix 是一个"回归本源"的框架:它没有发明新的数据获取 API,而是让你直接使用 fetchRequestResponseFormData;它没有创造全新的路由模式,而是把 HTML Form 的语义放大到全栈;它的嵌套路由不是噱头,而是让 UI 树与数据加载树完全对应,实现真正的并行。掌握 Remix,你同时也在深入理解 Web 标准,这些知识永远不会过时。