一文带你学懂React前端路由😃

2,528 阅读7分钟

这是我参与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组件。

  • 比如下图这个案例,切换的就是页面,而不是组件。(下面会有此案例同个页面多个组件的写法)

    luyou_03.gif

首先要认识一下react的一个的插件库react-router-dom(需要手动下载),该库专门用来实现一个SPA应用,基于react的项目基本都会用到它。

前端路由

一、路由的基本使用

历史记录 (前端路由基础)

在我们之前的学习BOM中,是有一个history方法的,在这里我们通过引入一个historyjs文件直接调用历史记录的一些方法。浏览器的历史记录是一个栈的结构。

<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也可以看到此时的浏览器并没有发送请求。

luyou2_03.gif

如果想要加上上面那个案例的高亮效果,把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属性,这里在用这个组件的时候并没有给它传值,但是它会有自带的propshistorylocationmatch)其中history.location=location。而对于普通组件,如果没有给它传值,它打印出来的props就是空的。

props.png 上面说的是路由的基础使用,接下来说一说在路由中是怎么进行组件之间的传参的。

二、向前端路由传递参数

  1. 传递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>
    
  2. 传递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>
    
  3. 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来管理路由的,它一直在维护浏览器的historyhistory把传递的东西记下了,因此不会丢失。

三、补充知识点..

路由的模糊匹配与精确匹配

  • 模糊匹配: 【输入的路径】必须包含【匹配的路径】,且顺序要一致。比如把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中引入SwitchRedirectreact-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可以提高路由匹配效率(单一匹配)。

加油!