博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
项目实践:从react-router v3迁移到v4
阅读量:6335 次
发布时间:2019-06-22

本文共 5473 字,大约阅读时间需要 18 分钟。

原文:

前言

今年3月初发布了react-router v4,相较之前的v3和v2版本做了一个破坏性的升级。遵循一切皆React Component的理念。静态路由变成了动态路由。这里记录下v3项目如何迁移到v4。

项目地址:

迁移步骤

  • 对React-Router和Redux同步进行重构

  • 重写路由

  • 代码分割

  • 琐碎的API替换

详细代码参阅这个


React-Router和Redux同步

这里我们仍然不使用react-router-redux这个库。为了和react-routerv4版本保持一致,react-router-redux发布了v5.0.0版本,你当然也可以使用它来实现这个功能。

1. 替换依赖包

v3我们引入的是react-router包,在v4我们只引入react-router-dom这个包。安装react-router-dom时会同时安装history

package.json

-    "react-router": "^3.0.0",+    "react-router-dom": "^4.1.2",

2. 改写对browserHistory的创建和当前location的获取

location.js

// v3import { browserHistory } from 'react-router'// 获取当前locationconst initialState = browserHistory.getCurrentLocation()

==>

// v4import createHistory from 'history/createBrowserHistory'export const history = createHistory()// Get the current location.const initialState = history.location

这里替换的是history,和当前location的获取方法。在v3,browserHistory存在于react-router中,而v4把history抽离了出来,提供了createBrowserHistory ,createHashHistory ,createMemoryHistory 三种创建history的方法。v4中创建的history导出,在后面会需要用到。

history API详见:

3. 对history绑定监听事件,把location的改变同步到Redux的store中

createStore

// v3import { browserHistory } from 'react-router'import { updateLocation } from './location'store.unsubscribeHistory = browserHistory.listen(updateLocation(store))

updateLocation用来把location的更新同步到store中。

export const updateLocation = ({ dispatch }) => {  return (nextLocation) => dispatch(locationChange(nextLocation))}

一切似乎都很顺利,接着第一个坑来了

根据historyAPI提供的

// Listen for changes to the current location.const unlisten = history.listen((location, action) => {  // location is an object like window.location  console.log(action, location.pathname, location.state)})

修改createStore.js

==>

// v4import { updateLocation, history } from './location'// 监听浏览器history变化,绑定到store。取消监听直接调用store.unsubscribeHistory()store.unsubscribeHistory = history.listen(updateLocation(store))

接着修改app.js

// v3// ...import { browserHistory, Router } from 'react-router'// ...

==>

// ...import {  BrowserRouter, Route } from 'react-router-dom'// ...
//...

我们到浏览器中查看,发现URL变化并没有触发updateLocation(store),state并没有变化。

What a f**k!

问题出在BrowserRouter在创建的时候在内部已经引入了一个historyupdateLocation(store)应该监听的是内部的这个history。这里贴下的代码

import React from 'react'import PropTypes from 'prop-types'import createHistory from 'history/createBrowserHistory'import { Router } from 'react-router'/** * The public API for a 
that uses HTML5 history. */class BrowserRouter extends React.Component { static propTypes = { basename: PropTypes.string, forceRefresh: PropTypes.bool, getUserConfirmation: PropTypes.func, keyLength: PropTypes.number, children: PropTypes.node } history = createHistory(this.props) render() { return
}}export default BrowserRouter

于是,我们放弃使用BrowserRouter,而使用Router

修改app.js

==>

// v4import { Router, Route } from 'react-router-dom'//...

这样,这个坑算是填上了。也就完成了history和store之间的同步。


重写路由

v4取消了 中心化配置路由。Route是一个react component。

取消了IndexRoute,通过Switch来组件提供了相似的功能,当<Switch>被渲染时,它仅会渲染与当前路径匹配的第一个子<Route>

routes/index.js

