一、安装vue-router
脚手架安装:
npm install vue-routeroryarn add vue-router
二、创建组件
src->router->index.js:这个文件创建路由的核心文件:
import Vue from 'vue' // 先引入vue文件
import Router from 'vue-router' // 引入vue-router 文件
Vue.use(Router) // 全局注册路由
import Home from '../components/router-demo/home.vue'
import About from '../components/router-demo/about.vue'
import User from '../components/router-demo/user'
import Name from '../components/router-demo/name'
export default new Router({ // 创建路由实例
routes:[{ // 配置路由信息
path:'/home', // 路径
name:'home', // 路径name
component:Home // 对应组件
},{
path:'/about', // 路径
name:'about', // 路径name
// 当前路径下的子路由信息
children: [{
path:'user',
name:'user',
component:User
},{
path:'name',
name:'name'
component:Name
}]
}]
})
// app.vue
<template>
<div>
<router-link to="/home">Home</router-link>
<router-link to="/about">About</router-link>
<router-view></router-view>
</div
</template>
// 子路由配置about.vue
<template>
<div>
<router-link to="/about/user">user</router-link>
<router-link to="/about/name">name</router-link>
<router-view></router-view>
</div>
</template>
三、页面导航/页面显示
1、
router-link
默认会被渲染成带有链接的标签,可有通过to来指定跳转的组件
被选中router-link将自动添加一个class属性值router-link-active
to:导航的路径,要填写router/index.js中配置path值。
2、
router-view
用于渲染匹配到的组件,同时页面显示。
-
- 如果想要有过度效果可以在router-view外层包裹transition
-
keep-live可以缓存数据,这样不至于重新渲染路由组件时候,之前哪个路由组件被清楚。
<template>
<transition>
<keep-alive>
<router-view></router-view>
</keep-alive>
</transition>
</template>
四、动态路由匹配
我们经常需要把某种模式匹配到的所有路由,全都映射到同个组件,调用
router的map方法映射路由,每条路由是以key=>value的形式存在,key是路径,value是组件。
例:User组件下,对于ID不同的用户,都使用这个组件来渲染,那么,可以在vue-router的路径中使用[动态路由参数]来达到这个效果。
const router = new VueRouter({
routes: [{
// 动态路径参数 以冒号开头
path: '/user/:id',
component: User
}]
})
export default router
另外一种:'user/foo'和'user/bar' 都映射到相同的路由,参数值可以通过
$route.params.id访问
<template>
<div>
<p>user: {{$routes.params.id}}</p>
</div>
</template>
**or**
// 使用路由守卫
<template> '...'</template>
<script>
beforeRouteUpdate (to, from, next) {
// react to route changes...
// don't forget to call next()
}
</script>
在一个路由中设置多段『路径参数』,对应的值都会设置到 $route.params 中。例如:

