Remix 是一个基于 React 的全栈 Web 框架,它的设计哲学与 Next.js 有着本质区别。Remix 不是为了“服务端渲染 React 页面”而生的框架,而是以 Web 标准为核心,把服务端和客户端视为一个统一的运行时,致力于提升开发者体验和用户体验。
核心设计理念一:回归 Web 标准
Remix 最核心的信仰是“拥抱 Web 平台,而不是抽象掉它”。许多框架在 React 之上构建了复杂的抽象层(如自定义的请求/响应对象),而 Remix 选择直接暴露浏览器和服务器原生的 Web API,例如 Request、Response、FormData、URL、Headers 等。
这意味着你在 Remix 中写的代码更像是传统的 Web 开发,只不过享受了 React 的声明式 UI 能力:
- 数据加载:每个路由可以导出一个
loader函数,它接收标准的Request对象,返回数据。这个loader只运行在服务端,让你直接访问数据库、文件系统或内部 API。 - 数据提交:每个路由可以导出一个
action函数,它接收Request和params,处理表单提交,同样只运行在服务端。 - 响应处理:
loader和action返回的数据直接作为组件的useLoaderData()或useActionData()的返回值,无缝对接。
这种设计让你几乎摆脱了传统的状态管理库和数据请求库,因为数据的加载和变更已经与路由深度集成。
// 路由 /products/$id.tsx
export async function loader({ params }) {
const product = await db.product.findUnique({ where: { id: params.id } });
return json({ product });
}
export default function ProductPage() {
const { product } = useLoaderData<typeof loader>();
return <h1>{product.name}</h1>;
}
核心设计理念二:嵌套路由与数据依赖
Remix 的路由系统基于嵌套路由,并且每个路由层都可以有自己的 loader。当用户访问一个嵌套路由时,父路由和子路由的 loader 会并行执行,数据层层累积,最终页面一次性渲染,避免了传统 SPA 的水合(hydration)等待问题。
这种设计天然解决了数据依赖问题:你只需要在每个路由组件中声明它需要什么数据(通过 loader),Remix 会在渲染该路由之前把所有数据准备好。无需手动触发请求、管理 loading 状态或处理竞态问题。
// 父路由:/dashboard.tsx
export async function loader() {
const user = await getCurrentUser();
return json({ user });
}
// 子路由:/dashboard/projects.tsx
export async function loader({ request }) {
const projects = await getProjects();
return json({ projects });
}
并行加载 + 嵌套组合,减少了请求瀑布流,提升了页面加载速度。
核心设计理念三:基于表单的突变,反对过度“状态化”
Remix 推崇使用原生 HTML 表单 + 服务器 action 来提交数据,而不是在客户端通过 onSubmit 调用 API、管理表单状态。当表单提交后,Remix 会自动处理页面导航和数据重新验证,完全模拟了传统多页面应用的体验,但又保留了单页应用的即时反馈。
这种做法带来了两个直接好处:
- 无需 JavaScript:即使浏览器禁用了 JavaScript,表单依然可以正常提交,因为 Remix 表单走的是标准的 HTTP POST,action 处理完重定向即可。
- 自动处理加载状态:通过
useNavigation()或内置的fetcher,你可以轻松获取提交状态、显示加载指示器,而无需手动管理isSubmitting等状态。
export async function action({ request }) {
const formData = await request.formData();
await db.todo.create({ title: formData.get("title") });
return redirect("/todos");
}
export default function NewTodo() {
const navigation = useNavigation();
const isSubmitting = navigation.state === "submitting";
return (
<Form method="post">
<input name="title" required />
<button type="submit" disabled={isSubmitting}>
{isSubmitting ? "添加中..." : "添加"}
</button>
</Form>
);
}
核心设计理念四:渐进增强(Progressive Enhancement)
Remix 的架构天然支持渐进增强。你可以先用标准的 HTML 和服务器逻辑构建一个完全可用的基础应用,然后逐步添加 CSS、JavaScript 和 React 交互,而不需要重写代码。例如:
- 一个普通的
<Link>标签在 JS 失效时仍然可以导航(退化为<a href>)。 - 表单提交依赖原生 HTML 提交,JS 增强后可以提供 optimistic UI(乐观更新)。
- 页面数据由服务端
loader提供,即使没有客户端 JS,首屏内容依然存在。
这种“以 HTML 为基础,逐步添加交互”的模式,与 React 社区中常见的“全量 JS 驱动”形成了鲜明对比,也带来了更好的性能和 SEO。
与 Next.js 的分野
| 维度 | Remix | Next.js |
|------|-------|---------|
| 核心理念 | 拥抱 Web 标准,nest 路由为中心 | 文件系统路由 + 多种渲染策略(SSG/ISR/SSR) |
| 数据获取 | 路由级 loader,强依赖 Web API | getServerSideProps / getStaticProps(Pages Router)或 Server Components(App Router) |
| 突变 | 基于表单 action,推崇原生 Form | 客户端调用 API Routes 或 Server Actions |
| 渲染模式 | 专注 SSR + 客户端导航,无静态生成 | 支持 SSG、ISR、SSR,客户端组件等 |
| 学习曲线 | 较陡,需要理解 Web 基础 | 中等,抽象层更多 |
Remix 更适合那些希望“回归 Web 初衷”、讨厌过度封装、追求极简数据流和高性能的团队。它的设计迫使开发者思考数据加载的边界,虽然初期需要适应,但代码编写和维护的长期体验非常统一。
总结
Remix 的全栈理念可以浓缩为一句话:让 React 应用更像 Web 应用。它通过路由驱动的数据加载、原生表单处理、嵌套组件设计和对 Web 标准的直接映射,提供了比传统 SPA 更自然的开发体验。如果你重视“先保证功能可用,再增强用户体验”的哲学,Remix 是一个值得深入的方向。