react-router
路由权限拦截
组件
tsx
import { useEffect, useMemo } from 'react';
import { useMatches, useOutlet } from 'react-router';
const AuthRoute = () => {
const matches = useMatches(); //这个钩子只有 data Router 下能用
const outlet = useOutlet(); //
const page = useMemo(() => {
const route = matches?.at(-1);
console.log('route', route);
/**
* 根据路由信息判断路由权限
* 也可以根据 token 判断, token 不存在, 返回登录页
* if(!token) {
* return <Navigate to="/login" />
* }
*/
return outlet;
}, [matches, outlet]);
useEffect(() => {
const title = (matches?.at(-1) as any)?.handle?.title;
const isHasTitle = typeof title === 'string';
if (isHasTitle) {
document.title = title;
}
}, [matches]);
return page;
};
export default AuthRoute;使用
tsx
export const router = createBrowserRouter([
{
path: '/',
element: <AuthRoute></AuthRoute>, // 所有路径都经过AuthRoute
children: [],
},
]);路由懒加载
hooks
tsx
import React from 'react';
export function useLazy(callback: () => Promise<{ default: React.ComponentType }>) {
const LazyComp = React.lazy(callback);
return (
<React.Suspense fallback={<div>loading...</div>}>
<LazyComp />
</React.Suspense>
);
}使用
tsx
export const router = createBrowserRouter([
{
path: '/',
element: <AuthRoute></AuthRoute>,
children: [
{
path: '/',
element: useLazy(() => import('xxxx')),
},
],
},
]);路由缓存
组件
tsx
import { createContext, useContext } from 'react';
import { matchPath, useLocation, useOutlet } from 'react-router';
const keepElements = {};
export type KeepAliveContextType = {
keepPaths: any[];
keepElements: Record<any, any>;
dropByPath: (path: any) => void;
};
export const KeepAliveContext = createContext<KeepAliveContextType>({
keepPaths: [],
keepElements,
dropByPath(path) {
keepElements[path] = null;
},
});
const isKeepPath = (keepPaths, path) => {
let isKeep = false;
for (let i = 0; i < keepPaths.length; i++) {
const item = keepPaths[i];
if (item === path) {
isKeep = true;
}
if (item instanceof RegExp && item.test(path)) {
isKeep = true;
}
if (typeof item === 'string' && item.toLowerCase() === path) {
isKeep = true;
}
}
return isKeep;
};
export function useKeepOutlet() {
const location = useLocation();
const outlet = useOutlet();
const { keepElements, keepPaths } = useContext(KeepAliveContext);
const isKeep = isKeepPath(keepPaths, location.pathname);
if (isKeep) {
keepElements[location.pathname] = outlet;
}
return (
<>
{!isKeep && outlet}
{Object.entries(keepElements).map(([pathname, element]) => (
<div style={{ height: '100%', width: '100%' }} key={pathname} hidden={!matchPath(location.pathname, pathname)}>
{element}
</div>
))}
</>
);
}
const KeepAliveLayout = (props: { keepPaths: any[]; [key: string]: any }) => {
const { keepPaths, ...other } = props;
const { keepElements, dropByPath } = useContext(KeepAliveContext);
return <KeepAliveContext.Provider value={{ keepPaths, keepElements, dropByPath }} {...other}></KeepAliveContext.Provider>;
};
export default KeepAliveLayout;使用
src/App.tsx
tsx
const App = () => {
return (
<KeepAliveLayout keepPaths={[]}>
<RouterProvider router={router}></RouterProvider>
</KeepAliveLayout>
);
};
export default App;src/layouts/index.tsx
tsx
const LayoutComponent = () => {
const outlet = useKeepOutlet();
return <div className="size-full">{outlet}</div>;
};
export default LayoutComponent;