Skip to main content

TanStack Router

TanStack Router(原 React Location)是一个现代的、功能强大的路由库,专注于类型安全和开发体验。

特点优势

  1. 完全类型安全

    • 路由定义时的类型检查
    • 参数和查询字符串的类型推断
    • 导航和链接的类型安全
  2. 文件系统路由

    • 基于文件系统的路由组织
    • 自动路由生成
    • 动态路由支持
  3. 优秀的开发体验

    • 自动代码分割
    • 开发时的热重载
    • 详细的开发时错误提示

基础使用

1. 安装

npm install @tanstack/router

2. 基本路由配置

import { 
Router,
Route,
RootRoute,
RouterProvider
} from '@tanstack/router';

// 定义根路由
const rootRoute = new RootRoute({
component: () => (
<div>
<h1>Root Layout</h1>
<Outlet />
</div>
),
});

// 定义索引路由
const indexRoute = new Route({
getParentRoute: () => rootRoute,
path: '/',
component: () => <div>首页</div>,
});

// 定义用户路由
const usersRoute = new Route({
getParentRoute: () => rootRoute,
path: 'users',
component: () => <div>用户列表</div>,
});

// 创建路由器
const router = new Router({
routeTree: rootRoute.addChildren([
indexRoute,
usersRoute,
]),
});

// 应用路由器
function App() {
return <RouterProvider router={router} />;
}

3. 类型安全的路由参数

interface UserRouteParams {
userId: string;
}

const userRoute = new Route({
getParentRoute: () => usersRoute,
path: '$userId',
component: () => {
const { userId } = useParams();
return <div>用户 ID: {userId}</div>;
},
validateParams: (params): params is UserRouteParams => {
return !!params.userId;
},
});

高级特性

1. 路由加载器(Loaders)

const userRoute = new Route({
path: 'users/$userId',
loader: async ({ params: { userId } }) => {
const user = await fetchUser(userId);
return { user };
},
component: () => {
const { user } = useLoaderData();
return <div>{user.name}</div>;
},
});

2. 路由搜索参数

const searchRoute = new Route({
path: 'search',
validateSearch: (search): search is { q: string } => {
return typeof search.q === 'string';
},
component: () => {
const { search } = useSearch();
return <div>搜索: {search.q}</div>;
},
});

3. 延迟加载和预加载

const dashboardRoute = new Route({
path: 'dashboard',
loader: async () => {
const [stats, notifications] = await Promise.all([
fetchStats(),
fetchNotifications(),
]);
return { stats, notifications };
},
component: lazy(() => import('./Dashboard')),
preload: () => import('./Dashboard'),
});

4. 路由守卫

const protectedRoute = new Route({
path: 'admin',
beforeLoad: async () => {
const isAuthenticated = await checkAuth();
if (!isAuthenticated) {
throw redirect({ to: '/login' });
}
},
component: AdminPanel,
});

实践模式

1. 组织路由文件

// routes/root.tsx
export const rootRoute = new RootRoute({
component: RootComponent,
});

// routes/index.tsx
export const indexRoute = new Route({
getParentRoute: () => rootRoute,
path: '/',
component: IndexComponent,
});

// routes/users.tsx
export const usersRoute = new Route({
getParentRoute: () => rootRoute,
path: 'users',
component: UsersComponent,
});

// routes/index.ts
export const routeTree = rootRoute.addChildren([
indexRoute,
usersRoute,
]);

2. 类型安全的链接

import { Link } from '@tanstack/router';

function Navigation() {
return (
<nav>
<Link
to="/users/$userId"
params={{ userId: '123' }}
search={{ tab: 'profile' }}
>
用户资料
</Link>
</nav>
);
}

3. 错误处理

const userRoute = new Route({
path: 'users/$userId',
errorComponent: ({ error }) => {
return (
<div className="error">
<h2>错误</h2>
<p>{error.message}</p>
</div>
);
},
});

性能优化

1. 路由预加载

const router = new Router({
routeTree,
defaultPreload: 'intent', // 鼠标悬停时预加载
});

// 手动预加载
router.preloadRoute({
to: '/dashboard',
search: { view: 'analytics' },
});

2. 并行数据加载

const dashboardRoute = new Route({
path: 'dashboard',
loader: async () => {
const promises = [
fetchUserData(),
fetchAnalytics(),
fetchNotifications(),
];

const [userData, analytics, notifications] = await Promise.all(promises);

return {
userData,
analytics,
notifications,
};
},
});

最佳实践

  1. 路由组织

    • 使用文件系统路由
    • 按功能模块组织路由文件
    • 利用类型系统保证路由安全
  2. 数据加载

    • 使用 loader 函数预加载数据
    • 实现并行数据加载
    • 适当使用预加载策略
  3. 错误处理

    • 为每个路由定义错误边界
    • 实现优雅的错误展示
    • 提供清晰的错误信息
  4. 性能优化

    • 实现路由级别的代码分割
    • 使用预加载提升用户体验
    • 优化数据加载策略

常见问题

1. 类型错误处理

// 确保参数类型正确
interface PostParams {
postId: string;
}

const postRoute = new Route({
path: 'posts/$postId',
validateParams: (params): params is PostParams => {
return typeof params.postId === 'string';
},
});

2. 动态导入优化

// 使用动态导入和预加载
const LazyComponent = lazy(() => import('./HeavyComponent'));

const route = new Route({
component: LazyComponent,
preload: () => import('./HeavyComponent'),
});

参考资源