Skip to content

深入路由

什么是路由?

路由维护了URL地址和组件的映射关系,通过这个映射关系,我们就可以根据不同的URL地址,去渲染不同的组件

使用

安装

shell script

npm i react-router-dom

初体验路由

jsx

import React from "react";
import "./App.css"
import { BrowserRouter,HashRouter,Link,Route} from 'react-router-dom'
import Home from "./components/Home";
import About from "./components/About";
class App extends React.PureComponent{
    constructor(props) {
        super(props);

    }

    render() {
        return(
            <div>
                {/*
                BrowserRouter和HashRouter
                指定路由的监听模式 history模式 / hash模式
                http://www.28yanyu.cn/
                http://www.28yanyu.cn/home history模式
                http://www.28yanyu.cn/#/home hash模式
                */}
                {/*<button onClick={()=>{this.btnClick(true)}}>Home</button>*/}
                {/*<button onClick={()=>{this.btnClick(false)}}>About</button>*/}
                <BrowserRouter>
                {/*
                link的作用:
                用于修改URL的资源地址
                */}
                    <Link to={'/home'}>Home</Link>
                    <Link to={'/about'}>About</Link>
                {/*
                Router作用:
                用于维护URL和组件的关系
                Router是一个占用组件,将来他会根据匹配到的资源地址渲染对应的组件
                */}
                <Route path={'/home'} component={Home} />
                <Route path={'/about'} component={About} />
                </BrowserRouter>
            </div>
        )
    }

    btnClick(flag){

    }
}

export default App

注意点:

  • BrowserRouter history模式使用的是H5的特性,所以兼容性会比HashRouter hash模式差一些 如果需要兼容低级版本浏览器,那么只能使用HashRouter

  • 无论是Link还是Route都只能方法到BrowserRouter或HashRouter中才能生效

  • Route在匹配路由的时候,是利用当前资源地址从左至右的path中的地址进行匹配, 只要当前资源地址从左至右完整的包含了path中的地址那么就认为匹配

    例如:

    • 当前资源地址:/home/about
    • path中的地址:/home
    • path中的地址:/home/about

    两个组件都会显示,如何解决这个问题:在Route组件中加入如下的属性exact精准匹配即可

  • NavLink注意点: 在NavLink中有一个属性 activeStyle属性,激活状态下会变色,但是其也和Route一样,是模糊匹配,如果定义了包含的路由地址, 就会设置为激活颜色,如何解决这个问题,同上加上一个exact,无论是NavLink还是Link,渲染出来的本质都是A标签

Switch

在使用Route组件的时候不指定path,那么表示这个Route和所有的资源地址都匹配, 并会找到所有相关的组件地址,并显示出来。

如何只让当前的地址匹配到对应的地址之后就不再继续往下进行匹配呢?

使用一个组件为Switch,来实现

Redirect

资源重定向,也就是可以在访问某个资源地址的时候重定向到另外一个资源地址

例如:访问/user 重定向到 /login

jsx

import React from "react";

import { Redirect } from "react-router-dom"

class User extends React.PureComponent{
    constructor(props) {
        super(props);
        this.state = {
            isLogin:false
        }
    }

    render() {
        let user = (
            <div>
                <h1>用户界面</h1>
                <p>测试用户</p>
                <p>三生三世</p>
            </div>
        )
        let login = <Redirect to={'/login'}/>
        return this.state.isLogin?user:login
    }
}

export default User

嵌套路由

路由里面又有路由,称之为嵌套路由

一级路由

jsx

import React from "react";
import "./App.css"
import { BrowserRouter,HashRouter,Link,Route,NavLink,Switch} from 'react-router-dom'
import Home from "./components/Home";
import About from "./components/About";
import Other from  './components/Other'
import User from "./components/User";
import Login from "./components/Login";
import Discover from "./components/Discover";
class App extends React.PureComponent{
    constructor(props) {
        super(props);

    }

    render() {
        return(
            <div>
                <BrowserRouter>

                    <NavLink to={'/home'} activeStyle={{color:'red'}}>Home</NavLink>
                    <NavLink to={'/about'} activeStyle={{color:'red'}}>About</NavLink>
                    <NavLink to={'/discover'} activeStyle={{color:'red'}}>广场</NavLink>
                <Switch>
                    <Route exact path={'/home'} component={Home} />
                    <Route exact path={'/about'} component={About} />
                    <Route exact path={'/user'} component={User} />
                    <Route exact path={'/login'} component={Login} />
                    {/*
                    如果要使用嵌套路由,外层的路由不能添加精准匹配
                    */}
                    <Route path={'/discover'} component={Discover} />
                    <Route component={Other} />
                </Switch>
                </BrowserRouter>
            </div>
        )
    }

