天天看点

React路由 路由传参 路由上下文 组件跳转

官方网站

一、简介

路由最基本的职责就是当页面的访问地址与Route上的 path匹配时,就渲染出对应的 UI 界面(组件)。 实现SPA应用,页面切换不会刷新页面,内容局部更新。同时路由也可以带货(传参)。

二、react-router提供了两种路由模块

1.React-Router

提供了一些router的核心API,包括Router, Route, Switch等,但是它没有提供 DOM 操作进行跳转的API。

2.React-Router-DOM*

提供了 BrowserRouter,HashRouter , Route, Link等 API,我们可以通过 DOM 的事件控制路由,例如点击一个按钮进行跳转。

所以在开发过程中,我们更多是使用React-Router-DOM。

三、路由的两种模式

1.HashRouter

url中会有个#,例如localhost:3000/#,HashRouter就会出现这种情况,它是通过hash值来对路由进行控制。如果使用HashRouter,地址栏就会默认有这个#。

2.BrowserRouter*

如果不需要这个#,需要优雅的跳转,这时就使用BrowserRouter。

在开发过程中整个SPA项目中,只能存在一个BrowserRouter或者HashRouter标签,否则React在进行路由时就不能正常渲染,需要刷新才能渲染。(这不就与SPA背道而驰…)

四、路由跳转

1.声明式导航

a.Link组件

//1.直接在to属性后写要去的地址
<Link to="/About">去About页面</Link>
<Link to={pathname:'/About'}>去About页面</Link>
           

Link组件主要属性是to,to可以接受string或者一个object(如果是object要写成对象的形式,键名为pathname),来控制url。

b.NavLink组件

<NavLink to="/About" activeClassName="red">去About</NavLink>
<NavLink to={pathname:'/about'} activeClassName="red">去About</NavLink>
           

NavLink组件可以为当前选中的路由设置激活时的类名(activeClassName)、样式(activeStyle);以及回调函数等。

2.编程式导航

如果页面不是跳转过来的,或者不是有状态组件,直接使用编程式导航会存在不能跳转的问题。

五、路由的配置

1.Route组件

Route组件用来表示路径和组件的对应关系,当访问地址与Route中的path对应时,就在它的位置渲染出对应的组件。

<BrowserRouter>
    //此处BrowserRouter只是例子,实际项目中直接把它放到根文件中
   <Route path="/" component={App}/>  
   <Route path="/About" component={About}/>  
</BrowserRouter>
           

在SPA项目中,只能存在一个BrowserRouter标签,因此一般这个BrowserRouter标签或HashRouter会写在根文件即index.js下包裹住所有组件。

六、基本使用步骤

1.下载路由模块

npm install --save react-router-dom
           

2.在跳转处设置

a.声明式

//开始先引入模块
import { Link, NavLink } from "react-router-dom";

//有状态组件的render函数中,或无状态组件的return中设置Link或NavLink组件
<Link to="/User">User组件</Link>
<NavLink to="/Goods">Goods组件</NavLink>
//这里to属性的值是自定义的,只要与显示处Route的to属性值匹配即可
           

如果把link标签写成a标签,那么页面会有闪动,因为浏览器会识别为这是一次请求,刷新页面。而l点击ink标签时,url会更新,组件也会被重新渲染,但页面不会刷新。

b.编程式

import React from "react";

export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    go() {
        this.props.history.push({ pathname: '/Goods' })
    }

    render() {
        return (
            <div>
                这里是User组件
                <input type="button" value="走你,去Goods" onClick={() => this.go()} />
            </div>
        );
    }
}
           

3.在展示处设置

//根文件 index.js
//因为整个项目中只能使用一个BrowserRouter或HashRouter组件,因此直接把它放在根组件之中
import { BrowserRouter } from "react-router-dom";
ReactDOM.render(
  <React.StrictMode>
  <BrowserRouter>
    <App />
  </BrowserRouter>
  </React.StrictMode>,
  document.getElementById('root')
);
           
//开始先引入模块
import { BrowserRouter, Route } from "react-router-dom";

//有状态组件的render函数中,或无状态组件的return中设置BrowserRouter或HashRouter组件
    <Route path="/" component={App}/>  
    <Route path="/Goods" component={Goods}/>  
    <Route path="/User" component={User}/>  
           

七、二级路由

直接在父组件里写即可,子组件不用做任何更改

