React Router 4.x 开发,这些雷区咱们都帮你踩过了

前言

在前端框架层出不穷的今天,React 以其虚拟 DOM 、组件化开发思想等特性迅速占据了主流位置,成为前端开发工程师热衷的 Javascript 库。做为 React 体系中的重要组成部分:React Router 也成为开发者首选的路由库,其主要功能是经过管理 url 实现组件的切换和状态的变化。javascript

正文

在 React Router 4.x 发布以前,咱们在项目中使用的是 React Router 3.x。随着第四版 React Router 的正式亮相,其精简的 API 、语义化的路由匹配方案以及动态路由等变化,都彰显着这次升级的颠覆性。因此,咱们决定在新的 React 项目中使用 4.x(React Router 4.x)开发,亲身实践以后发现: 4.x 不只是 API 的简单改变,还有整个设计理念的变化;初次使用确实有一些别扭,更可怕的是按照 API 文档写出的代码,有时运行时会出现一些莫名其妙的红色错误(此刻的心情 down 到谷底~)。为了不你们在使用时遇到一样的问题多绕弯子,咱们把开发过程当中遇到的问题以及相应的解决方法作了汇总。css

4.x 使用问题汇总

1. 包引用方式

在以前的 3.x 中,在入口文件中定义路由时,咱们会这么写:html

import React from 'react';
import ReactDOM from 'react-dom';
import {Router, Route, browserHistory} from 'react-router';
import * as routePaths from './js/constants/routePaths';
import Index from './js/pages/Index';

ReactDOM.render((
    <Router history={history}>
        <div className="container">
            <Route path={routePaths.INDEX} component={Index} />
        </div>
    </Router>
    ),
    document.getElementById('app')
);

可是保存后运行发现页面报错了,以下图所示:前端

解决方法:java

import React from 'react';
import ReactDOM from 'react-dom';
import { BrowserRouter, Route } from 'react-router-dom';
import * as routePaths from './js/constants/routePaths';
import Index from './js/pages/Index';