    btnClick(flag){

    }
}

export default App

注意事项

如果要使用嵌套路由,外层的路由不能添加精准匹配,否则会找不到路由地址

二级路由界面

jsx

import React from "react";
import { NavLink, Route, Switch} from "react-router-dom";
import Other from "./Other";

function Hot(){
    return(
        <div>
            推荐
        </div>
    )
}
function TopList(){
    return(
        <div>
            排行榜
        </div>
    )
}function PlayList(){
    return(
        <div>
            歌单
        </div>
    )
}

class Discover extends React.PureComponent{
    render() {
        return(
            /**
             * 当前的组件是在BrowserRouter or HashRouter中显示的,所以在当前的组件中不需要使用期包裹
             */
            <div>
                <NavLink exact to={'/discover'} activeStyle={{color:'red'}}>推荐</NavLink>
                <NavLink exact to={'/discover/toplist'} activeStyle={{color:'red'}}>排行榜</NavLink>
                <NavLink exact to={'/discover/palylist'} activeStyle={{color:'red'}}>歌单</NavLink>
                <Switch>
                    <Route exact path={'/discover'} component={Hot} />
                    <Route exact path={'/discover/toplist'} component={TopList} />
                    <Route exact path={'/discover/palylist'} component={PlayList} />
                    <Route component={Other} />
                </Switch>
            </div>
        )
    }
}

export default Discover

手动路由跳转

不通过NavLink或者Link来设置资源地址,而是通过JS来设置资源地址

实现history和hash模式的路由跳转

jsx

import React from "react";
import { NavLink, Route, Switch} from "react-router-dom";
import Other from "./Other";

function Hot(){
    return(
        <div>
            推荐
        </div>
    )
}
function TopList(){
    return(
        <div>
            排行榜
        </div>
    )
}function PlayList(){
    return(
        <div>
            歌单
        </div>
    )
}

class Discover extends React.PureComponent{
    render() {
        return(
            /**
             * 当前的组件是在BrowserRouter or HashRouter中显示的,所以在当前的组件中不需要使用期包裹
             */
            <div>
                <NavLink exact to={'/discover'} activeStyle={{color:'red'}}>推荐</NavLink>
                <NavLink exact to={'/discover/toplist'} activeStyle={{color:'red'}}>排行榜</NavLink>
                <NavLink exact to={'/discover/palylist'} activeStyle={{color:'red'}}>歌单</NavLink>
                <button onClick={()=>{this.btnClick()}}>歌单</button>
                <Switch>
                    <Route exact path={'/discover'} component={Hot} />
                    <Route exact path={'/discover/toplist'} component={TopList} />
                    <Route exact path={'/discover/palylist'} component={PlayList} />
                    <Route component={Other} />
                </Switch>
            </div>
        )
    }

    btnClick(){
        // 如果是Hash模式,直接设置hash值就可以
        // window.location.hash = "/discover/palylist"
        // 如果一个组件是通过路由创建出来的,那么系统会自动传递一个history给我们
        // 如果我们只需要拿到这个history对象,调用这个对象的push方法,通过push方法修改资源地址即可
        // console.log(this.props.history);
        this.props.history.push('/discover/palylist')
    }
}

export default Discover

::: waring 注意点

如果一个组件中并不是通过路由创建的组件,而是手动创建的组件,所以默认情况下是不能被传递this.props.history

如果想在非路由创建出来的组件中使用history对象,那么需要借助withRouter高阶组件,只要把一个组件传递给withRouter方法, 那么这个方法就会通过路由将传入的组件创建出来

如果一个组件要使用路由创建,那么这个组件必须被BrowserRouter或HashRouter包裹起来,所以在最跟节点需要将其包裹

index.js

jsx

import ReactDom from "react-dom"
import React from "react";
import App from "./App";
import { BrowserRouter } from 'react-router-dom'
ReactDom.render(
    <BrowserRouter>
        <React.StrictMode>
            <App/>
        </React.StrictMode>
    </BrowserRouter>
    ,document.getElementById("root"));

:::

路由参数

传统URL参数

jsx

<NavLink to={'/home?name=test&age=19'} activeStyle={{color:'red'}}>Home</NavLink>

会自动将参数存放到this.props.location当中,格式为

