前言
对比下vue-router和react-router的使用异同点,以便更好的记忆两者的知识
使用
vue
安装
npm install vue-router
man.js中应用他
import Vue from "vue"
import VueRouter from "vue-router"
Vue.use(VueRouter)
//1. 定义路由
let routes = [
{
path:'/home',
name:'home',//给路由命名,
component:Home,
meta:{info:'123'}//路由元信息
},
{
path:'/login',
component:Login
}
]
//2. 创建路由实例
let router = new VueRouter({
routes
})
//3. 挂载路由到根实例上
const app = new Vue({
router
}).$mount('#app')
react
安装
npm install react-router-dom
使用
import {BrowserRouter,Route} from "react-router-dom"
function App(props){
return (
//使用时,一定要在外围包裹一个BrowserRouter或HashRouter
<BrowserRouter>
<Route path='/home' component={Home}></Route>
<Route path='/login' component={Login}></Route>
</BrowserRouter>
)
}
Link标签
vue
<router-link to='/path'>跳转链接</router-link>
<router-link :to="{path:'/path'}">跳转链接</router-link>
//命名路由 等同/user/123
<router-link :to="{name:'user',params:{ userId:123 }}"></router-link>
//带查询参数 等同/user?name=cat
<router-link :to="{name:'user',query:{name:'cat'}}"></router-link>
// replace属性 使用router.replace和不是router.push
<router-link to="/path" replace></router-link>
<router-link>
默认是a
标签,想替换成span
标签,可<router-link tag='span'>
<router-link>
点击后,会默认给标签添加.router-link-active
类名,可基于这个类名来添加点击态的样式
自定义链接激活时使用的css类名<router-link active-class="激活时的类名">
react
import {Link} from "react-router-dom"
function App(props){
return (
<Link to='/path'>跳转链接</Link>
// 等同 /path?name=cat#the-hash 在组件中可通过this.props.location.state获取Link中的state内容
<Link to={{pathname:'/path',search:'?name=cat',hash:'#the-hash',state:{info:'123'}}}></Link>
//func => 返回对象
<Link to={location=>({...location,pathname:'/path'})}></Link>
// func => 返回字符串
<Link to={location=>`${location.pathname}?name=cat`}></Link>
// replace
<Link to='/path' replace>链接</Link>
)
}
Link没有激活属性,需要有激活属性用<NavLink>
import {NavLink} from "react-router-dom"
function App(props){
return (
<NavLink to='path' activeClassName='激活态类名'></NavLink>
<NavLink to='path' activeStyle={{color:'red'}}></NavLink>
)
}
视图组件
视图组件,解决匹配到路由时,组件在哪里渲染的问题
vue
<router-view>
会渲染路径匹配到的视图组件
//App.vue
<template>
<div>
我是app组件
<router-view></router-view>
</div>
</template>
// user.vue
<template>
<div>
我是user组件
<router-view></router-view>
</div>
</template>
// name.vue
<template>
<div>
我是name组件
</div>
</template>
// routes
<script>
const router = new VueRouter({
routes:[
{
path:'/user',
component:user,
children:[
path:'/user/name',
component:name
]
}
]
})
</script>
当路由匹配/user时,会将user组件渲染到App.vue中的router-view的位置
当路经匹配到/user/name时。name组件渲染到user.vue中的router-view的位置,uer组件渲染到App.vuew的router-view的位置
当一个路由要渲染多个组件时,可以使用命名视图
<router-view class='view one'></router-view>
<router-view class='view two' name='a'></router-view>
<router-view class='view three' name='b'></router-view>
<script>
const router = new VueRouter({
routes:[
{
path:'/',
components:[//记得加s
default:componentOne,
a:componentTwo,
b:componentThree
]
}
]
})
</script>
react
<Route>
负责匹配路由负责路视图渲染的位置,<Route>
在哪,组件就渲染在哪
<Route>
渲染组件的三种方式
<Route component={Home}></Route>//路由匹配到时渲染
<Route children={Home}></Route>// 路由不管有没有匹配上,都会渲染
<Route><Home></Home></Route>// 等同上面,不管有没有匹配上,都会渲染
// value 是[history,loacation,match]三个对象
<Route render={value=><Home {...value}></Home>}></Route>
children的方式会覆盖component和render
children不管路由匹不匹配都会渲染,render和component只有在路由匹配时渲染
路由对象
vue
在组件中this.$route
即可拿到路由对象
-
$route.path
当前路由的绝对路径/user/name
-
$route.params
获取动态路由的参数当路由是这样设计时:
path:'/user/:id'
,浏览器访问'/user/123'时,在user组件中this.$route.params.id
即可获取id值123 -
$route.query
获取查询参数访问
/user?name=123
,在user组件this.$route.query.name
即可获取查询参数
react
在react中,要获取路由对象,必须在组件外层包裹上withRouter
function App(props){
return (
<Route path='/home' component={Home}></Route>
)
}
// Home.jsx
import {withRouter} from "react-router-dom"
class Home extends Component{
render(){
return <div>home</div>
}
}
export default withRouter(Home)
只有包裹上withRouter
,Home组件的props中才有history,location,match
三个对象
history
action
,可得知路由栈的操作是POP
,PUSH
,REPLACE
location
pathname
当前路由的绝对路径search
查询参数,访问/user?userId=123
=>可拿到?userId=123
hash
:#the-hash
state
:类似vue的路由元信息meta
,在这里的存储的信息不会明文显示在浏览器上
match
params
当<Route to='/user/:id'/ component={user}>
,访问/user/123
在user
组件中。this.props.match.params.id
可以获取idisExact
,当前路由是不是完全匹配
编程式导航
vue
$route
是路由对象,而$router
上挂载方法
this.$router.push('/path')
this.$router.push({path:'/path'})
this.$router.push({name:'user',params:{id:123}})
等同/user/123
this.$router.push({name:'user,query:{name:'123'}'})
等同/user?name=123
this.$router.replace()
同上,不会向路由栈中添加新路由,只会替换当前路由this.$router.go(1)
向前进一步,等同history.forward()
this.$router.go(-1)
后退一步
react
this.props.history.push('/user',{info:123})
,第一个参数path,第二个是state(类似vue路由的元信息)this.props.history.replace(path,state)
this.props.history.go(n)
等同vuerouter
this.props.history.goBack()
浏览器回退一步this.props.history.forForward()
浏览器前进一步
全局导航守卫
用于保护那些需要权限才能访问的页面
vue
import Vue from "vue"
import VueRouter from "vue-router"
Vue.use(VueRouter)
let router=new VueRouter({
routes:[
{
path:'/login',
component:Login
name:'login'
},
{
path:'/home',
component:home,
name:'home'
},
{
path:'/user',
name:'user'
component:User
}
]
})
//路由白名单,没有权限也能访问的页面
let whiteList = ['/login','/home']
//全局前置导航守卫,每次跳转前都会调用该
router.beforeEach((to,from,next)=>{
//to是将要访问的路由对象
//from是将要离开的路由对象
//访问路径在白名单内,任其跳转
if(whitelist.includes(to.path){
next()//记住一定要next()
}else{
if(isLogin)//已经登录的,任其跳转
next()
}else{
next('/login')//没有登录的跳转到登录页面
}
}
})
react
在路由组件中,this.props.history.listen(fn)
可以监听路由的变化,我们可以在路由跟组件,监听路由变化,做出相应的跳转
// App.jsx
function App(){
<BrowserRouter>
<RouterGuard></RouterGuard>
</BrowserRouter>
}
// RouterGuard.jsx
import {withRouter,Route} from "react-router"
class RouterGuard extends Component{
whilteList=['/login','/home']
componentDidMount() {
this.unlisten = this.props.histroy.listen((location,action) => {
//location是路由对象,action是操作路由栈的方式
let { pathname } = location;
//访问路径不在白名单内并且没有登录
if(!this.whiteList.includes(pathname)&&!isLogin){
this.props.history.replace('/login')
}
});
}
// 组件卸载时,解除监听
componentWillUnmount() {
this.unlisten();
}
render(){
return (
<>
// 在这里写路由
<Route path='/home' component={home}></Route>
...
// 也可使用下面将使用的方法getRoue()
</>
)
}
}
export default withRouter(RouterGuard)
##react模拟vue配置路由的方式
// routerConfig.js
import Login from './view/Login';
import Home from './view/Home';
import Root from './view/Root';
let routes = [
{
path: '/',
component: Root,
children: [
{
path: '/login',
component: Login,
},
{
path: '/home',
component: Home,
},
],
},
];
// 将路由path转化成绝对路径
routes.forEach(item => {
if (item.children) {
item.children.map(child => {
if (!child.path.startsWith('/')) {
child.path = item.path + child.path;
}
});
}
});
export default routes
在写个组件遍历routes,返回<Route>
组件
function getRoute(routes){
//1. 判断routes是不是array
if(!Array.isArray(routes)){
return null;
}
// 遍历routes
let routesDom = routes.map((item,index)=>{
// 解构出item中的children,递归处理children
let {children,component:Component,render,..rest} = item;
return <Route
key={index}
{...rest}
render={value=>{
// value是[history,location,match]等信息
<Component {...value}>
//递归处理children
{getRoute(children)}
</Component>
}}/>
})
return <Switch>//switch组件保证只匹配一个路由(从上到下)
{routesDom}
</Switch>
}
export default withRouter(getRoute)
使用
function App(){
return (
<BrowserRouter>
<getRoute></getRoute>
</BrowserRouter>
)
}
路由元信息
使用路由元信息来设置页面的title
vue
import Vue from 'vue'
import VueRouter from "vue-router"
Vue.use(VueRouter)
let router = new VueRouter({
routes:[
{
path:'/home',
component:Home,
meta:{title:'首页'}//路由元信息,不会明文显示在浏览器上
}
]
})
router.beforeEach((to,from,next)=>{
let title = to.meta.title
document.title=title
})
react
在组件中设置location.state
this.props.location.state={title:'首页'}
//在根组件中监听路由变化
componentDidMount(){
this.unlisten = this.props.history.listen((location,action)=>{
const {title}=location.state
document.title=title
})
}
componentWillUnmount(){
this.unlisten()
}
路由跳转阻塞
当用户在一个页面中的input中输入了内容,还没保存就要跳转页面时,应该给予提示,此时阻塞路由
vue
导航守卫,组件独享的拦截器
beforeRouteLeave(to, from, nexxt) {
const allowTransition = window.confirm('确定跳转?')
if (allowTransition) {
next()//继续跳转
} else {
next(false)//取消跳转
}
},
react
componentDidMount(){
this.unblock = this.props.history.block((localtion,action)=>{
// location是将要跳转的路由对象
return '离开?'
})
}
//组件销毁时解除监听
componentWillUnmount() {
this.unblock();
}

如果想要自定义提示,
<BrowserRouter
getUserConfirmation={(message, callback) => {
// this is the default behavior
//const allowTransition = window.confirm(message);
//callback(allowTransition);
message就是history.block传递过来的信息‘离开’
接收message后,你可以自定义弹出一个modal来获取用户的操作
callback(boolean)//true则跳转,false则不跳转
}}
/>
结语
作者:胡志武
时间:2020/05/21
如果文中有错漏处,请看官们指正,如果觉得写得不错,请点个赞吧!