本文章是react的路由笔记
一、react路由(v5版本)
1. 什么是路由(前端)?
- 一个路由就算一个映射关系(key: value)
- key为路径,value为组件
2. 前端路由的工作原理
根据浏览器历史记录:history(BOM)
3. 路由的基本使用(react-router-dom)
3.1 react-router
:(有三种库)
- web (react-router-dom)
- react-native
- any(react-router)
3.2 react-router-dom
的api(版本5/v5)
安装
npm i react-router-dom@5
内置组件
1. <BrowserRouter>
2. <HashRouter>
3. <Route>
4. <Redirect>
5. <Link>
6. <NavLink>
7. <Switch>
其他
- history 对象
- match 对象
- withRouter 函数
3.3 基本使用
App.js中
import { Component } from 'react'
import { Link, Route } from 'react-router-dom'
import Home from './pages/Home'
import About from './pages/About'export default class App extends Component {render() {return {<div><header>{/* 编写路由链接 */}<Link to="/home">首页</Link><Link to="/about">关于</Link></header><main>{/* 注册路由 */}<Route path="/home" component={Home} /><Route path="/about" component={About} /></main></div>}}
}
index.js中
import React from 'react'
import ReactDOM from 'react-dom'
import { BrowserRouter, HashRouter } from 'react-router-dom'
import App from './App'ReactDOM.render({/* history路由 */}<BrowserRouter><App /></BrowserRouter>{/* hash路由 */}<HashRouter></HashRouter>, document.querySelect('#root'))
3.4 路由组件与一般组件
一般组件的props信息需要通过父组件传递,而路由组件的props信息是路由信息,不需要传递。
3.5 NavLink的使用
NavLink
的基本使用
匹配上的路由会默认加上active
这个类名
<NavLink to="/home">首页</NavLink>
<NavLink to="/about">关于</NavLink>
通过activeClassName
可以自定义选中的类名
<NavLink to="/home" activeClassName="header-active">首页</NavLink>
<NavLink to="/about" activeClassName="header-active">关于</NavLink>
- 对
NavLink
的封装
组件的使用:
{/* 「首页」标题:组件标签标签体内容(特殊的标签属性,组件内可以通过this.props.children获取) */}
<MyNavLink to="/home">首页</MyNavLink>
<MyNavLink to="/about">关于</MyNavLink>
组件的封装:
{/* 一组props */}
<NavLink to={to} activeClassName="header-active">首页</NavLink>
{/* 多组props */}
<NavLink {...this.props} activeClassName="header-active"/>
3.6 Switch的使用
Switch
:当匹配到第一个路由的时候就停止
<Switch><Route path="/home" component={Home} /><Route path="/about" component={About} /><Route path="/about" component={Demo} />
</Switch>
3.7 路由的模糊匹配与精准匹配
模糊匹配:
<header><NavLink to="/about/a/b">关于</NavLink>
</header>
<main><Switch><Route path="/about" component={About} /></Switch>
</main>
精准匹配:exact={true}
<Route exact={true} path="/about" component={About} />
3.8 Redirect的使用
Redirect(重定向):谁都匹配不上,就走Redirect
<Switch><Route path="/home" component={Home} /><Route path="/about" component={About} /><Redirect to="/about" />
</Switch>
3.9 嵌套路由
嵌套路由要写完整路径
<div><NavLink to="/home/news">News</NavLink><NavLink to="/home/message">Message</NavLink>
</div>
<div><Switch><Route path="/home/news" component={News} /><Route path="/home/message" component={Message} /></Switch>
</div>
3.10 路由组件传参
1. 向路由组件传递params
参数
传递的参数:
<NavLink to={`/home/news/${title}/${page}`}>News</NavLink>
<Route path="/home/news/:title/:page" component={News} />
News组件可以通过this.props.match.params
获取到params
参数
2. 向路由组件传递search(query)
参数(显式传参)
使用:
<NavLink to={`/home/news?title=${title}&page=${page}`}>News</NavLink>{/* search参数无需声明接收,正常注册路由即可 */}
<Route path="/home/news" component={News} />
接收:
接收到的search参数是urlencoded编码的字符串,需要借助querystring解析
key=value&key=value => urlencoded编码
安装query-string
npm i query-string
import qs from 'query-string'qs.parse(this.props.location.search)
3. 向路由组件传递state
参数(隐式传参)
使用:
<NavLink to={{pathname: '/home/news', state: { title, page }}}>News</NavLink>{/* state参数无需声明接收,正常注册路由即可 */}
<Route path="/home/news" component={News} />
接收:
通过this.props.location.state
接收,BrowserRouter
刷新参数不丢失,HashRouter
刷新参数丢失
3.11 push与replace模式
- push:会留下痕迹,可以通过回退键,回到上一页
- replace:不会留下痕迹,不能回到上一页
3.12 编程式路由导航
// 路由跳转
this.props.history.replace('/xxx', [state])
this.props.history.replace('/xxx', { id, title })
this.props.history.push('/xxx', [state])
this.props.history.push('/xxx', { id, title })
// 路由前进
this.props.history.goForward()
// 路由后退
this.props.history.goBack()
// 与vue中的go类似,传入number,正数表示前进,负数表示后退
this.props.history.go(number)
3.13 withRouter的使用
在一般组件内,不能像路由组件一样获取到路由信息,需要在组件上包裹一个withRouter
。withRouter
可以加工一般组件,让一般组件具备路由组件所特有的api,withRouter
的返回值是一个新组件。
import React, { Component } from 'react'
import { withRouter } from 'react-router-dom'class App extends Component {handleClick = () => {console.log(this.props)}render() {return (<div onClick={this.handleClick}>关于</div>)}
}export default withRouter(App)
二、react路由(v6版本)
1. Component
1.1 <Routes/>
和<Route/>
-
v6版本中移除了之前的
<Switch>
,用<Routes>
代替 -
<Routes/>
和<Route/>
要配合使用,且必须要用<Routes>
包裹<Route>
-
<Route caseSensitive>
属性用于指定:匹配时是否区分大小写(默认为false) -
<Route>
也可以嵌套使用,且可配合useRoutes()
配置路由表,但需要通过<Outlet>
组件来渲染其子路由 -
<Route>
的end
属性:如果他的子路由匹配,父亲路由失去高亮 -
写法区别:
import { Routes, Route, Navigate } from 'react-router-dom'{/* Switch组件 替换成 Routes */} <Routes>{/* Route的component属性变成element属性 */}{/* v5写法:<Route path='/' component={Home} /> */}{/* v6写法:<Route path='/' element={<Home />} /> 写成标签 */}<Route caseSensitive path='/home' element={<Home />} /><Route path='/about' element={<About />} />{/* 重定向,不用Redirect,而是Navigate */}{/* 用法:<Navigate to="/home" replace={true} /> */}{/* replace跳转模式:push/replace模式,默认false(push) */}<Route path='/' element={<Navigate to="/home" />} />{/* end属性:如果他的子路由匹配,父亲路由失去高亮 */} <Route path='/' end element={<Navigate to="/home" />} /> </Routes>
1.2 <NavLink>
-
自定义选中的高亮样式
{/* v5写法 */} <NavLink activeClassName="xxx"> {/* v6写法:在className属性中写入函数,函数返回类名 */} <NavLink to='/home' className={({isActive}) => { return isActive ? 'item active' : 'item' }}>Home</NavLink>
<NavLink to='/home' className={(e) => {console.log(e);}}>Home</NavLink>
下面是的参数e:
1.3 <Navigate>
<Navigate>
:重定向,不用Redirect,而是Navigate。渲染到页面上,会让路由进行跳转。- 用法:
<Navigate to="/home" />
- replace属性:路由跳转模式,push/replace模式,默认false(push)
<Navigate to="/home" replace={true} />
2. Hooks
2.1 useRoutes()
注册路由:
- 写法1:
{/* 注册路由 */}
<Routes><Route path='/home' element={<Home />} /><Route path='/about' element={<About />} /><Route path='/' element={<Navigate to="/home" />} />
</Routes>
- 写法2:由
useRoutes
生成路由表
import React from 'react'
import { NavLink, Navigate, useRoutes } from 'react-router-dom'
import About from './pages/About'
import Home from './pages/Home'export default function App() {// 通过路由表生成路由const routeElement = useRoutes([{path: '/home',element: <Home />},{path: '/about',element: <About />},{path: '/',element: <Navigate to="/home" />}])return (<div><NavLink to='/home'>Home</NavLink> | <NavLink to='/about'>About</NavLink><hr />{/* 注册路由 */}{ routeElement }</div>)
}
2.2 嵌套路由
在路由表添加children
属性即可
const routeElement = useRoutes([{path: '/home',element: <Home />},{path: '/about',element: <About />,children: [{path: 'news',element: <div>news</div>},{path: 'message',element: <div>message</div>}]},{path: '/',element: <Navigate to="/home" />}
])
在About
组件中,需要一个路由出口,用Outlet
来展示二级子路由
import React from 'react'
import { NavLink, Outlet } from 'react-router-dom'export default function index() {return (<div>About<br />{/* v5写法 */}<NavLink to="/about/news">news</NavLink> | <NavLink to="/about/message">message</NavLink>{/* v6中:同v5,也可以只写子路由路径,但是不能带斜杠,类似于vue */}<NavLink to="news">news</NavLink> | <NavLink to="message">message</NavLink>{/* 路由出口 */}<Outlet /></div>)
}
2.3 useParams()(获取params参数)
useParams()
获取传递的params参数
import { useParams } from 'react-router-dom'
2.4 useMatch()(获取params参数)
useMatch
同useParams
都可以用来读取params参数
使用:useMatch('/home/:id/:title')
2.5 useSearchParams()(获取search参数)
useSearchParams
用来获取search(query)
参数,返回的是一个数组
- searchParams:获取到search参数,但是需要通过
get()
方法获取
// 我的url为http://localhost:5173/about/news?id=1&title=haha
const [searchParams, setSearchParams] = useSearchParams()
// 需要通过get方法,传入key,获取到value
let id = searchParams.get('id'))
- setSearchParams:更改search参数
setSearchParams('id=2&title=hello')
2.6 useLocation()(获取search、state参数)
- 获取
search
参数:
useLocation
同useSearchParams
都可以获取search
参数,不用像useMatch
那样传递路径。
- 获取
state
参数:
传递state
参数(隐式传参):
{/* v5写法 */}
<NavLink to={{ pathname: '/home', state: {name: 'xiaotian', age: 20} }}>Home</NavLink>
{/* v6写法 */}
<NavLink to='/home' state={{ name: 'xiaotian', age: 20 }}>Home</NavLink>
- 使用:
useLocation()
// 对象的连续解构
const { state: {name, age} } = useLocation()
2.7 路由传参总结
params => useParams、useMatch
search(query) => useSearchParams、useLocation
state => useLocation
2.8 编程式路由导航
let navigate = useNavigate()
// 1. 基本使用
navigate('/about')
// 2. 子路由
navigate('news')
// 3. 传递state参数
navigate('news', {replace: false,state: {name: 'xiaotian',age: 20},
})
// 4. 前进
navigate(1)
// 5. 后退
navigate(-1)
2.9 useInRouterContext()
- 作用:用于判断组件是否被路由包裹着
- 返回值:boolean
2.10 useNavigationType()
- 作用:返回当前的导航类型(用户是如何来到当前页面的)
- 返回值:
pop
、push
、replace
- 备注:
pop
是指直接在浏览器中打开了这个路由组件(复制链接、刷新进入)
2.11 useOutlet()
- 作用:用来呈现当前组件中渲染的嵌套路由
- 返回值:如果嵌套路由没有挂载,则res返回
null
;如果嵌套路由已经挂载,则展示嵌套路由对象。
let res = useOutlet()
2.12 useResolvedPath
作用:给定一个url值,解析其中的path
、search
、hash
值。
三、项目中如何配置路由表(v6版本)
1. 标签形式
- 在
src
下创建router
目录,新建index.jsx
这里用到了嵌套路由的写法
import { BrowserRouter, Routes, Route } from 'react-router-dom'
import App from '../App'
import Home from '../views/Home/Home'
import About from '../views/About/About'const baseRouter = () => {return (<BrowserRouter><Routes>{/* 嵌套路由写法 */}<Route path='/' element={<App />}><Route path='/home' element={<Home />}></Route><Route path='/about' element={<About />}></Route></Route></Routes></BrowserRouter>)
}export default baseRouter
- 在
main.jsx
中将原有的App
根组件替换成路由表组件
import ReactDOM from 'react-dom/client'
import BaseRouter from './router/index.jsx'ReactDOM.createRoot(document.getElementById('root')).render(<BaseRouter />,
)
- 在
App.jsx
中配置路由出口
import { Outlet } from 'react-router-dom'export default function App() {return (<div>{/* 占位符,用于展示组件,类似于vue的router-view(路由出口) */}<Outlet /></div>)
}
2. Hook形式
2.1 基本使用
- 在
src
下创建router
目录,新建index.jsx
import { Navigate } from 'react-router-dom'
import Home from '../views/Home/Home'
import About from '../views/About/About'const routes = [// 当App组件使用了useRoutes渲染路由时,就不允许在路由表中配置App组件有关的路由// 错误写法:会导致页面不停渲染,最终卡死// {// path: '/',// element: <App />// },{path: '/',element: <Navigate to="/home" />},{path: '/home',element: <Home />},{path: '/about',element: <About />},// 错误页面,重定向到404{path: '*',element: <Navigate to="/404" />}
]export default routes
- 在
main.jsx
中用BrowserRouter
或HashRouter
包裹一下根组件
import React from 'react'
import ReactDOM from 'react-dom/client'
import { BrowserRouter } from 'react-router-dom'
import App from './App.jsx'ReactDOM.createRoot(document.getElementById('root')).render(<BrowserRouter><App /></BrowserRouter>,
)
- 在
App.jsx
中渲染路由
import { useRoutes } from 'react-router-dom'
import router from './router'export default function App() {const outlet = useRoutes(router)return (<div>// 渲染路由{ outlet }</div>)
}
2.2 路由懒加载
在router/index.jsx
中,使用lazy
即可实现路由的懒加载
import { lazy } from 'react'const Home = lazy(() => import('../views/Home/Home'))
但是通常情况下,lazy
和Suspense
是结合使用的,不然会出现报错(但是我目前写lazy没有出现报错),可以通过如下代码书写我们的懒加载路由:
import { lazy, Suspense } from 'react'
import { Navigate } from 'react-router-dom'const Home = lazy(() => import('../views/Home/Home'))
const About = lazy(() => import('../views/About/About'))const routes = [{path: '/',element: <Navigate to="/home" />},{path: '/home',element: (// 当路由很多时,书写这段代码会显得非常繁琐,可以封装一个withLoadingComponent方法<Suspense fallback={<div>loading...</div>}><Home /></Suspense>)},{path: '/about',element: withLoadingComponent(<About />),}
]// comp的ts类型为JSX.Element
const withLoadingComponent = (comp) => {return (<Suspense fallback={<div>loading...</div>}>{ comp }</Suspense>)
}export default routes
2.3 嵌套路由
路由表通过children
属性可以书写子路由,但是子路由并不会直接展示在页面上,需要通过在展示子路由的页面上放置路由出口<Outlet />
路由表配置:
const routes = [{path: '/',element: <Navigate to="/home" />},{path: '/home',element: <Home />},{path: '/about',element: <About />,// 子路由children: [{path: '/about/news',element: <News />}]}
]
路由出口:(在About组件内)
import React from 'react'
import { Outlet } from 'react-router-dom'export default function about() {return (<div>about<Outlet /></div>)
}