美文网首页
React-router V4.0

React-router V4.0

作者: Veb | 来源:发表于2018-12-26 15:36 被阅读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>

加油!

相关文章

网友评论

      本文标题:React-router V4.0

      本文链接:https://www.haomeiwen.com/subject/bxehlqtx.html