import React from "react";
import { Route, Link } from "react-router-dom";
import User_son1 from "../components/User_son1"
import User_son2 from "../components/User_son2"


export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    render() {
        return (
            <div>
                这里是User组件<br />
                {/* Link组件的to属性一定带上父一级路由的路径即可 */}
                <Link to="/User/User_son1">显示User_son1</Link> | <Link to="/User/User_son2">显示User_son2</Link>
                <hr />
                {/* 显示子组件时,也要加上父组件的路径 */}
                <Route path="/User/User_son1" component={User_son1}></Route>
                <Route path="/User/User_son2" component={User_son2}></Route>
            </div>
        );
    }
}
           

八、路由传参

1.params

传出值的组件

import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"

export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    goparams() {
        this.props.history.push('/User/UserDetil/' + '003')
    }

    render() {
        return (
            <div>
                这里是User组件,路由跳转params带货<br />
                {/* 点击的位置,此处配置路由跳转,传值 */}
                <Link to="/User/UserDetil/001">显示User1的详情</Link> |
                <NavLink to={"/User/UserDetil/002"}>显示User2的详情</NavLink> |
                <input type="button" value="显示User3的详情" onClick={() => this.goparams()} />
                <hr />
                {/* 展示的位置,此处配置路由传值的变量名 */}
                <Route path="/User/UserDetil/:userid" component={UserDetil}></Route>
            </div>
        );
    }
}
           

接收值的组件

import React from "react";

export default class UserDetil extends React.Component {
    constructor(props) {
        super(props)
    }

    render() {
        return (
            <div>
                这里是UserDetil组件<br />
                {/* 获取值通过this.props.match.params.变量名 获取,函数式组件没有this,在定义函数的形参中声明props即可获取props */}
                <p>用户id{this.props.match.params.userid}</p>
            </div>
        );
    }
}
           

优点:刷新页面,参数依然存在;很好理解…地址栏的地址并没有变。

缺点:只能传字符串,而且如果传的值太多的话,url会变得很长。

2.query

传出的组件

import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"

export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    goquery() {
        this.props.history.push({ pathname: "/User/UserDetil", query: { userid: "01003" } })
    }

    render() {
        return (
            <div>
                这里是User组件,路由跳转query带货<br />
                {/* 点击的位置,此处配置就必须写成对象,跳转地址pathname,传值query{变量名:变量值} */}
                {/* 注:两层{{}},第一层{}表示js代码,第二层{}表示这东西是个对象 */}
                <Link to={{ pathname: "/User/UserDetil", query: { userid: "01001" } }}>显示User1的详情</Link> |
                <NavLink to={{ pathname: "/User/UserDetil", query: { userid: "01002" } }}>显示User2的详情</NavLink> |

                <input type="button" value="显示User3的详情" onClick={() => this.goquery()} />
                <hr />
                {/* 展示的位置,此处路由配置不用变 */}
                <Route path="/User/UserDetil" component={UserDetil}></Route>
            </div>
        );
    }
}
           

传入的组件

import React from "react";

export default class UserDetil extends React.Component {
    constructor(props) {
        super(props)
    }

    render() {
        return (
            <div>
                这里是UserDetil组件,接收query带货<br />
                <p>用户id{this.props.location.query.userid}</p>
            </div>
        );
    }
}
           

优点:传参优雅,地址栏不会添加任何东西;传递参数可以传对象。

缺点:一旦页面刷新,参数就会丢失。

3.state*

传出的组件

import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"

export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    gostate() {
        this.props.history.push({ pathname: "/User/UserDetil", state: { userid: "01003" } })
    }

    render() {
        return (
            <div>
                这里是User组件,路由跳转state带货<br />
                {/* 点击的位置,此处配置就必须写成对象,跳转地址pathname,传值query{变量名:变量值} */}
                {/* 注:两层{{}},第一层{}表示js代码,第二层{}表示这东西是个对象 */}
                <Link to={{ pathname: "/User/UserDetil", state: { userid: "01001" } }}>显示User1的详情</Link> |
                <NavLink to={{ pathname: "/User/UserDetil", state: { userid: "01002" } }}>显示User2的详情</NavLink> |
                <input type="button" value="显示User3的详情" onClick={() => this.gostate()} />
                <hr />
                {/* 展示的位置,此处路由配置不用变 */}
                <Route path="/User/UserDetil" component={UserDetil}></Route>
            </div>
        );
    }
}
           

