React-router V4.0

React Router更新到4.0,使用起来发生了很大的变化,这个变化只要是指完全的组件化-----嵌套的JSX路由器!接下来,像刚接触路由一样的姿势,一探究竟(简书文字太小,建议大家调整一下页面比例)

使用React脚手架搭建项目结构

create-react-app内在使用的npm,所以速度会很慢,你cnpm的大刀已经饥渴难耐,可以考虑设置npm为cnpm镜像源:

npm config set registry http://registry.npm.taobao.org/
//通过get registy检测是否成功
npm get registry
>http://registry.npm.taobao.org/

生成的项目结构src目录如下

a.png

因为来回切换页面可能会破坏读文的愉悦心情,所以咱们主要在app.js内部大动手脚

app.js基本内容
import React,{Component} from "react";


//创建App根组件
class App extends Component{
    render(){
        return(
            <h1>hello React!</h1>
        )
    }
}

export default App;

控制台输入

npm start

项目已经启动,不接受任何形式的报错

下载react-router
cnpm install react-router-dom --save

在app.js内部引入

import React,{Component} from "react";

//引入路由组件
import { BrowserRouter, Route,Link} from 'react-router-dom';

//创建App根组件
class App extends Component{
    render(){
        return(
            <h1>hello React!</h1>
        )
    }
}

export default App;

接下来我创建函数式组件作为页面级组件

//首页组件
let Index=()=>{
    return(
        <h2>我是首页内容</h2>
    )
}
//列表组件
let List=()=>{
    return(
        <div>
            <h2>我是列表页</h2>
            <ul>
                <li>我是列表元素</li>
                <li>我是列表元素</li>
                <li>我是列表元素</li>
            </ul>
        </div>
    )
}

在App组件内部配置路由组件

class App extends Component{
    render(){
        return(
            <BrowserRouter>
                <div>
                    <h1>hello React!</h1>
                    <Link to="/index">首页</Link>
                    <Link to="/list">列表页</Link>

                    <Route path="/index" component={Index}></Route>
                    <Route path="/list" component={List}></Route>
                </div>
            </BrowserRouter>   
        )
    }
}

注意:

  • BrowserRouter内部只能放一个子元素
  • Route组件类似于判断,路径符合的会被显示
1.如果没有匹配到路径怎么办?

当然是显示空白,但是使用<Redirect>组件
路由引入改为

//引入路由组件
import { BrowserRouter, Route,Link,Redirect} from 'react-router-dom';

App写成

<BrowserRouter>
        <div>
              <h1>hello React!</h1>
              <Link to="/index">首页</Link>
              <Link to="/list">列表页</Link>

              <Route path="/index" component={Index}></Route>
              <Route path="/list" component={List}></Route>
              <Redirect to="/index" />
       </div>
</BrowserRouter>   

这样当匹配不到路径的时候会被重定向到/index

2.我想把Index组件path定义为/可以吗?

当然可以,只不过需要注意一些问题

<BrowserRouter>
        <div>
              <h1>hello React!</h1>
              <Link to="/">首页</Link>
              <Link to="/list">列表页</Link>

              <Route path="/" component={Index}></Route>
              <Route path="/list" component={List}></Route>
       </div>
</BrowserRouter>  

浏览器访问,大家会发现,两个组件同时出现,这是因为路由进行的是非精准匹配

//假设为组件path
let str="/list/hot";
let str2="/list/discount";
//假设为真实访问路径
let path="/list";
//假设为Route组件判断
if(str.indexOf(path+"/")==0){
      alert("str")
}
if(str2.indexOf(path+"/")==0){
      alert("str2")
}
// 真实路径  /             path:/   
// 真实路径  /list         path:/  /list  
// 真实路径  /list/abc     path:/  /list  /list/abc

毫无悬念,会弹出两次,相信也都已经理解了,怎么解决这个问题?

1.exact

给Route组件添加exact进行精准匹配,相当于

path==str1
path==str2

你可能觉得非精准匹配的存在是一个烦恼,其实到嵌套路由时它的作用就凸显出来了

App组件代码

<BrowserRouter>
        <div>
              <h1>hello React!</h1>
              <Link to="/">首页</Link>
              <Link to="/list">列表页</Link>

              <Route path="/" exact component={Index}></Route>
              <Route path="/list" exact component={List}></Route>
       </div>
</BrowserRouter> 
2.Switch

只显示一个,匹配成功之后不再往下查找,但是不是精准查找,实际开发过程中需要加上exact

路由引入改为

//引入路由组件
import { BrowserRouter, Route,Link,Redirect,Switch} from 'react-router-dom';

App写成

