Vue Router 高级
导航守卫
导航表示路由发生改变
正如其名,vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。有多种机会植入路由导航过程中:全局的, 单个路由独享的, 或者组件级的。
记住参数或查询的改变并不会触发进入/离开的导航守卫。你可以通过观察 $route 对象来应对这些变化,或使用 beforeRouteUpdate 的组件内守卫。
全局前置守卫
你可以使用 router.beforeEach 注册一个全局前置守卫:
const router = new VueRouter({ ... })
router.beforeEach((to, from, 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的toprop 或router.push中的选项。 -
next(error): (2.4.0+) 如果传入next的参数是一个Error实例,则导航会被终止且该错误会被传递给router.onError()注册过的回调。
-
**确保 next 函数在任何给定的导航守卫中都被严格调用一次。它可以出现多于一次,但是只能在所有的逻辑路径都不重叠的情况下,否则钩子永远都不会被解析或报错。
demo:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
// 引入外链js
<script src="../../vue.js"></script>
<script src="./vue-router.js"></script>
<style>
*{
margin: 0;
padding: 0;
}
#navgation{
position: absolute;
bottom: 0;
width: 100%;
height: 30px;
font-size: 18px;
background-color: rgba(127, 255, 212,0.5);
display: flex;
justify-content: space-around;
}
#navgation>a{
text-decoration: none;
width: 30%;
text-align: center;
}
#navgation .router-link-active{
background-color: rgba(127, 255, 212, 1);
color: seagreen;
}
.router-link-active{
color: steelblue;
}
</style>
</head>
<body>
<div id="app">
<div id="navgation" >
<router-link to="/home" >主页</router-link>
<router-link to="/news" >新闻</router-link>
<router-link to="/user" >我的</router-link>
</div>
<router-view></router-view>
</div>
</body>
<script>
const Home = {
template: `
<div>
<h1>home页</h1>
</div>
`
}
const News = {
template: `
<div>
<h1>news页</h1>
<router-link to='/news/native'>国内新闻</router-link>
<router-link to='/news/foreign'>国外新闻</router-link>
<router-view></router-view>
</div>
`
}
// 定义News中的子组件
const NativeNews = {
template: `
<div>
<h3>国内新闻</h3>
</div>
`
}
const ForeignNews = {
template: `
<div>
<h3>国外新闻</h3>
</div>
`
}
const User = {
template: `
<div>
<h1>user页</h1>
</div>
`
}
const NotFound = {
template: `
<div>
<center>
<h1>404</h1>
<h3>无法请求到页面</h3>
</center>
</div>
`,
}
// 以上为路由组件,需要在路由配置中挂载
// 定义路由
const routes = [
// 路由规则
{
path:'/',
redirect:'/home'
},
{
path: '/home',
component: Home
},
{
path: '/news',
component: News,
children:[
{
path: '/news/',
component: NativeNews
},
{
path: '/news/native',
component: NativeNews
},
{
path: '/news/foreign',
component: ForeignNews
}
]
},
{
path: '/user',
component: User
},
{
path: '*',
component: NotFound
}
]
const router = new VueRouter({
routes
})
router.beforeEach((to,from,next)=>{
console.log(to);
console.log(from);
next()
})
const vm = new Vue({
el: '#app',
router: router,
})
</script>
</html>
全局解析守卫
在 2.5.0+ 你可以用 router.beforeResolve 注册一个全局守卫。这和 router.beforeEach 类似,区别是在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。
全局后置钩子
你也可以注册全局后置钩子,然而和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:
router.afterEach((to, from) => {
// ...
})
路由独享守卫
你可以在路由配置上直接定义 beforeEnter 守卫:
const router = new VueRouter({
routes: [
{
path: '/foo',
component: Foo,
beforeEnter: (to, from, next) => {
// ...
}
}
]
})
这些守卫与全局前置守卫的方法参数是一样的。
demo
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<style>
.router-link-active{
background: red;
}
</style>
</head>
<body>
<div id="app">
<router-link tag="button" to="/home">
首页
</router-link>
<router-link tag="button" to="/news">
新闻
</router-link>
<router-link tag="button" to="/about">
关于我们
</router-link>
<h1>我是单页面应用</h1>
<router-view></router-view>
</div>
<script src="vue.js"></script>
<script src="vue-router.js"></script>
<script>
/*
/home home页
/new news页
/about about
*/
const Home = {
template: `
<div>
<h1> home页 </h1>
</div>
`
}
const News = {
template: `
<div>
<h1> 新闻页 </h1>
</div>
`
}
const About = {
template: `
<div>
<h1> 关于我们页 </h1>
</div>
`
}
const Login = {
template: `
<div>
<button @click="login">登录</button>
</div>
`,
methods: {
login(){
// token 加密 diwhd9e39wdhcit4re-39ue
setTimeout(()=>{
const token = "dwiudwo3r3r4ferfe";
// 存储token
localStorage.setItem('access_token', token)
if( this.$route.params.from ){
this.$router.push(this.$route.params.from)
}else{
this.$router.push('/home')
}
},500)
}
}
}
// 以上称为 路由组件 需要在 路由 配置中 挂载
// 定义路由
const routes = [ // 路由规则
/* {
path: '/',
component: Home
}, */
{
path: '/',
redirect: '/home'
},
{
path: '/home',
name:'首页',
component: Home
},
{
path: '/news',
name:'新闻',
component: News
},
{
path: '/about',
name: '关于我们',
component: About,
beforeEnter: (to, from, next) => {
/*
直接判断是否登录即可
*/
const access_token = localStorage.getItem('access_token')
if(access_token){
next()
}else{
next('/login')
}
}
},
{
path: '/login',
name:'登录',
component: Login
}
]
const router = new VueRouter({
// mode: 'hash',
routes
})
const vm = new Vue({
el: '#app',
router
})
</script>
</body>
</html>
组件内的守卫
最后,你可以在路由组件内直接定义以下路由导航守卫:
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`
}
}
登录鉴权案例
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
<script src="../../vue.js"></script>
<script src="./vue-router.js"></script>
<style>
*{
margin: 0;
padding: 0;
}
#navgation{
position: absolute;
bottom: 0;
width: 100%;
height: 30px;
font-size: 18px;
background-color: rgba(127, 255, 212,0.5);
display: flex;
justify-content: space-around;
}
#navgation>a{
text-decoration: none;
width: 30%;
text-align: center;
}
#navgation .router-link-active{
background-color: rgba(127, 255, 212, 1);
color: seagreen;
}
.router-link-active{
color: steelblue;
}
</style>
</head>
<body>
<div id="app">
<div id="navgation" >
<router-link to="/home" >主页</router-link>
<router-link to="/news" >新闻</router-link>
<router-link to="/user" >我的</router-link>
</div>
<router-view></router-view>
</div>
</body>
<script>
const Home = {
template: `
<div>
<h1>Home页</h1>
</div>
`
}
const News = {
template: `
<div>
<h1>News页</h1>
</div>
`
}
const User = {
template: `
<div>
<h1>User页</h1>
</div>
`
}
const Login = {
template: `
<div>
<button @click = "login">登录</button>
</div>
`,
methods: {
login () {
// 定义一个token用来存储登录密钥
const token = 'sdkajfklsajdf';
// 实现浏览器本地存储
localStorage.setItem('access_token',token);
if(this.$route.params.fullPath){
this.$router.push(this.$route.params.fullPath)
}else{
this.$router.push('/home')
}
}
}
}
const NotFound = {
template: `
<div>
<h1>404</h1>
<h2>找不到页面</h2>
</div>
`
}
// 定义路由
const routes = [
// 路由规则
{
path: '/',
redirect: '/home' // 重定向到home页
},{
path: '/home',
name: '首页',
meta:{
needLogin: false
},
component: Home
},{
path: '/news',
name: '新闻',
meta:{
needLogin: true
},
component: News
},{
path: '/user',
name: '用户',
meta:{
needLogin: false
},
component: User
},{
path: '/login',
name: '登录',
component: Login
},{
// 定义404的路径
path: '*',
name: '404',
component: NotFound
}
]
const router = new VueRouter({
routes
})
router.beforeEach((to,from,next)=>{
const access_token = localStorage.getItem('access_token')
console.log(to);
if(to.fullPath == '/login' || to.fullPath == '/register' || access_token || !to.meta.needLogin){
next()
}else{
next({
name: '登录',
params: {
from: to.fullPath
}
})
}
})
const vm = new Vue({
el: '#app',
router,
})
</script>
</html>
本文深入探讨Vue Router的高级特性,详细介绍了全局前置守卫、全局解析守卫、全局后置钩子、路由独享守卫和组件内的守卫。并提供登录鉴权的案例,讲解如何利用导航守卫实现用户身份验证,确保只有已登录用户才能访问特定路由。

4248

被折叠的 条评论
为什么被折叠?



