React-Router4升级之路

最近将React升级到了16.2,也将项目的React-Router升级了v4,我主要说一下我的升级之路。

React 16 新特性

1、render支持了多种的返回类型,例如数组和代码片段等;
2、更好的错误处理,新增componentDidCatch钩子
3、支持自定义DOM属性,属性会透传;
4、体积更小(比15减少了32%),删除了内置的类型检查React.PropTypes;
5、Portals(处理弹窗或者渲染外部dom结构更加方便);
6、性能优化(https://www.itcodemonkey.com/article/1306.html);

上述参考:

http://www.jb51.net/article/127955.htmhttp://blog.csdn.net/lx376693576/article/details/78192768

React Router v4 新特性

1、全组件化
2、修复多次渲染问题
3、新的异步加载方式

安装

下面就以React-Router和最新的React16为例子安装,由于React的API变化不是很大,此处就不多做介绍了。

  1. 先删除项目中依赖的旧版React和React-Router(如果不删除,升级需要指定版本号)
npm uninstall react react-dom react-router --save
  1. 安装最新版本(此处的最新版本就是16和4,注意router的包名称是react-router-dom, history是用来访问历史的)
npm install react react-dom react-router-dom history --save
  1. 查看项目目录下面的package.json
"dependencies": {
    "history": "^4.7.2",
    "react": "^16.2.0",
    "react-dom": "^16.2.0",
    "react-router-dom": "^4.2.2"
  }

使用

一、路由的配置方式

先上修改后的代码了

import React from 'react';
import {HashRouter, Route, Switch} from 'react-router-dom';
class App extends Component {
    render() {
        return (
            <HashRouter>
                <Switch>
                    <Route path="/" exact component={BindEmail}/>
                    <Route path="/bindemail" component={BindEmail}/>
                    <Route path="/bindemailresult/:type" component={BindEmailResult}/>
                    <Route path="/updatepassword" component={UpdatePassword}/>
                    <Route path="/updatemail" component={UpdateMail}/>
                    <Route component={BindEmail}/>
                </Switch>
            </HashRouter>
        );
    }
}

看上面的代码首先要注意几点

1、Router替换成了HashRouter,并且少了一个history的属性,当然你也可以使用BrowserRouter不过需要后台支持。
2、Route中删除了getComponent属性,那我们异步加载怎么办呢,下面会说一下。
3、多了一个Switch组件,以前设置404路由的时候只要在最后加上path="*"就好了,但是现在只需要在最后加一个路由并且由Switch组件包裹即可。
4、第一行的Route上有一个exact属性,这个属性说明精确匹配,否则所有/开头的组件都会展示/配置的组件(比较绕,可以到时候讲一下)。
5、HashRouter组件内部只能包含一个组件,如果有多个会直接报错。
6、当然了,我们导入的时候只需要导入react-router-dom。

二、异步加载的方式

我们可以使用高阶组件和webpack的import方法进行异步加载js。

  1. 首先我们得有一个用来加载异步组件的高阶组件,我们暂且称之为"asyncComponent",实现如下:
import React from 'react'
export const asyncComponent = loadComponent => (
    class AsyncComponent extends React.Component {
        constructor(props) {
            super(props);
            this.state = {
                Component: null
            }
        }
        componentWillMount() {
            if (this.hasLoadedComponent()) {
                return
            }
            loadComponent()
                //  取到module的default作为组件,因为我们导出组件的时候使用的是 export default
                .then(module => module.default)
                .then((Component) => {
                    this.setState({Component})
                })
                .catch((err) => {
                    console.error(`Cannot load component in <AsyncComponent />`);
                    throw err
                })
        }
        hasLoadedComponent() {
            return this.state.Component !== null
        }
        render() {
            const {Component} = this.state;
            return (Component) ? <Component {...this.props} /> : null
        }
    }
);
  1. 我们将需要拆分的js文件,进行下面的处理,import方法会返回一个promise,注意/ * webpackChunkName: "test" * / 这种注释不能删除,它描述了拆分后的文件名称。
import {asyncComponent as async} from './views/async'
// 测试
export const Test = async(() => import(/* webpackChunkName: "test" */ './test'));
// 绑定邮箱
export const BindEmail = async(() => import(/* webpackChunkName: "bindemail" */ './views/bindemail'));
// 修改密码
export const UpdatePassword = async(() => import(/* webpackChunkName: "updatepassword" */ './views/bindemail/updatepassword'));
// 修改邮箱
export const UpdateMail = async(() => import(/* webpackChunkName: "updatemail" */ './views/bindemail/updatemail'));
// 邮箱绑定结果
export const BindEmailResult = async(() => import(/* webpackChunkName: "bindemailresult" */ './views/bindemail/result'));
  1. 然后我们在路由中直接配置component属性就好了,相比之前会简单一点
<Route path="/bindemail" component={BindEmail}/>

三、可编程路由(路由切换)

  1. 我们一般会在js中获取路由的参数,我们以前的方式是这样的
this.props.route.params.xxx;

在v4中,会在props上挂载一个match对象,所以获取参数,使用下面的方式

this.props.match.params.xxx

参考:https://reacttraining.com/react-router/web/api/match

  1. 在代码中切换路由,以前是直接调用hashHistory的方法切换,如
import { hashHistory } from 'react-router';
hashHistory.push('/home');

在v4中将操作历史的工具单独抽离了,也就是history包,API和hashHistory基本一致

import { createHashHistory } from 'history';
const history = createHashHistory();
history.push('/home');

常见问题

低端的浏览器上面可能不支持Map和Set类,会报错:

Map is not undefined

解决方案:
安装core-js,在引入Set,Map相关的语法糖(或许你也可以babel-polyfill,React官方使用的是core-js),如下就可以了

import Map from 'core-js/es6/map';
import Set from 'core-js/es6/set';

//  绑定在window上,就可以全局访问了
window.Map = Map;
window.Set = Set;

参考:
1、https://reactjs.org/docs/javascript-environment-requirements.html
2、https://reacttraining.com/react-router/web/example/basic

留下回复