现在都 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';
Router 组件变化
在 v5
里没有 Router
组件,换成更具体的组件了,HashRouer
和BrowserRouter
。
看代码更清晰
//v3 <Router history={browserHistory}></Router> <Router history={hashHistory}></Router> //v4+ v5+ <BrowerRouter></BrowerRouter> <HashRouer></HashRouer>
这个升级挺好,不用再配什么参数了
Route 升级了
Route
组件和 v3
版本的 Route
作用一致,都是在path
匹配的 时候,渲染指定组件,但是写法上有些变化,而且增加了一些特性。
组件渲染方式1
<Route component={Home}/>
component
属性和 v3
中的 component
属性保持一致,表示path
匹配的时候才会渲染的组件。
组件渲染方式2
增加了render
属性,v3
中不存在这个属性,render
表示在path
匹配时被调用的方法,而不是创建一个组件,但是需要一个返回值,返回一个组件或者null。
<Route render={(props) => (<Home></Home>)/> <Route render={(props) => (<div>hello,前端技术江湖</div>)/>
组件渲染方式3
新增children
属性,children
与 render
一样,但是不会匹配地址,路径不匹配时 URL的match 值为 null,可以用来根据路由是否匹配动态调整UI。
Switch 组件的坑
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> );
此时将会被匹配到,后面
Switch
会停止继续后面的匹配,只渲染About
组件。
exact 属性
问题就这样解决了吗?
在下面代码增加了新的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 一个新特性
这个是在 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} />
props.params 取值
//v3 this.props.params //v4 v5 this.props.match.params
location.query 取值
//V3 获取query可以这么获取 this.props.location.query //V4 5 只能通过 search 来取值,一般是自己写一个方法或者使用query-string库 this.props.location.search
location.action 属性转移
// 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>
使用起来很不顺手,所以我直接放弃了嵌套的写法,不要和我一样。
Route 监控事件移除
在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
升级改动不仅仅是上面这些,整体改动真的很大。与其说是升级不如说是重写,难怪很多人不愿意升级,升级了才知道升级的痛。现在都 React Router 5 了,你是不是还在用v3呢?
不光是你在用,我们很多项目也在用,懒得升级,感觉改动太大,升级了后谁知道会出什么问题,别没事找事。
毕竟v4是两年前的了,再不升级真的就有点说不过去了,直接拿一个小项目开刀,直接3-5吧,整体来说还好,v5版完全向下兼容react15,所以如果你的 react 是15的话没啥影响。
这里就简单的总结下,这次升级的一些内容。