文章目录
  1. 1.Vue Router 中的 导航守卫
  2. 2.one 完整的导航解析流程
    1. 2.1.simplified version 流程
  3. 3.two 流程 demo
    1. 3.1.全局守卫 global
    2. 3.2.路由独享的守卫 single
    3. 3.3.组件内的守卫 component
  4. 4.three 应用场景
    1. 4.1.beforeEach
    2. 4.2.afterEach
    3. 4.3.beforeRouteLeave
    4. 4.4.beforeRouteUpdate
  5. 5.more

Vue Router 中的 导航守卫

https://router.vuejs.org/zh/guide/advanced/navigation-guards.html

one 完整的导航解析流程

① 导航被触发。
② 在失活的组件里调用离开守卫。
③ 调用全局的 beforeEach 守卫。
④ 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
⑤ 在路由配置里调用 beforeEnter。
⑥ 解析异步路由组件。
⑦ 在被激活的组件里调用 beforeRouteEnter。
⑧ 调用全局的 beforeResolve 守卫 (2.5+)。
⑨ 导航被确认。
⑩ 调用全局的 afterEach 钩子。
⑪ 触发 DOM 更新。
⑫ 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

simplified version 流程

❷ 组件 beforeRouteLeave
❸ 全局 beforeEach
❹ 组件 beforeRouteUpdate
❺ 独享 beforeEnter
❼ 组件 beforeRouteEnter
❽ 全局 beforeResolve
❿ 全局 afterEach

一定要确保调用 next() 方法。 (afterEach 除外)
afterEach 不接收第三个参数 next 函数,也不会改变导航本身

two 流程 demo

全局守卫 global

main.js

// 全局前置守卫
router.beforeEach((to, from, next) => {
    console.log('全局 beforeEach')
    next()
})
// 全局解析守卫
router.beforeResolve((to, from, next) => {
    console.log('全局 beforeResolve')
    next()
})
// 全局后置钩子
router.afterEach((to, from) => {
    console.log('全局 afterEach')
})

路由独享的守卫 single

src/router/index.js

import Vue from 'vue'
import Router from 'vue-router'
import HelloWorld from '@/components/HelloWorld'
import Foo from '@/components/Foo'
import FooDetail from '@/components/FooDetail'

Vue.use(Router)

export default new Router({
  mode: 'history',
  routes: [
    {
      path: '/',
      name: 'HelloWorld',
      component: HelloWorld,
      beforeEnter: (to, from, next) => {
          console.log('独享 HelloWorld beforeEnter')
          next()
      }
    },
    {
    	path: '/foo',
    	name: 'Foo',
    	component: Foo,
    	children: [
            {
            	path: ':id',
            	name: 'FooDetail',
            	component: FooDetail,
            	beforeEnter: (to, from, next) => {
		          console.log('独享 FooDetail beforeEnter')
		          next()
		        }
            },
            {
            	path: 'hello',
            	name: 'HelloWorld',
            	component: HelloWorld,
            	beforeEnter: (to, from, next) => {
		          console.log('独享 HelloWorld beforeEnter')
		          next()
		        }
            }
    	]
    }
  ]
})

组件内的守卫 component

src/components/HelloWorld.vue

<template>
  <div class="hello">
    <h1></h1>
    <p @click="goFoo">goFoo</p>
  </div>
</template>

<script>
export default {
  name: 'HelloWorld',
  data () {
    return {
      msg: 'Welcome to Your Vue.js App'
    }
  },
  methods: {
    goFoo() {
      this.$router.push({name: 'Foo'})
    }
  },
  beforeRouteEnter(to, from, next) {
    console.log('组件内 HelloWorld beforeRouteEnter')
    next()
  },
  beforeRouteUpdate(to, from, next) {
    console.log('组件内 HelloWorld beforeRouteUpdate')
    next()
  },
  beforeRouteLeave(to, from, next) {
    console.log('组件内 HelloWorld beforeRouteLeave')
    const answer = confirm('Do you really want to leave')
    if (answer) {
      next()
    } else {
      next(false)
    }
  }
};
</script>

src/components/Foo.vue

<template>
  <div class="foo">
    <h1></h1>
    <router-link :to="{name: 'FooDetail', params: {id: 1}}">to foo1</router-link>
    <router-link to='/foo/2'>to foo2</router-link>
    <span @click="setId(3)">to foo3</span>
    <router-view/>
  </div>
</template>