ReactDOM.render((
    <BrowserRouter>
        <div className="container">
            <Route path={routePaths.INDEX} component={Index} />
        </div>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

页面显示正常,问题解决。接下来咱们对该解决方法进行详细的解释:node

4.x 中采用了单代码仓库模型架构,因此里面包含了若干个相互独立的包,以下所示:react

  • react-router  React Router 核心
  • react-router-dom  用于 DOM 绑定的 React Router
  • react-router-native  用于 React Native 的 React Router
  • react-router-redux  React Router 和 Redux 的集成
  • react-router-config  用于配置静态路由

所以,在实际的 web 项目开发中,react-router  和  react-router-dom  没必要同时引用。在 react-router-dom  中包含相似  <BrowserRouter>  的 DOM 类组件,因此只须要引入 react-router-dom  包就能够了。webpack

使用方法:git

import { BrowserRouter as Router, Route, Link } from 'react-router-dom';

关于  <BrowserRouter> 是什么?下面的问题中会有详细介绍,莫急~github

2. 页面中多个模块同时渲染问题

咱们同时定义了多个模块,当路由切换时发现这些模块会同时渲染出来。好比像下面这样定义:

ReactDOM.render((
    <BrowserRouter>
        <div className="container">
            <Route path="/" component={Index} />
            <Route path="/card" component={Card} />
        </div>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

当访问  path="/card" 的页面时,path="/" 的页面也会被渲染出来。不一样于 3.x 中路由匹配时的独一无二特性,4.x 中有了一层包含关系:如匹配  path="/card"  的路由会匹配  path="/"的路由。那么这个问题怎么解决呢?有如下两种方法:

(1)使用  <Router>  的  exact 关键字
ReactDOM.render((
    <BrowserRouter>
        <div className="container">
            <Route path="/" exact component={Index} />
            <Route path="/card" component={Card} />
        </div>
    </BrowserRouter>
    ),
    document.getElementById('app')
);
(2)使用独立路由: <Switch>
import { BrowserRouter, Route, Switch, Redirect } from 'react-router-dom';

ReactDOM.render((
    <BrowserRouter>
        <Switch>
            <div className="container">
                <Route path="/" exact component={Index} />
                <Route path="/card" component={Card} />
            </div>
        </Switch>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

完美!模块能够实现独立渲染了。咱们来看下这样解决问题的缘由:

首先须要了解下 <Router> 。它是全部路由组件共用的底层接口,在 4.x 中,你能够将各类组件及标签放进  <Router>  组件中。好比:

<Router>
    <div>
        <ul>
            <li><Link to="/">Home</Link></li>
        </ul>
        <hr/>
        <Route exact path="/" component={Home}/>
        <Route path="/about" component={About}/>
    </div>
</Router>

须要注意的是: <Router> 下只容许存在一个子元素,如存在多个则会报错。因此在上面的代码中,须要使用  <div></div> 将其余元素包裹起来。

在实际的项目中,咱们通常不会直接使用 <Router>  ,而是使用以下所示的更高级的路由。在这里将会介绍到第一个问题中用到的  <BrowserRouter>  以及其余经常使用的高级路由。咳咳,集中精力了哈,重头戏来了!

 <BrowserRouter>

使用 HTML5 提供的 history API (  pushState  ,  replaceState  和  popstate  事件) 来保持 UI 和 url 的同步。下面介绍一下该路由组件中的 5 个属性:

  • basename: string

当前位置的基准 url 。若是你的页面部署在服务器的二级(子)目录,你须要将  basename 设置到此子目录。 正确的 url 格式是前面有一个前导斜杠,但不能有尾部斜杠。

<BrowserRouter basename="/calendar"/>
<Link to="/today"/> // 渲染为 <a href="/calendar/today">
  • getUserConfirmation: func

当导航须要确认时执行的函数。默认使用  window.confirm  。

// 使用默认的确认函数
const getConfirmation = (message, callback) => {
  const allowTransition = window.confirm(message)
  callback(allowTransition)
}
<BrowserRouter getUserConfirmation={getConfirmation}/>
  • forceRefresh: bool

当设置为 true 时,在导航的过程当中整个页面将会刷新。 只有当浏览器不支持 HTML5 的 history API 时,才设置为 true 。

const supportsHistory = 'pushState' in window.history
<BrowserRouter forceRefresh={!supportsHistory}/>
  • keyLength: number

location.key 的长度,默认是 6。点击同一个连接时,每次该路由下的 location.key 都会改变,能够经过 key 的变化来刷新页面。

<BrowserRouter keyLength={12}/>
  • children: node

渲染单一子组件(元素)。

 <HashRouter>

HashRouter 是一种特定的 <Router> , HashRouter 使用 url 的 hash (例如: window.location.hash ) 来保持 UI 和 url 的同步。因为使用  hash  的方式记录导航历史不支持 location.key  和  location.state ,该技术仅用于支持传统的浏览器,此 API 再也不赘述。

这两个是常用的,剩下的还有  <MemoryRouter> 、<NativeRouter> 、 <StaticRouter>,有兴趣的同窗能够本身了解一下。

介绍完  <Router>,固然也要说下  <Route> ,咱们使用最频繁的组件,主要职责是当页面的访问地址与  Route 上的  path  匹配时渲染出对应的 UI 界面。

<Route>  属性值主要有:

  • path: string

能够被 path-to-regexp 解析的有效 url 路径。若是没有 path,路由将老是被匹配。

<Route path="/users/:id" component={User}/>
  • exact: bool

为 true 时,则要求路径与  location.pathname  必须彻底匹配。经过下表解释一下:

  •  strict: bool

为  true 时,有结尾斜线的路径只能匹配有斜线的  location.pathname  。见下表:

第二个属性值  exact  就是咱们用来解决多个模块同时渲染问题的,默认值为 true 。

在这里简要提下  <Route>  渲染的三种方法:

  • <Route component> 当访问地址和路由匹配时,一个 React component 将会被渲染
  • <Route render>  此方法适用于内联渲染,并且不会产生重复装载问题
  • <Route children>

当须要判断访问地址与路由是否匹配时,可使用此方法。当不匹配时, match 为 null。

咱们上面代码示例中使用的是第一种方法  <Route component>,这三种渲染方法都会用到  match 、location 、 history 这些属性值。这里再也不详细介绍,使用时能够查阅官方文档。

须要注意的是 :每一种渲染方法都有其适用背景, <Route component>  的优先级比  <Route render>  高,而他们又都优先于  <Route children>  ,因此在同一个  <Route>  应该只使用一种方法,咱们大多数使用的是 component 方法。

说完第一种解决方法的原理,来剖析下第二种方法: <Switch> 。

该组件只渲染第一个与当前访问地址匹配的  <Route>  或  <Redirect>  。它和多个堆叠的  <Route> 组件之间的区别是: <Switch>  只渲染一个路由。

在解决方法 (2)中,当咱们访问  /card  , <Switch>  将会开始寻找与之匹配的路由,查找到  <Route path="/card"/>  匹配后,<Switch>  将会中止寻找而后开始渲染  /card 。<Switch>

  •  children: node

<Switch> 下的子节点只能是 <Route> 或 <Redirect> 元素且只有与当前访问地址匹配的第一个子节点才会被渲染。

3. 如何定义默认访问页面

当用户手动修改 url 时,有时咱们须要定义一个默认页面,从新引导用户的操做,这个该如何实现呢?

使用 <Redirect> 。好比当用户手动输入 /test 以后,咱们须要跳转至首页,则代码以下所示:

ReactDOM.render((
    <BrowserRouter>
        <div className="container">
            <Switch>
                <Route path={routePaths.INDEX} exact component={Index} />
                <Route path={routePaths.CARD} component={Card} />
                <Redirect to="/" />
            </Switch>
        </div>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

方法解析:<Redirect> 渲染时将会导向一个新的地址,这个新的地址将会覆盖掉 history 堆栈中的当前地址。

其经常使用的属性是:

  • to: string

重定向的 url 地址。

<Redirect to="/somewhere/else"/>
  • to: object

重定向的 location 对象。

<Redirect to={{
  pathname: '/login',
  search: '?utm=your+face',
  state: { referrer: currentLocation }
}}/>
  • push: bool

取值为 true 时,重定向操做将会把新地址加入到访问的历史记录里面,并不会替换掉当前的地址。

<Redirect push to="/somewhere/else"/>
  • from: string

须要匹配的将要被重定向的路径。

<Switch>
  <Redirect from='/old-path' to='/new-path'/>
  <Route path='/new-path' component={Place}/>
</Switch>

须要注意的是:<Route> 元素使用 path 属性进行匹配,<Redirect> 元素使用 from 属性进行匹配。若是元素中没有对应的 path 或 from,那么它们将匹配任何当前的访问地址。

4. 路由激活状态的控制

相信你们对于 <link> 组件都不陌生,在 3.x 中,当须要将当前点击的理由置为激活状态时,咱们能够这样设置:

//经过Style进行配置
<li><Link to="/home" activeStyle={{ color: 'red' }}>Home</Link></li>

//经过类名进行设置
<li><Link to="/home" activeClassName="active">Home</Link></li>

可是,通常状况下只有主导航的连接才须要知道本身是否被激活。所以须要对主导航的 <Link>进行一层包装,这样就没必要记得哪些地方有 activeClassName 或 activeStyle 。所谓的对 <Link> 包装就是自定义 <Link> 组件,经过自定义实现特别的 Style 。在 3.x 中,具体实现方法以下:

// modules/NavLink.js
import React from 'react';
import { Link } from 'react-router';

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

    render() {
        return <Link {...this.props} activeClassName="active"/>
    }
})
//使用方法:
import NavLink from './NavLink'
// ...
<li><NavLink to="/home">Home</NavLink></li>

可是,在 4.x 中咱们不须要作这些包装工做,由于它直接提供了 <NavLink> 组件供咱们使用。使用方法:

<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>

下面对 <NavLink> 作下简单介绍:

该组件是 <Link> 的特殊版本,当遇到匹配的 URL 渲染元素时会添加样式属性,适用于页面导航部分。

  • activeClassName: string

导航选中时的样式名,默认样式名为 active

<NavLink
  to="/faq"
  activeClassName="selected"
>FAQs</NavLink>
  • activeStyle: object

导航选中时的样式。

<NavLink
  to="/faq"
  activeStyle={{
    fontWeight: 'bold',
    color: 'red'
   }}
>FAQs</NavLink>
  • exact: bool

若值为 true,当访问地址严格匹配时激活样式才会生效。

<NavLink
  exact
  to="/profile"
>Profile</NavLink>
  • strict: bool

若值为 true,只有当访问地址后缀斜杠严格匹配(有或无)时激活样式才会生效。

<NavLink
  strict
  to="/events/"
>Events</NavLink>
  • isActive: func

用于添加页面激活时的操做逻辑。

const oddEvent = (match, location) => {
  if (!match) {
    return false
  }
  const eventID = parseInt(match.params.eventID)
  return !isNaN(eventID) && eventID % 2 === 1
}

<NavLink
  to="/events/123"
  isActive={oddEvent}
>Event 123</NavLink>

以上是 <NavLink> 组件的属性介绍,供你们学习。

5. 页面跳转

在 3.x 中,跳转页面可使用如下方式:

import { browserHistory } from 'react-router';
//第一种方法
browserHistory.push('/some/path');
//第二种方法
this.context.router.push('/some/path');
//第三种方法
<Link to="'/some/path'"></Link>

在 4.x 中使用 push 方法跳转页面时,若是按照上面的写法,页面会报错:

  

针对该问题有如下两种解决方法:

(1)使用 history 控制路由的跳转
this.props.history.push('/some/path');

简单介绍下 history

history 指的是 history 包,是 4.x 中的重要依赖之一。常见的 history 路由方案有三种形式,分别是:

  • browser history  在 DOM 上的实现,用于支持 HTML5 history API 的浏览器;
  • hash history  在 DOM 上的实现,用于旧版浏览器;
  • memory history  在内存上的实现,用于测试或非 DOM 环境(如 React Native)。

history 对象包含的属性和方法以下所示:

  • length: number history 堆栈中的地址数目
  • action: string 当前的动做 (PUSH , REPLACE , 或者是 POP )
  • location: object 当前访问地址信息组成的对象
  • push(path, [state]): func 在 history 堆栈信息里加入一个新路径
  • replace(path, [state]) : func 替换 history 堆栈信息里的当前路径
  • go(n) : func 将 history 堆栈中的指针向前移动 n
  • goBack(): func 等同于 go(-1)
  • goForward(): func 等同于 go(1)
  • block(prompt) : func 阻止跳转

须要注意的是:history 对象是可变的,因此不要从 history.location 直接获取,而是须要经过 <Route> 的prop 来获取 location

(2)使用 Context,得到 router 对象
import React from "react";
import PropTypes from "prop-types";

class MyComponent extends React.Component {
  static contextTypes = {
    router: PropTypes.object
  }
  constructor(props, context) {
     super(props, context);
  }
  ...
  myFunction() {
    this.context.router.history.push("/some/Path");
  }
  ...
}

以上两种方法中推荐使用第一种,由于 React 不推荐使用 context , 在将来版本中有可能被抛弃哦。

6. 切换路由后,页面仍然停留在上一个页面的位置

由 A 页面跳转到 B 页面,B 页面停留在 A 页面的位置,没有返回到顶部。

问题分析:在 React Router 早期版本中你们可使用滚动恢复的开箱即用功能,可是在 4.x 中路由切换时并不会恢复滚动位置,用户须要对 window 和独立组件的滚动位置进行管理。可使用 withRouter 组件: withRouter 能够访问历史对象的属性和最近的 <Route> 匹配项,当路由的属性值 { matchlocationhistory } 改变时,withRouter 都会从新渲染。该组件能够携带组件的路由信息,避免组件之间一层层传递。使用方法以下:

withRouter(MyComponent)

这样就能够获取到 MyComponent 组件的路由信息了。

解决方法:使用 withRouter 封装 ScrollToTop 组件。这里就用到了 withRouter 携带路由信息的特性,经过对比props 中 location 的变化,实现页面的滚动。

(1)定义 ScrollToTop 组件,代码以下:
import React, { Component } from 'react';
import { Route, withRouter } from 'react-router-dom';
class ScrollToTop extends Component {
    componentDidUpdate(prevProps) {
        if (this.props.location !== prevProps.location) {
          window.scrollTo(0, 0)
        }
    }
    render() {
        return this.props.children
    }
}

export default withRouter(ScrollToTop);
(2)在定义路由处引用该组件,例如:
ReactDOM.render((
    <BrowserRouter>
        <ScrollToTop>
            <div className="container">
                <Route path={routePaths.INDEX} exact component={Index} />
                <Route path={routePaths.CARD} component={Card} />
            </div>
        </ScrollToTop>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

这样处理以后,当跳转页面时都会自动回到该页面的顶部位置。

7. 页面之间如何传值?

问题背景:当路由发生跳转时咱们可能须要携带一些参数。

解决方法:使用 props 属性,介绍如下三种传值方法:

(1)props.params

指定一个 path ,而后指定通配符能够携带参数到指定的 path :

<Route path='/user/:name' component={UserPage}></Route>

跳转 UserPage 页面时,能够这样写:

//link方法
<Link to="/user/sam">用户</Link>
//push方法
this.props.history.push("/user/sam");

在 UserPage 页面中经过 this.props.params.name 获取值。

上面的方法能够传递一个或多个值,可是每一个值的类型都是字符串,无法传递一个对象。若是要传的话能够将 json 对象转换为字符串,传递过去以后再将 json 字符串转换为对象。

let data = {id:3,name:sam,age:36};
data = JSON.stringify(data);
let path = '/user/${data}';

//在页面中获取值时
let data = JSON.parse(this.props.params.data);
(2)query

query 方式能够传递任意类型的值,可是页面的 url 也是由 query 的值拼接的,url 很长且是明文传输。

//定义路由
<Route path='/user' component={UserPage}></Route>

//数据定义
let data = {id:3,name:sam,age:36};
let path = {
    pathname: '/user',
    query: data,
}

//页面跳转
<Link to={path}>用户</Link>
this.props.history.push(path);

//页面取值
let data = this.props.location.query;
let {id,name,age} = data;
(3)state

state 方式相似于 post,依然能够传递任意类型的数据,并且能够不以明文方式传输。

//定义路由
<Route path='/user' component={UserPage}></Route>

//数据定义
let data = {id:3,name:sam,age:36};
let path = {
    pathname: '/user',
    state: data,
}

//页面跳转
<Link to={path}>用户</Link>
this.props.history.push(path);

//页面取值
let data = this.props.location.state;
let {id,name,age} = data;

在实际的项目开发中,你们能够根据项目须要选择合适的传值方法。

8. 使用 <BrowserRouter> 配置路由,上传页面至服务器后页面出现 404

问题背景:项目中控制路由跳转使用的是 <BrowserRouter>,代码以下:

ReactDOM.render((
    <BrowserRouter>
        <div className="container">
            <Route path={routePaths.INDEX} exact component={Index} />
            <Route path={routePaths.CARD} component={Card} />
        </div>
    </BrowserRouter>
    ),
    document.getElementById('app')
);

在开发过程当中使用是没有问题的,可是将页面上传至服务器以后,问题就来了:用户访问的资源不存在,页面是空白的。

问题分析:<BrowserRouter> 是使用 React-Router 应用推荐的 history 方案。它使用浏览器中的 History API 用于处理 url,建立一个像 example.com/list/123 这样真实的 url。当经过真实 url 访问网站的时候,因为路径是指向服务器的真实路径,但该路径下并无相关资源,因此用户访问的资源不存在。

解决方法:

(1)使用 <HashRouter>

它使用 url 中的 hash(#)部分去建立路由,举例来讲,用户访问 http://www.example.com/ ,实际会看到的是 http://www.example.com/#/ 。
为何本地开发时没有问题呢?那是由于咱们的 React 脚手架中使用 webpack-dev-server 作了配置。

webpackConfig.devServer = {
    disableHostCheck: true,
    contentBase: path.resolve(__dirname, 'build'),
    compress: true, //gzip压缩
    historyApiFallback: true
};
(2)若是要使用 <BrowserRouter> 的话,服务器须要进行相关路由配置,方法见扩展阅读 [1]。

9. React Router 4.x 如何配置按需加载?

问题背景:当访问首页时会一次性请求全部的 js 资源,这会大大影响页面的加载速度和用户体验。因此添加按需加载功能是必要的。

问题分析:4.x 官网中推荐使用 bundle loader 实现代码拆分,因此咱们选择使用 bundle loader 。

解决方法:配置按需加载的步骤以下:

(1)安装 bundle-loader
npm install --save-dev bundle-loader
(2)定义 Bundle.js

<Bundle> 组件会接受一个名为 load 的 props, load 是一个组件异步加载的方法,该方法须要传入一个回调函数做为参数,而后回调函数会在方法内异步接收加载完的组件,详细代码以下:

import React, { Component } from 'react';
export default class Bundle extends React.Component {
    constructor(props) {
        super(props);
        this.state = {
            // short for "module" but that's a keyword in js, so "mod"
            mod: null
        }
    }

    componentWillMount() {
        this.load(this.props)
    }

    componentWillReceiveProps(nextProps) {
        if (nextProps.load !== this.props.load) {
            this.load(nextProps)
        }
    }

    load(props) {
        this.setState({
            mod: null
        })
        props.load((mod) => {
            this.setState({
                // handle both es imports and cjs
                mod: mod.default ? mod.default : mod
            })
        })
    }

    render() {
        if (!this.state.mod)
            return false
        return this.props.children(this.state.mod)
    }
}
(3)入口文件 app.jsx 配置。

使用 bundle-loader 为每一个页面组件配置按需加载,默认打开的首页能够直接引入,不须要使用 <Bundle> 组件进行处理,代码以下所示:

import React from 'react';
import ReactDOM from 'react-dom';
import { HashRouter, Route } from 'react-router-dom';
import * as routePaths from './js/constants/routePaths';
import Bundle from './js/constants/Bundle.js';
//默认打开页面直接引入
import Index from './js/pages/Index';
//其余页面异步引入
import CardContainer from 'bundle-loader?lazy&name=app-[name]!./js/pages/Card';
import './assets/css/index.scss';

const Card = () => (
    <Bundle load={CardContainer}>
        {(Card) => <Card />}
    </Bundle>
)

ReactDOM.render((
    <HashRouter>
        <div className="container">
            <Route path={routePaths.INDEX} exact component={Index} />
            <Route path='/card' component={Card} />
        </div>
    </HashRouter>
    ),
    document.getElementById('app')
);
(4)webpack.config.js 修改,配置子文件名称。
webpackConfig.output = {
    path: path.resolve(__dirname, 'build/' + config.ftpTarget),
    publicPath: config.publicPath + '/',
    filename: 'js/[name].js',
    chunkFilename: 'js/[id].js'
}

这样配置以后,打包出来的 js 文件已经进行了拆分,每一个页面对应一个本身的 js ,当访问该页面时才加载相应的资源。

10. this.props.history.push() 进行路由跳转时报错

问题背景:配置按需加载后,首页能够正常跳转子页面,而在子页面中进行页面跳转时出现报错:this.props.history.push中'push'is undefined 。

问题分析:项目中使用 this.props.history.push() 方法实现路由跳转,未进行文件拆分时能够正常使用,可是当进行文件拆分以后,子文件中没法获取 this.props.history 对象。

解决方法:使用 withRouter 封装页面组件。示例代码以下:

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

class Card extends React.Component {
  constructor(props) {
      super(props);

    }
    render() {
      return (
        <div className="wrapper">
          <p>这是第二页:测试</p>
      </div>
      );
    }
}
export default withRouter(Card);

这样封装以后,就无需一级级传递 React Router 的属性,子文件内也能够拿到须要的路由信息。

以上列出的内容就是咱们在使用 4.x 开发时遇到的一些问题,针对每一个问题不只给出了解决方法,还介绍了解决方法中涉及到的知识点。对于一些未介绍到的内容,你们能够移步官网进行深度学习(见扩展阅读 [2])。

小结

对于新项目来讲,使用 React Router 4.x 进行开发是一个不错的选择。由于其组件化的思想和 React 很像,学习起来比较容易、上手快。除了官网 [2] ,扩展阅读中咱们还给出了 React Router 4.x 的其余学习文档 [3][4],供你们参考。值得注意的是,上述问题解决方法中的示例代码使用的是 React v16 版本,在实际使用时须要结合本身的环境进行适当修改。

React Router 4.x 这次带来的改变是颠覆性的,对于咱们使用者来讲是一种挑战,同时也是一种知识的有趣探索。组件化路由设计理念和动态路由的配置给咱们的项目开发带来了更大的灵活性。文中在介绍问题解决方法时对一些 4.x 的 API 作了简要的介绍,对于一些更细节化的东西,须要咱们移步官网进行深刻的学习,而后再慢慢的探索。React Router 的做者说过版本号要永远和 React 保持一致,因此相信咱们的 React Router 探索之旅将会一直进行着……同志们,Hold On!

好了,关于 React Router 4.x 开发实践中遇到的问题就介绍到这里了,若有任何疑问,欢迎留言。

扩展阅读

[1] https://www.thinktxt.com/react/2017/02/26/react-router-browserHistory-refresh-404-solution.html

[2] http://reacttraining.cn/

[3] https://github.com/ReactTraining/react-router

[4] https://www.sitepoint.com/react-router-v4-complete-guide/

有兴趣欢迎关注咱们的公众号:全栈探索。欢迎交流。