Vue3与React之Vue-Router和React-Router的使用比较

390 阅读6分钟

安装

vue-router:

  1. 通过create-vue脚手架在命令行选择router模板生成
  2. 在项目后续添加使用npm add pinia也可以添加并追加模板

react-router:

  1. create-react-app并没有路由的初始模板,可以自己配置一个自定义模板
  2. 安装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为例

  1. 创建一个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)),防止产生垃圾路由
(无作用的路由)*/
};