unplugin-convention-routes
Version:
File system base router plugin for unplugin
408 lines (311 loc) • 10.1 kB
Markdown
<p align="center">
<a href="https://www.npmjs.org/package/unplugin-convention-routes">
<img src="https://img.shields.io/npm/v/unplugin-convention-routes.svg" alt="npm version">
</a>
<a href="https://npmcharts.com/compare/unplugin-convention-routes?minimal=true">
<img src="https://img.shields.io/npm/dm/unplugin-convention-routes.svg" alt="npm downloads">
</a>
<br>
</p>
<p align="center">unplugin-convention-routes</p>
🔥 基于文件系统的约定式路由解决方案。
🔥 基于 `unplugin` 开发,支持 `Vite` 和 `Rspack` 构建工具,同时支持 `React` 和 `Vue` 框架。
⚡️ 使用 `import.meta.glob` (Vite) 和 `import.meta.webpackContext` (Rspack) 实现零运行时依赖,自动支持 HMR。
***
## 📦 安装
```bash
pnpm i unplugin-convention-routes
```
## 🚀 使用
### Vite
```ts
// vite.config.ts
import Pages from "unplugin-convention-routes/vite"
export default defineConfig({
plugins: [
Pages({ resolver: "vue" }), // 或 'react'
],
})
```
### Rspack
```ts
// rsbuild.config.ts
import Pages from "unplugin-convention-routes/rspack"
export default defineConfig({
tools: {
rspack: {
plugins: [
Pages({ resolver: "vue" }), // 或 'react'
],
},
},
})
```
## 📖 路由约定
### 文件命名规则
| 文件名 | 路由路径 |
| ------------------- | ----------------- |
| `index.vue` | `/` |
| `about.vue` | `/about` |
| `about/index.vue` | `/about` |
| `blog/[id].vue` | `/blog/:id` |
| `blog/[...all].vue` | `/blog/:all(.*)*` |
### Vue 使用
```ts
// env.d.ts
/// <reference types="unplugin-convention-routes/client-vue" />
```
```ts
import { createRouter, createWebHistory } from "vue-router"
// main.ts
import routes from "~pages"
const router = createRouter({
history: createWebHistory(),
routes,
})
```
### React 使用
```ts
// env.d.ts
/// <reference types="unplugin-convention-routes/client-react" />
```
```tsx
import { BrowserRouter, useRoutes } from "react-router-dom"
// main.tsx
import routes from "~react-pages"
function App() {
return useRoutes(routes)
}
createRoot(document.getElementById("root")!).render(
<BrowserRouter>
<App />
</BrowserRouter>
)
```
## ⚙️ 配置选项
### 完整类型定义
```ts
interface UserOptions {
/** 路由解析器类型(必填) */
resolver: "vue" | "react"
/** 页面目录配置 */
dirs?: string | PageDir[]
/** 文件扩展名 */
extensions?: string[]
/** 排除的文件模式 */
exclude?: string[]
/** 导入路径风格 */
importPath?: "absolute" | "relative"
/** 路径大小写敏感 */
caseSensitive?: boolean
/** 路由名称分隔符 */
routeNameSeparator?: string
/** 扩展路由配置的钩子函数 */
extendRoute?: (route: any, parent: any | undefined) => any | void
/** 路由生成后的钩子函数 */
onRoutesGenerated?: (routes: any[]) => Awaitable<any[] | void>
/** 客户端代码生成后的钩子函数 */
onClientGenerated?: (clientCode: string) => Awaitable<string | void>
}
interface PageDir {
/** 目录路径 */
dir: string
/** 基础路由前缀 */
baseRoute?: string
/** 文件匹配模式 */
filePattern?: string
}
```
### 基础配置选项
| 选项 | 说明 | 默认值 |
| -------------------- | ------------------------------- | ---------------------------------------------------------------- |
| `resolver` | 框架类型,支持 `vue` 或 `react` | **必填** |
| `dirs` | 页面目录配置,支持字符串或数组格式 | `'src/pages'` |
| `extensions` | 文件扩展名列表 | Vue: `['vue', 'ts', 'js']` / React: `['tsx', 'jsx', 'ts', 'js']` |
| `exclude` | 排除的文件模式列表 | `['node_modules', '.git', '**/__*__/**', '**/components/**', '**/components.*']` |
| `caseSensitive` | 路径是否大小写敏感 | `false` |
| `routeNameSeparator` | 路由名称分隔符 | `'-'` |
| `importPath` | 导入路径风格,`absolute` 或 `relative` | `'relative'` |
---
### `resolver`(必填)
指定路由解析器类型,决定生成的路由代码格式。
```ts
Pages({
resolver: "vue", // 生成 Vue Router 格式的路由
})
Pages({
resolver: "react", // 生成 React Router 格式的路由
})
```
---
### `dirs`
配置页面目录,支持字符串或数组格式。
#### 单目录配置(字符串)
```ts
Pages({
resolver: "vue",
dirs: "src/pages", // 默认值
})
```
#### 多目录配置(数组)
当项目有多个页面目录时,可以使用数组配置:
```ts
Pages({
resolver: "vue",
dirs: [
// 主页面目录
{ dir: "src/pages", baseRoute: "" },
// 功能模块页面目录,所有路由添加 /features 前缀
{ dir: "src/features/**/pages", baseRoute: "features" },
// 管理后台页面目录,所有路由添加 /admin 前缀
{ dir: "src/admin/pages", baseRoute: "admin" },
],
})
```
**`PageDir` 配置项说明:**
| 属性 | 说明 | 示例 |
| ------------ | -------------------- | ----------------------- |
| `dir` | 目录路径,支持 glob 模式 | `'src/pages'` |
| `baseRoute` | 基础路由前缀,所有路由会添加此前缀 | `'admin'` → `/admin/...` |
| `filePattern` | 文件匹配模式,用于进一步筛选文件(可选) | `'**/*.vue'` |
---
### `extensions`
指定要识别的文件扩展名。
```ts
Pages({
resolver: "vue",
extensions: ["vue"], // 只识别 .vue 文件
})
Pages({
resolver: "react",
extensions: ["tsx", "jsx"], // 只识别 .tsx 和 .jsx 文件
})
```
---
### `exclude`
排除不需要生成路由的文件或目录。支持基本的 glob 模式匹配。
**支持的通配符:**
| 通配符 | 说明 | 示例 |
| ------ | ---- | ---- |
| `**` | 匹配任意层级的目录和文件 | `**/components/**` 匹配任意层级的 components 目录 |
| `*` | 匹配单个路径段中的任意字符(不包括 `/`) | `*.test.*` 匹配 `foo.test.ts` |
| `?` | 匹配单个字符(不包括 `/`) | `file?.vue` 匹配 `file1.vue` |
> ⚠️ **注意**:不支持 minimatch 的高级功能,如否定模式 `!`、花括号扩展 `{a,b}` 等。
**默认排除列表:**
- `node_modules` - 依赖目录
- `.git` - Git 目录
- `**/__*__/**` - 测试相关目录(如 `__tests__`、`__mocks__`)
- `**/components/**` - components 目录
- `**/components.*` - components 文件(如 `components.vue`、`components.tsx`)
```ts
// 自定义排除列表(会覆盖默认值)
Pages({
resolver: "vue",
exclude: [
"node_modules",
".git",
"**/__*__/**",
"**/components/**",
"**/*.test.*", // 排除测试文件
"**/*.spec.*",
],
})
```
---
### `importPath`
控制组件导入路径的风格。
| 值 | 说明 | 示例 |
| ----------- | ------------------------ | ----------------------------------------- |
| `'relative'` | 相对路径(默认) | `'./pages/index.vue'` |
| `'absolute'` | 绝对路径(基于项目根目录) | `'/src/pages/index.vue'` |
```ts
Pages({
resolver: "vue",
importPath: "absolute", // 使用绝对路径
})
```
---
### `caseSensitive`
控制路由路径是否大小写敏感。
```ts
Pages({
resolver: "vue",
caseSensitive: true, // /About 和 /about 是不同的路由
})
```
---
### `routeNameSeparator`
设置路由名称的分隔符,用于从文件路径生成路由名称。
```ts
// 文件路径: src/pages/blog/[id].vue
// 默认分隔符 '-': 路由名称为 'blog-id'
Pages({
resolver: "vue",
routeNameSeparator: "_", // 路由名称变为 'blog_id'
})
```
---
### 高级钩子函数
#### `extendRoute`
扩展或修改单个路由配置。在每个路由生成时调用。
```ts
Pages({
resolver: "vue",
extendRoute: (route, parent) => {
// 为所有路由添加 meta 信息
route.meta = {
...route.meta,
title: route.name,
}
// 为特定路由添加额外配置
if (route.path === "/admin") {
route.meta = { requiresAuth: true }
}
// 返回修改后的路由(或返回 void 保持原引用)
return route
},
})
```
**参数说明:**
- `route` - 当前路由配置对象
- `parent` - 父路由配置(如果是嵌套路由)
---
#### `onRoutesGenerated`
在所有路由生成完成后调用,可以批量修改路由数组。
```ts
Pages({
resolver: "vue",
onRoutesGenerated: async (routes) => {
// 添加全局路由(如 404 页面)
routes.push({
path: "/:pathMatch(.*)*",
name: "NotFound",
component: () => import("./src/pages/404.vue"),
})
// 过滤掉某些路由
return routes.filter(route => !route.path.startsWith("/draft"))
},
})
```
---
#### `onClientGenerated`
在客户端代码生成完成后调用,可以修改最终生成的代码字符串。
```ts
Pages({
resolver: "vue",
onClientGenerated: (clientCode) => {
// 在代码开头添加注释
const header = "// 自动生成的路由文件,请勿手动修改\n"
return header + clientCode
},
})
```
---
## 🔨 示例项目
- [Vite + Vue](./examples/vite-vue)
- [Vite + React](./examples/vite-react)
- [rsbuild + Vue](./examples/rsbuild-vue)
- [rsbuild + React](./examples/rsbuild-react)
## 友情链接
- [unplugin](https://github.com/unjs/unplugin)
- [vite-plugin-pages](https://github.com/hannoeru/vite-plugin-pages)