现在都 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的话没啥影响。
这里就简单的总结下,这次升级的一些内容。


