一、定义和理解

导航守卫的作用vue-router 提供的导航守卫主要用来通过跳转或取消的方式守卫导航。

》》项目中应用场景举例:路由页面跳转时候进行登陆验证;路由跳转判断;

有多种机会植入路由导航过程中全局路由, 单个路由独享的, 或者组件级的

全局守卫包括:router.beforeEach(是全局前置守卫)、router.beforeResolve(是全局解析守卫)、router.afterEach(是全局后置钩子)

单个路由独享的导航守卫:在路由配置上直接定义 beforeEnter 守卫

组件内守卫包括:在组件内定义路由的导航守卫,有beforeRouteEnter 、beforeRouteUpdate 、beforeRouteLeave

每个导航守卫都接受3个参数 from 、to、next() ,参数的定义如下:

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 的 to prop 或 router.push 中的选项。

next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError() 注册过的回调。

注意事项:

1、确保要调用 next 方法,否则钩子就不会被 resolved。

2、当一个导航触发时,导航守卫按照顺序调用。守卫是异步解析执行,导航在所有守卫 resolve 完之前一直处于 等待中

二、导航守卫使用

首先vue-router知识回顾:

0、在vue项目中使用vue-router步骤概述:

// 0. 如果使用模块化机制编程,导入Vue和VueRouter,要调用 Vue.use(VueRouter)// 1. 定义 (路由) 组件。
// 可以从其他文件 import 进来
const Foo = { template: '<div>foo</div>' }
const Bar = { template: '<div>bar</div>' }// 2. 定义路由
// 每个路由应该映射一个组件。 其中"component" 可以是
// 通过 Vue.extend() 创建的组件构造器,
// 或者,只是一个组件配置对象。
// 我们暂时不套路讨论嵌套路由、传参、元数据、导航守卫等。
const routes = [{ path: '/foo', component: Foo },{ path: '/bar', component: Bar }
]// 3. 创建 router 实例,然后传 `routes` 配置
// 你还可以传别的配置参数, 不过先这么简单着吧。
const router = new VueRouter({routes // (缩写) 相当于 routes: routes
})// 4. 创建和挂载根实例。
// 记得要通过 router 配置参数注入路由,
// 从而让整个应用都有路由功能
const app = new Vue({router
}).$mount('#app')// 现在,应用已经启动了!

1、可以在任何组件内通过 this.$router 访问路由器,也可以通过 this.$route 访问当前路由:

