vue-router
动态匹配
比如有一个列表页 /list
,进入详情页都要使用同一个组件
const router = new VueRouter({
routes: [
{
path: '/user/:id', // 动态路径参数,以冒号开头
component: DetailComponent
}
]
});
这样/list/1
、/list/2
都会匹配到同一个组件
404 路由
含有通配符的路由一定要放在最后
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
const router = new Router({
mode: 'history',
routes: [
{
path: '/',
name: 'adminLayout',
component: () => import('@/admin/layout')
},
{
path: '/404',
component: () => import('@/views/404')
},
{
path: '*',
redirect: '/404'
}
]
});
export default router;
嵌套路由
import Vue from 'vue';
import Router from 'vue-router';
Vue.use(Router);
const router = new Router({
mode: 'history',
routes: [
{
path: '/admin',
name: 'adminLayout',
component: () => import('@/admin/layout'),
redirect: '/admin/ecs',
children: [
{
path: 'ecs',
name: 'ECS',
component: () => import('@/admin/ecs')
},
{
path: 'oss',
name: 'OSS',
component: () => import('@/admin/oss')
}
]
}
]
});
export default router;
以 /
开头的嵌套路径会被当作根路径,所以 children 中的路径不用设置成 path: '/admin/ecs'
,
直接设置成 path: 'ecs'
即可,不要加/
。但是路由重定向时要写完整 redirect: '/admin/ecs'
编程式导航
在 Vue 实例内部,可以通过 $router
访问路由实例。
router.push()
this.$router.push(...)
这种方式会向 history 栈中添加一个新的记录,当点击浏览器后退按钮时,会回到上一个 url。
类似于window.history.pushState()
声明式导航 <router-link :to="...">
会创建 a 标签来定义导航链接。它会在内部调用router.push
方法
参数可以是字符串路径或者地址对象
this.$router.push('/admin/ecs');
路由传参
方式一、query
// 传参
this.$router.push({ path: '/admin/ecs', query: { id: 1 } });
// 取值
this.$route.query.id;
使用这种方式,参数会拼接在路由后面,出现在地址栏
方式二、params
// 传参
this.$router.push({ name: 'ECS', params: { id: 1 } });
// 取值
this.$route.params.id;
使用这种方式,参数不会拼接在路由后面,地址栏上看不到参数
由于动态路由也是传递 params 的,所以在 this.$router.push()
方法中 path 不能和 params 一起使用,否则 params 将无效。
需要用 name 来指定页面,即通过路由配置的 name 属性访问。
如果需要传递多个参数,如下所示:
this.$router.push({
name:'second',
params: { id:'20180822', name: 'query' }
});
// params接收参数
this.id = this.$route.params.id ;
this.name = this.$route.params.name ;
// 路由
{
path: '/second/:id/:name',
name: 'second',
component: () => import('@/view/second')
}
如果路由后面没有 /:id/:name
,刷新页面后会发现页面失败。
注意:使用 query 刷新不会丢失数据、使用 params 刷新会丢失 params 里面的数据
router.replace()
不会向 history 栈中添加记录,而是会替换当前的 history 记录,类似window.history.replaceState()
this.$router.replace('/admin/ecs');
声明式 <router-link :to="..." replace>
router.go(n)
在 history 记录中向前或者后退多少步,类似window.history.go(n)
// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1);
// 后退一步记录,等同于 history.back()
router.go(-1);
router.back();
router.forward();
路由组件传参
比如从列表页进入详情页,需要携带 id 参数
路由配置:
const router = new VueRouter({
routes: [{ path: '/user/:id', component: User, props: true }]
});
如果 props
被设置为 true
,route.params
将会被设置为组件属性。
详情页使用 props: ['id']
接收参数。
export default {
props: ['id'],
data() {
return {
tableLoading: false
};
}
};
在组件中使用 $route
会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。
不推荐使用this.$route.params.id
获取参数。
推荐使用 props
将组件和路由解耦。
路由模式
vue-router 默认是 hash
模式,这种模式会在路径中带一个#
号。如果不想要#
号可以用 history 模式,配置如下:
const router = new VueRouter({
mode: 'history',
routes: [...]
})
history
模式需要后端支持,比如在 nginx 中需要添加如下配置,否则刷新页面就会报 404 错误。
location / {
try_files $uri $uri/ /index.html;
}
导航守卫
「导航」表示路由正在发生改变。守卫是异步解析执行,此时导航在所有守卫resolve
完之前一直处于等待中。
全局前置守卫
使用 router.beforeEach
注册一个全局守卫
const router = new VueRouter({ ... })
router.beforeEach((to, from, next) => {
// ...
})
- to 即将要进入的目标
- from 当前导航正要离开的路由
- next 函数,进行管道中的下一个钩子
比如要做路由拦截,用户没登录不让访问控制台
import Vue from 'vue';
import Router from 'vue-router';
import NProgress from 'nprogress';
import 'nprogress/nprogress.css';
Vue.use(Router);
const router = new Router({
routes: [
{
path: '/',
name: 'adminLayout',
meta: {
requireAuth: true
},
component: () => import('@/admin/layout'),
redirect: '/admin/ecs',
children: []
}
]
});
router.beforeEach((to, from, next) => {
if (to.matched.some(res => res.meta.requireAuth)) {
if (sessionStorage.getItem('access_token')) {
NProgress.start();
next();
} else {
next({ path: '/login' });
}
} else {
NProgress.start();
next();
}
});
router.afterEach(() => {
NProgress.done();
});
此处的 nprogress 是一个可以显示路由加载进度动画的插件,可忽略。
to
、from
均表示路由对象,
路由对象有一个matched
属性,是一个数组,包含当前路由的所有嵌套路径片段的路由记录。
some()是数组方法,表示一些,只要数组中的某一个元素符合指定的条件,就会返回 true,否则返回 false。
所以整体思路是先做一个路由全局前置守卫,若即将要进入的目标需要鉴权且sessionStorage
中有登录时存的 token,
则跳转到目标页,否则跳转到登录页。
全局后置钩子
不接受next
函数,也不会改变导航本身。
在上个例子中可以用来关闭加载动画
router.afterEach((to, from) => {
NProgress.done();
});
路由独有的守卫
beforeEnter
守卫,区别于全局守卫,这个只对单个路由有效
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
});
组件内的守卫
可以直接在组件内定义导航守卫
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
中不能获取实例,但可以通过传一个回调给next
来访问组件实例
beforeRouteEnter (to, from, next) {
next(vm => {
// 通过 `vm` 访问组件实例
})
}