{pathname: "/home", search: "?name=test&age=19", hash: "", state: null, key: "uqy11h"}

存在的问题:需要手动去提取数据

动态路由

通过路由定义参数,再通过地址传递参数的方式

jsx

<Route exact path={'/about/:name/:age'} component={About} />
<NavLink to={'/about/test/19'} activeStyle={{color:'red'}}>About</NavLink>

获取数据的方式为this.props.match

{"path":"/about/:name/:age","url":"/about/test/19","isExact":true,"params":{"name":"test","age":"19"}}

存在问题:参数比较多的情况下不适合使用

路由传参

Link或NavLink中的to可以传一个对象

jsx

let obj = {
    name:'test',
    age:19
}

<NavLink to={{
    pathname:'/discover',
    search:'',
    hash:'',
    state:obj
}} activeStyle={{color:'red'}}>广场</NavLink>

通过this.props.location获取数据

{"pathname":"/discover","search":"","hash":"","state":{"name":"test","age":19},"key":"jfjpzs"}

集中式管理

安装

shell script

npm i react-router-config

使用

在src目录下创建一个router文件夹,然后再创建一个index.js文件

index.js

js

import Home from "../components/Home";
import Discover from "../components/Discover";
import Other from "../components/Other";
import About from "../components/About";
const routers = [
    {
        path:"/home",
        exact:true,
        component:Home
    },
    {
        path:"/about",
        exact:true,
        component:About
    },
    {
        path: '/discover',
        component:Discover
    },
    {
        component:Other
    }
]

export default routers;

App.js

jsx

import React from "react";
import "./App.css"
import { BrowserRouter,withRouter,Link,Route,NavLink,Switch} from 'react-router-dom'
import {renderRoutes} from 'react-router-config'
import routers from "./router";
class App extends React.PureComponent{
    constructor(props) {
        super(props);

    }

    render() {
        let obj = {
            name:'test',
            age:19
        }
        return(
            <div>
                <BrowserRouter>
                    <NavLink to={'/home?name=test&age=19'} activeStyle={{color:'red'}}>Home</NavLink>
                    <NavLink to={'/about/test/19'} activeStyle={{color:'red'}}>About</NavLink>
                    <NavLink to={{
                        pathname:'/discover',
                        search:'',
                        hash:'',
                        state:obj
                    }} activeStyle={{color:'red'}}>广场</NavLink>
                    {renderRoutes(routers)}
                </BrowserRouter>
            </div>
        )
    }

    btnClick(flag){

    }
}

export default withRouter(App)

出现二级或者三级路由的时候如何渲染

jsx

import Home from "../components/Home";
import Discover from "../components/Discover";
import Other from "../components/Other";
import About from "../components/About";
import {Hot,TopList,PlayList} from "../components/Discover"
const routers = [
    {
        path:"/home",
        exact:true,
        component:Home
    },
    {
        path:"/about",
        exact:true,
        component:About
    },
    {
        path: '/discover',
        component:Discover,
        routes:[
            {
                path:"/discover",
                exact:true,
                component:Hot
            },
            {
                path:"/discover/toplist",
                exact:true,
                component:TopList
            },
            {
                path:"/discover/playlist",
                exact:true,
                component:PlayList
            }
        ]
    },
    {
        component:Other
    }
]

export default routers;

Discover.js

jsx

import React from "react";
import { NavLink, Route, Switch} from "react-router-dom";
import { renderRoutes } from "react-router-config"

export function Hot(){
    return(
        <div>
            推荐
        </div>
    )
}
export function TopList(){
    return(
        <div>
            排行榜
        </div>
    )
}
export function PlayList(){
    return(
        <div>
            歌单
        </div>
    )
}

class Discover extends React.PureComponent{
    constructor(props) {
        super(props);
        console.log(JSON.stringify( this.props.location));
    }


    render() {
        return(
            <div>
                <NavLink exact to={'/discover'} activeStyle={{color:'red'}}>推荐</NavLink>
                <NavLink exact to={'/discover/toplist'} activeStyle={{color:'red'}}>排行榜</NavLink>
                <NavLink exact to={'/discover/playlist'} activeStyle={{color:'red'}}>歌单</NavLink>
                <button onClick={()=>{this.btnClick()}}>歌单</button>
                {/*如果父组件是通过renderRoutes创建的路由,就可以通过this.props.route.routes拿到数据*/}
                {renderRoutes(this.props.route.routes)}
            </div>
        )
    }
}

export default Discover

Released under the MIT License.