React 路由
路由
路由基本功能
- 保证视图和url的同步。
history
history可以用来兼容在不同浏览器、不同环境下对历史记录的管理。
history分为三类,分别是browserHistory、hashHistory和memoryHistory。browserHistory利用的是H5中的history接口。hashHistory利用的是history中的location属性的hash。
browserHistory采用push和replace(编程式路由)来实现url的改变,这两个方法分别封装了history的pushState和replaceState方法。这两个方法都会改变当前的url,但不会刷新页面。还有例如go()、back()、forward()等方法。这些方法都会触发popState事件,所以在browseHistory采用手动触发popState的方式来实现对url改变的监听。
hashHistory通过区分history对象中的location属性中包含的hash字段来渲染不同的组件。
React-Router的实现原理
React-Router是建立在history之上的,history会监听浏览器地址栏的变化,并解析url转化为location对象,然后router匹配到对应的路由,最后渲染对应的组件。
Router负责根据当前的url来渲染相应的组件。
Route根据当前的url与自身的path属性进行匹配,匹配成功就渲染对应的组件。
何如配置React-Router实现路由切换
- 使用<Route>组件
路由匹配是通过比较的path属性与当前的pathname来实现的。可以给<Route>组件加上 exact 属性来实现路由精准匹配。
<Route path="/home" component={Home} />
- <Switch>和<Route>
因为<Route>采用的是模糊匹配,所以可能一次会匹配到多个路由,<Switch>的作用就是控制每次只匹配一个路由。在<Route>外层包裹<Switch>表示只会匹配到第一个匹配的<Route>。
<Swicth>
<Route path="/home" component={Home} />
<Route path="/about" component={About} />
</Switch>
- <Link>、<NavLink>和<Redirect>
<Link>就是渲染在页面中的路由跳转链接,<Link>组件将被渲染为一个<a>标签。
<Link to="/home">Home</Link>
// 在页面中被渲染为:<a href="/home">Home</a>
<Link>的跳转行为只会触发相匹配的组件的内容更新,而不会重新渲染整个页面。
<NavLink>是一种特殊的<Link>,当它的to属性与当前地址匹配时,可以将其定义为active状态。
<Redirect>组件用于重定向路由。
<Redirect from="/users/:id" to="/user/profile/:id">
从/users/:id 转到 /user/profile/:id。
Link和a标签的区别
从最终渲染的DOM来看,两者都是链接。区别在于:<Link>是实现路由跳转的链接,一般配合
<Route>使用,<Link>的跳转行为只触发了相匹配的<Route>对应的页面内容更新,而不会刷新整个页面。
我个人的理解:因为React是一个单页面应用,在每次进行路由切换时都是在一个HTML文件中发生的,这个HTML文件在页面首次加载时就已经下载下来了,使用<Link>在路由切换时不会重新去请求一个HTML文件;而<a>在每次跳转时都会加载对应页面的HTML文件,在某些网速慢得情况下会出现空白页,导致用户体验不好。<Link>在通过阻止<a>标签的默认事件,然后根据href(即<Link>的to属性)进行页面跳转。
Router如何获取URL的参数和历史对象
获取URL参数:
- get传值。通过
props.location.search
取值。 - 动态路由传值。在V6之前的版本可以使用
props.match.params.属性值
来取值;V6
版本可以通过useParams和useSearchParams钩子函数来取值。
获取历史对象:
- useHistory钩子函数获取历史对象。
props.history
获取历史对象。
路由拦截(路由守卫)
const isAuth = () => {
// 判断是否有token字段
return localStorage.getItem("token")
}
// 实现路由拦截
<Route path="/cart" render={()=>{
return isAuth() ? <Cart /> : <Redirect to="/login">
}} />
Router V6
V6版本的一些更改
- <Switch>重命名为<Routes>
- <Route>的特性变更(component/render被element替代)
- 嵌套路由变得更简单
- 使用<useNavigate>代替<useHistory>
- 使用<useRoutes>代替react-router-config
- 更小的体积
// history模式
<BrowserRouter>
{/* 路由入口:指定跳转到哪一个组件,to用来配置路由地址 */}
<Link to='/'>首页</Link>
<Link to='/about'>关于</Link>
<Link to='/login'>登录</Link>
{/* 路由出口:路由对应的组件会在这里进行渲染 */}
<Routes>
{/* 指定路径和组件的对应关系,用于指定导航链接,完成路由匹配 */}
<Route path="/" element={<Home />}></Route>
<Route path="/about" element={<About />}></Route>
<Route path="/login" element={<Login />}></Route>
</Routes>
</BrowserRouter >
- Link:路由入口,用于指定导航链接,完成路由跳转,最终渲染为a标签
- Routes:路由出口,路由对应的组件在Routes中进行渲染
- Route:指定路由地址与组件的对应关系,用于指定导航链接,完成路由匹配
- BrowserRouter:history模式
编程式导航
Router v6通过useNavigate钩子函数实现编程式导航
import { useNavigate } from 'react-router-dom'
// 指定钩子函数得到跳转函数
const navigate = useNavigate()
// 执行跳转函数,跳转到about页
navigate('/about') // 相当于push
navigate('/about', { replace: true }) // 相当于replace
navigate(-1) // 相当于back
navigate(1) // 相当于forward
navigate(-2) // 相当于go
传参
Router v6有两种传参方式
- searchParams传参
// 传参:
navigage('/about?id=1001')
// 取参:
let [params] = useSearchParams()
let id = params.get('id')
params是一个对象,里面包含很多方法:get、append、delete、forEach等,可以对传来的参数进行操作。
- params传参
// 传参:
navigage('/about/1001')
// 取参:
let params = useParams()
let id = params.id
第二种方法的参数就是一个参数对象。
嵌套路由
- 定义嵌套路由声明
{/* 指定路径和组件的对应关系,用于指定导航链接,完成路由匹配 */}
<Route path="/" element={<Home />}>
{/* 定义二级路由 */}
<Route path='board' element={<Board />}></Route>
<Route path='article' element={<Article />}></Route>
</Route>
- 使用指定二级路由出口
然后再Home组件中配置二级路由出口。
import { Outlet } from 'react-router-dom'
{/* 二级路由出口 */}
<Outlet></Outlet>
配置默认路由
使用index关键字配置默认路由
<Route index element={<Board />}></Route>