现在都 React Router 5
了,你是不是还在用v3
呢?
不光是你在用,我们很多项目也在用,懒得升级,感觉改动太大,升级了后谁知道会出什么问题,别没事找事。
毕竟v4
是两年前的了,再不升级真的就有点说不过去了,直接拿一个小项目开刀,直接3-5吧,整体来说还好,v5
版完全向下兼容react15
,所以如果你的 react
是15
的话没啥影响。
这里就简单的总结下,这次升级的一些内容。
//V3 下
npm i react-router
//V5 下
npm i react-router-dom
react-router
为核心库,运行于浏览器端就用react-router-dom
,如果是native端 ,那就用react-router-native
,安装react-router-dom
后会自动安装react-router
。
所以Link
组件的导入也变了,同时增加了属性 replace
表示是否替换掉原地址
//v3
import {Link} from react-router;
//v4 v5
import { Link } from 'react-router-dom';
在 v5
里没有 Router
组件,换成更具体的组件了,HashRouer
和BrowserRouter
。
看代码更清晰
//v3
<Router history={browserHistory}></Router>
<Router history={hashHistory}></Router>
//v4+ v5+
<BrowerRouter></BrowerRouter>
<HashRouer></HashRouer>
这个升级挺好,不用再配什么参数了
Route
组件和 v3
版本的 Route
作用一致,都是在path
匹配的 时候,渲染指定组件,但是写法上有些变化,而且增加了一些特性。
<Route component={Home}/>
component
属性和 v3
中的 component
属性保持一致,表示path
匹配的时候才会渲染的组件。
增加了render
属性,v3
中不存在这个属性,render
表示在path
匹配时被调用的方法,而不是创建一个组件,但是需要一个返回值,返回一个组件或者null。
<Route render={(props) => (<Home></Home>)/>
<Route render={(props) => (<div>hello,前端技术江湖</div>)/>
新增children
属性,children
与 render
一样,但是不会匹配地址,路径不匹配时 URL的match 值为 null,可以用来根据路由是否匹配动态调整UI。
Switch
用来渲染和 path
相匹配的第一个路由,当匹配到一个路由后就不会继续往后匹配,反之则会渲染和 path
匹配的所有路由。
举个栗子
let routes = (
<div>
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</div>
);
根据上面的配置来说,如果此时路径是/about
,那么About
,User
,NoMatch
都会渲染,因为都会被匹配到。
那怎么才能只渲染About
组件呢,那就需要使用Switch
组件。
let routes = (
<Switch>
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</Switch>
);
此时<Route path="/about" />
将会被匹配到,后面 Switch
会停止继续后面的匹配,只渲染About
组件。
问题就这样解决了吗?
在下面代码增加了新的Route
, 用于渲染 Index
组件,那还能正常的渲染About
组件吗。
let routes = (
<Switch>
<Route path="/" component={Index} />
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</Switch>
);
结果并不会渲染About
组件,而会渲染Index
组件,Swith
的特性就是只渲染第一个匹配到的,因为/about
也会匹配/
。
这个时候就需要使用exact
属性了,表示是否精确匹配,让路由的匹配更严谨。
let routes = (
<Switch>
<Route path="/" exact component={Index} />
<Route path="/about" component={About} />
<Route path="/:user" component={User} />
<Route component={NoMatch} />
</Switch>
);
v3
里的路由就是个配置文件,所有的路由规则都写在一起,而且必须写在一起。
v3
下的代码
import { Router, Route, IndexRoute } from 'react-router'
const Layout = props => (
<div className="Layout-box">
<header>
React Router 3 App
</header>
<main>
{props.children}
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<Router history={browserHistory}>
<Route path="/" component={Layout}>
<IndexRoute component={HomePage} />
<Route path="/users" component={UsersPage} />
</Route>
</Router>
)
render(<App />, document.getElementById('root'))
v4 v5
的理念是一切都是组件,路由也是组件,那就可以随意的摆放它的位置,比如写在别的组件里。
v5
下的代码
import { BrowserRouter, Route } from 'react-router-dom'
const Layout = () => (
<div className="layout-box">
<header>
React Router 5 App
</header>
<main>
<Route path="/" exact component={HomePage} />
<Route path="/users" component={UsersPage} />
</main>
</div>
)
const HomePage =() => <div>Home Page</div>
const UsersPage = () => <div>Users Page</div>
const App = () => (
<BrowserRouter>
<Layout />
</BrowserRouter>
)
render(<App />, document.getElementById('root'))
但我还是喜欢集中式的,v5
里仍然可用这种方式,可能习惯了。
这个是在 v5
里增加的,如果你想让不同的多个 path
渲染同一个组件,可以不用写多个 Route
,v5
的 path
已经支持数组。
// v4 这样写
<Switch>
<Route path="/users/:id" component={User} />
<Route path="/profile/:id" component={User} />
</Switch>
// v5 这样写
<Route path={["/users/:id", "/profile/:id"]} component={User} />
//v3
this.props.params
//v4 v5
this.props.match.params
//V3 获取query可以这么获取
this.props.location.query
//V4 5 只能通过 search 来取值,一般是自己写一个方法或者使用query-string库
this.props.location.search
// V3 获取location的action
this.props.location.action
//V4 5
this.props.history.action
V3
中使用路由嵌套是很平常的事儿,而且写起来也很简单
<Router history={hashHistory}>
<Route path='/' component={App}>
<Route path='a' component={A} />
<Route path='b' component={B} />
</Route>
</Router>
V4 V5
中不在支持 Route
嵌套
<Switch>
<Route path="/" component={BaseLayout} />
</Switch>
//BaseLayout 组件
<div className='layout'>
<Switch>
<Route exact path="/a" component={A} />
<Route exact path="/b" component={B} />
<Route exact path="/c" component={C} />
</Switch>
</div>
使用起来很不顺手,所以我直接放弃了嵌套的写法,不要和我一样。
在v3
中,可以使用使用 Route
的 onEnter
, onUpdate
和 onLeave
事件来做一些事情。
在v4 5
中,Route
的这些事件没了,不过我还没用到这些事件,只是简单的提一句。
v3
里实现组件按需加载还是很方便的,因为提供了特定的方法。
const Route = {
path: '/a',
getComponents(location, callback) {
require.ensure([], function (require) {
callback(null, require('./pages/a'))
})
}
}
<Route {...Route} ></Route>
v4
开始就完全不一样了,已经移除了getComponent
这个属性。
这里我使用的是一个现有的库react-loadable
搞定的,当然也可以自己写一个。
import React from 'react';
import Loadable from 'react-loadable';
//加载动画
const loadingComponent =()=>{
return (
<div>loading</div>
)
}
//Home.js
export default Loadable({
loader:import('./index.js'),
loading:loadingComponent
});
// 和路由结合
<Route path="/home" component={Home}/>
v3
到v5
升级改动不仅仅是上面这些,整体改动真的很大。与其说是升级不如说是重写,难怪很多人不愿意升级,升级了才知道升级的痛。