<script>
export default {
  name: 'Foo',
  data () {
    return {
      msg: 'Welcome to Foo'
    }
  },
  methods: {
    setId(id) {
    	this.$router.push(`${id}`)
    }
  },
  beforeRouteEnter(to, from, next) {
    console.log('组件内 beforeRouteEnter')
    next()
  },
  beforeRouteUpdate(to, from, next) {
    console.log('组件内 beforeRouteUpdate')
    next()
  },
  beforeRouteLeave(to, from, next) {
    console.log('组件内 beforeRouteLeave')
    next()
  }
};
</script>

src/components/FooDetail.vue

<template>
  <div class="foo-detail">
    <h1> </h1>
  </div>
</template>

<script>
export default {
  name: 'FooDetail',
  data () {
    return {
      msg: 'Welcome to FooDetail',
    }
  },
  beforeRouteEnter(to, from, next) {
    console.log('组件内 FooDetail beforeRouteEnter')
    next()
  },
  beforeRouteUpdate(to, from, next) {
    console.log('组件内 FooDetail beforeRouteUpdate')
    next()
  },
  beforeRouteLeave(to, from, next) {
    console.log('组件内 FooDetail beforeRouteLeave')
    next()
  }
};
</script>

three 应用场景

beforeEach

1、验证用户是否登录(若未登录,且当前非登录页面,则自动重定向登录页面);

2、用户权限;

3、控制某个路由只能由某个路由进入

const noReturn = ['/login', '/home']; //登陆后不能回到的页面

router.beforeEach((to, from, next) => {
    // 判断是否有token
	if (store.getters.token) {
        // 有token则不能再回到login页面了
		if (langRouterGenerator(noReturn).indexOf(to.path) !== -1) {
            next(`/${to.path.split('/')[1]}/news`);
        // 判断是否有权限进入
		} else if (hasPermission(store.getters.groups, to.meta.groups)) {
            // 来源受限路由
            if (to.meta.source) { 
                // 控制某个路由只能由某个路由进入
				if (to.meta.source.indexOf(from.meta.id) >= 0 || to.meta.id === from.meta.id || from.name === null) {
                    next();
                // 如果不是在source来源内的路由,则回退
				} else {
					next(false);
                }
            // 正常路由
			} else {
				next();
            }
        // 无权进入
		} else {
			next(`/${to.meta.lang}/401`);
        }
    // 在免登录白名单,直接进入
	} else if (langRouterGenerator(noLoginList).indexOf(to.path) >= 0) { 
        next();
    // 否则全部重定向到登录页
	} else { 
		next(`/${to.path.split('/')[1]}/login`);
	}
});

afterEach

访问不同路由地址,显示每个页面对应的title

router.afterEach(() => {
	window.scrollTo(0, 0); // 跳转后回到页面顶部
	i18n.locale = router.currentRoute.meta.lang; // 全称的lang
	axios.defaults.baseURL = url.base; // 设置axios的根请求路径
	document.title = `${router.currentRoute.name}`;
});

beforeRouteLeave

1、清除当前组件中的定时器

beforeRouteLeave (to, from, next) {  
    window.clearInterval(this.timer) // 清除定时器
    next()
}

2、 当页面中有未关闭的窗口, 或未保存的内容时, 阻止页面跳转

beforeRouteLeave (to, from, next) {
  const answer = window.confirm('Do you really want to leave? you have unsaved changes!')
  if (answer) {
    next()
  } else {
    next(false)
  }
}

3、保存相关内容到Vuex中或Session中

当用户需要关闭页面时, 可以将公用的信息保存到session或Vuex中

 beforeRouteLeave (to, from, next) {
     // 保存到localStorage中
    localStorage.setItem(name, content); 
    next()
}

beforeRouteUpdate

在当前路由改变,但是该组件被复用时调用。

1、父路由监听子路由变化

2、对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候

more

vue-router-beforeeach demo 代码请看 github, 项目 v-router-beforeeach

文章目录
  1. 1.Vue Router 中的 导航守卫
  2. 2.one 完整的导航解析流程
    1. 2.1.simplified version 流程
  3. 3.two 流程 demo
    1. 3.1.全局守卫 global
    2. 3.2.路由独享的守卫 single
    3. 3.3.组件内的守卫 component
  4. 4.three 应用场景
    1. 4.1.beforeEach
    2. 4.2.afterEach
    3. 4.3.beforeRouteLeave
    4. 4.4.beforeRouteUpdate
  5. 5.more