传入的组件

import React from "react";

export default class UserDetil extends React.Component {
    constructor(props) {
        super(props)
    }

    render() {
        return (
            <div>
                这里是UserDetil组件,接收state带货<br />
                <p>用户id{this.props.location.state.userid}</p>
            </div>
        );
    }
}
           

同query差不多,只是属性不一样,而且state传的参数是加密的,query传的参数是公开的,与query相比,只需要把query改为state即可。

优点:传参优雅,地址栏不会添加任何东西;传递参数可以传对象。

缺点:页面刷新,HashRoute方式会丢失参数,BrowserRouter模式不会丢失参数。

4.seach

传出的组件

import React from "react";
import { Route, Link, NavLink } from "react-router-dom";
import UserDetil from "../components/UserDetil"

export default class User extends React.Component {
    constructor(props) {
        super(props)
    }

    gosearch() {
        this.props.history.push({pathname:'/Inbox',search:'?a=1&b=2'})
    }
    
    render() {
        return (
            <div>
                <Link to='/Inbox?a=1&b=2' >显示User1的详情</Link> |
                <NavLink to='/Inbox?a=1&b=2' >显示User2的详情</NavLink> |
                <input type="button" value="显示User3的详情" onClick={() => this.gosearch()} />
                <hr />
                {/* 展示的位置,此处路由配置不用变 */}
                <Route path="/User/UserDetil" component={UserDetil}></Route>
            </div>
        );
    }
}
           

取值

this.props.location.search
           

用location.search所获取的是查询字符串,即获取到的是传来的一长串字符串(例:?a=1&b=2)所以,需要使用search方法时需要自行解析字符串,也可以使用第三方模块:qs,或者nodejs里的query-string。

九、路由上下文

在react-router里面,如果组件是通过路由跳转的,那么它会把路由相关的API挂在了组件的props上,并且分为history,location,match。

history:历史,用来跳转的,并做历史记录。有函数:push(),replace(),go() …………

location:地址,地址栏上路径,并保存着query,state,search等数据。

match:匹配,匹配到的路径,有params

//使用state的跳转方式打印一下props
import React from "react";

export default class UserDetil extends React.Component {
    constructor(props) {
        super(props)
    }
    componentDidMount() {
        console.log(this.props);
    }

    render() {
        return (
            <div>
                这里是UserDetil组件,接收state带货<br />
                <p>用户id{this.props.location.state.userid}</p>
            </div>
        );
    }
}
           
//打印结果
props: {
    { history: {… }, location: {… }, match: {… }, staticContext: undefined }
    
    history:
    action: "PUSH"
    block: ƒ block(prompt)
    createHref: ƒ createHref(location)
    go: ƒ go(n)
    goBack: ƒ goBack()
    goForward: ƒ goForward()
    length: 50
    listen: ƒ listen(listener)

    location: { pathname: "/User/UserDetil", state: {… }, search: "", hash: "", key: "h00797" }
    push: ƒ push(path, state)
    replace: ƒ replace(path, state)
    __proto__: Object
    location:
    hash: ""
    key: "h00797"
    pathname: "/User/UserDetil"

    search: ""
    state: { userid: "01001" }
    __proto__: Object

    match:
    isExact: true
    params: { }
    path: "/User/UserDetil"
    url: "/User/UserDetil"
    __proto__: Object
    staticContext: undefined
    __proto__: Object
}
//确实history,location,match三个属性被挂到了props上;示例中是用stste跳转的,所以只有location中有信息。
           

十、非路由跳转的组件获取上下文

在页面上展示组件有两种方式,一种是通过路由跳转,一种是使用标签名直接展示。

a)使用路由跳转的方式:就算这个组件没有属性,通过路由时,props都会自动增加属性:history,match,location。

b)使用标签名直接展示时:因为没有属性,所以组件的props是空。

如果使用标签名展示组件的方式,还想获取到路由上下文,有以下解决方案:

1.通过属性传递

首先要求,当前父组件是路由跳转过来的(起码得保证他爸有,儿子才具有世袭的可能性),然后把路由上下文通过属性的方式传递给子组件。

2.通过withRouter包装(高阶组件)

import {withRouter} from 'react-router-dom'

class 组件 extends Component{
    
}

export default withRouter(组件)
           

withRouter是react-router-dom提供的一个高阶函数,引入解构后就可以使用,通过这个高阶函数,就可以给组件添加上下文,导出时通过withRouter这个高阶函数即可。