<BrowserRouter>
        <div>
                <h1>hello React!</h1>
                <Link to="/">首页</Link>
                <Link to="/list">列表页</Link>
                <Switch>
                        <Route path="/list" component={List}></Route>
                        <Route path="/" component={Index}></Route>
                        <Route path="/list" component={List}></Route>
                        <Route path="/list" component={List}></Route>
                </Switch>
        </div>
</BrowserRouter>   

说完Route组件再来看一下Link,这个组件最终会被解析成a标签,在有些场景下使用会很受限,例如程序执行到某个环节需要路由进行跳转,类似location.href或者说Vue中的router.push。BrowserRouter组件外层包括,将路由信息注入到每个路由组件的props上。接下来将Link换成button

注意:因为App组件不是路由组件,所以该组件内部的props访问不到路由信息

对组件嵌套结构进行改造:

//创建Content组件
class Content extends Component{
    constructor(props){
        super(props);
        console.log(props);
    }
    render(){
        return(
            <div>
                <h1>hello React!</h1>
                <button onClick={()=>{this.props.history.push("/")}}>Index</button>
                <button onClick={()=>{this.props.history.push("/list")}}>List</button>
                <Switch>
                    <Route path="/" exact component={Index}></Route>
                    <Route path="/list" component={List}></Route>
                </Switch>
            </div>
        )
    }
}

//创建App根组件
class App extends Component{
    render(){
        return(
            <BrowserRouter>
                <Route path="/" component={Content}></Route>   
            </BrowserRouter> 
        )
    }
}
withRouter

如上代码,用Route组件包括Content组件将路由信息注入组件。组件内部需要访问路由对象时这样做就太过麻烦了,默认情况下,只有经过路由匹配渲染的组件才能访问路由对象下的属性和方法,然而不是所有组件都与路由相连,我们可以使用withRouter将路由对象注入到组件

首先引入withRouter

//引入路由组件
import { BrowserRouter, Route,Link,Redirect,Switch,withRouter} from 'react-router-dom';

上述代码修改如下

//创建Content组件
class Content extends Component{
    constructor(props){
        super(props);
        console.log(props);
    }
    render(){
        return(
            <div>
                <h1>hello React!</h1>
                <button onClick={()=>{this.props.history.push("/")}}>Index</button>
                <button onClick={()=>{this.props.history.push("/list")}}>List</button>
                <Switch>
                    <Route path="/" exact component={Index}></Route>
                    <Route path="/list" component={List}></Route>
                </Switch>
            </div>
        )
    }
}
//将路由对象注入到组件
Content=withRouter(Content)

//创建App根组件
class App extends Component{
    render(){
        return(
            <BrowserRouter>
                <Content/>//这里直接使用组件即可
            </BrowserRouter> 
        )
    }
}
Link or NavLink

在4.0版本中,这两个组件都可以解析为a链接进行页面跳转,工作方式相同,但是NavLink可以根据路由匹配提供一些样式功能,代码如下

<BrowserRouter>
        <div>
                <h1>hello React!</h1>
                <NavLink to="/" exact activeClassName="active">Index</NavLink>
                <NavLink to="/list" activeClassName="active">List</NavLink>
    
                <Switch>
                        <Route path="/list" component={List}></Route>
                        <Route path="/" component={Index}></Route>
                        <Route path="/list" component={List}></Route>
                        <Route path="/list" component={List}></Route>
                </Switch>
        </div>
</BrowserRouter>   

当匹配路径为/时对应a链接会有active类名,生效对应样式

Route

Route组件通过上面的使用,应该不陌生了,匹配路径显示对应组件,基础用法存在一些局限性:

1.当需要往路由组件额外传一些props

这种写法在List组件内部是接收不到age

<Route path="/list" component={List} age="20"></Route>

需要借助render属性

<Route path="/list" render={(props)=>{
        return <List age="20"/>
}}></Route>

render指向一个函数式组件将List组件返回,这样就可以在List组件上随意定义props,此种情况下List不再属于路由组件,所以访问不到路由相关对象,但是箭头函数props中包含路由相关信息,所以

<Route path="/list" render={(props)=>{
        return <List {...props} age="20"/>
}}></Route>

一切照常进行

2.当在匹配跳转之前进行一些拦截时(如登录验证)
<Route path="/list" render={(props)=>{
        if(this.state.login){//根据登录状态决定如何响应
                return (<List {...props} age="20"></List>)    
        }else{
                return(<Redirect to="/login">)
        }  
}}></Route>

加油!

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,271评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,275评论 2 380
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,151评论 0 336
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,550评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,553评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,559评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,924评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,580评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,826评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,578评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,661评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,363评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,940评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,926评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,156评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,872评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,391评论 2 342

推荐阅读更多精彩内容