一、常见问题:
复用组件,路由反复变化了,组件created不再触发
解决方案:使用watch 监听$router
watch:{
$router:{
deep:true,
immediate: true, // 立即执行
handler(newValue,oldValue){
}
}
}
二、进阶-路由守卫
正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。
有多种机会植入路由导航过程中:
1、全局的
2、单个路由独享的
3、组件级的。
1、全局
// 全局
router.beforeEach((to, from, next) => { // ...
// to: Route: 即将要进入的目标 路由对象
// from: Route: 当前导航正要离开的路由
// next: Function: 一定要调用该方法来 resolve 这个钩子。
})
// 使用
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
if (window.isLogin) {
next()
} else {
next('/login?redirect='+to.fullPath)
}
} else {
next()
}
})
router.afterEach((to, from) => {
// ...
})
1.1、使用场景:全局登录【白名单】
1、使用router:meta数据,区分是否要校验登录(配合meta创建一个白名单)
2、使用beforeEach拦截。
3、防止出现死循环(排除登录、注册页面和mete中不需要校验页面)
// router 的 meta数据区分该路由是否需要校验登录
router.beforeEach((to, from, next) => {
if (to.meta.auth) {
if (window.isLogin) {
next()
} else {
next('/login?redirect='+to.fullPath)
}
} else {
next()
}
})
2、单个路由独享
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的。
3、组件内
beforeRouteEnterbeforeRouteUpdate(2.2 新增)beforeRouteLeave
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` 访问组件实例
})
}
注意 beforeRouteEnter 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。
beforeRouteUpdate (to, from, next) {
// just use `this`
this.name = to.params.name
next()
}
这个离开守卫通常用来禁止用户在还未保存修改前突然离开, 该导航可以通过 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)
}
}
三、进阶-完整路由解析流程
step1、导航被触发。
step2、在失活的组件里调用 beforeRouteLeave 守卫。
step3、调用全局的 beforeEach 守卫。
step4、在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
step5、在路由配置里调用 beforeEnter。
step6、解析异步路由组件。(各个组件的生命周期)。
step7、在被激活的组件里调用 beforeRouteEnter。
step8、调用全局的 beforeResolve 守卫 (2.5+)。
step9、导航被确认。 (各个组件的生命周期)。
step10、调用全局的 afterEach 钩子。
step11、触发 DOM 更新。
step12、调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
四、进阶-数据获取时机
路由激活时,获取数据的时机有两个:
4.1、路由导航前
// 组件未渲染,通过给next传递回调访问组件实例
beforeRouteEnter (to, from, next) {
getPost(to.params.id, post => {
next(vm => vm.setData(post))
})
},
// 组件已渲染,可以访问this直接赋值
beforeRouteUpdate (to, from, next) {
this.post = null
getPost(to.params.id, post => {
this.setData(post)
next()
})
},
4.2、路由导航后
created () {
this.fetchData()
},
watch: {
'$route': 'fetchData'
}
五、进阶-动态添加-router.addRoutes()
[真正的白名单] 通过 router.addRoutes(routes) 方式动态添加路由
根据不同用户的不同权限,添加多份路由表!!!
// Login.vue用户登录成功后动态添加/about
login() {
window.isLogin = true;
this.$router.addRoutes([
{ path: "/about"}
]);
const redirect = this.$route.query.redirect || "/";
this.$router.push(redirect);
}
const router1 = [{},{},{},...];
const router2 = [{},{},{},...];
const router3 = [{},{},{},...];
login() {
...
window.isLogin = true;
if(user.auth === A){
this.$router.addRoutes(router1);
}
if(user.auth === B){
this.$router.addRoutes(router2);
}
if(user.auth === C){
this.$router.addRoutes(router3);
}
}
五、进阶-路由缓存
方法:
利用keepalive做组件缓存,保留组件状态,提高执行效率
特点:
两个特别的生命周期:activated、deactivated
坑点:activated触发 影响 子组件的生命周期触发
方法一:
// include 使用 “,”号隔开的字符串 或者数组,区分路由组件。
// max使用,设置缓存栈数量:number。
<keep-alive include="about" :max="10">
<router-view></router-view>
</keep-alive>
使用include或exclude时要:《给组件设置name》不是路由name
max使用,设置缓存栈数量:number。
方法二:
使用meta数据控制
// router.js
[{
path: '/search',
name: 'search',
meta: {
title: '搜索',
showTabbar: false,
keepAlive: true,
noShowBackHome: true,
showFloatCart: true,
hideHeader: true,
visitor: true
},
component: function (resolve) {
require(['../components/Search'], resolve);
},
}]
// index.vue
<keep-alive v-if="$route.meta.keepAlive">
<router-view class="router-view" :class="{'no-header':!showHeader}"></router-view>
</keep-alive>
<router-view
v-if="!$route.meta.keepAlive"
class="router-view"
:class="{'no-header':!showHeader}"
></router-view>