react-router
第一节、前端路由 react-router介绍
Section titled “第一节、前端路由 react-router介绍”前端路由的核心就是,改变URL,但是页面不进行整体的刷新。
-
方式一:
H5的history -
方式二:
hash锚点hash的优势就是兼容性更好,在老版IE中都可以运行,但是缺陷是有一个#,显得不像一个真实的路径
hash 兼容性好,
一、React-router 6 -21年底更新
Section titled “一、React-router 6 -21年底更新”
React Router在最近两年版本更新的较快,并且在最新的React Router6.x版本中发生了较大的变化
-
目前
React Router6.x已经非常稳定,我们可以放心的使用;- 安装时选择
react-router-dom,react-router会包含一些react-native的内容,web开发并不需要
Terminal window npm i react-router-dom - 安装时选择
二、React-router5 的使用区别
Section titled “二、React-router5 的使用区别”1. withRouter获取路由参数
Section titled “1. withRouter获取路由参数”-
router6全部使用的hooks, -
已经没有
withRouterhoc了, -
所以要在类组件当中使用的话,
需要根据相关hooks手动封装一个 withRouter
例如:
{ useLocation, useNavigate, useParams, useSearchParams }… 一些hooks 对 props 进行扩展- 因为
hooks只能在函数组件或者自定义的hooks的顶层中使用
- 因为
-
router5 为什么要使用 withRouter
因为当路由跳转到的组件是一个普通渲染(就是非路由跳转的)组件,那么不可以直接获取history、location、match对象
- 需要手动封装hoc的场景,因为 router6中已经全部都是使用hook获取history对象,hooks只能在函数组件或者自定义hooks中使用,不能在class组件中使用,这个时候可以手动封装withRouter
router5-手动跳转
Section titled “router5-手动跳转”-
方式一:如果该组件是通过路由直接跳转过来的,那么可以直接获取history、location、match对象;
-
方式二:如果该组件是一个普通渲染的组件,那么不可以直接获取history、location、match对象;、
- App组件必须包裹在
Router组件之内; - App组件使用
withRouter高阶组件包裹;router6就需要自己封装了。
- App组件必须包裹在
-
使用
通过上述两个方式完成之后,可以直接使用 history 进行跳转
react-router会将history、location、match这3个对象存放到props属性中
this.props.history.push("/profile");
router5-传递参数
Section titled “router5-传递参数”传递参数有三种方式:
动态路由的方式;
Section titled “动态路由的方式;”同vue相似、使用路径参数进行占位
<Route path="/detail/:id" component={Detail}/>获取:通过 match 对象中的 params 进行获取
<h2>Detail: {this.props.match.params.id}</h2>search传递参数;
Section titled “search传递参数;”就是 queryString 的传值方式
<NavLink to="/detail2?name=why&age=18">详情2</NavLink>获取:通过 location 对象中的 search 属性进行获取
console.log(this.props.location.search); // ?name=why&age=18to传入对象;
Section titled “to传入对象;”<NavLink to={{ pathname: "/detail2", query: {name: "kobe", age: 30}, state: {height: 1.98, address: "洛杉矶"}, search: "?apikey=123" }}> {/*直接获取location对象*/} console.log(this.props.location);2、路由组件的使用
Section titled “2、路由组件的使用”router5
Section titled “router5”-
使用的是
Switch组件,作用排他思想,当有一个路由组件匹配到了,其他的就不要匹配了,从上到下
-
Route可以单独使用,
不使用
Switch的情况下,也能单独使用,例:/about和/:userid这两个组件都会匹配到且都能显示
-
抽取配置文件需要额外引入插件
-
引入组件使用的
component<Switch>{/*另外这里的路由指定的组件是没有标签包裹的*/}<Route exact path="/" component={Home} /><Route path="/about" component={About} /><Route path="/profile" component={Profile} /><Route path="/:userid" component={User} /><Route component={NoMatch} /></Switch> -
路由嵌套
所有的
Route组件都是在同一个层级 -
exact:boolean 类型,精准匹配,只有精准匹配到完全一致的路径,才会渲染对应的组件;
<Route exact path="/" component={Home} />Router6.x不再支持该属性 -
路由配置
需要下载对应的库
react-router-config- 之后将
Switch配置,换成react-router-config中提供的renderRoutes(routes)函数:
import Home from "../pages/home";import About, { AboutMessage, AboutProduct } from "../pages/about";import Profile from "../pages/profile";import Login from "../pages/login";import User from "../pages/user";import Detail from "../pages/detail";import Detail2 from "../pages/detail2";import NoMatch from "../pages/nomatch";const routes = [{path: "/",exact: true,component: Home},{path: "/about",component: About,routes: [{path: "/about",exact: true,component: AboutProduct},{path: "/about/message",component: AboutMessage},]},{path: "/profile",component: Profile},{component: NoMatch}];export default routes; - 之后将
router6
Section titled “router6”-
使用的是
Routes组件 -
按
Route只能存在Routes组件内部, -
默认支持将路由抽取成配置文件
-
引入组件使用的
element这里指定的路由组件是有包裹的
-
路由嵌套
是
Route又包裹了Route -
手动跳转
这个就需要利用相关的 hooks 封装writeRouter hoc了
第二节、React-Router6
Section titled “第二节、React-Router6”-
应用开启路由的功能,需要使用
react-router-dom中指定的组件包裹-
BrowserRouter组件包裹 使用history模式;注意:该组件一定要包裹
App组件,如果被App组件包裹,在使用useRoutes的时候会报错 -
HashRouter组件包裹 使用hash模式;
-
一、路由配置
Section titled “一、路由配置”在react 中的路由配置是使用路由组件进行配置的
注意:所有的路由组件 route 都由 routes 包裹的
-
path属性定义访问路径 -
element定义该路径需要渲染的组件注意:一定要是**
React组件元素**,不能是classrouter5传入的是类
-
router5中exact精准匹配,否则两个组件都会显示,router6已经取消了
const App = memo(() => { return ( <BrowserRouter> <div className="container"> <Link to="/home">home</Link> <Link to="/about">about</Link> <Link to="/message">message</Link>
<div className="box"> <Routes> <Route path="/" element={<About/>}/> <Route path="/home" element={<Home/>}></Route> <Route path="/about" element={<About/>}></Route> <Route path="/message" element={<Message/>}></Route> </Routes> </div>
</div> </BrowserRouter> )})1、动态路由
Section titled “1、动态路由”- 同
vue动态路由,路径参数相同
<div className="box"> <Routes> <Route path="/" element={<Navigate to="/home"/>}/> <Route path="/home/:id" element={<Home/>}></Route> </Routes></div>2、NotFound Page
Section titled “2、NotFound Page”path属性值:*代表通配,当所有路由都匹配不到的时候,会匹配*的路由
<Routes> <Route path="/" element={<Navigate to={"/home"}/>}/> <Route path="/home" element={<Home/>}></Route> <Route path="*" element={<NotFound/>}></Route></Routes>二、Link、Navlink组件
Section titled “二、Link、Navlink组件”-
Link 组件的属性
Section titled “Link 组件的属性”-
replace:Boolean类型,配置之后代表替换当前路径 -
state:any类型,当前url保存的状态 -
to:String类型,指定点击之后要跳转的路由 -
reloadDocument:Boolean类型,默认false不刷新表示路由切换的时候是否需要重新加载。基本不用配置
react中没有keep-alive,也没有导航守卫
-
-
Navlink
Section titled “Navlink”和
link的区别就是,会给选中的a元素(Link和NavLink渲染之后就是a元素) 添加一个指定的active class-
一般不怎么用,如果是
button的就不会自动添加class了- 限用于 a 标签属性,太长的话放到一个函数里
-
Style 属性可以传入一个函数
<NavLink to={"/about"}className={//传入一个函数,形参是一个对象,形参的isActive 属性,来判断是否选中({isActive} ) => isActive ? "link-active" : ""}>about</NavLink><NavLink to={"/message"}className={({isActive} )=> isActive ? "link-active" : ""}>message</NavLink>
-
三、Navigate组件
Section titled “三、Navigate组件”
Naviage组件展示的时候会自动切换到to属性对应的路由组件
-
重点注意:只能在路由组件中使用
-
和
Link组件的区别就是,Navigate需要back两次Navigate不是Navigator
<div className="content"> {flag ? <Navigate to="/message"/> : <button onClick={e => this.trigger(e)} > trigger </button> } <Link to={"/message"}>message</Link></div>
也是相当于重定向
- 根据
to属性跳转到指定的路由组件。 - 还有一个
Navigate对象
<Routes> <Route path="/" element={<Navigate to={"/home"} />}/> <Route path="/home" element={<Home/>} ></Route></Routes>四、路由嵌套
Section titled “四、路由嵌套”Router6
Section titled “Router6”-
所有的嵌套都在一个
Routes一个组件中相当于顶层的
router-view+ 路由配置 -
之后使用
Outlet组件进行占位相当于嵌套组件中的
router-view -
唯一的区别:就是,
vue非配置文件中的children路由是不添加父级路由的path的,react标签中需要添加-
否则报错,把这个区别记下来基本就没事了
Section titled “否则报错,把这个区别记下来基本就没事了” -
跳转也是同样,就记住:加全路径
-
<Routes> <Route path='/home' element={<Home/>}> <Route path='/home' element={<Navigate to="/home/recommend"/>}/> <Route path='/home/recommend' element={<HomeRecommend/>}/> <Route path='/home/ranking' element={<HomeRanking/>}/> <Route path='/home/songmenu' element={<HomeSongMenu/>}/> </Route></Routes>Router5 了解
Section titled “Router5 了解”-
如果在
home下嵌套的话<Switch><Route path='/home' element={<Home/>}/></Switch> -
需要在
Home组件中进行嵌套//home组件return (<div><Link to="/zhangsan">切换</Link><Link to="/lisi">切换</Link><Switch><Route path='/zhangsan' element={<Zhangsan/>}/><Route path='/lisi' element={<Lisi/>}/></Switch></div>)
五、router6 hooks 跳转
Section titled “五、router6 hooks 跳转”
Router6.x版本之后,代码类的API都迁移到了hooks的写法
-
仅支持函数式组件使用
Section titled “仅支持函数式组件使用” -
类组件的话需要手动实现
hoc高阶组件对props进行扩展 -
注意:react-router-dom6中没有 query 对象
-
传值方式之有 search ,动态参数,和state
-
state传值是隐藏的
1、useNavigate
Section titled “1、useNavigate”还
hook,返回一个函数,创建函数的主要功能就是进行页面跳转
const navigate = useNavigate()navigate(path, options)- 参数一
path:将要跳转的路径 - 参数二
options:state:保存当前url状态replace:boolean是否替换当前历史
- 参数三
delta:Number类型,回退的级别, - 注意:只能拼查询字符串
trigger(path, id) { //state 在location 对象中获取 this.props.route.navigate(path, {state: {id},replace: true}) }2、useLocation
Section titled “2、useLocation”获取
location对象
-
返回值:
Object对象使用场景:当使用state传惨的时候,获取state
-
router5中会有query对象,router6里面没有
localtion:{ hash: "" key: "r5qxvly1" //path pathname: "/home/more" //查询字符串 search: "?name=yanxiaoliu" //当前路由状态 state: {id: 1}}3、useParams
Section titled “3、useParams”获取路径动态参数
-
返回值:
Object类型,路径参数为key,路径path为value//1、定义<Route path="/profile/:id" element={<Profile/>}></Route>//2、跳转this.props.route.navigate(“/home/zhangsan”)//3、获取动态参数const params = useParams()//4、打印console.log(params)//output {id: "zhangsan"}
4、useSearchParams
Section titled “4、useSearchParams”获取查询字符串,标准的
hook函数
-
返回值:
Section titled “返回值:Array类型”Array类型-
索引
0:获取值值, 主要就获取值索引 0 的位置,保存的是一个
URLSearchParams类型的实例对象 -
索引
1:设置值基本没有使用场景,设置的也是当前url的param
//组件一:const navigate = useNavigate()navigate("/home/about?name=zhnagsan&age=15") -
//组件二: import {useSearchParams} from ‘react-router-dom’ //获取索引0 const [search] = useSearchParams() console.log(Object.fromEntries(search));
## 六、类组件路由跳转 writeRouter 的实现
- 通过,高阶组件进行扩展- router5 默认会有这个函数,在exports对象里面
~~~typescriptimport React, { memo } from 'react'import { useNavigate, useParams, useLocation, useSearchParams} from 'react-router-dom'
const wirteRouter = function (Cpn) {
return memo((props) => {
const navigate = useNavigate()const params = useParams()const localtion = useLocation()const query = useSearchParams()
return ( <Cpn {...props} route={{navigate, params, localtion, query: query[0]}}/> )})}
export default wirteRouter第三节、路由配置文件
Section titled “第三节、路由配置文件”所有的路由定义都是直接使用Route组件,并且添加属性来完成的,这样的方式会让路由变得非常混乱。
可以放到配置文件集中管理。
- router5之前,Router并且没有提供相关的API,我们需要借助于
react-router-config完成; - 在
Router6.x中,为我们提供了useRoutesAPI可以完成相关的配置; - 和vue-router的区别就是,component 换成了 element
一、配置 routes 文件
Section titled “一、配置 routes 文件”import {lazy} from 'react'import { Navigate } from "react-router-dom";
const Profile = lazy(() => import(/* webpackChunkName: "profile" */'../pages/Profile') )const Home = lazy(() => import(/* webpackChunkName: "home" */'../pages/Home'))const Message = lazy(() => import('../pages/Message'))const NotFound = lazy( () => import('../pages/NotFound'))const MoreProduct = lazy( () => import('../pages/subPages/MoreProduct'))
export const routes = [ { path: "/", element: <Navigate to="/home"/> }, { path: "/home", element: <Home/>, children: [ { path: "home/more", element: <MoreProduct/> } ] }, { path: "/message", element: <Message/> }, { path: "/profile/:id", element: <Profile/> }, { path: "*", element: <NotFound/> },]useRoutes
Section titled “useRoutes”- 前提一:
BrowserRouter组件是否包裹了App根组件 - 前提二:
Suspense由于是懒加载,需要下载组件文件之后才进行渲染,所以要使用Suspense组件,在下载js期间显示一个同步等待的组件。 - 注意:只能在函数组件中使用
<div className="box"> {useRoutes(routes)}</div>- 懒加载, Route 也能懒加载
- 在
react包中 提供了一个lazy函数 - 同样也能应用在组件路由上
import {lazy} from 'react'const About = lazy(() => import(/* webpackChunkName: "about" */'../pages/About'))三、Suspense
Section titled “三、Suspense”撒死笨死,用于异步组件渲染完成前等待过度期间需要展示等待页面
-
注意:目前只支持路由懒加载
-
fallback元素属性-
值类型:
React元素该元素会在路由懒加载切换页面的时候展示
-
-
也是路由配置懒加载的一个必要组件
包裹异步路由组件
Section titled “包裹异步路由组件”<Suspense fallback={<h3>zhangasn</h3>}><div className="container"><Link to="/home">home</Link><div className="box">{useRoutes(routes)}</div></div></Suspense>
四、router5 配置文件
Section titled “四、router5 配置文件”安装react-router-config:npm i react-router-config
路由配置文件
Section titled “路由配置文件”const routes = [ { path: "/", exact: true, component: Home }, { path: "/about", component: About, routes: [ { path: "/about", exact: true, component: AboutProduct }, { path: "/about/message", component: AboutMessage }, ] }, { path: "/profile", component: Profile }, { path: "/login", component: Login }, { path: "/user", component: User }, { path: "/detail/:id", component: Detail }, { path: "/detail2", component: Detail2 }, { component: NoMatch }];
export default routes;renderRoutes
Section titled “renderRoutes”router5 的api,同useRoutes()做用相同
<div class="container"> {renderRoutes(routes)}</div>