一、路由守卫
某商场详情页 => 点击购买
- 如果登录了,直接跳转到付款页。
- 如果没登录,直接跳转到登录页,登录后直接跳转到付款页。
如何实现上面的功能?
可以通过路由守卫实现。
路由守卫一定会用到的api
路由守卫也叫路由钩子,路由生命周期 => 路由守卫其实都是在路由跳转的特定阶段触发的函数。
路由跳转都会经历那些阶段?
从路由Home跳转到路由News
路由跳转流程。
- 路由触发,准备跳转
- 触发Home组件的一个钩子函数,beforeRouteLeave (路由离开前)
- 调用全局的 beforeEach(所有路由跳转都会触发的一个钩子) 钩子
- 在重用的组件(动态路由组件)里调用 beforeRouteUpdate.(当前路由组件更新时)
- 在路由配置里调用 beforeEnter(路由选项独享守卫)
- 解析异步路由(路由懒加载)组件。
- 在News组件里调用 beforeRouteEnter (当前路由准备进入时)。
- 调用全局的 beforeResolve (完成跳转前)。
- 导航被确认。(完成路由跳转)
- 调用全局的 afterEach 钩子。
- 触发 DOM 更新(更新路由组件视图)
- 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。
守卫分类:
1:全局守卫
- beforeEach (很常用):一般用来做一些进入页面的限制。比如没有登录,就不能进入某些页面,只有登录了之后才有权限查看某些页面。。。简单说就是路由拦截。
- beforeResolve
- afterEach
2:路由独享守卫
- beforeEnter
3:组件独享守卫
- beforeRouteEnter (常用)
- beforeRouteUdpate (常用)
- beforeRouteLeave (常用)
<body>
<div id='app'></div>
</body>
如何知道路由发生了跳转?
beforeRouteLeave => 守卫(保安),可以让路由跳转被拦截。路由守卫的参数:
1:to 目标路由的$route对象
2:from 起始路由的$route对象
3:next 回调函数,用于拦截路由或者路由传参
next的参数:
a:没参数 => 正常跳转
b:false => 不让跳转
c:字符串路径 => 重定向
d:对象 => 重定向时路由传参.
e:函数 => 跳转的最后节点触发,参数是当前的组件实例.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Home = {
template: `
<div>
<h3>首页</h3>
</div>
`,
// Home路由跳转到其他路由前触发的钩子守卫.
beforeRouteLeave(to, from, next) {
// console.log('准备离开Home');
// 通过to对象,可以知道目标路由是哪个
console.log('to', to.path);
// 通过from对象,可以知道起始路由是哪个.
console.log('from', from.path);
// next是一个回调函数
console.log('next', next);
// 手动调用next才能正常跳转路由(一定不要忘记)
next();
// 参数写false就是跳转失败
// next(false);
}
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`,
data() {
return { msg: 'News组件' }
},
// 组件进入前的守卫.(别的路由进入News路由时触发)
// beforeRouteEnter钩子内部能使用this.
// 这里的this不指向当前的News组件.而是window
// beforeRouteEnter触发时,组件还没有创建
beforeRouteEnter(to, from, next) {
// undefined
console.log('msg',this.msg);
// next的参数可以是一个回调函数,在整个路由跳转的最后阶段触发
// 这个回调函数的参数就是当前的News组件实例.
// next((vm) => {
// console.log('msg', vm.msg)
// });
// 进入News前,重定向到sport。
// next('/sport');
// 进入News前,重定向到sport,并且传参msg给sport组件
next({ path: '/sport', query: { msg: 10000 } });
}
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home
}, {
path: '/news',
component: News
}, {
path: '/sport',
component: Sport
}, {
// 当你的路径是/时,把路径变成/home
path: '/',
// 重定向到home
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
二、组件路由守卫在组件组件中不触发
<body>
<div id='app'></div>
</body>
beforeRouteLeave和beforeRouteEnter只能在路由组件中触发.
在路由组件的子组件中是不触发的.
什么是路由组件?
routes中配置的组件.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const box = {
template: `
<h3>box子组件</h3>
`,
// 这里的leave的组件是不触发的
beforeRouteLeave(to, from, next) {
console.log('box的leave')
next();
}
}
const Home = {
template: `
<div>
<h3>首页</h3>
<box />
</div>
`,
components: { box },
beforeRouteLeave(to, from, next) {
console.log('Home的leave')
next();
}
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`,
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home
}, {
path: '/news',
component: News
}, {
path: '/sport',
component: Sport
}, {
// 当你的路径是/时,把路径变成/home
path: '/',
// 重定向到home
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
三、beforeRouteUpdate
<body>
<div id='app'></div>
</body>
动态路由间的切换时不会触发beforeRouteLeave和beforeRouteEnter
动态路由切换,可以通过beforeRouteUpdate来监测.
同样的.beforeRouteUpdate在路由组件的子组件中也不触发.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const box = {
template: `
<h3>box子组件</h3>
`,
// 这里的leave的组件是不触发的
beforeRouteUpdate(to, from, next) {
console.log('box的Update')
next();
}
}
const Home = {
template: `
<div>
<h3>首页--{{$route.path}}</h3>
<box />
</div>
`,
components: { box },
// 动态路由中不触发beforeRouteLeave
beforeRouteLeave(to, from, next) {
console.log('Home的leave')
next();
},
// 动态路由切换时触发.
beforeRouteUpdate(to, from, next) {
console.log(from.params.path + '到' + to.params.path);
next();
}
}
// 实例化路由
const router = new VueRouter({
routes: [
{
path: '/:path',
component: Home
}, {
path: '/',
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
四、动态路由的路由守卫
<body>
<div id='app'></div>
</body>
从非动态路由跳转到动态路由,会触发动态路由内的Enter.
从动态路由跳转到非动态路由,会触发动态路由内的Leave.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Login = {
template: `
<div>
<input type='text' />
<button @click='$router.push("/home")'>登录</button>
</div>
`
}
const Home = {
template: `
<div>
<h3>首页--{{$route.path}}</h3>
</div>
`,
// 动态路由中不触发beforeRouteLeave
beforeRouteEnter(to, from, next) {
console.log('Home的Enter')
next();
},
// 动态路由切换时触发.
beforeRouteUpdate(to, from, next) {
console.log(from.params.path + '到' + to.params.path);
next();
}
}
// 实例化路由
const router = new VueRouter({
routes: [
{
path: '/login',
component: Login
}, {
path: '/:path',
component: Home
}, {
path: '/',
redirect: '/login'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
五、全局守卫
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Home = {
template: `
<div>
<h3>首页</h3>
</div>
`,
// beforeRouteEnter(to, from, next) {
// console.log('进入' + to.path + '组件');
// next();
// }
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`,
// beforeRouteEnter(to, from, next) {
// console.log('进入' + to.path + '组件');
// next();
// }
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`,
// beforeRouteEnter(to, from, next) {
// console.log('进入' + to.path + '组件');
// next();
// }
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home
}, {
path: '/news',
component: News
}, {
path: '/sport',
component: Sport
}, {
// 当你的路径是/时,把路径变成/home
path: '/',
// 重定向到home
redirect: '/home'
}
]
});
// 全局守卫,所有的路由跳转都会触发这个钩子.
// beforeEach相对于是全局的beforeRouteEnter(没有全局的Leave守卫)
router.beforeEach((to, from, next) => {
console.log('进入' + to.path + '组件');
next();
});
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
六、登录的守卫逻辑
<body>
<div id='app'></div>
</body>
一个App应用
1:有写页面需要登录,有些页面不需要登录.
2:跳转到需要登录的页面时,先检测你是不是已经登录了,如果没有登录直接跳转到登录页.,如果登录了,直接跳转.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
// 默认没有登录.
let isLogin = false;
const Login = {
template: `
<div>
<input type='text' />
<button @click='toPage'>登录</button>
</div>
`,
props: ['name'],
methods: {
toPage() {
// 切换登录状态
isLogin = true;
// 跳转到指定路由.
this.$router.push({ name: this.name })
}
}
}
const Home = {
template: `
<div>
<slot />
<h3>首页</h3>
</div>
`,
}
const News = {
template: `
<div>
<slot />
<h3>新闻</h3>
</div>
`,
}
const Sport = {
template: `
<div>
<slot />
<h3>体育</h3>
</div>
`,
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home,
name: 'home',
meta: {
// 不需要登录
authLogin: false
}
}, {
path: '/news',
component: News,
name: 'news',
meta: {
authLogin: true
}
}, {
path: '/sport',
component: Sport,
name: 'sport',
meta: {
authLogin: true
}
}, {
path: '/login',
component: Login,
name: 'login',
meta: {
authLogin: false
},
props: true
},{
path: '/',
redirect: '/home'
}
]
});
// 全局守卫处理 登录逻辑
router.beforeEach((to, from, next) => {
if (to.meta.authLogin) {
if (!isLogin) {
// 重定向,并且传递目标路由的name
next({ name: 'login', params: { name: to.name }})
} else {
next()
}
} else {
next()
}
});
const App = {
template: `
<div>
<router-view>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
</router-view>
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
七、路由独享守卫
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Home = {
template: `
<div>
<h3>首页</h3>
</div>
`
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`,
data() {
return { msg: 'News组件' }
}
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home
}, {
path: '/news',
component: News,
// News选项独享的守卫,写在这里和写在组件中的效果一致.
// 这里没办法通过next的回调获取组件实例.
beforeEnter(to, from, next) {
console.log('Enter');
next();
}
}, {
path: '/sport',
component: Sport
}, {
// 当你的路径是/时,把路径变成/home
path: '/',
// 重定向到home
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
八、一个路径对应多个路由视图
<body>
<div id='app'></div>
</body>
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Home = {
template: `
<div>
<h3>首页</h3>
</div>
`,
name: 'Home'
}
const box = {
template: `
<div>
<h3>box组件</h3>
</div>
`,
name: 'box'
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`
}
// 实例化路由
const router = new VueRouter({
routes: [
{
path: '/home',
components: { Home, box },
// 对于选项的contents有意义
beforeEnter(to, from, next ) {
console.log('home和box的Enter')
next();
}
}, {
path: '/news',
component: News,
}, {
path: '/sport',
component: Sport
}, {
path: '/',
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view name='Home' />
<router-view name='box' />
<router-view />
</div>
`
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>
九、如何监听路由跳转
<body>
<div id='app'></div>
</body>
如何在路由跳转时实现一些额外的逻辑?
如何监听路由的跳转?
1:不缓存组件的情况
a: created和mounted中监听.
b: watch监听$route变化(任意组件中都可以使用)
c: 路由守卫监听.(只能在路由组件中使用)
2:缓存的组件.
a: watch监听$route变化
b: ativated和deactivated.(路由组件和路由组件的子组件都可以触发)
c: 路由守卫监听.(只能在路由组件中使用)
3:动态路由
a: watch监听$route变化
b: beforeRouteUpdate守卫监听.
<script src="js/vue.js"></script>
<script src="js/vue-router.js"></script>
<script>
const Home = {
template: `
<div>
<h3>首页</h3>
</div>
`,
created() {
console.log('Home组件切换11');
},
watch: {
// 每次路由发生跳转,路径都会改变,路径改变$route内的属性就会变,从而触发watch
'$route': {
immediate: true,
handler() {
console.log('Home组件切换22');
}
}
},
beforeRouteEnter(to, from, next) {
console.log('Home组件切换33');
next();
}
}
const News = {
template: `
<div>
<h3>新闻</h3>
</div>
`,
data() {
return { msg: 'News组件' }
}
}
const Sport = {
template: `
<div>
<h3>体育</h3>
</div>
`
}
// 实例化路由
const router = new VueRouter({
// 路由组件和路由路径的对应关系 => 路由选项
routes: [
{
path: '/home',
component: Home
}, {
path: '/news',
component: News
}, {
path: '/sport',
component: Sport
}, {
path: '/',
redirect: '/home'
}
]
})
const App = {
template: `
<div>
<router-link to='/home'>首页</router-link>
<router-link to='/news'>新闻</router-link>
<router-link to='/sport'>体育</router-link>
<router-view />
</div>
`,
watch: {
$route: {
immediate: true,
handler() {
console.log('路由切换了')
}
}
}
};
new Vue({
render: h => h(App),
// 挂载路由
router
}).$mount('#app');
</script>