unplugin-react-router-dom
v1.1.0
Published
File-based routing for React + react-router-dom. Works with Vite, webpack, Rspack, Rollup and esbuild via unplugin.
Downloads
657
Maintainers
Readme
unplugin-react-router-dom
基于 unplugin 的 React + react-router-dom 文件系统路由插件。
支持 Vite · webpack · Rspack · Rollup · esbuild。
English | 中文
灵感来源于 Next.js App Router 和 Expo Router 的文件约定。
三步上手(Vite)
第 1 步 · 安装
npm install unplugin-react-router-dom react react-dom react-router-dom第 2 步 · 配置插件
// vite.config.ts
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import fileRouter from 'unplugin-react-router-dom/vite'
export default defineConfig({ plugins: [react(), fileRouter()] })第 3 步 · 改入口、加页面
// src/main.tsx
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
// import App from './App.tsx' ← 注释或删除;App.tsx 可不再使用
createRoot(document.getElementById('root')!).render(<StrictMode />)// src/pages/home.tsx → 访问 /home
export default function Home() {
return <h1>Home</h1>
}然后 npm run dev。在 src/pages/ 下新建文件 = 新建路由,无需手写路由表。
原理(一句话):入口只留空壳组件,插件在构建时自动注入
RouterProvider;页面全部由src/pages/文件生成。
需要手动控制入口时
默认不用改——按上面三步即可。只有当你要自己决定怎么挂载路由时,才用下面任一方式(用了之后自动注入会关闭):
方案 A — 内置 App
import { createRoot } from 'react-dom/client'
import App from 'unplugin-react-router-dom/app'
createRoot(document.getElementById('root')!).render(<App />)方案 B — 自己挂 RouterProvider
import { StrictMode } from 'react'
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from 'unplugin-react-router-dom/router'
createRoot(document.getElementById('root')!).render(
<StrictMode>
<RouterProvider router={router} />
</StrictMode>,
)方案 C — 路由外再包 Provider
import { createRoot } from 'react-dom/client'
import { RouterProvider } from 'react-router-dom'
import router from 'unplugin-react-router-dom/router'
import { ThemeProvider } from './theme'
createRoot(document.getElementById('root')!).render(
<ThemeProvider>
<RouterProvider router={router} />
</ThemeProvider>,
)其他打包器
从 unplugin-react-router-dom/<打包器> 导入,加入 plugins 即可:
| 打包器 | 导入路径 |
|--------|----------|
| Vite | ./vite |
| webpack | ./webpack |
| Rspack | ./rspack |
| Rollup | ./rollup |
| esbuild | ./esbuild |
webpack / Rspack — 开发服务器需 historyApiFallback: true,否则刷新 /home 等路径会 404:
import fileRouter from 'unplugin-react-router-dom/webpack' // 或 /rspack
export default {
plugins: [fileRouter()],
devServer: { historyApiFallback: true },
}CJS 用 require('unplugin-react-router-dom/webpack'),用法相同。
Rsbuild — 插件挂在 tools.rspack.plugins,并设置 server.historyApiFallback: true:
import fileRouter from 'unplugin-react-router-dom/rspack'
export default defineConfig({
plugins: [pluginReact()],
server: { historyApiFallback: true },
tools: { rspack: { plugins: [fileRouter()] } },
})Rollup / esbuild — plugins: [fileRouter()] 即可。
增删 src/pages/ 下文件会自动更新路由;改页面内容走 HMR。
进阶
文件约定
在 src/pages/(可配置 pagesDir)下,文件名即路由:
| 路径 | URL |
|------|-----|
| index.tsx | / |
| home.tsx | /home |
| detail/[id].tsx | /detail/:id |
| (group)/page.tsx | /page(括号目录不进 URL) |
特殊文件(下划线前缀,不生成路由):
| 文件 | 作用 |
|------|------|
| _layout.tsx | 布局,渲染 {children}(勿用 <Outlet />) |
| loading.tsx / error.tsx | Suspense / ErrorBoundary 兜底 |
| _guard.ts | 根目录守卫 beforeEach / afterEach |
| not-found.tsx | 404 |
页面可导出 loader、action 等 React Router 数据 API(须直接命名导出)。
配置项
fileRouter({
pagesDir: 'src/pages', // 页面目录
routerType: 'browser', // 'browser' | 'hash' | 'memory'
routerOptions: {}, // 传给 createBrowserRouter 等,如 { basename: '/app' }
indexRedirect: '/home', // 根路径重定向
defaultImport: 'lazy', // 'lazy' | 'sync';或 login.sync.tsx / detail.lazy.tsx
animated: false, // 路由过渡动画
animationType: 'cover', // 'cover' | 'slide' | 'fade'
logLevel: 'info', // 'info' | 'warn' | 'silent'
transformRoutes(rootNode) {}, // 生成前修改路由树
})Hash 路由(Capacitor 等):fileRouter({ routerType: 'hash' })
导航守卫
import { setBeforeEach } from 'unplugin-react-router-dom/router'
setBeforeEach((to, from) => {
if (!token && to !== '/login') return '/login'
})或在 src/pages/_guard.ts 导出 beforeEach / afterEach(仅根目录生效)。
TypeScript
tsconfig.json 添加 "types": ["unplugin-react-router-dom/client"]。
可选:在 src/routes.d.ts 扩展 RouteMap 获得类型化路径与查询参数。
动画过渡
fileRouter({ animated: true, animationType: 'cover' })。嵌套 _layout.tsx 渲染 {children} 即可(勿手写 <Outlet />)。
单页后缀:page.modal.tsx / .slide / .cover / .fade / .none。
迁移
自 vite-plugin-file-router:安装本包,把 import 改为 unplugin-react-router-dom/vite,types 改为 unplugin-react-router-dom/client。页面文件无需改动。
已知限制
开发 / HMR
- Vite — 已接受的限制(仅 dev,P2) — 正在浏览某页时删该页可能打出一条
[vite] Failed to reload。这是 Vite 客户端在活跃 lazy chunk 被删时的 HMR 竞态;功能仍正确(已删 URL 落到not-found.tsx)。先离开该页再删则通常无此日志。跟踪与复现:docs/vite-issue-active-route-delete.md。 - 路由重载 — 增删页面、改
_layout/loading/error/_guard,或改loader/action/meta.title会触发整表重载(Vite 为 full-reload,webpack/Rspack 为 dev-server 重编译);仅改页面 JSX 且路由表未变时走普通 HMR
路由语义
_guard.ts仅pagesDir根目录生效[[...slug]]映射为可选 catch-all*?;[...slug]映射为必选 catch-all*meta.title仅支持静态字面量- 路由数据 API 仅支持直接命名导出(
loader、action等) - 非标准入口需手动
app/router
工具链
- 开发本插件:改源码后执行
npm run build;在业务项目里用打包器自带 watch 即可
License
MIT
