对比下vue-router和react-router的使用异同点,以便更好的记忆两者的知识
安装
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') 复制代码
安装
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> ) } 复制代码
<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="激活时的类名">
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> ) } 复制代码
视图组件,解决匹配到路由时,组件在哪里渲染的问题
<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> 复制代码
<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只有在路由匹配时渲染
在组件中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中,要获取路由对象,必须在组件外层包裹上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
,当前路由是不是完全匹配$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)
后退一步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()
浏览器前进一步用于保护那些需要权限才能访问的页面
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')//没有登录的跳转到登录页面 } } }) 复制代码
在路由组件中,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
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 }) 复制代码
在组件中设置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中输入了内容,还没保存就要跳转页面时,应该给予提示,此时阻塞路由
导航守卫,组件独享的拦截器
beforeRouteLeave(to, from, nexxt) { const allowTransition = window.confirm('确定跳转?') if (allowTransition) { next()//继续跳转 } else { next(false)//取消跳转 } }, 复制代码
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
如果文中有错漏处,请看官们指正,如果觉得写得不错,请点个赞吧!