// v3//..export const createRoutes = (store) => ({  path        : '/',  component   : CoreLayout,  indexRoute  : Home,  childRoutes : [    CounterRoute(store),    ZenRoute(store),    ElapseRoute(store),    RouteRoute(store),    PageNotFound(),    Redirect  ]})//...

==>

// ...const Routes = () => (  
)export default Routes//

这里路由的定义方式由PlainRoute Object改写成了组件嵌套形式,在PageLayout.js中插入<Routes />


代码分割

v3版本通过getComponetrequire.ensure实现代码分割和动态路由。在v4版本,我们新增异步高阶组件,并使用import()替代require.ensure()

Counter/index.js

// v3import { injectReducer } from '../../store/reducers'export default (store) => ({  path : 'counter',  /*  动态路由 */  getComponent (nextState, cb) {    /* 代码分割 */    require.ensure([], (require) => {      const Counter = require('./containers/CounterContainer').default      const reducer = require('./modules/counter').default      /*  将counterReducer注入rootReducer  */      injectReducer(store, { key : 'counter', reducer })      cb(null, Counter)    }, 'counter')  }})

首先,新增AsyncComponent.js

import React from 'react'export default function asyncComponent (importComponent) {  class AsyncComponent extends React.Component {    constructor (props) {      super(props)      this.state = {        component: null,      }    }    async componentDidMount () {      const { default : component } = await importComponent()      this.setState({        component: component      })    }    render () {      const C = this.state.component      return C        ? 
: null } } return AsyncComponent}
  1. 这个asyncComponent 函数接受一个importComponent 的参数,importComponent 调用时候将动态引入给定的组件。

  2. componentDidMount 我们只是简单地调用importComponent 函数,并将动态加载的组件保存在状态中。

  3. 最后,如果完成渲染,我们有条件地提供组件。在这里我们如果不写null的话,也可提供一个菊花图,代表着组件正在渲染。

接着,改写Counter/index.js

==>

import { injectReducer } from '../../store/reducers'import { store } from '../../main'import Counter from './containers/CounterContainer'import reducer from './modules/counter'injectReducer(store, { key : 'counter', reducer })export default Counter

一旦加载Counter/index.js,就会把counterReducer注入到Rudecer中,并加载Counter组件。


琐碎API的替换

v4 移除了onEnter onLeave等属性,history替换router属性,新增match

this.props.router.push('/')

==>

this.props.history.push('/')
this.props.params.id

==>

this.props.match.params.id

总结

这里可以看出,使用v4替换v3,对于大型项目并不是一件轻松的事情,有许多小坑要踩,这就是社区很多项目仍然使用v2/v3的原因。笔者认为,v4更符合React的组件思想,于是做了一个实践。最后欢迎指正拍砖,捂脸求star ? 。

参考

转载地址:http://ygioa.baihongyu.com/

你可能感兴趣的文章
javascript的作用域
查看>>
新形势下初创B2B行业网站如何经营
查看>>
初心大陆-----python宝典 第五章之列表
查看>>
java基础学习2
查看>>
sysbench使用笔记
查看>>
有关电子商务信息的介绍
查看>>
NFC·(近距离无线通讯技术)
查看>>
多线程基础(三)NSThread基础
查看>>
PHP的学习--Traits新特性
查看>>
ubuntu下,py2,py3共存,/usr/bin/python: No module named virtualenvwrapper错误解决方法
查看>>
Ext.form.field.Number numberfield
查看>>
Linux文件夹分析
查看>>
解决部分月份绩效无法显示的问题:timestamp\union al\autocommit等的用法
查看>>
nginx 域名跳转 Nginx跳转自动到带www域名规则配置、nginx多域名向主域名跳转
查看>>
man openstack >>1.txt
查看>>
linux几大服务器版本大比拼
查看>>
在BT5系统中安装postgresQL
查看>>
Can't connect to MySQL server on 'localhost'
查看>>
【Magedu】Week01
查看>>
写给MongoDB开发者的50条建议Tip25
查看>>