什么是前端路由
现代的前端页面大多是SPA(单页面应用程序), 也就是只有一个HTML页面的程序,这样用户体验好,服务器压力小,所以更受欢迎。但是一个页面可能有很多功能,比如一个页面,可能会有导航栏,有内容页。头部区域和尾部区域,那么如果是互联网刚起来的那几年,每个导航栏可能就是一个a标签,点击跳转新页面。但是这种跳转不管是对于服务器还是对于用户来说,可能都是不友好的。那么怎么能实现这样一种功能呢,就是我们点击导航栏的时候,只有内容刷新?你用点击事件固然可以这么做。但是,react-router提供了一种更简单的方式,就是以路由的方式来映射不同的组件,你操作了不同的路由,就可以调用不同的组件,实现不同的页面。
React-Router
React Router 是一个基于 React之上的强大路由库,可以实现无刷新的条件下切换显示不同的页面。
react-router主要分成了几个不同的包:
-
react-router: 实现了路由的核心功能
-
react-router-dom: 基于 react-router,加入了在浏览器运行环境下的一些功能
-
react-router-native:基于 react-router,加入了 react-native 运行环境下的一些功能
-
react-router-config: 用于配置静态路由的工具库
那这里,我们就先看我们最常用到的react-router-dom组件
react-router 6 新特性
- v6版本移除了之前的Switch,引入了新的替代者Routes,Routes和Route配合使用,且必须用Routes包裹Route
当url发生变化时,Routes会查看其所有子Route元素找到最佳匹配并呈现组件
Route caseSensitive属性用于指定:匹配时是否区分大小写(默认为false) - component 改为 element
- 组件传递props ,比如传递一个name:'kobe' 的属性
javascript
//传递
<Route path='/news' element={<HomeNews name='kobe'/>}/>
//接受
function HomeNews(propsName) {
console.log(propsName);//{name: 'kobe'}
return (
<News>{propsName.name}</News>
)
}
- 路由重定向 Navigate,代替之前的Redirect
react-router-dom
react-router-dom下主要的组件有BrowserRouter,HashRouter,HashRouter,Link,NavLink,switch,redirect,我们一个个看一下
BrowserRouter
它的主要作用是为React应用程序提供客户端路由功能。它使用 HTML5 的 history API 来处理URL的变化,并根据URL的路径匹配渲染相应的组件。它通常是应用的根组件,用于包裹整个应用。
HashRouter
HashRouter和BrowserRouter作用差不多。都是用来绑定path和component的,但是两者采用的模式不一样,我们前面说过,BrowserRouter使用 HTML5 的 history API,而HashRouter是基于hash来实现的
HashRouter和BrowserRouter的区别和使用场景
HashRouter
- 基于hash模式:页面跳转原理是使用了location.hash、location.replace;和vue router的hash模式实现一致
- 比较丑:在域名后,先拼接/#,再拼接路径;也就是利用锚点,实现路由的跳转;如:http://www.abc.com/#/xx
BrowserRouter
- 基于history模式:页面跳转原理是使用了HTML5为浏览器全局的history对象新增了两个API,包括 history.pushState、history.replaceState;不兼容IE9及以下版本。
- 更加优雅: 直接拼接路径;如:http://www.abc.com/xx
Routes
Routes组件用于定义应用程序的不同路由和对应的组件。它允许我们在应用程序中定义多个路由,每个路由对应一个特定的组件,当用户访问不同的路由时,会渲染相应的组件。Routes组件还可以设置路由参数、嵌套路由、重定向等功能,帮助我们构建复杂的单页面应用程序。通过Routes组件,我们可以实现页面之间的切换和导航,提供更好的用户体验。
Route
route用于定义路由的规则,就是我这个路由对应哪个组件。其实从作用上我们也能知道,route至少接收两个参数,一个用来定义path路径,一个用来指定调用的组件component。
接下来我们看一个简单的示例,访问/a路径的时候,展示a的页面,我访问/b的时候,显示b的页面
javascript
import React, {Component} from 'react';
import { BrowserRouter, Routes, Route } from "react-router-dom";
class App extends Component {
render() {
return (
// 注意此处要用BrowserRouter包括根组件
<BrowserRouter>
{/*定义多个路由组件组件*/}
<Routes>
<Route path="/a" element={<A/>} />
<Route path="/b" element={<B/>} />
</Routes>
</BrowserRouter>
);
}
}
class A extends Component {
render() {
return (
<div>
<h1>A</h1>
</div>
);
}
}
class B extends Component {
render() {
return (
<div>
<h1>B</h1>
</div>
);
}
}
export default App;
Link
我们知道,当我们想实现局部刷新的时候,我们可能会使用事件来处理。react路由里面也提供了一个更加简洁的api,即link组件,这个组件用于创建导航链接。它会生成一个 标签,并处理点击事件以避免浏览器重新加载页面。你可以使用 to属性和route的path属性绑定来实现这个功能。我们来试一下
javascript
render() {
return (
// 注意此处要用BrowserRouter包括根组件,可以提到index.js里面包裹App标签
<BrowserRouter>
{/*这是导航区域*/}
<ul>
<li>
<Link to="/a">我是a</Link>
</li>
<li> <Link to="/b">我是b</Link></li>
</ul>
{/*定义多个路由组件组件 展示组件的返回值*/}
<Routes>
<Route path="/a" element={<A/>} />
<Route path="/b" element={<B/>} />
</Routes>
</BrowserRouter>
);
}
link的to属性看定义为一个对象,相关属性如下
- pathname: 表示要链接到的路径的字符串。
- search: 表示查询参数的字符串形式。
- hash: 放入网址的 hash,例如 #a-hash。
- state: 状态持续到 location。通常用于隐式传参(埋点),可以用来统计页面来源
javascript
<Link to={{
pathname: '/a',
search: '?id=1',
hash: '#someHash',
state: { fromWechat: true }
}}>我是a</Link>
NavLink
可以看做 一个特殊版本的 Link,当它与当前 URL 匹配时,为其渲染元素添加样式属性。相关属性如下
- exact: 如果为 true,则仅在位置完全匹配时才应用 active 的类/样式。
- strict: 当为 true,要考虑位置是否匹配当前的URL时,pathname 尾部的斜线要考虑在内。
- location 接收一个location对象,当url满足这个对象的条件才会跳转
- isActive: 接收一个回调函数,只有当 active 状态变化时才能触发,如果返回false则跳转失败
最简单的例子就是当路由是/a的时候,会给绑定当前路由的a标签添加一个active的类属性。
Navigate
将一个路由组件重定向到某个新地址
javascript
<Route path="/c" element={<Navigate to="/a"/>} />
路由嵌套
我们知道,在现实中,写的路由大部分都是有统一前缀的,那么我们就要在前缀的基础上写路由,而不是所有的路由都把前缀带上,那么我们怎么写呢,我们来看一下
javascript
import React, {Component} from 'react';
import {Routes, Route,Link } from "react-router-dom";
class App extends Component {
A=()=>{
return <h1>我是a</h1>
}
B=()=>{
return <h1>我是B</h1>
}
render() {
return (
// 注意此处要用BrowserRouter包括根组件,可以提到index.js里面包裹App标签
<div>
<ul>
<li><Link to="/h5/a">我是a</Link></li>
<li> <Link to="/h5/b">我是b</Link></li>
</ul>
{/*在这里配置前缀*/}
<Routes>
<Route path="/h5/*" >
<Route path="a" element={this.A()}></Route>
<Route path="b" element={this.B()}></Route>
</Route>
</Routes>
</div>
);
}
}
export default App;
- 路由的集中管理
我们看到,在代码里面写满了路由,明显是很low的,那么我们能不能把路由集中管理起来呢,比如我新建了一个路由文件,叫Route.jsx
javascript
import {useRoutes} from "react-router-dom";
import A from "../A/A"
import B from "../B/B"
const DefineRoute=()=>{
return useRoutes(
[
// 单路由
{
path:"/",
element:<div>Home</div>
},
//嵌套路由
{
path:"/h5",
children:[
{
path:"a",
element:<A/>
},
{
path:"b",
element:<B/>
}
]
}
]
)
}
export default DefineRoute
- 完毕之后我需要在使用的地方把它渲染出来,比如我在index.js里面渲染,完成之后,即可使用路由
javascript
import DefineRoute from "./components/Route/Route"
const app= <BrowserRouter><DefineRoute /><App/></BrowserRouter>
路由参数
前面我们说了很多路由相关的基础知识呢,那么最重要的问题来了,路由的参数我们怎么获取?我们来看一下
我们改一下路由b
javascript
{
path:"b/:age/:name",
element:<B/>
}
- 修改b组件
javascript
import React from 'react';
import { useParams,useSearchParams } from "react-router-dom";
function B() {
const [search,setsearch] = useSearchParams()//返回search参数 和 更新search参数的函数
const {age,name}= useParams();
console.log(search.get("title")) // ['123']
console.log(search.getAll("title")) // ['123']
return (
<div>
<h1>B</h1>
<h1>age:{age}</h1>
<h1>name:{name}</h1>
<button onClick={()=>setsearch('id=2&title=更新search')}>点我更新search参数</button>
</div>
);
}
export default B;