什么是路由?
- 一个路由就是一个映射关系(
key:value
);key
为路径
,value
可能是function
或component
。
在
React
里面,对于路由,我们需要用到react-router-dom
,但是,react
脚手架里面并没有帮我们去下载react-router-dom
,所以,我们需要自己安装react-router-dom
react-router
的官方网站苦于找不到好的中文文档?推荐一个很厉害的网站:印记中文。
第一步、进入印记中文 或 百度搜索印记中文
第二步、见下图第三步、进入react-router
的官方网站后,见下图
react-router-dom
的安装与使用1、安装方法:yarn add react-router-dom
或者 npm install react-router-dom
【注意:yarn
和npm
混着用,及其容易造成包的丢失】
2、使用路由库
举个例子吧~
【注意】下面给出的案例中的react-router-dom
是v6(6.2.2版本)
1、实现效果:
2、思维分析:假如先忽略盒子的高亮效果(粉色背景,白色字体),则需要使用到 路由链接
Link
(相当于a标签
)和 注册路由Route
。
1)、路由库的引入和使用
它这个是需要什么则引入什么,例如:路由链接
Link
【Link
要放在Router
里面】、注册路由Route
【Route
要放在Routes
里面,Routes
要放在Router
里面】
Router
分为两种:BrowserRouter
和HashRouter
BrowserRouter
:url地址里面没有#
符,如:localhost:3000
;HashRouter
:url地址里面会有#
符,#
符后边的内容(哈希值)都不会作为资源发送给服务器,如:localhost:3000/#/
。BrowserRouter与HashRouter的区别 1.底层原理不一样: BrowserRouter使用的是H5的history API,不兼容IE9及以下版本。 HashRouter使用的是URL的哈希值。 2.path表现形式不一样 BrowserRouter的路径中没有#,例如:localhost:3000/demo/test HashRouter的路径包含#,例如:localhost:3000/#/demo/test 3.刷新后对路由state参数的影响 (1).BrowserRouter没有任何影响,因为state保存在history对象中。 (2).HashRouter刷新后会导致路由state参数的丢失!!! 4.备注:HashRouter可以用于解决一些路径错误相关的问题。
First、 路由链接 Link
【Link
要放在Router
里面】
对应代码
{/* 编写路由链接:在React中靠路由链接实现切换组件 —— Link要放在Router里面 */}
<BrowserRouter>
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</BrowserRouter>
结果
1、页面一进来时,url地址栏为初始地址,如:
localhost:3000
;
2、当点击下方的About盒子
后,url地址栏在初始地址上加了个/about
,如:localhost:3000/about
;
3、当点击下方的Home盒子
后,url地址栏在初始地址上加了个/home
,如:localhost:3000/home
。
Second、 注册路由Route
【Route
要放在Routes
里面,Routes
要放在Router
里面】
对应代码
{/* 注册路由 —— Route要放在Routes里面,Routes要放在Router里面 */}
<BrowserRouter>
<Routes>
{/* 旧版写法 */}
{/*
*/ }
{/* 新版写法 */}
<Route path="/" element={<About />}/>
<Route path="/about" element={<About />}/>
<Route path="/home" element={<Home />}/>
</Routes>
</BrowserRouter>
到这里,小萝卜儿们肯定觉得已经可以达到我们需要实现的效果了吧,
But
,当我们运行代码后,点击盒子切换时,对应组件内容并没有相应变化也~ 什么?怎么会这亚子,好奇宝宝又开始挠自己滴小脑袋咯
就不藏着掖着啦~造成上面无效的原因就是:上面代码中使用了两个路由器
BrowserRouter
进行包裹,这两个路由器相互之间并没有联系:路由器A
变化了 并没有通知路由器B
,路由器B
变化了 并没有通知路由器A
,它们两个之间没有进行数据的沟通。So
,针对上面代码,不能链接路由Link
里面包了一个路由器,注册路由Route
里面再包一个路由器,而应该整个应用都由一个路由器进行包裹,而不是多个。
这时爱动脑筋的小萝卜儿们心想:在这个页面里面,把那两个路由器往外扩展 变成 一个路由器就好啦(如下图)
But
,这样滴话,假如往后新增页面代码,就又需要调整的位置,会比较麻烦哦。
So
,我们可以直接在index.js
里面将整个App组件
包裹住哦(如下图)。
【注意】要回去App.jsx
里面将引入BrowserRouter
相关的代码删除掉哟
2)、未实现盒子的高亮效果时,App.jsx
的代码
import React, { Component } from 'react'
import {Link, Routes ,Route} from 'react-router-dom'
import Home from './components/Home'
import About from './components/About'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<div className="page-header"><h2>React Router Demo</h2></div>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠a标签跳转到不同的页面 */}
{/*
*/}
{/* 编写路由链接:在React中靠路由链接实现切换组件 —— Link要放在Router里面 */}
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 —— Route要放在Routes里面,Routes要放在Router里面 */}
<Routes>
{/* 旧版写法 */}
{/*
*/ }
{/* 新版写法 */}
<Route path="/" element={<About />}/>
<Route path="/about" element={<About />}/>
<Route path="/home" element={<Home />}/>
</Routes>
</div>
</div>
</div>
</div>
</div>
)
}
}
- 对于
路由链接的高亮效果
,该如何实现呢?这个就需要用到NavLink
标签啦~(替换掉原先使用的Link
标签)
- 用
NavLink
后,就是点谁就给谁追加一个样式的类名active
;- 假如你不想用默认的类名
active
的样式,那你就可以通过activeClassName
指定样式名,But
,注意:6.0版本
官方已经不再支持activeClassName
这种写法了,换成动态设置className
了。
比如:实现点谁就给谁追加样式类名selected
{/* 编写路由链接:在React中靠路由链接实现切换组件 —— Link要放在Router里面 */}
{/* About
Home */}
{/* 编写路由链接(并实现高亮效果):在React中靠路由链接实现切换组件 —— NavLink要放在Router里面 */}
{/* 旧版写法 */}
{/* About
Home */}
{/* 新版v6写法 */}
<NavLink className={({ isActive }) => "list-group-item" + (isActive ? " selected" : "")} to="/about">About</NavLink>
<NavLink className={({ isActive }) => "list-group-item" + (isActive ? " selected" : "")} to="/home">Home</NavLink>
当当当,这样就可以实现高亮效果啦~
总结:路由链接(相当于
a
标签): 不需要高亮效果用Link
,需要高亮效果用NavLink
。
But
,通过观察可以发现,上面的写法中,两个NavLink
有共同部分:className={({ isActive }) => "list-group-item" + (isActive ? " selected" : "")}
;在需要出现多个NavLink
的情况下,岂不是就显得比较繁琐了;假如我们可以只展示它们之间不同的部分,岂不乐乎~ 所以 我们最好对路由组件进行封装
。
- 封装一个一般组件
MyNavLink
一般组件MyNavLink
里的index.jsx
(待完善):
App.jsx
:
- 但是按照上面写法的话,假如需要传很多数据,这样一个一个写的话,就会显得代码冗余咯,比如:
还需要传入
About a
、b
、c
滴话,岂不是在一般组件MyNavLink
里就需要通过props
接收到to
、a
、b
、c
的值,然后按照to={to} a={a} b={b} c={c}
这样的写法传到组件MyNavLink
里去,那有没有什么简洁的好办法呢?- 当然有哟,那就是
{...this.props}
:在组件MyNavLink
里,不用接收props
里的值,直接用{...this.props}
替换掉to={to} a={a} b={b} c={c}
就好啦;- 通过
{...this.props}
获取到props
里的值,直接渲染上去就可以啦,不管传了好多个值,不用再一个一个写了,简洁了不少。- 这时爱动脑筋的小萝卜儿们肯定想问:那
title
还不是要接收的嘛,不然之间的标签体怎么展示呢?
- 所以直接在
单标签
里写入{...this.props}
就相当于给其加入了标签体About和Home
- 那么 一般组件
MyNavLink
里的index.jsx
(最终代码):
到这里,关于盒子的高亮问题,还有一个细节就是:
url
地址为'/'
时,我让它默认展示的是About组件
的内容,这没错,但是细心的小萝卜儿们就会发现,为什么页面一进来,About盒子
没有出现高亮效果呢?那。。。该如何实现呢?
- 首先回答第一个问题——为什么页面一进来,
About盒子
没有出现高亮效果呢?
- 因为上面代码中,我们是通过
来实现:
}/> url
地址为'/'
时,让其展示About组件
的内容,但是我们并没有针对url
地址为'/'
时的盒子,自然就不能出现高亮效果咯~- 那。。。第二个问题——该如何实现呢?
这里我们就需要再学习一个新知识点啦:Redirect(重定向)
Redirect
(重定向):若url地址
与path=""
的都没匹配上,则去Redirect
指向的路径。(一般写在所有路由注册的最下方) 【注意:在v6
已被移除,在v6
中需引入Navigate标签
来替代之前Redirect的重定向操作
】- 比如:
}/>
本案例App.jsx
的最终代码
import React, { Component } from 'react'
import {Routes ,Route , Navigate} from 'react-router-dom'
// 引入路由组件
import Home from './pages/Home'
import About from './pages/About'
// 引入一般组件
import Header from './components/Header'
import MyNavLink from './components/Header/MyNavLink'
export default class App extends Component {
render() {
return (
<div>
<div className="row">
<div className="col-xs-offset-2 col-xs-8">
<Header/>
</div>
</div>
<div className="row">
<div className="col-xs-2 col-xs-offset-2">
<div className="list-group">
{/* 原生html中,靠a标签跳转到不同的页面 */}
{/*
*/}
{/* 编写路由链接:在React中靠路由链接实现切换组件 —— Link要放在Router里面 */}
{/* About
Home */}
{/* 编写路由链接(并实现高亮效果):在React中靠路由链接实现切换组件 —— NavLink要放在Router里面 */}
{/* 旧版写法 */}
{/* About
Home */}
{/* 新版v6写法 */}
{/* "list-group-item" + (isActive ? " selected" : "")} to="/about">About
"list-group-item" + (isActive ? " selected" : "")} to="/home">Home */}
{/* 上面写法太繁琐,所以采用封装组件的方法 */}
<MyNavLink to="/about">About</MyNavLink>
<MyNavLink to="/home">Home</MyNavLink>
</div>
</div>
<div className="col-xs-6">
<div className="panel">
<div className="panel-body">
{/* 注册路由 —— Route要放在Routes里面,Routes要放在Router里面 */}
{/* 旧版写法 exact是开启严格模式,默认是false(非严格匹配模式)【注意:严格匹配不能随便开启,要 因为这个严格匹配/模糊匹配 产生问题,才开启】 */}
{/*
*/}
{/* 新版写法 */}
<Routes>
{/* Redirect(重定向):若都没匹配上,则去Redirect指向的路径 [在v6已被移除,在v6中需引入Navigate标签来替代之前Redirect的重定向操作]*/}
{/* 采用Navigate实现重定向,可以使url地址为'/'时,About盒子产生高亮效果 */}
<Route path="*" element={<Navigate to="/about"/>}/>
{/* 采用路由方式,不可以使url地址为'/'时,About盒子产生高亮效果 */}
{/* }/> */}
<Route path="/about" element={<About />}/>
<Route path="/home" element={<Home />}/>
</Routes>
</div>
</div>
</div>
</div>
</div>
)
}
}
3)、案例中遇到的报错总结
Error1: 【注册路由Route
】
解决方法:注意旧版和新版的写法哟~改成新版写法就可以啦【在v6
中,Switch
标签替换成了Routes
标签、element
代替了component
】
{/* 注册路由 —— Route要放在Routes里面,Routes要放在Router里面 */}
<Routes>
{/* 旧版写法 */}
{/*
*/ }
{/* 新版写法 */}
<Route path="/" element={<About />}/>
<Route path="/about" element={<About />}/>
<Route path="/home" element={<Home />}/>
</Routes>
Error2: 【注册路由Route
】
No routes matched location "/"
解决方法:添加一个 “/” 路径的路由
,就可以啦
{/* 新版写法——解决方法2(重定向) */}
<Route path="/about" element={<About />}/>
<Route path="/home" element={<Home />}/>
<Route path="*" element={<Navigate to="/about"/>}/>
These are bilibili尚硅谷React学习视频的 学习笔记~