安装
vue-router:
- 通过
create-vue脚手架在命令行选择router模板生成 - 在项目后续添加使用
npm add pinia也可以添加并追加模板
react-router:
create-react-app并没有路由的初始模板,可以自己配置一个自定义模板- 安装react-router:
npm install -S react-router-dom
路由模式(history和hash)
vue-router:通过createWebHashHistory和createWebHistory配置路由模式
import { createRouter, createWebHashHistory } from 'vue-router'
const router = createRouter({
history: createWebHashHistory(), //hash模式
//createWebHistory() history模式
routes: [
//路由表
],
})
react-router:react中的history名称变为browerserRouter,hash还是hashRouter,在渲染根组件时在外部嵌套browerserRouter或者hashRouter改变路由模式
import { createRoot } from 'react-dom/client'
import reportWebVitals from './reportWebVitals'
import './index.css'
import Router from './router'
import { BrowserRouter } from 'react-router-dom'
const container = document.getElementById('root')!
const root = createRoot(container)
root.render(
<BrowserRouter>//外部嵌套改变路由模式,hash模式为HashRouter
<Router></Router>
</BrowserRouter>
)
reportWebVitals()
配置基本路由表
vue-router:Vue通过配置路由表分发路由
const routes: RouteRecordRaw[] = [
{
path: "/login",
name: "Login",
//非懒加载组件
component: Component,
//路由元信息
meta: {
requiresAuth: false,
title: "登录页"
}
},
];
react-router:React支持在组件中分发路由,也可以使用最新的useRoutes统一配置路由表,这里以useRoutes为例
- 创建一个router文件夹,放置入口组件index.tsx(useRoutes本质返回一个组件)
import { Navigate, RouteObject, useRoutes } from 'react-router-dom'
import Route1 from './Route1'
export const rootRouter: RouteObject[] = [
{
path: '/route1',
//非懒加载组件
element: <Route1 />,
//路由元信息
meta: {
requiresAuth: false,
title: "登录页"
}
},
]
export default function Router() {
const routes = useRoutes(rootRouter)
return routes
}
2.在App组件包裹useRoutes返回的组件
import { BrowserRouter } from 'react-router-dom'
import Router from './router/Index'
import './App.css'
function App() {
return (
<div className="App">
<BrowserRouter>
<Router></Router>
</BrowserRouter>
</div>
)
}
export default App
路由重定向
vue-router:利用redirect属性
const routes: RouteRecordRaw[] = [
{
path: "/",
redirect: { name: "Login" }
}
]
react-router:利用Navigate组件
const rootRouter: RouteObject[] = [
{
path: '/',
//路由表的路由重定向用Navigate组件跳转的方式模拟
element: <Navigate to="/route1" replace/>
}
]
路由匹配404的路由
vue-router:vue3利用/:pathMatch(.*)
const routes: RouteRecordRaw[] = [
{
// 找不到路由重定向到404页面
path: "/:pathMatch(.*)",
redirect: { name: "404" }
}
]
react-router:利用*匹配(一定要把404路由放置于最后,代表没有匹配到就展示最后一个路由)
const rootRouter: RouteObject[] = [
{
path: '*',
element: <Navigate to="/404" />
}
]
路由嵌套
vue-router:两者没有区别但是通过占位符<Router-View>匹配子级路由,名称有区别
const accountSettingRouter: Array<RouteRecordRaw> = [
{
path: "/father",
name: "Father",
component: Father,
children: [
{
//默认路由:如果没有index则匹配path为空且最靠前的哪一个,空path效果和index一样,但是index优先级更高
path: "",
name: "Son1",
component: Son1
},
{
//添加index:true后为默认路由无需再添加空path,默认path与父级一样
index:true,
name: "Son2",
component: Son2
}
]
}
];
react-router:两者没有区别但是通过占位符<Outlet>匹配子级路由,名称有区别
const rootRouter: RouteObject[] = [
{
path: '/route1',
element: <Route1 />,
children: [
{
//未命名是默认路由
path: '',
element: <Router2 />
},
{
path: '',
element: <Router3 />
}
]
}
]
路由懒加载
vue-router:在component传入一个异步导入回调函数
const routes: RouteRecordRaw[] = [
{
path: "/login",
name: "Login",
//路由懒加载
component: () => import("@/views/login/Index.vue"),
meta: {
requiresAuth: false,
title: "登录页"
}
}
]
react-router:通过React.lazy传入异步导入回调函数加载异步组件,但是和vue不同的是必须存在suspense来过渡加载状态,否则会报错
1.封装全局lazyLoad这个高阶组件,传入一个异步导入的组件,最后返回一个加载完成的组件,期间用suspense的fallback过度加载状态
import React, { Suspense } from 'react'
const lazyLoad = (Comp: React.LazyExoticComponent<any>) => {
return (
<Suspense fallback={<div>加载中</div>}>
<Comp />
</Suspense>
)
}
export default lazyLoad
2.调用lazyLoad传入要异步载入的组件
const rootRouter: RouteObject[] = [
{
path: '/',
element: lazyLoad(React.lazy(() => import('./Route1'))),
},
]
声明式导航
vue-router:采用<Router-Link>,传入一个组件name或者path
<Router-Link
:to="{
name:'xxx',
path:'xxx',
params:{xxx:xxx},
query:{xxx:xxx}
}"
><Router-Link />
react-router:采用Link
<Link to={{
pathname: '/courses',
search: '?sort=name',
hash: '#the-hash',
state: { fromDashboard: true }
}}/>
编程式导航
vue-router:通过useRouter获取路由器后进行跳转
const router=useRouter()
router.push({
name:'xxx',
path:'xxx',
params:{xxx:xxx},
query:{xxx:xxx}
})
react-router:通过useNavigate获取导航后跳转
const navigate=useNavigate()
navigate('/xxx')//也可以填入数字代表跳转历史
获取路由参数
vue-router:通过useRoute获取路由
const route=useRoute()
console.log(route.params,route.match(获取所有父级路由信息),route.query,route.fullpath......)
react-router:
1.通过useParams获取动态路由参数(为避免不必要的加载,获取路由参数应该写在useEffect并添加对应依赖项)
const params = useParams()
useEffect(()=>{console.log(params)},[])
2.通过useSearchParams获取和设置query
//第一个参数是获取地址参数,第二个是设置地址参数
const [getSearch, setSearch] = useSearchParams()
useEffect(() => {
//由于getSearch.entries()返回迭代器,所以要通过Object.fromEntries把迭代器转换为对象,比vue的route.query复杂
console.log(Object.fromEntries(getSearch.entries()))
}, [])
//获取query中单个名为age的属性
console.log(getSearch.get('age'))
//获取query中所有名为age的属性
console.log(getSearch.getAll('age'))
//设置query,相当于vue的router.push({query:{xxx:xxx}})
setSearch({name:'xxx',age:'xxx'})
3.通过useLocation获取hash(hash路由),pathname(query前的path),search(未格式化带有?和&query字符串)等为未整合的路径参数...
const location = useLocation()
console.log(location.hash,location.pathname,location.search)
Match(多用于面包屑,鉴权等)
vue-router:
//路由守卫中使用match匹配要跳转的路由to的所有父级别路由信息(before同理)
console.log(to.matched)
//获取当前路由的所有父级别路由信息
const route=useRoute()
console.log(route.matched)
react-router:没有对于api只能自己递归匹配,参考我的另一篇文章在React中利用递归和迭代手撕一个useRoute和useMatched的功能 - 掘金 (juejin.cn)
监听路由变化
vue-router:利用vue提供的onBeforeRouteUpdate钩子
//to是最新的路由信息
onBeforeRouteUpdate(to => {
keyword.value = to.query.keyword;
searchText.value = to.query.keyword;
});
react-rotuer:没有官方提供钩子,但是页面路由变化会重新调用render,也可以使用useLocation等钩子,放置于useEffect钩子的依赖项中
const location=useLocation()
useEffect(()=>{
console.log(location)
},[location])
全局路由守卫和指定路由守卫
vue-router:利用vue提供的beforeEach
route.beforeEach((to, from, next) => {})
react-router:没有官方提供的api,可以利用高阶组件传入路由根组件实现路由切换时拦截进行控制
//高阶组件AuthRouter传入Router,鉴权逻辑写在AuthorRouter中,Router的当前组件因为跳转等变化AuthorRouter重新加载,鉴权逻辑也会重新判断,
<AuthRouter>
<Router />
</AuthRouter>
//AuthorRouter具体内容
const AuthRouter = (props: { children: JSX.Element }) => {
//守卫前置判断逻辑
if (逻辑成立) return props.children;
//否则最后重定向到403页面
return <Navigate to="/403" replace/>;//路由拦截的重定向必须加上replace,否则历史记录会紊乱
/*如果没加replace例如A->B,但是由于B的权限不足或者未找到,重定向于C,此时在C路由下navigate(-1)
返回的则是B而不是A所有拦截导致的重定向必须要加上replace(A->B时权限不足产生B->C是把C这条记录覆盖
到B而不是重新添加B,所以此时历史纪录的变化是原本(A->B->C)因为C覆盖B所以变成(A->C)),防止产生垃圾路由
(无作用的路由)*/
};