目录
React 路由介绍
现代的前端应用大多都是 SPA(单页应用程序),也就是只有一个 HTML 页面的应用程序。因为它的用户体验更好、对服务器的压力更小,所以更受欢迎。
为了有效的使用单个页面来管理原来多页面的功能,前端路由应运而生。前端路由的功能:让用户从一个视图(页面)导航到另一个视图(页面)
-
前端路由是一套映射规则,在React中,是 URL路径 与 组件 的对应关系
-
使用 React 路由简单来说就是:配置路径和组件(配对)
react模拟hash路由的实现
实现原理
hash值: localhost:3000/#/page1
#之后的部分就是hash值,它有两个特点:
-
hash值的变化(地址栏中改了hash值再回车)不会重新发请求。它不是请求路径的一部分。
-
hash值的变化会触发hashChange事件。
思路
维护一个状态curHash。
-
当hash变化(hashchange事件)时修改它;
-
根据curHash的值来决定显示哪个组件
目录结构
pages/Comment.jsx
pages/Home.jsx
pages/Search.jsx
index.js
index.js
import React, { useEffect, useState } from 'react'
import Home from './pages/Home.jsx'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
import ReactDom from 'react-dom'
export default function App () {
const [curHash, setCurHash] = useState('')
useEffect(() => {
const onChange = () => {
// console.log(window.location.hash)
setCurHash(window.location.hash.slice(1))
}
onChange()
window.addEventListener('hashchange', onChange)
return () => {
window.removeEventListener('hashchange', onChange)
}
}, [])
return (
<div>
<ul>
<li>
<a href="#/home">首页</a>
</li>
<li>
<a href="#/comment">评论</a>
</li>
<li>
<a href="#/search">搜索</a>
</li>
</ul>
<hr />
{curHash === '/home' && <Home />}
{curHash === '/search' && <Search />}
{curHash === '/comment' && <Comment />}
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
hash路由的原理是监听hashChange事件
React路由使用的基本
react-router-dom 在2021年10月发步了6.0版本,目前不稳定。推荐5x版本
官网:
Declarative routing for React apps at any scale | React Router (6.0)
https://v5.reactrouter.com/ (5.X)
步骤
-
安装包。
npm i [email protected]
这个包提供了三个核心的组件: HashRouter, Route, Link
-
导入包,并使用。
使用HashRouter包裹整个应用,一个项目中只会有一个Routerimport { HashRouter, Route, Link } from 'react-router-dom'
-
使用Link指定导航链接
使用Route
指定路由规则(哪个路径展示哪个组件)
实例代码
import React from 'react'
import ReactDom from 'react-dom'
import { HashRouter, Route, Link } from 'react-router-dom'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<HashRouter>
<Link to="/comment">评论</Link>
<Link to="/search">搜索</Link>
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
</HashRouter>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
路由三大对象之-Router
了解两种路由
HashRouter :hash模式
BrowserRouter: history模式
Router 组件:包裹整个应用,一个 React 应用只需要使用一次
两种常用 Router:HashRouter
和 BrowserRouter
-
HashRouter:使用 URL 的哈希值实现
-
原理:监听 window 的
hashchange
事件来实现的
-
-
(推荐)BrowserRouter:使用 H5 的 history.pushState() API 实现
-
原理:监听 window 的
popstate
事件来实现的
-
使用es6的导入重命名来统一名字: 无论导入的是哪个路由对象,都叫Router
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'
import { HashRouter as Router, Route, Link } from 'react-router-dom'
路由三大对象之-Link
import { Link, NavLink } from 'react-router-dom'
Link
Link组件最终会渲染成a标签,用于指定路由导航
-
to属性,将来会渲染成a标签的href属性
-
Link
组件无法展示哪个link处于选中的效果
NavLink
NavLink
组件,一个更特殊的Link
组件,可以用用于指定当前导航高亮
格式:
<NavLink to="/xxx" activeClassName="active">链接</NavLink>
说明:
-
to属性,用于指定地址,会渲染成a标签的href属性
-
activeClassName: 用于指定高亮的类名,默认
active
。一般不去修改。 -
exact: 精确匹配,表示必须地址栏和to的属性值 精确匹配类名才生效
代码示例
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter as Router, Route, Link, NavLink } from 'react-router-dom'
import Search from './pages/Search.jsx'
import Comment from './pages/Comment.jsx'
export default function App () {
return (
<div>
<h1>react路由基本使用-Link</h1>
<Router>
<div>
Link:
<Link to="/search">搜索</Link>
<Link to="/comment">评论</Link>
</div>
<div>
NavLink: 自带高亮类
<NavLink to="/" exact>主页</NavLink>
<NavLink to="/search">搜索</NavLink>
<NavLink to="/comment">评论</NavLink>
</div>
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
路由三大对象之-Route
route的作用和格式
-
作用: 决定路由匹配规则
-
格式:
<Route path="/xx/xx" component={组件}></Route>
匹配规则
名词约定:
-
path: Route组件中path属性的值
-
pathname: 指的如下格式
-
link组件中to的属性值
-
地址栏中的地址
-
模糊匹配规则
-
只要pathname以path开头就算匹配成功
-
匹配成功就加载对应组件;
-
整个匹配过程是逐一匹配,一个匹配成功了,并不会停止匹配。
模糊匹配和精确匹配
-
默认是模糊匹配的
-
补充exact可以设置成精确匹配
代码示例
import React from 'react'
import ReactDom from 'react-dom'
import { BrowserRouter as Router, Route, NavLink } from 'react-router-dom'
const Home = () => <div>主页</div>
const Article = () => <div>文章列表页</div>
const ArticleDetail = () => <div>文章详情页</div>
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<Router>
<NavLink to="/">主页</NavLink>
<NavLink to="/article">文章列表页</NavLink>
<NavLink to="/article/123">文章详情页-123</NavLink>
<hr />
<Route path="/" component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
exact
<Route path="/" exact component={Home} />
-
path 的说明
-
默认情况下,/能够匹配任意/开始的路径
-
如果 path 的路径匹配上了,那么就可以对应的组件就会被 render
-
-
exact , exact 表示精确匹配某个路径
-
一般来说,如果路径配置了 /, 都需要配置 exact 属性
-
Switch与404
问题
Route组件的匹配成功之后并不会停止,它可能会匹配多个组件。
解决方式
用Switch组件包裹多个Route
组件。
在Switch
组件下,不管有多少个Route的路由规则匹配成功,都只会渲染第一个匹配的组件
import React from 'react'
import ReactDom from 'react-dom'
import {
BrowserRouter as Router,
Route,
NavLink,
Switch
} from 'react-router-dom'
const Home = () => <div>主页</div>
const Article = () => <div>文章列表页</div>
const ArticleDetail = () => <div>文章详情页</div>
export default function App () {
return (
<div>
<h1>react路由基本使用</h1>
<Router>
<NavLink to="/">主页</NavLink>
<NavLink to="/article">文章列表页</NavLink>
<NavLink to="/article/123">文章详情页-123</NavLink>
<hr />
<Switch>
<Route path="/" exact component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
</Switch>
</Router>
</div>
)
}
ReactDom.render(<App />, document.getElementById('root'))
处理404页
思路: 不设置path属性,将404页对应的路由放在switch内部的最后位置
通过Switch
组件非常容易的就能实现404错误页面的提示
<Switch>
<Route path="/" exact component={Home} />
<Route path="/article" component={Article} />
<Route path="/article/123" component={ArticleDetail} />
<Route component={Page404} />
</Switch>
页面跳转 Redirect
格式
<Redirect from="/" exact to="/comment" />
示例
<Route path="/comment" component={Comment} />
<Route path="/search" component={Search} />
{/* <Route path="/" component={Comment} /> */}
<Redirect from="/" to="/comment" />
编程式导航
页面跳转有两类方式:
-
用户点击链接跳转-声明式导航
-
写代码跳转-编程式导航
掌握history对象的使用,会用它来跳转页面。
编程式导航的格式
import {useHistory} from 'react-router'
export default function App() {
const history = useHistory()
history.push('/find')
// 前进或后退到某个页面,参数 n 表示前进或后退页面数量(比如:-1 表示后退到上一页)
history.go(-1)
// 进入/frend,并替换记录
history.replace('/frend')
}
history.replace和push的区别
push:向历史记录中添加一条
replace:在历史记录中用目标记录来替换当前记录
示例
push
详情页 --> login页(push) ----> 主页
此时,从主页后退,会回到login页。
replace
详情页 --> login页(replace) ----> 主页
此时,从主页后退,会回到详情页。
动态路由与路由参数获取
问题 : 如何设置如下路由匹配规则,以显示文章详情
<NavLink to="/article/1">文章1</NavLink>
<NavLink to="/article/2">文章2</NavLink>
动态路由
// 可以匹配 /article/1 /article/2 /article/xxx
<Route path="/article/:id" component={Article} />
说明:
-
上面的/:id 称为占位符。id可以改成其他的变量名。
-
占位符可以有多个。例如:
/article/:形参1/:形参2
在组件中接收到路由的参数
有两个方式:
-
通过
props
可以
function Article(props){
console.log(props.match.params.id)
}
2. 通过hooks
可以
impoirt { useParams } from 'react-router'
const params = useParams()
console.log(params) // 这里有数据
嵌套路由的配置
示例代码
App () {
return
<Router>
<ul>
<li><NavLink to="/find">发现</NavLink></li>
<li><NavLink to="/my">我的音乐</NavLink></li>
<li><NavLink to="/frend">朋友</NavLink></li>
</ul>
<Switch>
<Route path="/find" component={Find}></Route>
<Route path="/my" component={My}></Route>
<Route path="/frend" component={Frend}></Route>
</Switch>
</Router>
}
find.js
Find(){
return <Router>
<ul>
<li><NavLink to="/find/recommand">推荐</NavLink></li>
<li><NavLink to="/find/top">排行榜</NavLink></li>
<li><NavLink to="/find/list">歌单</NavLink></li>
</ul>
<Switch>
<Route path="/find/recommand" component={Com1}></Route>
<Route path="/my/top" component={Com2}></Route>
<Route path="/frend/list" component={Com3}></Route>
</Switch>
</Router>
}
注意:
配置嵌套路由的时候,需要对路径进行处理,必须要先匹配到父级路由,才能匹配到子路由
路由小结
用到的组件
HashRouter, BrowserRouter, Link, NavLink, Route, Redirect, Switch
执行过程
-
点击 Link 组件(a标签),修改了浏览器地址栏中的 url
-
React 路由监听到地址栏 url 的变化 hashChange popState
-
React 路由内部遍历所有 Route 组件,使用路由规则(path)与 pathname(hash)进行匹配
-
当路由规则(path)能够匹配地址栏中的 pathname(hash) 时,就展示该 Route 组件的内容