react-router4的好处究竟在哪里
最近被人问到你的项目中为什么要使用react-router v4,瞬间有点懵逼。回答说,帮我们实现路由啊,被反问,仅仅是为了帮助实现路由吗,自己也可以手写一套路由啊,没有什么难度啊,为什么要使用别人的呢。当他问到这的时候,我就知道问到盲区了。于是晚上空闲的时候我仔细研究和查阅了一下,说实话,react-router不知道已经用了多少次了。可以说,里面的源码我都已经吃透了,但是竟然从来没有想过react-router的优点在哪里,这点确实是值得反思的
react-router v4与react-router v3的比较
查阅文档及翻看博客发现,React Router v4 最大的变更,不是API的变更,而是从静态路由到动态路由的变化。什么是静态路由呢?静态路由是一堆在应用运行前就已经定义好的路由配置,应用需要在启动时,加载这些配置,构建出整个应用的路由表,然后当接收到某一请求时,根据请求地址,到应用路由表中找到对应的处理页面或处理方法。就是说路由一定要集中配置,没有集中配置的路由无法找到匹配页面。不管是前端开发,还是后端开发,只要涉及到路由,大部分情况下,其实我们使用的都是静态路由。例如,React Router v3版本中,我们会配置一个类似下面形式的路由:
<Router history={browserHistory}>
<Route path='/' component={login}>
<Route path='about' component={About}>
<Route path='contact' component={Contact}>
// ...
</Route>
</Router>
它的基本工作流程是:Router组件根据所有的子组件Route,生成全局的路由表,路由表中记录了path与UI组件的映射关系,然后Router监听path的变化,当path变化时,根据新的path,找出对应的component,按一定层级将这些UI组件渲染出来。
这种方式看起来简单明了,理解起来也非常的轻松。但是这种实现方式是违背了react的组件化思想的。react提倡一切皆组件,组件是什么,对应到页面上它就是页面的一部分,也就是UI。但是,v3的route组件只是包了一层react的壳子,实际上它并没有渲染组件,这点我们可以通过它的源码就可以看出来:
const Route = createReactClass({
// 省略无关代码
/* istanbul ignore next: sanity check */
render() {
invariant(
false,
'<Route> elements are for router configuration only and should not be rendered'
)
}
})
可以发现render里面并没有渲染任何东西,所以说它不是传统意义上的react组件。当然,router的开发者也意识到了这个问题,所以在v4中,对React Router 进行了重写,将Route作为普通React组件看待,每个Route也负责UI的渲染工作,让React Router在React的大框架下运转。我们用v4版本实现一个路由:
<BrowserRouter>
<Switch>
<Route exact path="/login" render={() => <LoginView />} />
<Route
path="/:tabIndex/:module"
render={() => <HomeComponent />}
/>
<Route
path="/"
render={() => <RedirectComponent moduleName="community" />}
/>
</Switch>
</BrowserRouter>
看起来写法跟上面的那个例子没有什么大的区别,所以说它这个路由升级还是比较友好的。但是深层次的东西是有巨大的改变的,我们再俩看一下route的实现:
class Route extends React.Component {
//...省略无关代码
render() {
const { match } = this.state
const { children, component, render } = this.props
const { history, route, staticContext } = this.context.router
const location = this.props.location || route.location
const props = { match, location, history, staticContext }
return (
component ? ( // component prop gets first priority, only called if there's a match
match ? React.createElement(component, props) : null
) : render ? ( // render prop is next, only called if there's a match
match ? render(props) : null
) : children ? ( // children come last, always called
typeof children === 'function' ? (
children(props)
) : !Array.isArray(children) || children.length ? ( // Preact defaults to empty children array
React.Children.only(children)
) : (
null
)
) : (
null
)
)
}
}
可以发现每一个router对应的都会渲染对应的组件,macth方法会自动给我们计算,如果没有匹配的。则返回一个null。这种模式的路由就是动态路由。可见,动态路由发挥作用的时间是在组件渲染时,而不是通过提前配置的方式,在应用刚收到请求时,就已经知道该渲染哪些组件了。
从上面的分析,可以得出动态路由的一个优点是,它会同时负责UI的渲染工作,而不是单纯的路由配置工作。此外,动态路由的另外一个优点是,你可以在任意时间、任意地点自由添加新的Route。例如,在上面的例子中,我想在后面定义更多的页面,你想在哪都可以,在主页面或者其他地方都可以,它会将route匹配渲染的组件作为当前组件的一部分进行渲染。非常的方便,下面是一个例子:
class Layout extends React.Component {
render(){ return
(<div className="Layout-content">
<div className="Layout-header-part">
<div className="header-right-part">
<MenuList
menuList={menuList}
activeMenu={activeMenu}
menuClick={this.menuClick.bind(this)}
/>
<UserAction
//userInfo={UserStore.userInfo}
userPopTolls={this.userPopTolls}
logOut={this.logOut.bind(this)}
showAlarmAction={this.showAlarmAction.bind(this)}
clickPopAction={this.clickPopAction.bind(this)}
//ifShake={ifShake}
/>
</div>
</div>
{/* <MenuList menuClick={this.menuClick} activeMenu={activeMenu}/> */}
<Switch>
{menuList.map((v, i) => <Route key={i} path={`/mainPage/${v.name}`} render={() => <v.component />} />)}
</Switch>
</div>)
}}
我们直接在layout里面定义了我们需要的路由,V4方便的地方就是我们可以将路由写在一个文件里面随意的配置改动,非常的方便。
总结一下,虽然React Router v4 重构了路由使用的思想,但却和React的设计思想更加切合,个人认为是一个巨大的进步。使用React Router v4 时,你需要忘掉以前使用静态路由的思维方式,把路由当成普通组件看待,习惯了这个思维转变后,你就会发现React Router v4的魅力所在了。