@gulibs/react-vroutes
v0.0.14
Published
基于 React Router v7 的文件系统自动路由生成器,为所有 React + Vite 项目提供类似 Next.js 的开发体验
Maintainers
Readme
@gulibs/react-vroutes
🚀 基于 React Router v7 的文件系统自动路由生成器,为所有 React + Vite 项目提供类似 Next.js 的开发体验
✨ 特性
🚀 核心功能
- 🔥 文件系统路由 - 基于文件结构自动生成路由,零配置启动
- ⚡ 完美热更新 - 文件变化实时同步,开发体验丝滑
- 🎯 智能识别 - 自动区分页面、布局、数据加载等文件类型
- 📦 代码分割 - 内置 React.lazy 支持,自动优化性能
- 🛡️ 类型安全 - 完整 TypeScript 支持,智能提示无忧
📁 路由风格支持
- 目录约定式:
pages/about/page.tsx→/about - 扁平风格:
pages/about.page.tsx→/about - 混合模式: 同时支持两种风格
- 动态路由:
[id].page.tsx→/:id - 通配符路由:
[...slug].page.tsx→/* - 嵌套路由: 支持多层嵌套结构
- 索引路由: 自动处理 index 路由
🔧 React Router v7 新特性支持
- ✅
loader/action- 数据加载和操作(服务端 / 客户端) - ✅
middleware- 路由中间件支持(Data Mode) - ✅
errorElement- 错误边界组件(通过error.tsx文件) - ✅
loadingElement- 加载元素(通过loading.tsx文件)
🛡️ 中间件系统
- 中间件自动发现 - 自动扫描
middleware.ts文件 - 基于文件路径自动匹配 - 通过文件系统层级结构自动应用到对应路由
- 执行顺序控制 - Root → Parent → Child 执行顺序
- React Router v7 Data Mode - 使用官方中间件 API
📦 数据加载和处理
- loader 支持 - 服务端 / 客户端数据加载
- action 支持 - 表单处理和数据操作
- 优先级控制 - 独立文件和页面导出共存
- 文件约定 - 支持
loader.ts和action.ts独立文件
🎯 文件类型识别
- 页面文件 -
page.tsx,index.tsx - 布局文件 -
layout.tsx,_layout.tsx - 错误文件 -
error.tsx,_error.tsx,404.tsx - 加载文件 -
loading.tsx,_loading.tsx - 处理文件 -
handle.ts,_handle.ts - 中间件文件 -
middleware.ts - 加载器文件 -
loader.ts,_loader.ts - 操作文件 -
action.ts,_action.ts
⚡ 性能优化
- 动态导入模式 -
dynamic: true使用 React.lazy - 静态导入模式 -
dynamic: false使用静态导入 - 代码分割 - 自动按路由分割代码
- 缓存机制 - 智能缓存提升性能
- 文件监听优化 - 避免循环监控和性能问题
🔍 开发体验
- 调试模式 - 详细的调试日志
- 错误处理 - 完善的错误边界和错误处理
- 日志系统 - 分级日志输出
- 类型提示 - 完整的 TypeScript 类型定义
- 虚拟模块 - 避免 HMR 冲突的虚拟模块系统
📦 安装
npm install @gulibs/react-vroutes
# 或
pnpm add @gulibs/react-vroutes
# 或
yarn add @gulibs/react-vroutes🚀 快速开始
1. 配置 Vite 插件
// vite.config.ts
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { autoRoutes } from '@gulibs/react-vroutes';
export default defineConfig({
plugins: [
react(),
autoRoutes({
dirs: [{ dir: './src/pages', baseRoute: '/' }],
dynamic: true, // 启用代码分割
debug: true // 开发时启用调试
})
]
});2. 创建页面文件
src/pages/
├── page.tsx # / 路由
├── about.page.tsx # /about 路由
├── layout.tsx # 根布局
├── middleware.ts # 全局中间件
└── users/
├── page.tsx # /users 路由
├── layout.tsx # 用户模块布局
├── middleware.ts # 用户模块中间件
└── [id]/
├── page.tsx # /users/:id 路由
├── loader.ts # 数据加载
├── action.ts # 表单处理
├── handle.ts # 路由元数据
├── loading.tsx # 加载状态
├── error.tsx # 错误边界
└── middleware.ts # 用户详情中间件3. 使用路由
// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from 'react-router';
import { routes } from '@gulibs/react-autopages';
const router = createBrowserRouter(routes);
ReactDOM.createRoot(document.getElementById('root')!).render(
<React.StrictMode>
<RouterProvider router={router} />
</React.StrictMode>
);📁 文件约定
路由文件(生成路由)
| 文件名 | 路由路径 | 说明 |
|--------|----------|------|
| page.tsx | / | 目录首页 |
| about.page.tsx | /about | 扁平风格页面 |
| [id].page.tsx | /:id | 动态路由 |
| [...slug].page.tsx | /* | 通配符路由 |
| layout.tsx | - | 嵌套布局组件 |
辅助文件(不生成路由)
| 文件名 | 功能 | React Router v7 |
|--------|------|-----------------|
| loader.ts | 数据加载(服务端 / 客户端) | ✅ |
| action.ts | 表单处理 | ✅ |
| handle.ts | 路由元数据 | ✅ |
| middleware.ts | 路由中间件 | ✅ |
| error.tsx | 错误边界(errorElement) | ✅ |
| loading.tsx | 加载状态 | ✅ |
⚙️ 配置选项
interface IAutoRoutesPluginOptions {
// 路由目录配置(必需)
dirs: Array<{
dir: string; // 扫描目录
baseRoute: string; // 基础路由
include?: string[]; // 包含模式(文件匹配)
exclude?: string[]; // 排除模式(文件匹配)
nested?: boolean; // 嵌套模式
}>;
// 动态导入
dynamic?: boolean; // 代码分割(默认: false)
// 路由模式
routeMode?: 'nested' | 'flat'; // 默认: 'flat'
// 全局文件匹配模式(应用到所有目录)
include?: string[]; // 全局包含模式
exclude?: string[]; // 全局排除模式
// 路由风格检测
routeStyleDetection?: {
enabled?: boolean; // 自动检测路由风格
forceStyle?: 'directory' | 'flat' | 'mixed';
};
// Loader/Action 优先级
loaderActionPriority?: {
preferFiles?: boolean; // 优先独立文件(默认: true)
};
// React Router v7 特性
enableMiddleware?: boolean; // 启用中间件支持(默认: false)
useNewSystem?: boolean; // 使用新的 loader/action 系统(默认: true)
// 缓存配置
cache?: boolean; // 启用缓存(默认: true)
cacheTime?: number; // 缓存时间(毫秒,默认: 60000)
// 调试
debug?: boolean; // 调试模式(默认: false)
}🔄 动态导入模式
dynamic: true (默认)
启用代码分割,使用 React.lazy 进行动态导入:
// 生成的路由代码
export const routes = [{
path: "/",
element: React.createElement(React.lazy(() => import("/src/pages/layout.tsx"))),
children: [{
index: true,
element: React.createElement(React.lazy(() => import("/src/pages/page.tsx")))
}]
}];优点:
- 自动代码分割
- 按需加载,提升首屏性能
- 支持 Suspense 边界
dynamic: false
使用静态导入,所有组件在构建时打包:
// 生成的路由代码
import __$React_Route_Srcpages_Page_$page__ from "/src/pages/page.tsx"
import __$React_Route_Srcpages_Layout_$layout__ from "/src/pages/layout.tsx"
export const routes = [{
path: "/",
element: React.createElement(__$React_Route_Srcpages_Layout_$layout__),
children: [{
index: true,
element: React.createElement(__$React_Route_Srcpages_Page_$page__)
}]
}];优点:
- 更快的运行时性能
- 减少网络请求
- 适合小型应用
📝 示例代码
页面组件
// pages/users/page.tsx
import { useLoaderData } from 'react-router';
export default function UsersPage() {
const { users } = useLoaderData();
return (
<div>
<h1>用户列表</h1>
{users.map(user => (
<div key={user.id}>{user.name}</div>
))}
</div>
);
}数据加载器
// pages/users/loader.ts
export async function loader({ params }) {
const users = await fetch('/api/users').then(res => res.json());
return { users };
}表单处理器
// pages/users/action.ts
import { redirect } from 'react-router';
export async function action({ request }) {
const formData = await request.formData();
const name = formData.get('name');
await fetch('/api/users', {
method: 'POST',
body: JSON.stringify({ name }),
headers: { 'Content-Type': 'application/json' }
});
return redirect('/users');
}布局组件
// pages/layout.tsx
import { Outlet } from 'react-router';
export default function RootLayout() {
return (
<div>
<nav>
<a href="/">首页</a>
<a href="/about">关于</a>
<a href="/users">用户</a>
</nav>
<main>
<Outlet />
</main>
</div>
);
}路由元数据
// pages/users/[id]/handle.ts
export const handle = {
breadcrumb: (params) => [
{ label: '首页', href: '/' },
{ label: '用户管理', href: '/users' },
{ label: `用户 ${params.id}`, href: `/users/${params.id}` }
],
title: (params) => `用户详情 - 用户 ${params.id}`,
permissions: ['user:read'],
meta: {
keywords: ['用户', '详情'],
robots: 'index, follow'
}
};中间件系统
基础中间件
// pages/middleware.ts - 全局中间件
export default [
async ({ request, context, params }, next) => {
console.log('🌍 全局中间件: 请求开始', request.url);
const response = await next();
console.log('🌍 全局中间件: 请求结束');
return response;
}
];路径特定中间件
// pages/users/middleware.ts - 用户模块中间件
export default [
async ({ request, context, params }, next) => {
// 只对 /users/* 路径生效
console.log('👥 用户模块中间件: 请求开始', request.url);
const response = await next();
console.log('👥 用户模块中间件: 请求结束');
return response;
}
];中间件执行顺序
中间件按照以下顺序执行:
- Root 中间件 - 匹配
/的中间件 - Parent 中间件 - 匹配父级路径的中间件
- Child 中间件 - 匹配当前路径的中间件
例如,访问 /users/123 时:
- 先执行匹配
/的全局中间件 - 再执行匹配
/users的中间件 - 最后执行匹配
/users/123的中间件
🔧 TypeScript 支持
添加类型声明以获得完整的智能提示:
// vite-env.d.ts
/// <reference types="@gulibs/react-vroutes/react-routes" />或在 tsconfig.json 中添加:
{
"compilerOptions": {
"types": ["@gulibs/react-vroutes/react-routes"]
}
}🎯 核心改进
相比 vgrove-autoroutes
- ✅ 修复嵌套路由生成错误
- ✅ 正确处理 layout 和 page 的组合关系
- ✅ 支持可选的中间层路由(除根目录外)
- ✅ 支持 React Router v7 新特性
- ✅ 改进数据加载方式(独立文件和页面导出可共存)
- ✅ 更好的 TypeScript 支持
- ✅ 完善的热更新机制
- ✅ 简化的中间件系统(Data Mode)
关键技术点
- 虚拟模块系统 - 使用路径嵌入方式避免 HMR 冲突
- 路由生成逻辑 - 正确处理 layout + page 组合
- Loader/Action 优先级 - 支持独立文件和页面导出共存
- 热更新优化 - 文件监听过滤,避免循环监控
- React Router v7 中间件 - 使用官方 Data Mode API
📚 项目结构
react-vroutes/
├── lib/ # 库源码
│ ├── constant.ts # 常量定义
│ ├── context.ts # 路由上下文
│ ├── errors.ts # 错误处理
│ ├── logger.ts # 日志系统
│ ├── parser.ts # 路由解析器
│ ├── plugin.ts # Vite 插件
│ ├── stringifier.ts # 路由生成器
│ ├── types.ts # 类型定义
│ ├── client.ts # 客户端 hooks
│ └── index.ts # 入口文件
├── test/src/ # 测试代码
│ └── pages/ # 测试页面
├── react-routes.d.ts # 类型声明
├── vite.config.ts # Vite 配置
└── package.json # 项目配置🐛 故障排除
热更新不工作?
- 确保文件在配置的目录中
- 检查文件命名是否符合约定
- 尝试重启开发服务器
路由没有生成?
- 文件名是否包含
.page.tsx或为page.tsx - 文件是否被
exclude规则排除 - 启用
debug: true查看详细日志
TypeScript 类型错误?
// 添加类型声明
declare module '@gulibs/react-autopages' {
export const routes: Array<RouteObject>;
}📄 许可证
MIT License
🤝 贡献
欢迎提交 Issue 和 Pull Request!
🚀 立即体验零配置路由的便利!
