当前位置:   article > 正文

vue-router面试题_vue路由守卫面试题

vue路由守卫面试题

一、路由守卫的具体方法

1.全局前置守卫。

使用 router.beforeEach 注册一个全局前置守卫

  1. const router = new VueRouter({ ... })
  2. router.beforeEach((to, from, next) => {
  3. // ...
  4. })
  5. ​ 每个守卫方法接收三个参数(往后的守卫都大同小异):
  6. 1. to: Route: 即将要进入的目标 路由对象
  7. 2. from: Route: 当前导航正要离开的路由
  8. 3. next: Function: 一定要调用该方法将控制权交给下一个守卫,执行效果依赖 next 方法的参数。
  9. 1next(): 进入下一个守卫。如果全部守卫执行完了。则导航的状态就是 confirmed (确认的)。
  10. 2next(false): 中断当前的导航(把小明腿打断了)。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器 后退按钮),那么 URL 地址会重置到 from 路由对应的地址。
  11. 3next('/') 或者 next({ path: '/' }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航(小明 被打断腿并且送回家了)。你可以向 next 传递任意位置对象,且允许设置诸如 replace: truename: 'home' 之类的选项以及任何用在 router-link 的 to prop 或 router.push 中的选项。
  12. 4next(error): (2.4.0+) 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给router.

当一个导航开始时,全局前置守卫按照注册顺序调用。守卫是异步链式调用的,导航在最后的一层当中。

  1. new Promise((resolve, reject) => {
  2. resolve('第一个全局前置守卫')
  3. }.then(() => {
  4. return '第二个全局前置守卫'
  5. }.then(() => {
  6. ...
  7. }.then(() => {
  8. console.log('导航终于开始了') // 导航在最后一层中
  9. })

2.全局解析守卫

这和 router.beforeEach 类似,但他总是被放在最后一个执行。

3.全局后置钩子

  1. router.afterEach((to, from) => {
  2. // 你并不能调用next
  3. // ...
  4. })

4.路由独享的守卫。

在路由内写的守卫

  1. const router = new VueRouter({
  2. routes: [
  3. {
  4. path: '/foo',
  5. component: Foo,
  6. beforeEnter: (to, from, next) => {
  7. // ...
  8. }
  9. }
  10. ]
  11. })

5.组件内的守卫

  1. const Foo = {
  2. template: `...`,
  3. beforeRouteEnter (to, from, next) {
  4. // 路由被 confirm 前调用
  5. // 组件还未渲染出来,不能获取组件实例 `this`
  6. },
  7. beforeRouteUpdate (to, from, next) {
  8. // 在当前路由改变,但是该组件被复用时调用
  9. // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  10. // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  11. // 可以访问组件实例 `this`,一般用来数据获取。
  12. },
  13. beforeRouteLeave (to, from, next) {
  14. // 导航离开该组件的对应路由时调用
  15. // 可以访问组件实例 `this`
  16. }
  17. }

二、导航守卫是什么

导航守卫主要用来通过跳转或取消的方式守卫导航。简单的说,导航守卫就是路由跳转过程中的一些钩子函数路由跳转是一个大的过程,这个大的过程分为跳转前中后等等细小的过程,在每一个过程中都有一函数,这个函数能让你操作一些其他的事的时机,这就是导航守卫。

三、导航全过程

  • 导航被触发。
  • 在准备离开的组件里调用 beforeRouteLeave 守卫。
  • 调用全局的 beforeEach 守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。(如果你的组件是重用的)
  • 在路由配置里调用 beforeEnter。
  • 解析即将抵达的组件。
  • 在即将抵达的组件里调用 beforeRouteEnter。
  • 调用全局的 beforeResolve 守卫 (2.5+)。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 触发 DOM 更新。
  • 用创建好的实例调用 beforeRouteEnter 守卫中传给 next 的回调函数。

四、路由守卫进行判断登录

在vue项目中,切换路由时肯定会碰到需要登录的路由,其原理就是在切换路径之前进行判断,你不可能进入页面再去判断有无登录重新定向到login,那样的话会导致页面已经渲染以及它的各种请求已经发出。

1.如需要登录的路由可在main.js中统一处理(全局前置守卫)

我们可以在入口文件man.js里面进行配置,使用router.beforeEach方法,不懂得可以打印to,from的参数就ok,requireAuth可以随意换名的,只要man.js里面跟配置路由的routes里面的字段保持一致:

  1. // main.js
  2. import router from './router'
  3. router.beforeEach((to, from, next) => {
  4. if (to.matched.some(record => record.meta.requireAuth)){ // 判断该路由是否需要登录权限
  5. if(!sessionStorage.getItem('token') && !localStorage.getItem('token')){
  6. next({
  7. path: '/login',
  8. query: {redirect: to.fullPath} // 将跳转的路由path作为参数,登录成功后跳转到该路由
  9. })
  10. }else{
  11. next();
  12. }
  13. }else {
  14. next();
  15. }
  16. });
  17. new Vue({
  18. el: '#app',
  19. router,
  20. render: h => h(App)
  21. })
  22. // router
  23. export default new Router({
  24. routes: [
  25. {
  26. path: '/',
  27. name: 'home',
  28. redirect: '/home'
  29. },
  30. {
  31. path: '/home',
  32. component: Home,
  33. meta: {
  34. title: '',
  35. requireAuth: true, // 添加该字段,表示进入这个路由是需要登录的
  36. }
  37. },
  38. {
  39. path:'/login',
  40. name:'login',
  41. component:Login
  42. },
  43. {
  44. path:'/register',
  45. name:'register',
  46. component:Register
  47. }
  48. ]
  49. })

2.全局后置守卫

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

3.单独路由独享守卫(与全局一致,可单独对某个路由进行配置)

  1. const router = new VueRouter({
  2. routes: [
  3. {
  4. path: '/foo',
  5. component: Foo,
  6. beforeEnter: (to, from, next) => {
  7. // ...
  8. }
  9. }
  10. ]
  11. })

4.组件内部路由守卫(可写在与生命周期同级位置)

  1. beforeRouteEnter (to, from, next) {
  2. // 在渲染该组件的对应路由被 confirm 前调用
  3. // 不!能!获取组件实例 `this`
  4. // 因为当守卫执行前,组件实例还没被创建
  5. },
  6. beforeRouteUpdate (to, from, next) {
  7. // 在当前路由改变,但是该组件被复用时调用
  8. // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
  9. // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
  10. // 可以访问组件实例 `this`
  11. },
  12. beforeRouteLeave (to, from, next) {
  13. // 导航离开该组件的对应路由时调用
  14. // 可以访问组件实例 `this`
  15. }

五、vue-router 实现懒加载

什么是懒加载?

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

如何实现懒加载?

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

首先,可以将异步组件定义为返回一个 Promise 的工厂函数 (该函数返回的 Promise 应该 resolve 组件本身):

const Foo = () => Promise.resolve({ /* 组件定义对象 */ })

在 Webpack 2 中,我们可以使用动态 import语法来定义代码分块点 (split point):

import('./Foo.vue') // 返回 Promise

结合这两者,这就是如何定义一个能够被 Webpack 自动代码分割的异步组件。

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

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

  1. const router = new VueRouter({
  2. routes: [
  3. { path: '/foo', component: Foo }
  4. ]
  5. })

六、说说Vue路由模式(HashRouter 和 HistoryRouter的原理、区别、使用方法)

vue-router是Vue官方的路由管理器。它和 Vue.js 的核心深度集成,让构建单页面应用变得易如反掌。vue-router 默认 hash 模式,还有一种是history模式。

原理:

  1. hash路由:hash模式的工作原理是hashchange事件,可以在window监听hash的变化。我们在url后面随便添加一个#xx触发这个事件。vue-router默认的是hash模式—使用URL的hash来模拟一个完整的URL,于是当URL改变的时候,页面不会重新加载,也就是单页应用了,当#后面的hash发生变化,不会导致浏览器向服务器发出请求,浏览器不发出请求就不会刷新页面,并且会触发hasChange这个事件,通过监听hash值的变化来实现更新页面部分内容的操作

    对于hash模式会创建hashHistory对象,在访问不同的路由的时候,会发生两件事:
    HashHistory.push()将新的路由添加到浏览器访问的历史的栈顶,和HasHistory.replace()替换到当前栈顶的路由

  2. history路由:

    主要使用HTML5的pushState()和replaceState()这两个api结合window.popstate事件(监听浏览器前进后退)来实现的,pushState()可以改变url地址且不会发送请求,replaceState()可以读取历史记录栈,还可以对浏览器记录进行修改

区别:

  1. hash模式较丑,history模式较优雅,路径比较正规无#
  2. pushState设置的新URL可以是与当前URL同源的任意URL;而hash只可修改#后面的部分,故只可设置与当前同文档的URL
  3. pushState设置的新URL可以与当前URL一模一样,这样也会把记录添加到栈中;而hash设置的新值必须与原来不一样才会触发记录添加到栈中
  4. pushState通过stateObject可以添加任意类型的数据到记录中;而hash只可添加短字符串
  5. pushState可额外设置title属性供后续使用
  6. hash兼容IE8以上,history兼容IE10以上
  7. history模式需要后端配合将所有访问都指向index.html,否则用户刷新页面,会导致404错误

使用方法

  1. <script>
  2. // hash路由原理***************************
  3. // 监听hashchange方法
  4. window.addEventListener('hashchange',()=>{
  5. div.innerHTML = location.hash.slice(1)
  6. })
  7. // history路由原理************************
  8. // 利用html5的history的pushState方法结合window.popstate事件(监听浏览器前进后退)
  9. function routerChange (pathname){
  10. history.pushState(null,null,pathname)
  11. div.innerHTML = location.pathname
  12. }
  13. window.addEventListener('popstate',()=>{
  14. div.innerHTML = location.pathname
  15. })
  16. </script>

七、js是如何监听HistoryRouter的变化的

通过浏览器的地址栏来改变切换页面,前端实现主要有两种方式:

  1. 通过hash改变,利用window.onhashchange 监听。

  2. HistoryRouter:通过history的改变,进行js操作加载页面,然而history并不像hash那样简单,因为history的改变,除了浏览器的几个前进后退(使用 history.back(), history.forward()和 history.go() 方法来完成在用户历史记录中向后和向前的跳转。)等操作会主动触发popstate 事件,pushState,replaceState 并不会触发popstate事件,要解决history监听的问题,方法是:首先完成一个订阅-发布模式,然后重写history.pushState, history.replaceState,并添加消息通知,这样一来只要history的无法实现监听函数就被我们加上了事件通知,只不过这里用的不是浏览器原生事件,而是通过我们创建的event-bus 来实现通知,然后触发事件订阅函数的执行。
     

    1. 订阅-发布模式
    2. class Dep { // 订阅池
    3. constructor(name){
    4. this.id = new Date() //这里简单的运用时间戳做订阅池的ID
    5. this.subs = [] //该事件下被订阅对象的集合
    6. }
    7. defined(){ // 添加订阅者
    8. Dep.watch.add(this);
    9. }
    10. notify() { //通知订阅者有变化
    11. this.subs.forEach((e, i) => {
    12. if(typeof e.update === 'function'){
    13. try {
    14. e.update.apply(e) //触发订阅者更新函数
    15. } catch(err){
    16. console.warr(err)
    17. }
    18. }
    19. })
    20. }
    21. }
    22. Dep.watch = null;
    23. class Watch {
    24. constructor(name, fn){
    25. this.name = name; //订阅消息的名称
    26. this.id = new Date(); //这里简单的运用时间戳做订阅者的ID
    27. this.callBack = fn; //订阅消息发送改变时->订阅者执行的回调函数
    28. }
    29. add(dep) { //将订阅者放入dep订阅池
    30. dep.subs.push(this);
    31. }
    32. update() { //将订阅者更新方法
    33. var cb = this.callBack; //赋值为了不改变函数内调用的this
    34. cb(this.name);
    35. }
    36. }
    37. 重写history方法,并添加window.addHistoryListener事件机
    38. var addHistoryMethod = (function(){
    39. var historyDep = new Dep();
    40. return function(name) {
    41. if(name === 'historychange'){
    42. return function(name, fn){
    43. var event = new Watch(name, fn)
    44. Dep.watch = event;
    45. historyDep.defined();
    46. Dep.watch = null; //置空供下一个订阅者使用
    47. }
    48. } else if(name === 'pushState' || name === 'replaceState') {
    49. var method = history[name];
    50. return function(){
    51. method.apply(history, arguments);
    52. historyDep.notify();
    53. }
    54. }
    55. }
    56. }())
    57. window.addHistoryListener = addHistoryMethod('historychange');
    58. history.pushState = addHistoryMethod('pushState');
    59. history.replaceState = addHistoryMethod('replaceState');制

八、路由跳转和location.href的区别

使用location.href='/url'来跳转,简单方便,但是刷新了页面;
使用路由方式跳转,无刷新页面,静态跳转;

九、$route和$router的区别

1、this.$router:是 router 实例。通过 this.$router 访问路由器,相当于获取了整个路由文件,在$router.option.routes中,或查看到当前项目的整个路由结构 具有实例方法

2、this.$route:当前激活的路由信息对象。这个属性是只读的,里面的属性是 immutable (不可变) 的,不过可以 watch (监测变化) 它。通过 this.$route 访问的是当前路由,获取和当前路由有关的信息

十、vue-loader做了哪些事情(工作原理)

通过 vue-loader, webpack 可以将 .vue 文件 转化为 浏览器可识别的javascript

vue-loader 的工作流程, 简单来说,分为以下几个步骤:

  1. 将一个 .vue 文件 切割成 templatescriptstyles 三个部分。
  2. template 部分 通过 compile 生成 render、 staticRenderFns
  3. 获取 script 部分 返回的配置项对象 scriptExports
  4. styles 部分,会通过 css-loadervue-style-loader, 添加到 head 中, 或者通过 css-loaderMiniCssExtractPlugin 提取到一个 公共的css文件 中。
  5. 使用 vue-loader 提供的 normalizeComponent 方法, 合并 scriptExports、render、staticRenderFns, 返回 构建vue组件需要的配置项对象 - options, 即 **{data, props, methods, render, staticRenderFns...}**。

十一、路由传值

1、显式

  1. http://localhost:8080/about?a=1
  2. 1.1 传:this.$router.push({
  3. path:'/about',
  4. query:{
  5. a:1
  6. }
  7. })
  8. 1.2 接:this.$route.query.a

2、隐式

  1. http://localhost:8080/about
  2. 2.1 传:this.$router.push({
  3. name:'About',
  4. params:{
  5. a:1
  6. }
  7. })
  8. 2.2 接:this.$route.params.a

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/羊村懒王/article/detail/430323
推荐阅读
相关标签
  

闽ICP备14008679号