使用时正常使用组件,不受任何影响。

十一、Route组件里的各种属性

首先说明:react的路由匹配默认是模糊的,包容的。

1.exact严格匹配

{/* 如果没有exact={true}这个属性,页面会展示两个Goods组件,因为"/Goods"路径也符合"/" */}
    <Route exact={true} path="/" component={Goods} />
    <Route path="/Goods" component={Goods} />
    <Route path="/User" component={User} />
           

exact={true} 表示地址栏的路径和提供的地址必须相等,一模一样,才会被匹配。

2.Switch排他性匹配

<Switch>
    {/* 有Switch之后如果没有exact={true}这个属性,那么所有的路径都指向/,并且不能匹配后面的 */}
    <Route exact={true} path="/" component={Goods} />
    <Route path="/Goods" component={Goods} />
    <Route path="/User" component={User} />
</Switch>
           

React路由匹配到的多个路由对应的所有组件会同时被显示在页面上。如果只希望显示匹配到的第一个组件(换句话说:匹配到的第一个符合要求的路径后,其它路径就不再做匹配),那么使用switch。使用时,Switch组件包裹住所有Route组件。

3.404

在路由配置里不设置path属性,那么,就总是会匹配上。404页面就需要这样做(当然还得结合Switch)

4.Redirect重定向

如果匹配到from中的路径,那就给你重定向到to的路径去。使用时一定要加exact={true},否则轻易一匹配,所有路径都会被重定向。

详解

1.组件及其作用

组件 作用
路由模式 BrowserRouter 约定模式 为 history,使用 HTML5 提供的 history API 来保持 UI 和 URL 的同步
路由模式 HashRouter 约定模式 为 hash,使用 URL 的 hash 来保持 UI 和URL 的同步
声明式跳转 NavLink 声明式跳转 还可以约定 路由激活状态
声明式跳转 Link 声明式跳转 无激活状态
重定向 Redirect 重定向 ~~ replace
匹配并展示 Route 匹配组件,并展示组件。即匹配成功后,组件立即被替换成匹配的组件
排他性匹配 Switch 排他性匹配。如果不想使用包容性,那么使用Switch。
高阶组件 withRouter 把不是通过路由切换过来的组件中,将 history、location、match 三个对象传入props对象上(高阶组件)

2.结构

BrowserRouter|HashRouter

App(或其它组件)

  • NavLink|Link
  • Route
  • Redirect
    • 子组件
      • NavLink|Link
      • Route

3.BrowserRouter

属性 类型 作用
basename string 所有位置的基本URL。如果您的应用是从服务器上的子目录提供的,则需要将其设置为子目录。格式正确的基本名称应以斜杠开头,但不能以斜杠结尾
getUserConfirmation Function 用于确认导航的功能。默认使用

window.confirm

4.Route

属性 类型 作用
path string |object 路由匹配路径。没有path属性的Route 总是会 匹配
exact boolean 为true时,要求全路径匹配(/home)。路由默认为“包含”的(/和/home都匹配),这意味着多个 Route 可以同时进行匹配和渲染
component Function |component 在地址匹配的时候React的组件才会被渲染,route props也会随着一起被渲染
render Function 内联渲染和包装组件,要求要返回目标组件的调用

5.Link

属性 类型 作用
to string | 对象{pathname:,search:,hash:} 要跳转的路径或地址
replace boolean 是否替换历史记录

6.NavLink

属性 类型 作用
to string|对象{pathname:,search:,hash:} 要跳转的路径或地址
replace boolean 是否替换历史记录
activeClassName string 当元素被选中时,设置选中样式,默认值为 active
activeStyle object 当元素被选中时,设置选中样式

7.Switch

该组件用来渲染匹配地址的第一个Route或者Redirect,仅渲染一个路由,排他性路由,默认全匹配(场景:侧边栏,引导选项卡等)

属性 类型 作用
location string object
children node

8.Redirect

该组件用来渲染匹配地址的第一个Route或者Redirect,仅渲染一个路由,排他性路由,默认全匹配(场景:侧边栏和面包屑,引导选项卡等

属性 类型 作用
from string 来自
to string object 去向
push boolean 添加历史记录
exact boolean 严格匹配
sensitive boolean 区分大小写
能看到这里同样辛苦的是你。。。也不枉我24k纯人手。

继续阅读