创建/src/router/index.tsx
import Home from '@/pages/Home'
import { Navigate,RouteObject } from 'react-router-dom';
import Login from "@/pages/Login";
import { lazy, Suspense } from 'react';
interface Route{
children?: Route[];
element?: React.ReactNode;
path?: string;
meta?:Record<string,any>
}
// 路由懒加载 需要使用lazy和Suspense包裹
const NotFound = lazy(()=>import('@/pages/404'))
const Merter = lazy(()=>import('@/pages/Merter'))
const router:Route[] =[
{
path:'/',
element: <Navigate to='/home'/>,
},
{
path:'/home',
element: <Home/>,
meta:{title:'首页'},
},
{
path:'/login',
element: <Login/>,
meta:{title:'登录'},
},
{
path:'/merter',
element:<Merter/>,
children:[
{
},
]
},
{
// 404页面必须放在最后
path:'/404',
// Suspense写在这里代码比较重复,每一个懒加载都需要写,所以我们把他写到外部
// element:(<Suspense fallback={<h1>loading...</h1>}>{<NotFound/>}</Suspense>),
element:<NotFound/>,
meta:{title:'页面未找到'},
},
{
path:'*',
element:<Navigate to='/404'></Navigate>,
},
]
export default router
在根App.tsx中使用useRoutes创建路由,由于react没有提供像vue那样的官方路由守卫api,所以得自己实现,我的方法是路由组件外层套个 BeforeEach 组件进行路由拦截
import router from "/src/router";
import { useRoutes } from 'react-router-dom';
import BeforeEach from "/src/router/beforEach";
// 路由组件外层套一个 BeforeEach 组件充当路由守卫,使用了插槽
function App() {
const element = useRoutes(router)
return (
<>
<BeforeEach>
{element}
</BeforeEach>
</>
)
}
export default App
main.tsx 使用BrowserRouter包裹
import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import { BrowserRouter } from "react-router-dom";
import { Provider } from "react-redux";
import store from "@/store/store";
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Provider store={store}>
<App />
</Provider>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
)
/src/router/beforEach 路由守卫
import { outLogin, userInfo,User } from '@/store/reducer/user'
import { Suspense, useEffect, useState } from 'react'
import { connect, useDispatch, useSelector } from 'react-redux'
import { useLocation, useNavigate } from 'react-router-dom'
// 路由守卫
const BeforeEach = (props: any) => {
const { children} = props //拿到路由组件
// 拿到store中用户信息,判断是否登录
const user = useSelector((state:{user:User})=>state.user)
const dispatch = useDispatch()
const navigate = useNavigate()
const route = useLocation()
const token = localStorage.getItem('token')
// 监听路由变化:route.pathname
useEffect(() => {
const title = children?.props?.match?.route?.meta?.title
document.title = title||'后台管理'
window.scrollTo(0, 0)
if (token) { // 判断有无token,没有跳转登录
if (route.pathname != '/login') {
if (!user.userInfo?.id) //没有用户信息
try {
dispatch(userInfo() as any) // 获取用户信息
} catch (error) {
// 获取用户信息失败 清除用户信息、token,跳转登录
dispatch(outLogin())
navigate('/login')
}
} else {
// 有token不允许跳转登录,重定向到首页
navigate('/',{replace:true})
}
} else {
navigate('/login')
}
}, [route.pathname])
//Suspense 放在这里或者App里都行,这样就只写一遍
return (<>
<Suspense fallback={<h1>loading...</h1>}>{/* 路由懒加载必须添加 Suspense */}
{props.children}
</Suspense>
</>)
}
export default BeforeEach
顺便说一下,组件外部无法使用hook:useNavigate()跳转路由,网上也有很多方法,但最后
我觉得最简单的还是直接使用 window.location.href = "/login"