这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战
React路由
SPA
即单页面web(single page web application)
应用。整个应用只有一个完整的页面,点击页面中的链接不会刷新页面,只会做页面的局部更新。数据都需要通过ajax
获取。
也就是说,正因为有了路由这个技术的存在,才能实现我们在同一个页面中进行局部刷新(单页面多组件)。
路由的理解
-
一个路由就是一个映射关系(key: value)
-
key为路径,value可能是function(后端路由) 或 component(前端路由);
-
后端路由:注册:
router.get(path, function(req, res))
。当node接收到一个请求时,根据请求路径找到匹配的路由,调用路由中的函数来处理请求,返回响应数据。 -
前端路由:注册:
Route path="/test" component={Test}
。用于展示页面内容。当浏览器的path变为/test时,当前路由组件就会变成Test组件。比如对于一个在127.0.0.1:3000
的页面,当它为127.0.0.1:3000/home
(而不是/home.html
)显示的就是home组件。 -
比如下图这个案例,切换的就是页面,而不是组件。(下面会有此案例同个页面多个组件的写法)
首先要认识一下react的一个的插件库react-router-dom
(需要手动下载),该库专门用来实现一个SPA应用,基于react的项目基本都会用到它。
前端路由
一、路由的基本使用
历史记录 (前端路由基础)
在我们之前的学习BOM
中,是有一个history方法的,在这里我们通过引入一个history
的js
文件直接调用历史记录的一些方法。浏览器的历史记录是一个栈的结构。
<script src="https://cdn.bootcss.com/history/4.7.2/history.js"></script>
let history = History.createBrowerHistory(); // 直接使用H5推出的history的API
// let history = History.createBrowerHistory(); hash值 锚点跳转(多了个#)
function push(path) {
history.push(path); // 入栈
}
function replace(path) {
history.replace(path); // 替代栈顶元素
}
function back() {
history.goBack(); // 回退
}
function forward() {
history.goForward(); // 前进
}
history.listen(location)=> { // 监听路径变化
console.log(location);
}
案例
接下来实现上面那个案例。在原生js
中是通过<a>标签
跳转到不同的页面,而在react中,是靠路由链接<Link><link/>
实现切换组件的(编写路由链接)。注意:这里的Link
是从react-router-dom
库里面拿的。在组件分别展示的时候需要注册路由route
。
基本步骤:(1)确认界面中的导航区、展示区;(2)导航区的a标签
改为Link标签
;(3)展示区的Route标签进行路径的匹配;(4)在<App>
的最外侧包裹一个<BrowserRouter>
或者<HashRouter>
// App.jsx
import { Link, BrowserRouter, Route } from 'react-router-dom' // 引入react-router-dom库
// 编写路由链接
<Link className="list-group-item" to="/about">About</Link>
<Link className="list-group-item" to="/home">Home</Link>
// 注册路由
<Route path="/about" component={About}></Route>
<Route path="/home" component={Home}></Route>
路由在使用的时候,需要在外层中包裹一层<BrowserRouter><BrowserRouter/>
,相当于一个路由器,管理里面的东西。而且只能有一个路由器,不能写2个,否则就相当于两个路由器,两个路由器之间是没有数据沟通的。因此可以在App
组件渲染的时候,在其外层放路由器。即
// index.js
ReactDOM.render(
<BrowserRouter>
<App />
</BrowserRouter>,
document.getElementById('root')
);
这样子就可以实现一个SPA了。此时可以看到页面路径发生了变化,而且<Link>
标签渲染到浏览器中其实是转换为<a>
标签的,而to
转换为href
(转换为浏览器认识的标签)。但是是组件的展示而不是页面之间的跳转(没有.html
后缀),打开控制台的network
也可以看到此时的浏览器并没有发送请求。
如果想要加上上面那个案例的高亮效果,把Link
改成NavLink
即可(记得从react-router-dom
中引入)。默认点击的时候默认给active
样式,但是如果想要给的样式名不是active
,那就需要加一个activeClassName="..."
(这个的默认值是active)
<NavLink activeClassName="home" className="list-group-item" to="/home">Home</NavLink>
//相当于
<NavLink activeClassName="home" className="list-group-item" to="/home" children="Home"></NavLink>
总结:NavLink
可以实现路由链接的高亮,通过activeClassName
指定样式名,标签体内容是一个特殊的标签属性,通过this.props.children
可以获取到标签体内容。
上面这种组件就是路由组件,那么它和普通组件还有什么区别呢?
-
写法不同。一般组件:直接写组件标签:
<home/>
;路由组件:
<Route path="/about" component={Home}></Route>
-
存放位置不同。一般组件放在
components
;路由组件一般放在pages
中。 -
接收到的
props
不同。一般组件:给它传递什么就收到什么;路由组件:接收到3个固定属性
对于路由组件,我们打印出它的props
属性,这里在用这个组件的时候并没有给它传值,但是它会有自带的props
(history
、location
、match
)其中history.location=location
。而对于普通组件,如果没有给它传值,它打印出来的props
就是空的。
上面说的是路由的基础使用,接下来说一说在路由中是怎么进行组件之间的传参的。
二、向前端路由传递参数
-
传递params参数
在路由组件传递参数的时候,在Link中传递参数,要在注册的路由将这些参数让Detail接收,采用下面这种方式。此时在Detail组件中打印
this.props
的话就可以在this.props.match.params
里面看到我们通过Link传递过去的参数。<Link to={`/home/message/detail/${msgObj.id}/${msgObj.title}`}>{msgObj.title}</Link> <Route path="/home/message/detail/:id/:title" component={Detail}></Route>
-
传递search参数
search参数在传递的时候形式如下。而且search不用在注册路由的时候接收参数,直接注册即可,在
this.props.location.search
中就可以找到传递的参数,不过在这里传递的对象是?key=value&key=value
,我们要手动改成{key: value;}
的形式;可以使用引入内置库
import qs from queryString
,再用qs.parse(search)
来处理从this.props
中得到的search
对象(注意:用parse之后前面还是会有一个?
,用slice(1)
截掉即可<Link to={`/home/message/detail/?id=${msgObj.id}&title=${msgObj.title}`}>{msgObj.title}</Link>
-
state参数
上面说的params参数和search参数在传递参数的时候都会将传递的键值对暴露在地址栏上;state和search一样,不用声明接收,直接注册即可;传递参数方式如下(传对象);
传递之后在
this.props.location.state
里面就可以找到所传递的参数;注意:路由中的state跟组件中的state是不一样的;state参数只是路由中的一个属性,跟组件里面的state的没有关系<Link to={{pathname='/home/message/detail', state:{id:msgObj.id,title:msgObj.title}}}></Link>
用state传递参数的话刷新也可以保留参数。此时虽然参数没有在地址栏,但是也不会丢失,因为在这里用
BrowserRouter
来管理路由的,它一直在维护浏览器的history
,history
把传递的东西记下了,因此不会丢失。
三、补充知识点..
路由的模糊匹配与精确匹配
- 模糊匹配: 【输入的路径】必须包含【匹配的路径】,且顺序要一致。比如把to后面的链接改成
home/a/b
时,在找home的路由的时候,会先匹配前面的home,匹配到了就找到了。这就是模糊匹配,且默认就是默认匹配。 - 严格匹配:可以通过
exact={true}
或exact
来开启严格匹配 一般情况下我们不开启严格匹配,因为有时候开启严格匹配会导致无法继续匹配二级路由
// 编写路由链接 输入的路径
<Link to="/home/a/b">Home</Link>
// 注册路由 匹配的路径
<Route exact={true} path="/home" component={Home}></Route>
Switch
组件和Redirect
组件
提高效率:Switch
组件。如下代码,通过/home
匹配到Home组件之后就不会继续匹配下去了。否则test组件也会被展示在页面中。注意:要从react-router-dom
中引入Switch
。
Redirect
是react-router-dom
里面的一个内置组件,会让刚刷新浏览器的时候,默认跳转到指定组件。(一般写在所有路由注册的最下方,当所有路由都无法匹配时,跳转到Redirect
指定的路由)
<Switch>
<Route path="/about" component={About}></Route>
<Route path="/home" component={Home}></Route>
<Route path="/home" component={test}></Route>
<Redirect to="/home"/>
</Switch>
嵌套组件
- 注册子路由的时候要写上父路由的path值
- 路由的匹配是按照注册路由的顺序进行的
一般情况下我们不会让同一个路径去对应展示多个组件,如果出现这种情况的话就可以使用Switch
。通常情况下,path和component是一一对应的关系,Switch可以提高路由匹配效率(单一匹配)。
加油!