// Home.vue
export default {computed: {username () {// 我们很快就会看到 `params` 是什么return this.$route.params.username}},methods: {goBack () {window.history.length > 1? this.$router.go(-1): this.$router.push('/')}}
}

2、动态路由匹配时使用this.$route.params获取路由的路径参数

应用场景:需要把某种模式匹配到的所有路由,全都映射到同个组件。例如,我们有一个 User 组件,对于所有 ID 各不相同的用户,都要使用这个组件来渲染。又像 /user/foo 和 /user/bar 都将映射到相同的路由。

注意:可以在一个路由中设置多段“路径参数”,如下:

模式 匹配路径 $route.params
/user/:username /user/evan { username: 'evan' }
/user/:username/post/:post_id /user/evan/post/123 { username: 'evan', post_id: '123' }

当使用路由参数时,例如从 /user/foo 导航到 /user/bar原来的组件实例会被复用。因为两个路由都渲染同个组件,比起销毁再创建,复用则显得更加高效。不过,这也意味着组件的生命周期钩子不会再被调用

参数或查询的改变并不会触发进入/离开的导航守卫。可以通过观察 $route 对象来应对这些变化,或使用  beforeRouteUpdate 的组件内守卫。

3、复用组件时,想对路由参数的变化作出响应的话,你可以简单地 watch (监测变化) $route 对象:

const User = {template: '...',watch: {'$route' (to, from) {// 对路由变化作出响应...}}
}

或者使用 2.2 中引入的 beforeRouteUpdate 导航守卫:

const User = {template: '...',beforeRouteUpdate (to, from, next) {// react to route changes...// don't forget to call next()}
}

4、在router配置文件中使用通配符 (*)捕获所有路由或 404 Not found 路由

注意:通常含有通配符的路由应该放在最后。路由 { path: '*' } 通常用于客户端 404 错误。如果你使用了History 模式,请确保正确配置你的服务器。

同一个路径可以匹配多个路由,此时,匹配的优先级就按照路由的定义顺序:谁先定义的,谁的优先级就最高

当使用一个通配符时,$route.params 内会自动添加一个名为 pathMatch 参数。它包含了 URL 通过通配符被匹配的部分:

// 给出一个路由 { path: '/user-*' }
this.$router.push('/user-admin')
this.$route.params.pathMatch // 'admin'
// 给出一个路由 { path: '*' }
this.$router.push('/non-existing')
this.$route.params.pathMatch // '/non-existing'

5、使用vue-router嵌套路由时,在路由定义文件中使用children 配置:

注意事项:以 / 开头的嵌套路径会被当作根路径。 这让你充分的使用嵌套组件而无须设置嵌套的路径。

const router = new VueRouter({routes: [{ path: '/user/:id', component: User,children: [{// 当 /user/:id/profile 匹配成功,// UserProfile 会被渲染在 User 的 <router-view> 中path: 'profile',component: UserProfile},{// 当 /user/:id/posts 匹配成功// UserPosts 会被渲染在 User 的 <router-view> 中path: 'posts',component: UserPosts}]}]
})

6、vue-router路由导航可以在组件中使用<router-link :to="..."> 的形式外,还有JS编程式的导航(包括可以调用 this.$router.push(location, onComplete?, onAbort?) 和 router.replace(location, onComplete?, onAbort?),以及router.go(n) )

注意事项:

  • 如果提供了 pathparams 会被忽略;
  • 如果目的地和当前路由相同,只有参数发生了改变 (比如从一个用户资料到另一个 /users/1 -> /users/2),你需要使用 beforeRouteUpdate 来响应这个变化 (比如抓取用户信息);
  • router.push 方法。这个方法会向 history 栈添加一个新的记录,所以,当用户点击浏览器后退按钮时,则回到之前的 URL。router.replace 方法它不会向 history 添加新记录,而是跟它的方法名一样 —— 替换掉当前的 history 记录。
  • 在 2.2.0+,可选的在 router.push 或 router.replace 中提供 onComplete 和 onAbort 回调作为第二个和第三个参数。这些回调将会在导航成功完成 (在所有的异步钩子被解析之后) 或终止 (导航到相同的路由、或在当前导航完成之前导航到另一个不同的路由) 的时候进行相应的调用。
const userId = '123'
router.push({ name: 'user', params: { userId }}) // -> /user/123
router.push({ path: `/user/${userId}` }) // -> /user/123
// 这里的 params 不生效
router.push({ path: '/user', params: { userId }}) // -> /user// 在浏览器记录中前进一步,等同于 history.forward()
router.go(1)// 后退一步记录,等同于 history.back()
router.go(-1)

router.push、 router.replace 和 router.go 跟 window.history.pushState、 window.history.replaceState 和 window.history.go好像, 实际上它们确实是效仿 window.historyAPI 的。

7、当需要在一个组件页面展示多个路由视图 router-view ,需要给视图命名:

<router-view class="view one"></router-view>
<router-view class="view two" name="a"></router-view>
<router-view class="view three" name="b"></router-view>
const router = new VueRouter({routes: [{path: '/',components: {default: Foo,a: Bar,b: Baz}}]
})

注意:正确使用 components配置 (带上 s):

8、vue-router路由重定向和别名使用

“重定向”的意思是,当用户访问 /a时,URL 将会被替换成 /b,然后匹配路由为 /b;重定向不能指向自身,会形成死循环。

“别名”的意思是给路由取“小名”,/a 的别名是 /b,意味着,当用户访问 /b 时,URL 会保持为 /b,但是路由匹配则为 /a,就像用户访问 /a 一样。

const router = new VueRouter({routes: [{ path: '/a', redirect: '/b' },{ path: '/b', redirect: { name: 'foo' }},{ path: '/c', redirect: to => {// 方法接收 目标路由 作为参数// return 重定向的 字符串路径/路径对象}},{ path: '/a', component: A, alias: '/b' }]
})

9、vue-router使用路由进行组件传参时,通过props取代 $route.params 可以降低耦合度:

注意:在组件中使用 $route 会使之与其对应路由形成高度耦合,从而使组件只能在某些特定的 URL 上使用,限制了其灵活性。通过props取代,这样你便可以在任何地方使用该组件,使得该组件更易于重用和测试。

如果 props 被设置为 trueroute.params 将会被设置为组件属性。

const User = {  //$route 的耦合template: '<div>User {{ $route.params.id }}</div>'
}
const router = new VueRouter({routes: [{ path: '/user/:id', component: User }]
})const User = { //通过 props 解耦props: ['id'],template: '<div>User {{ id }}</div>'
}
const router = new VueRouter({routes: [{ path: '/user/:id', component: User, props: true },// 对于包含命名视图的路由,你必须分别为每个命名视图添加 `props` 选项:{path: '/user/:id',components: { default: User, sidebar: Sidebar },props: { default: true, sidebar: false }}]
}) 

如果 props 是一个对象,它会被按原样设置为组件属性。当 props 是静态的时候有用。

const router = new VueRouter({routes: [{ path: '/promotion/from-newsletter', component: Promotion, props: { newsletterPopup: false } }]
})

你可以创建一个函数返回 props。这样你便可以将参数转换成另一种类型,将静态值与基于路由的值结合等等。

const router = new VueRouter({routes: [{ path: '/search', component: SearchUser, props: (route) => ({ query: route.query.q }) }]
})

开始导航守卫使用demo:

注意事项:注意导航守卫并没有应用在跳转路由上,而仅仅应用在其目标上。

1、全局前置守卫 router.beforeEach

const router = new VueRouter({ ... })router.beforeEach((to, from, next) => {// ...
})

2、全局解析守卫 router.beforeResolve

在导航被确认之前,同时在所有组件内守卫和异步路由组件被解析之后,解析守卫就被调用。

3、全局后置钩子router.afterEach

和守卫不同的是,这些钩子不会接受 next 函数也不会改变导航本身:

router.afterEach((to, from) => {// ...
})

4、路由独享的守卫router.beforeEnter

是在路由配置上直接定义 beforeEnter 守卫:

const router = new VueRouter({routes: [{path: '/foo',component: Foo,beforeEnter: (to, from, next) => {// ...}}]
})

5、组件内的守卫 beforeRouteEnter、beforeRouteUpdate (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 是支持给 next 传递回调的唯一守卫。对于 beforeRouteUpdate 和 beforeRouteLeave 来说,this 已经可用了,所以不支持传递回调,因为没有必要了。

beforeRouteEnter (to, from, next) {next(vm => {// 通过 `vm` 访问组件实例})
}

这个离开守卫通常用来禁止用户在还未保存修改前突然离开。该导航可以通过 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)}
}

三、导航守卫解析流程:

  1. 导航被触发。
  2. 在失活的组件里调用离开守卫。
  3. 调用全局的 beforeEach 守卫。
  4. 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  5. 在路由配置里调用 beforeEnter
  6. 解析异步路由组件。
  7. 在被激活的组件里调用 beforeRouteEnter
  8. 调用全局的 beforeResolve 守卫 (2.5+)。
  9. 导航被确认。
  10. 调用全局的 afterEach 钩子。
  11. 触发 DOM 更新。
  12. 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

四、其他:路由元信息、过渡动效、数据获取、滚动位置、懒加载

路由元信息:

定义路由的时候可以设置meta字段,可以存储该路由相关信息(例如:设置每个路由的title,取路由的title设置为选项卡的标题);还可以用来设置页面权限要求

 {path: '/router2',name: 'router2',component:router2,meta:{title:"router2"}}
// 全局前置守卫
router.beforeEach((to,from,next) => {console.log(to);console.log(from);if(to.meta.title) {document.title = to.meta.title;} else {document.title = '我是默认的title'}next();
});

路由过渡动效:

Vue 在插入、更新或者移除 DOM 时,提供多种不同方式的应用过渡效果。

const Foo = {template: `<transition name="slide"><div class="foo">...</div></transition>`
}const Bar = {template: `<transition name="fade"><div class="bar">...</div></transition>`
}<!-- 使用动态的 transition name -->
<transition :name="transitionName"><router-view></router-view>
</transition>// 接着在父组件内
// watch $route 决定使用哪种过渡
watch: {'$route' (to, from) {const toDepth = to.path.split('/').lengthconst fromDepth = from.path.split('/').lengththis.transitionName = toDepth < fromDepth ? 'slide-right' : 'slide-left'}
}

路由数据获取:(分别有导航完成前和完成后获取)

有时候,进入某个路由后,需要从服务器获取数据。例如,在渲染用户信息时,你需要从服务器获取用户的数据。我们可以通过两种方式来实现:

  • 导航完成之后获取:先完成导航,然后在接下来的组件生命周期钩子中获取数据。在数据获取期间显示“加载中”之类的指示。
  • 导航完成之前获取:导航完成前,在路由进入的守卫中获取数据,在数据获取成功后执行导航。从技术角度讲,两种方式都不错 —— 就看你想要的用户体验是哪种。

导航完成后获取数据,我们会马上导航和渲染组件,然后在组件的 created 钩子中获取数据。

export default {data () {return {loading: false,post: null,error: null}},created () {// 组件创建完后获取数据,// 此时 data 已经被 observed 了this.fetchData()},watch: {// 如果路由有变化,会再次执行该方法'$route': 'fetchData'},methods: {fetchData () {this.error = this.post = nullthis.loading = true// replace getPost with your data fetching util / API wrappergetPost(this.$route.params.id, (err, post) => {this.loading = falseif (err) {this.error = err.toString()} else {this.post = post}})}}
}

我们在导航转入新的路由前获取数据。我们可以在接下来的组件的 beforeRouteEnter 守卫中获取数据,当数据获取成功后只调用 next 方法。

export default {data () {return {post: null,error: null}},beforeRouteEnter (to, from, next) {getPost(to.params.id, (err, post) => {next(vm => vm.setData(err, post))})},// 路由改变前,组件就已经渲染完了// 逻辑稍稍不同beforeRouteUpdate (to, from, next) {this.post = nullgetPost(to.params.id, (err, post) => {this.setData(err, post)next()})},methods: {setData (err, post) {if (err) {this.error = err.toString()} else {this.post = post}}}
}

路由滚动行为:

使用前端路由,当切换到新路由时,想要页面滚到顶部,或者是保持原先的滚动位置,就像重新加载页面那样。 vue-router 能做到,而且更好,它让你可以自定义路由切换时页面如何滚动。

注意: 这个功能只在支持 history.pushState 的浏览器中可用。

当创建一个 Router 实例,你可以提供一个 scrollBehavior 方法:

const router = new VueRouter({routes: [...],scrollBehavior (to, from, savedPosition) {// return 期望滚动到哪个的位置}
})

scrollBehavior 方法接收 to 和 from 路由对象。第三个参数 savedPosition 当且仅当 popstate导航 (通过浏览器的 前进/后退 按钮触发) 时才可用。

这个方法返回滚动位置的对象信息,长这样:

  • { x: number, y: number }
  • { selector: string, offset? : { x: number, y: number }} (offset 只在 2.6.0+ 支持)

如果返回一个 falsy (译者注:falsy 不是 false,参考这里)的值,或者是一个空对象,那么不会发生滚动。

举例:

scrollBehavior (to, from, savedPosition) {return { x: 0, y: 0 }
}

对于所有路由导航,简单地让页面滚动到顶部。

返回 savedPosition,在按下 后退/前进 按钮时,就会像浏览器的原生表现那样:

scrollBehavior (to, from, savedPosition) {if (savedPosition) {return savedPosition} else {return { x: 0, y: 0 }}
}

如果你要模拟“滚动到锚点”的行为:

scrollBehavior (to, from, savedPosition) {if (to.hash) {return {selector: to.hash}}
}

路由懒加载:

当打包构建应用时,JavaScript 包会变得非常大,影响页面加载。如果我们能把不同路由对应的组件分割成不同的代码块,然后当路由被访问的时候才加载对应组件,这样就更加高效了。

结合 Vue 的异步组件和 Webpack 的代码分割功能,轻松实现路由组件的懒加载。

一个能够被 Webpack 自动代码分割的异步组件。

const Foo = () => import('./Foo.vue')

在路由配置中什么都不需要改变,只需要像往常一样使用 Foo

const router = new VueRouter({routes: [{ path: '/foo', component: Foo }]
})

五、应用demo

vue路由登陆验证:

前言:vue的项目的登录状态如果用vuex状态管理,页面一刷新vuex管理的状态就会消失,所以,会将登录的状态写到web Storage中进行存储管理。

步骤:

1、router/index.js 》在需要登录验证的路由元信息里加入登录验证标识requiresAuth:true

      routers: [{ path: '/home',name: 'home',component: Home,meta: {  requiresAuth: true  }},{path: '/login',name: 'Login',component: Login},]

2、登陆组件从服务端获取到token,将登录状态写入web Storage里

      login() {this.$api.login().then(function(res) {//调用axios二次封装的login方法,关闭mockjs时会传递到nodejssessionStorage.setItem('accessToken', res.data.token) // 成果返回后放置token到Cookie //console.log("登陆响应",JSON.stringify(res.data));router.push('/home')  // 登录成功,跳转到主页}).catch(function(res) {alert(res);});},

3、router/index.js 》再路由配置文件中使用全局前置导航守卫

//全局前置导航守卫,导航跳转前进行token登陆令牌判断
router.beforeEach((to, from, next) => {if(to.path === '/login')  {  next()  }else {if(to.meta.requiresAuth && !sessionStorage.getItem('accessToken')) {next({ path: '/login' })}else { //如果不需要登录验证,或者已经登录成功,则直接放行next() }  }});

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

vue-router进阶:路由使用归纳、路由导航守卫、导航守卫解析流程、相关推荐

  1. vue router返回到指定的路由

    vue router返回到指定的路由 一.项目场景 二.问题描述 三.原因分析 四.解决方案 一.项目场景 项目场景:示例:A(商品详情)--B(商品购买页面)-C(支付成功页面)--D(订单页面) ...

  2. Vue Router系列之详解路由守卫

    文章の目录 1.路由守卫是什么 2.导航守卫分类 2.1.全局守卫 2.1.1.全局前置守卫beforeEach 2.1.2.全局解析守卫beforeResolve(2.5.0 新增) 2.1.3.全 ...

  3. Vue源码 Vue Router(三)matcher 路由匹配器

    Vue源码 Vue Router(三)matcher Vue源码 Vue Router(三)matcher matcher createMatcher addRoutes match 总结 单步调试参 ...

  4. vue router连续点击多次路由报错根本原因和解决方法

    原因: vue-router 升级到 3.1.x 后,重复点击导航时,控制台出现报错 ,vue router ≥ v3.1 后 ,回调形式改成 promise api 了,返回的是 promise,如 ...

  5. vue router连续点击多次路由报错

    在编程式导航跳转时候,连续点击多次会报NavigationDuplicated错误 this.$router.push({name: "search"}); 出现这种错误原因是因为 ...

  6. Vue Router 实现路由控制实战

    本文是我在学习过程中记录学习的点点滴滴,目的是为了学完之后巩固一下顺便也和大家分享一下,日后忘记了也可以方便快速的复习. Vue Router 实现路由控制实战 前言 一.什么是单页面应用及实现前端路 ...

  7. vue路由与动态路由

    1.vue路由与动态路由 Vue是一个流行的JavaScript框架,提供了一种称为Vue Router的插件,用于管理单页面应用程序的路由.Vue Router允许开发人员定义应用程序的不同页面,并 ...

  8. Vue Uncaught SyntaxError: Unexpected token ‘<‘ 路由问题

    最近在部署vue项目是遇到,Uncaught SyntaxError: Unexpected token '<' 路由问题,项目能够正常访问,但是每次刷新之后,会显示空白页面.最后经过分析,是路 ...

  9. 【Vue.js】Vue.js中常用的UI组件库和Vue Router

    1.Vue生态中常用的UI组件库 1. vant 介绍 轻量级.可靠的移动端 Vue 组件库 有赞前端团队出品 GitHub地址:https://github.com/youzan/vant 特性 拥 ...

  10. Vue2.x - Vue Router

    目录 Vue与SPA 什么是SPA 简单了解SPA 什么是MPA SPA相较于MPA的优点 实现SPA的几个关键点 理解SPA单页面架构 什么是前端路由 锚链接与URL中的hash值 通过hashch ...

最新文章

  1. 第十二周-学习进度条
  2. C++horspool算法查找字符串是否包含子字符串(附完整源码)
  3. Windows Server 2016与旧版本系统比较
  4. 我们究竟还要学习哪些Android知识?附赠课程+题库
  5. VS2015配置环境支持opencv3库(网络方法总结)
  6. LeetCode 463. Island Perimeter
  7. laravel的一些笔记
  8. ospf避免环路_【网络干货】超全的OSPF路由协议技术汇总解析
  9. HDOJ1005(找循环节点)
  10. python中的ftplib模块
  11. IDEA格式化SQL代码
  12. python 保存bin文件,python bin文件处理
  13. ET框架学习1-服务端的认识
  14. 在Excel中用VBA制作俄罗斯方块游戏
  15. 电路设计_RS485总线典型电路介绍
  16. 最新河南电信、网通(联通)、铁通DNS地址ip
  17. python连接数据库生成可视化_python3.6 连接数据库并用matplotlib可视化代码
  18. 安卓自动操作软件 AUTO.JS 4.1.1 ALPHA2 免费版下载
  19. 多项式与快速傅立叶变换
  20. Matlab:表示 MATLAB 中的日期和时间

热门文章

  1. php中dump怎么使用,php – 如何正确使用print_r或var_dump?
  2. [转载] java 计算协方差_Java的深度:通过协方差暴露的API泄漏
  3. [转载] python通过adb获取android手机耗电量
  4. Java ObjectInputStream readUnshared()方法与示例
  5. 给定数字的b+树创建_在C ++中找到给定数字中的两个的下一个和上一个幂
  6. php拼接xml特殊字符不显示,使用PHP的XML特殊字符
  7. java 方法 示例_Java扫描仪的hasNextBoolean()方法与示例
  8. c#中textbox属性_C#.Net中带有示例的TextBox.Multiline属性
  9. vscode无法识别constexpr
  10. Windows 创建符号链接