最近在学习react,在路由这一块有点看不懂,第一感觉是灵活性很大,想怎么来就怎么来,但问题也来了,稍微复杂一点就GG了,不如vue的傻瓜式配置来的方便。
先说一下vue的路由配置方式,目录结构如下(简化了结构)
━ src
├━ App.vue
├━ layout.vue
├━ router.js
┕━ main.js
main.js
的内容是官方标配,没什么好说的,引入路由配置并加载
import router from './router' // 引入路由配置
import App from './App.vue'
new Vue({
router, // 加载路由配置
render: h => h(App)
}).$mount('#app')
App.vue
中添加一个router-view
作为一级路由视图
<template>
<div id="app">
<router-view/>
</div>
</template>
router.js
书写路由配置
import Layout from './layout'
export default new VueRouter({
mode: 'history',
base: process.env.BASE_URL,
routes: [
{
path: '/',
component: Layout,
children: [
{
path: 'about',
component: () => import('./About.vue')
},
{
path: 'about2',
component: () => import('./About2.vue')
}
]
},
{
path: '/404',
name: '404',
component: () => import('./404.vue')
}
]
})
layout.vue
布局文件,中间添加一个router-view
作为二级路由视图
<template>
<div class="layout-container">
<header>
<router-link to="/">Home</router-link>
<router-link to="/about">About</router-link>
<router-link to="/about2">About2</router-link>
<router-link to="/404">404</router-link>
</header>
<router-view class="layout-content"></router-view>
<footer></footer>
</div>
</template>
简单的说一下,
App.vue
就是根元素,路径/
和/404
都将对应路由的组件渲染在App中的router-view
位置,分别是layout.vue
和404.vue
;
路径/
下的子路由about
和about2
对应的完整路径分别为/about
和/about2
,当匹配这两个路径时,首先会在App
根元素下渲染一级路由的组件,即layout.vue
,然后再在一级路由组件(layout.vue
)中的router-view
位置渲染二级路由组件。
现在来看一下react-router
的工作方式,一个简单的路由如下(用的是react-router-dom
,)
修改App.js
,使用了Switch
包裹,表示只渲染第一个匹配路由的组件
import React from 'react';
import { BrowserRouter, Route, Link } from "react-router-dom";
const Layout = props => (
<div className="layout-container">
<header>
<Link to='/'>Home</Link>
<Link to='/about'>About</Link>
<Link to='/about2'>About2</Link>
<Link to='/404'>404</Link>
</header>
{props.children}
<footer></footer>
</div>
)
const About = props => (
<div>this is About Page</div>
)
const About2 = props => (
<div>this is About2 Page</div>
)
const Page404 = props => (
<div>this is 404 Page <Link to="/">GO HOME</Link></div>
)
const App = props => (
<BrowserRouter>
<Switch>
<Route path='/404' component={Page404}></Route>
<Layout>
<Route path="about" component={About}></Route>
</Layout>
</Switch>
</BrowserRouter>
)
export default App;
对比vue,react-router一个很明显的区别就是路由是直接写在组件中的,这继承了react的核心思想,一切皆为组件。
其实仔细想想,从感官层面来讲,vue-router中的router-view也是一个特殊的组件,功能有点类似vue的动态组件
,通过匹配地址与路由,将对应的组件替渲染出来;react-router也是如此,有了Switch
,react-router将匹配到的唯一路由对应的组件渲染出来,这样一想,两者其实很相似,区别在于vue将这一过程在内部封装简化了,而react则显式的需要我们手动去书写这一过程。
调试react-router可以发现,在404页面和home页面之间切换,页面时发生变化了的,说明路由生效,但是点击about页面无任何变化,这就有点惆怅了,难道不支持嵌套路由???
其实并不是,如果把404路由放到Layout下面去的话(如下更改,下面简称代码2,原来的简称代码1),会发现404页面也无法切换了
// 代码2
const App = props => (
<BrowserRouter>
<Switch>
<Layout>
<Route path="about" component={About}></Route>
</Layout>
<Route path='/404' component={Page404}></Route>
</Switch>
</BrowserRouter>
)
这是因为使用了Switch
,它只会渲染第一个匹配的组件,那么代码2中不管匹配到什么地址,Switch
在渲染了Layout
后就不在渲染其他的路由对应的组件了,那么加一层路由地址呢???(如下代码3)
const App = props => (
<BrowserRouter>
<Switch>
<Route path='/'>
<Layout>
<Route path='about' component={About}></Route>
</Layout>
</Route>
<Route path='/404' component={Page404}></Route>
</Switch>
</BrowserRouter>
结果还是不起作用,通过react-dev-tool
发现,内部的路由组件根本没有发生变化,其实到这里问题就已经很明显了,加上之前的分析,react复杂路由之所以不生效就是因为路由匹配问题,不管是/404
还是/about
,在代码3这种写法下,路由匹配到
这里的时候就截至了,不会再往下或往后继续匹配,这让我想起了ThinkPHP的路由,需要将静态路由和长路由写在前面,或者添加截至符号。react-router可以通过Route
的exact
属性来达到完全匹配的效果。代码修改如下(代码4)
const App = props => (
<BrowserRouter>
<Switch>
<Route exact path='/' component={Layout}></Route>
<Route path='/404' component={Page404}></Route>
<Layout>
<Route path='/about' component={About}></Route>
<Route path='/about2' component={About2}></Route>
</Layout>
</Switch>
</BrowserRouter>
)
代码4可以实现4个页面的切换,效果和vue一致。
总结:react的路由配置和vue相比,还是有很大区别的,vue的嵌套路由比较直观,书写也很方便,react的路由配置更贴近于传统的路由配置方式。官方倒是有嵌套路由的案例,但是那个还没有弄懂,代码调试不通过,我看的那个应该是旧版的文档,可能不适用新版的react-router。