oh-tanstack-router
v0.1.0
Published
[](https://www.npmjs.com/package/oh-tanstack-router) [](https://opensource.org/licenses/MIT)
Readme
oh-tanstack-router
为 TanStack Router 打造的基于类的中间件系统与类型安全守卫。
oh-tanstack-router 是 @tanstack/react-router 的一个轻量级封装。它引入了类似 Koa/NestJS 的中间件 (Middleware) 机制,自动处理路由元数据(Meta/Context)合并,并提供优雅的取消与重定向逻辑,同时完美保留了原生的类型安全特性。
✨ 核心特性
- 🛡️ 基于类的中间件:通过继承
Middleware类,以管道模式组织鉴权、日志、埋点等逻辑。 - 🧬 自动 Meta 合并:在
beforeLoad阶段自动合并父子路由的context,无需手动遍历。 - 🚦 智能取消与重定向:提供
cancel()机制,自动记录被拦截前的页面位置(lastLocation),实现登录后自动跳回。 - 💪 TypeScript 优先:完全保留
@tanstack/react-router的类型推导,无any污染。 - 🔌 无缝集成:仅替换 Root Route 的创建方式,其余 API 与原生保持一致。
📦 安装
npm install oh-tanstack-router
# 或者
pnpm add oh-tanstack-router
# 或者
yarn add oh-tanstack-router🚀 快速开始
1. 定义全局 Context 类型
首先定义应用中通用的路由上下文(Meta 数据)接口。
// types.ts
export interface AppRouterContext {
title?: string;
auth?: boolean; // 是否需要登录
roles?: string[]; // 角色权限
}2. 编写中间件
继承 Middleware 类来实现你的业务逻辑。
// middlewares/AuthMiddleware.ts
import { Middleware, type MiddlewareContext, cancel } from 'oh-tanstack-router';
import { AppRouterContext } from '../types';
export class AuthMiddleware extends Middleware<AppRouterContext> {
// 1. register: 决定当前中间件是否需要运行
register(ctx: MiddlewareContext<AppRouterContext>) {
// 只有当路由配置了 auth: true 时才执行 handle
return !!ctx.meta.auth;
}
// 2. handle: 执行具体的业务逻辑
async handle(ctx: MiddlewareContext<AppRouterContext>) {
const isLogged = checkUserLoginStatus(); // 你的检查逻辑
if (!isLogged) {
// 抛出 cancel(),库会自动处理重定向并记录当前页面
throw cancel();
}
}
}3. 创建 Root Route
使用 createRootRouteWithMiddleware 替代原生的 createRootRouteWithContext。
// routes/__root.tsx
import { createRootRouteWithMiddleware, Outlet } from 'oh-tanstack-router';
import { AuthMiddleware } from '../middlewares/AuthMiddleware';
import { AppRouterContext } from '../types';
export const rootRoute = createRootRouteWithMiddleware<AppRouterContext>({
// 注入中间件数组,按顺序执行
middlewares: [new AuthMiddleware()],
// 当中间件抛出 cancel() 时的默认跳转路径(通常是登录页)
defaultCancelPath: '/login',
// 初始 context
context: { auth: false },
component: () => <Outlet />,
});4. 定义子路由
像平时一样定义子路由。
⚠️ 重要:为了让中间件生效,context 必须定义为对象字面量(静态对象)。
// routes/index.tsx
import { createRoute } from 'oh-tanstack-router';
import { rootRoute } from './__root';
// 🔒 需要登录的页面
export const adminRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/admin',
// ✅ 静态对象:中间件可以读取到 auth: true
context: { auth: true, title: '控制台' },
component: () => <div>Admin Page</div>,
});
// 🔓 公开页面
export const loginRoute = createRoute({
getParentRoute: () => rootRoute,
path: '/login',
// ✅ 明确标记不需要鉴权
context: { auth: false },
component: () => <div>Login Page</div>,
});5. 创建 Router 实例
这步与原生 TanStack Router 完全一致。
// router.ts
import { createRouter } from 'oh-tanstack-router';
import { rootRoute } from './routes/__root';
import { adminRoute, loginRoute } from './routes/index';
const routeTree = rootRoute.addChildren([loginRoute, adminRoute]);
export const router = createRouter({
routeTree,
context: { auth: false },
});
// 注册类型安全
declare module '@tanstack/react-router' {
interface Register {
router: typeof router;
}
}📖 API 参考
createRootRouteWithMiddleware(options)
创建带有中间件能力的 Root Route。
| 参数 | 类型 | 说明 | 默认值 |
|Data | Type | Description | Default |
| --- | --- | --- | --- |
| middlewares | Middleware[] | 中间件实例数组,按顺序执行 | [] |
| defaultCancelPath | string | 捕获 CancelError 后的重定向目标路径 | '/' |
| context | T | 初始 Context 对象 | - |
| ...others | - | 其他 TanStack Router 原生配置 (loader, component等) | - |
class Middleware<Context>
抽象基类,用于定义中间件。
register(ctx: MiddlewareContext): boolean- 用于过滤。返回
true时,handle方法会被调用;返回false则跳过。
- 用于过滤。返回
abstract handle(ctx: MiddlewareContext): Promise<void> | void- 执行核心逻辑。如果验证失败,应抛出错误或重定向。
MiddlewareContext 对象
传递给中间件的上下文对象,包含以下核心属性:
meta:Context- 核心属性。已自动合并了当前匹配链上所有路由的context。to:ParsedLocation- 即将前往的目标位置。from:ParsedLocation | undefined- 来源位置。...others: 包含router,params,search等原生属性。
cancel()
一个辅助函数,用于中断导航。
- 当你在中间件中
throw cancel()时:- 库会检查是否存在
lastLocation(你从哪里被拦截的)。 - 如果有,它会自动携带这个位置信息重定向到
defaultCancelPath。 - 如果当前已经在
defaultCancelPath,则停止重定向以防止死循环。
- 库会检查是否存在
⚠️ 最佳实践与注意事项
1. Context 必须是静态对象
oh-tanstack-router 在 beforeLoad 阶段使用 matchRoutes 策略预先获取子路由信息。为了确保中间件能正确读取子路由的 Meta 数据,请务必使用对象字面量定义 context。
// ✅ 正确:中间件能读到 auth: true
createRoute({
path: '/admin',
context: { auth: true }
})
// ❌ 错误:中间件读不到,因为这是运行时函数
createRoute({
path: '/admin',
context: () => ({ auth: true })
})2. 中间件执行时机
中间件运行在 Root Route 的 beforeLoad 钩子中。这意味着它会先于所有子路由的 loader 执行。这非常适合做全局鉴权,因为可以在数据加载前就拦截请求。
📄 License
MIT