提醒一下,当使用路由参数时,例如从/user/foo导航到/user/bar,原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用。复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象
<template>...</template>
<script>
watch:{
'$route'(to,from){
// 对路由作出响应
}
}
</script>
五、vue-router参数传递
vue-router传递大致分为两类
- 编程式传参:router.push
- 声明式传参:
<router-link/>
声明式传参:
-
<router-link to = "/user"> -
router-link标签to传参<router-link :to = "{ name:'eric',params:{ key: value }}">跳转链接</router-link> // 访问参数:{{$route.params.key}} // 同时router/index.js中 path:'/home/:id',name: 就是我们路由配置中起的name值,命名式路由就是用一个名称来标示路由,在定义路由时候设置一个name属性即可,
params: 就是我们要传递的参数,它也是对象的形式,在对象可以传递多个值。
$route.params.key进行接收 -
<router-link :to="{ path:'user', query: { userID: 53 }}"> 跳转到详情页 </router-link>访问参数:{{$route.query.userID}}
编程式传参:
this.$router.push("detail"):detail为要跳转的路由地址,该方式简单但无法传递参数。this.$router.push({ name:"detail",params:{personId:53}}):detail为要跳转的路由地址,params为传递的参数,目标页面可以使用this.$router.params.personId来获取传递的参数,该方式有一个缺点就是在目标页面刷新时传递过来的参数会丢失。this.$router.push({path:"/detail",query:{personId:53}}):detail为要跳转的路由地址,query为传递的参数,目标页面使用this.$router.query.personId来获取传递的参数,该方式会把传递的参数放在url上,如:localhost:8080/#/detail/?personId=53。
url传参:
(:) 冒号的形式传递参数
const router = new VueRouter({
routes: [{
// 动态路径参数 以冒号开头
path: '/params/:id/:userName',
component: User
}]
})
export default router
// 访问url传递的参数
<template>
<div>
<h2>{{ msg }}</h2>
<p>新闻ID:{{ $route.params.id}</p>
<p>用户名:{{ $route.params.userName}}</p>
</div>
</template>
<script>
export default {
name: 'params',
data () {
return {
msg: 'params page'
}
}
}
</script>
query和params区别
- query: 相当于get请求,页面跳转,地址栏中会看到请求参数。
- params:相当于post请求,页面跳转,地址蓝中不会看到请求参数。
六、重定向
刚进入应用都是进入到“/”这个路由的,如果想直接进入到“/goods”怎么办。
有两种方法。一种是利用重定向,另一种是利用vue-router的导航式编程。
// 一种
const routes = [{
path: '/',
// 重定向
redirect: '/goods'
}]
// 重定向的目标也可以是一个命名的路由。
const routes = [{
path: '/',
redirect: {
name: 'goods'
}
}]
// 重定向时一个方法
{
path:'/',
redirect:to => {
// 方法接收 目标路由 作为参数
// return 重定向的 字符串路径/路径对象
}
}
alias:别名的使用- 首先我们在路由配置文件里给路径起一个别名:'eric'
{ path: '/hi', component: Hi, alias:'/eric' }
- 配置我们的,起过别名之后,可以直接使用标签里的to属性,进行重新定向。
router-link to="/eric">jspang</router-link>
区别:
redirect:仔细观察URL,redirect是直接改变了url的值,把url变成了真实的path路径。alias:URL路径没有别改变,这种情况更友好,让用户知道自己访问的路径,只是改变了<router-view>中的内容(⭐️⭐️⭐️⭐️⭐️别名alias在path为'/'中,是不起作用的)
七、vue-router导航守卫
当做vue-cli项目的时候感觉路由跳转前做一些验证,比如:登陆验证,是网站的普遍需求。对此,vue-router提供的beforeEach可以方便实现全局导航守卫。
如何设置全局守卫:
可以使用
router-beforeEach注册一个全局前置守卫:
const router = new VueRouter({...})
router.beforeEach((to,from,next)=>{
next();
})
当一个导航触发时,全局前置守卫按照创建顺序调用。守卫是异步解析执行,此时导航在所有守卫resolve完之前一直处于等待中
- to:route 即将要进入的目标路由。
- from:route 当前导航正要离开的路由
- next: function 一定要调用该方法来resolve这个钩子,执行效果依赖next方法调用参数。
- next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是 confirmed (确认的)。
- next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
- next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
- next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。
确保要调用next方法,否则钩子就不会被resolved
const router = new VueRouter({ ... }) //路由配置
const whiteList = ['/error', '/register/regindex', '/register/userauthent', '/register/submit']
// 路由白名单
router.beforeEach((to,from,next) => {
console.log("进入守卫
if (userInfo.user_id>0){
console.log("登录成功");
next(); //记得当所有程序执行完毕后要进行next(),不然是无法继续进行的;
}else{
console.log("登录失败");
getUserInfo.then(res => {
if(res){
if (res.user_id){
if (res.status == 4) {
//账号冻结
next({ path: '/error', replace: true, query: { noGoBack: true } })
}
if (res.status == 3) {
//认证审核中
next({ path: '/register/submit', replace: true, query: { noGoBack: true } })
}
if (res.status != 1 && res.status != 3) {
if (!res.mobile ) {
next({ path: '/register/regindex', replace: true, query: { noGoBack: true }})
} else {
//绑定完手机号了
next({ path: '/register/userauthent', replace: true, query: { noGoBack: true } })
}
}
next(); //记得当所有程序执行完毕后要进行next(),不然是无法继续进行的;
}else{
if (whiteList.indexOf(to.path) !== -1) { // 在免登录白名单,直接进入
next(); //记得当所有程序执行完毕后要进行next(),不然是无法继续进行的;
}else{
next({ path: '/register/regindex', replace: true, query: { noGoBack: true }})
}
}
}else{
}
}
}).catch(()=>{
//跳转失败页面
next({ path: '/error', replace: true, query: { noGoBack: true }})
})
}
});
export default router
最后和大家说下如果白名单太多或项目更大时,我们需要把白名单换为vue-router路由元信息: 直接在路由配置的时候,给每个路由添加一个自定义的meta对象,在meta对象中可以设置一些状态,来进行一些操作。用它来做登录校验再合适不过了。
// 路由信息配置
{
path: '/actile',
name: 'Actile',
component: Actile,
meta: {
login_require: false
},
},
{
path: '/goodslist',
name: 'goodslist',
component: Goodslist,
meta: {
login_require: true
},
children:[
{
path: 'online',
component: GoodslistOnline
}
]
}
这里我们只需要判断item下面的meta对象中的login_require是不是true,就可以做一些限制了
router.beforeEach((to, from, next) => {
if (to.matched.some(function (item) {
return item.meta.login_require
})) {
next('/login')
} else
next()
})
全局解析守卫
你可以用router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用
路由独享守卫
- 路由配置文件中的钩子函数
beforeEnter钩子,进入该路由时执行的操作:
// router/index.js
{
path:'/params/:userName',
component:Params,
// 路由信息钩子
beforeEnter:(to,from,next)=>{
console.log('我进入了params模板');
console.log(to);
console.log(from);
next();
}
- 组件内的守卫
beforeRouteEnter:在路由进入前的钩子函数。
beforeRouteLeave:在路由离开前的钩子函数。
beforeRouteUpdate:在路由更新是的够子函数
//.vue 文件
const Foo = {
template: `...`,
beforeRouteEnter (to, from, next) {
// 在渲染该组件的对应路由被 confirm 前调用
// 不!能!获取组件实例 `this`
// 因为当守卫执行前,组件实例还没被创建
},
beforeRouteUpdate (to, from, next) {
// 在当前路由改变,但是该组件被复用时调用
// 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
// 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
// 可以访问组件实例 `this`
},
beforeRouteLeave (to, from, next) {
// 导航离开该组件的对应路由时调用
// 可以访问组件实例 `this`
}
}
beforeRouteEnter 守卫 不能 访问 this,因为守卫在导航确认前被调用,因此即将登场的新组件还没被创建。 不过,你可以通过传一个回调给 next来访问组件实例。在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 next(false) 来取消。
beforeRouteLeave (to, from , next) {
const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
if (answer) {
next()
} else {
next(false)
}
}
导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示『加载中』之类的指示。当你使用这种方式时,我们会马上导航和渲染组件,然后在组件的created钩子中获取数据。这让我们有机会在数据获取期间展示一个loading状态,还可以在不同视图间展示不同的loading状态。假设我们有一个 Post 组件,需要基于 $route.params.id 获取文章数据
<template>
<div class="post">
<div class="loading" v-if="loading">
Loading...
</div>
<div v-if="error" class="error">
{{ error }}
</div>
<div v-if="post" class="content">
<h2>{{ post.title }}</h2>
<p>{{ post.body }}</p>
</div>
</div>
</template>
export default {
data () {
return {
loading: false,
post: null,
error: null
}
},
created () {
// 组件创建完后获取数据,
// 此时 data 已经被 observed 了
this.fetchData()
},
watch: {
// 如果路由有变化,会再次执行该方法
'$route': 'fetchData'
},
methods: {
fetchData () {
this.error = this.post = null
this.loading = true
// replace getPost with your data fetching util / API wrapper
getPost(this.$route.params.id, (err, post) => {
this.loading = false
if (err) {
this.error = err.toString()
} else {
this.post = post
}
})
}
}
}
导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后再执行导航。 通过这种方式,我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。
export default {
data () {
return {
post: null,
error: null
}
},
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
八、History模式
vue-router默认hash模式 - 当 URL改变是,页面不会重新加载。
vue-router设置history模式 - 充分利用history.pushState API来完成URL跳转而无须重新加载页面。
const router = new VueRouter({
mode: 'history',
routes: [...]
})
mode两个值:
histroy:当你使用history模式时,URL就像正常的url,http://www.dxl.com/user/id不过这种模式要玩好,还需要后台配置支持。因为我们的应用是个单页客户端应用,如果后台没有正确的配置,当用户在浏览器直接访问http://www.dxl.com/user/id就会返回404。所以要在服务端增加一个覆盖所有情况的候选资源:如果 URL 匹配不到任何静态资源,则应该返回同一个index.html页面,这个页面就是你 app 依赖的页面。 这么做以后,你的服务器就不再返回 404 错误页面,因为对于所有路径都会返回 index.html 文件。为了避免这种情况,你应该在 Vue 应用里面覆盖所有的路由情况,然后在给出一个 404 页面
const router = new VueRouter({
mode: 'history',
routes: [
// 这里的path:'*'就是找不到页面时的配置,component是我们新建的一个Error.vue的文件
{ path: '*', component: Error}
]
})
hash:默认’hash’值,但是hash看起来就像无意义的字符排列,不太好看也不符合我们一般的网址浏览习惯。不配置时是这样的:
http://localhost:8080/#/users,多个#号。
九、数据获取
有时候,进入某个路由后,需要从服务器获取数据。
- 导航完成后获取数据:先完成导航,然后在接下来的组件生命周期钩子中获取函数。在数据获取件显示“显示加载中”之类的指示。
- 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。
导航完成后获取数据:
使用这种方式,马上会导航和渲染组件,然后在组件的
create钩子中获取数据。这时候我们有机会在数据获取期间展示一个loading状态,还可以在不同视图间展示不同的loading状态。
<template>
<div>
<div v-if="loading" class="loading">
loading....
</div>
<div v-if= "error" class="error">
{{ error }}
</div>
<div v-if="post" class= "content">
<h3>{{post.title}}</h3>
<h3>{{post.body}}</h3>
</div>
</div>
</template>
<script>
export default {
data(){
return{
loading: false,
post:null,
error:null
}
},
created(){
// 调用方法
this.fetchData()
},
watch:{
// 监听路由有变化,再次执行该方法
'$route':'fetchData'
},
methods:{
// 获取数据 设置状态
fetchData(){
this.error = this.post = null
this.loading = true
// 请求数据
getPost(this.$route.params.id,(err,post)=>{
this.loading = false
if(err){
this.error = err.toString()
}else{
this.post = post
}
})
}
}
}
</script>
导航完成前获取数据:
通过这种方式,在导航赚到新的路由前获取数据。
<template>
<div>
<div v-if="loading" class="loading">
loading....
</div>
<div v-if= "error" class="error">
{{ error }}
</div>
<div v-if="post" class= "content">
<h3>{{post.title}}</h3>
<h3>{{post.body}}</h3>
</div>
</div>
</template>
<script>
export default {
data(){
return{
post:null,
error:null
}
},
//
beforeRouteEnter (to, from, next) {
getPost(to.params.id, (err, post) => {
next(vm => vm.setData(err, post))
})
},
// 路由改变前,组件就已经渲染完了
// 逻辑稍稍不同
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, (err, post) => {
this.setData(err, post)
next()
})
},
methods: {
// 判断数据获取又没有错误
setData (err, post) {
if (err) {
this.error = err.toString()
} else {
this.post = post
}
}
}
}
}
</script>