跳转至

Remix Web框架

一句话概述:Remix 是React全栈Web框架,通过嵌套路由和渐进增强构建快速、弹性的Web应用,已与React Router v7合并。

核心知识点表

概念白话解释
嵌套路由(Nested Routes)页面可以嵌套,外层保持不变只更新内层(如侧边栏+内容)
Loader服务端数据加载函数,页面渲染前获取数据
Action处理表单提交的服务端函数
渐进增强不用JS也能工作,JS加载后体验更好
React Router v7Remix已合并到React Router v7中
ErrorBoundary错误边界,某个路由报错不影响其他路由

安装配置

# 方式一:使用React Router v7(推荐,Remix的继任者)
npx create-react-router@latest my-app
cd my-app
npm install
npm run dev  # → http://localhost:5173

# 方式二:传统Remix
npx create-remix@latest my-remix-app
cd my-remix-app
npm run dev

基本使用

路由和Loader

// app/routes/users.tsx — /users页面
import type { LoaderFunctionArgs } from 'react-router'
import { useLoaderData } from 'react-router'

// Loader:在服务端加载数据
export async function loader({ request }: LoaderFunctionArgs) {
  const users = await db.user.findMany()  // 服务端查数据库
  return { users }  // 传给组件
}

// 组件
export default function UsersPage() {
  const { users } = useLoaderData<typeof loader>()  // 获取loader数据

  return (
    <ul>
      {users.map(user => (
        <li key={user.id}>{user.name}</li>
      ))}
    </ul>
  )
}

Action(处理表单)

// app/routes/login.tsx
import type { ActionFunctionArgs } from 'react-router'
import { Form, useActionData } from 'react-router'

// Action:处理POST请求
export async function action({ request }: ActionFunctionArgs) {
  const formData = await request.formData()
  const email = formData.get('email') as string
  const password = formData.get('password') as string

  // 验证
  if (!email || !password) {
    return { error: '请填写所有字段' }
  }

  // 登录逻辑...
  return redirect('/dashboard')
}

export default function LoginPage() {
  const actionData = useActionData<typeof action>()

  return (
    <Form method="post">  {/* Remix的Form组件,自动渐进增强 */}
      {actionData?.error && <p style={{color: 'red'}}>{actionData.error}</p>}
      <input name="email" type="email" placeholder="邮箱" />
      <input name="password" type="password" placeholder="密码" />
      <button type="submit">登录</button>
    </Form>
  )
}

嵌套路由

// app/routes/dashboard.tsx — 父路由(布局)
import { Outlet, NavLink } from 'react-router'

export default function Dashboard() {
  return (
    <div style={{ display: 'flex' }}>
      <nav>
        <NavLink to="/dashboard/overview">概览</NavLink>
        <NavLink to="/dashboard/settings">设置</NavLink>
      </nav>
      <main>
        <Outlet />  {/* 子路由在这里渲染 */}
      </main>
    </div>
  )
}

// app/routes/dashboard.overview.tsx — 子路由
export default function Overview() {
  return <h2>仪表盘概览</h2>
}

ErrorBoundary

// 每个路由都可以有自己的错误边界
export function ErrorBoundary() {
  const error = useRouteError()
  return (
    <div>
      <h1>出错了</h1>
      <p>{error instanceof Error ? error.message : '未知错误'}</p>
    </div>
  )
}

高级用法

乐观更新(Optimistic UI)

import { useFetcher } from 'react-router'

function LikeButton({ postId, liked }: { postId: string; liked: boolean }) {
  const fetcher = useFetcher()
  // 乐观更新:不等服务器响应就先更新UI
  const optimisticLiked = fetcher.formData
    ? fetcher.formData.get('liked') === 'true'
    : liked

  return (
    <fetcher.Form method="post" action={`/posts/${postId}/like`}>
      <input type="hidden" name="liked" value={(!optimisticLiked).toString()} />
      <button>{optimisticLiked ? '❤️' : '🤍'}</button>
    </fetcher.Form>
  )
}

流式渲染(Streaming)

import { defer } from 'react-router'
import { Suspense } from 'react'
import { Await } from 'react-router'

export async function loader() {
  const fastData = await getFastData()  // 快速数据立即返回
  const slowDataPromise = getSlowData() // 慢数据延迟加载

  return defer({
    fastData,
    slowData: slowDataPromise,  // Promise,边加载边渲染
  })
}

export default function Page() {
  const { fastData, slowData } = useLoaderData<typeof loader>()

  return (
    <div>
      <h1>{fastData.title}</h1>  {/* 立即显示 */}
      <Suspense fallback={<p>加载中...</p>}>
        <Await resolve={slowData}>  {/* 数据就绪后显示 */}
          {(data) => <div>{data.content}</div>}
        </Await>
      </Suspense>
    </div>
  )
}

常见报错与解决

报错信息原因解决方案
loader not found路由文件命名不对检查文件在routes/目录下且命名正确
Form action failedaction函数报错检查action中的逻辑和返回值
Hydration failed服务端和客户端渲染不一致避免在render中使用window等浏览器API

速查表

npx create-react-router@latest  # 创建项目
npm run dev                      # 开发
npm run build                    # 构建

# 路由文件约定(点号分隔表示嵌套)
# routes/index.tsx        → /
# routes/about.tsx        → /about
# routes/blog.$slug.tsx   → /blog/:slug
# routes/dashboard.tsx    → /dashboard (父)
# routes/dashboard._index.tsx → /dashboard/ (默认子)

同类工具对比

特性Remix/RR v7Next.jsSvelteKit
核心理念Web标准 + 渐进增强全能型编译优化
表单处理原生Form + ActionServer ActionForm Actions
嵌套路由核心特性支持支持
无JS工作完美支持有限有限

面试建议:Remix的核心理念是"拥抱Web平台标准"——使用原生Form、Request/Response API,不发明新概念。Shopify用Remix让管理后台快了30%。