vue面试题

1、什么是MVVM?

在这里插入图片描述

(1)View 层

View 是视图层,也就是用户界面。前端主要由 HTML 和 CSS 来构建 。

(2)Model 层

Model 是指数据模型,泛指后端进行的各种业务逻辑处理和数据操控,对于前端来说就是后端提供的 api 接口。

(3)ViewModel 层
ViewModel 通过双向数据绑定把 View 层和 Model 层连接起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。

优点:

  1. 低耦合。视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定到不同的"View"上,当View变化的时候Model可以不变,当Model变化的时候View也可以不变。
  2. 可重用性。可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑。
  3. 独立开发。开发人员可以专注于业务逻辑和数据的开发(ViewModel),设计人员可以专注于页面设计。
  4. 可测试。界面素来是比较难于测试的,而现在测试可以针对ViewModel来写。

2、vue的生命周期?

vue的生命周期就是从创建到销毁的过程,即从创建、初始化数据、编译模板、挂载domd到渲染、更新到渲染、销毁等一系列过程。
在这里插入图片描述
在这里插入图片描述

3、vue的响应式原理/双向绑定?

当你把一个普通的 JavaScript 对象传入 Vue 实例作为data选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。 这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。每个组件实例都对应一个watcher实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

在这里插入图片描述
检测变化的注意事项:
由于 JavaScript 的限制,Vue 不能检测数组和对象的变化。尽管如此我们还是有一些办法来回避这些限制并保证它们的响应性。

  • 对于对象:对于已经创建的实例,Vue 不允许动态添加根级别的响应式 property。但是,可以使用 Vue.set(object, propertyName, value)
  • 对于数组:也可以使用 vm.$set 实例方法,或者splicepushpull方法
  • 详情见官网:官网链接

4、vue异步更新队列与Vue.nextTick(callback)?

可能你还没有注意到,Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环“tick”中,Vue 刷新队列并执行实际 (已去重的) 工作。Vue 在内部对异步队列尝试使用原生的 Promise.then、MutationObserver 和 setImmediate,如果执行环境不支持,则会采用 setTimeout(fn, 0) 代替。

例如,当你设置 vm.someData = ‘new value’,该组件不会立即重新渲染。当刷新队列时,组件会在下一个事件循环“tick”中更新。多数情况我们不需要关心这个过程,但是如果你想基于更新后的 DOM 状态来做点什么,这就可能会有些棘手。虽然 Vue.js 通常鼓励开发人员使用“数据驱动”的方式思考,避免直接接触 DOM,但是有时我们必须要这么做。为了在数据变化之后等待 Vue 完成更新 DOM,可以在数据变化之后立即使用 Vue.nextTick(callback)。这样回调函数将在 DOM 更新完成后被调用。例如:

<div id="example">{{message}}</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: '123'
  }
})
vm.message = 'new message' // 更改数据
vm.$el.textContent === 'new message' // false
Vue.nextTick(function () {
  vm.$el.textContent === 'new message' // true
})

详情见官网:官网链接

5、SPA单页面应用?

7、Vue 的父组件和子组件生命周期钩子函数执行顺序?

Vue 的父组件和子组件生命周期钩子函数执行顺序可以归类为以下 4 部分:

加载渲染过程

父 beforeCreate -> 父 created -> 父 beforeMount -> 子 beforeCreate -> 子created -> 子 beforeMount -> 子 mounted -> 父 mounted

子组件更新过程

父 beforeUpdate -> 子 beforeUpdate -> 子 updated -> 父 updated

父组件更新过程

父 beforeUpdate -> 父 updated

销毁过程

父 beforeDestroy -> 子 beforeDestroy -> 子 destroyed -> 父 destroyed

8、谈谈你对 keep-alive 的了解?

keep-alive 是 Vue 内置的一个组件,可以使被包含的组件保留状态,避免重新渲染 ,其有以下特性:

1.一般结合路由和动态组件一起使用,用于缓存组件;

2.提供 include 和 exclude 属性,两者都支持字符串或正则表达式, include 表示只有名称匹配的组件会被缓存,exclude 表示任何名称匹配的组件都不会被缓存 ,其中 exclude 的优先级比 include 高;

3.对应两个钩子函数 activated 和 deactivated ,当组件被激活时,触发钩子函数 activated,当组件被移除时,触发钩子函数 deactivated。

9、事件修饰符有哪些?

在事件处理程序中调用 event.preventDefault() 或 event.stopPropagation() 是非常常见的需求。尽管我们可以在方法中轻松实现这点,但更好的方式是:方法只有纯粹的数据逻辑,而不是去处理 DOM 事件细节。
为了解决这个问题,Vue.js 为 v-on 提供了事件修饰符。之前提过,修饰符是由点开头的指令后缀来表示的。

  • .stop:阻止事件冒泡
  • .prevent:拦截默认事件
  • .capture
  • .self
  • .once:vue3中被移除了,详细使用
  • .passive:不拦截默认事件

10、vue-router如何响应路由动态参数的变化?

问题:当使用路由参数时,例如从/content?id=1 content?id=2,此时原来的组件实例会被复用。这也意味着组件的生命周期钩子不会再被调用,此时vue应该如何响应路由参数 的变化?
方法一:复用组件时,想对路由参数的变化作出响应的话, 可以watch (监测变化) $route 对象

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

方法二:使用 beforeRouteUpdate守卫:

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

11、vue-router有哪几种导航钩子( 导航守卫 )?

  1. 全局守卫: router.beforeEach
const router = new VueRouter({ ... })
  router.beforeEach((to, from, next) => {
  // ...
})
  1. 路由独享的守卫: beforeEnter
const router = new VueRouter({
  routes: [
    {
      path: '/foo',
      component: Foo,
      beforeEnter: (to, from, next) => {
        // ...
      }
    }
  ]
})

  1. 组件内的守卫: beforeRouteEnterbeforeRouteUpdatebeforeRouteLeave
const Foo = {
  template: `...`,
  beforeRouteEnter (to, from, next) {
    // 在渲染该组件的对应路由被 confirm 前调用
    // 不!能!获取组件实例 `this`
    // 因为当守卫执行前,组件实例还没被创建
  },
  //不过,你可以通过传一个回调给 next来访问组件实例。
  //在导航被确认的时候执行回调,并且把组件实例作为回调方法的参数。
  beforeRouteEnter (to, from, next) {
    next(vm => {
      // 通过 `vm` 访问组件实例
    })
  },
  beforeRouteUpdate (to, from, next) {
    // 在当前路由改变,但是该组件被复用时调用
    // 举例来说,对于一个带有动态参数的路径 /foo/:id,在 /foo/1 和 /foo/2 之间跳转的时候,
    // 由于会渲染同样的 Foo 组件,因此组件实例会被复用。而这个钩子就会在这个情况下被调用。
    // 可以访问组件实例 `this`
  },
  beforeRouteLeave (to, from, next) {
    // 导航离开该组件的对应路由时调用
    // 可以访问组件实例 `this`
  }
}
  1. 全局解析守卫: router.beforeResolve
  2. 全局后置钩子: router.afterEach

每个守卫方法接收三个参数

  • to: 即将要进入的目标路由对象

  • from: 当前导航正要离开的路由

  • next: 一定要调用该方法来resolve这个钩子。执行效果依赖 next 方法的调用参数。
    1、next(): 进行管道中的下一个钩子。如果全部钩子执行完了,则导航的状态就是confirmed (确认的)。
    2、next(false): 中断当前的导航。如果浏览器的 URL 改变了 (可能是用户手动或者浏览器后退按钮),那么 URL 地址会重置到 from 路由对应的地址。(退出软件提示我,真要退出吗?点不确定还是本页面就可以调用next(false)
    3、next(’/’) 或者 next({ path: ‘/’ }): 跳转到一个不同的地址。当前的导航被中断,然后进行一个新的导航。你可以向 next 传递任意位置对象,且允许设置诸如 replace: true、name: ‘home’ 之类的选项以及任何用在router-link的 to prop或 router.push中的选项。
    4、next(error): 如果传入 next 的参数是一个 Error 实例,则导航会被终止且该错误会被传递给 router.onError()注册过的回调。

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

12、如何处理401页面

出现场景:

1、 未登陆用户做一些需要权限才能做的操作(例如:关注作者),代码会报出401错误。这种情况下,应该让用户回到登陆页。

2、登录用户的token过期了

当用户登陆成功之后,返回的token中有两个值,说明如下:

`在这里插入图片描述

token:

作用:在访问一些接口时,需要传入token,就是它。

有效期:2小时(安全)具体是多长,是由后端决定)。

refresh_token:

作用: 当token的有效期过了之后,可以使用它去请求一个特殊接口(这个接口也是后端指定的,明确需要传入refresh_token),并返回一个新的token回来(有效期还是2小时),以替换过期的那个token。

有效期:14天。(最理想的情况下,一次登陆可以持续14天。)

方法一:通过axios响应拦截器来处理401问题。
参考链接

方法二:通过全局守卫处理401问题。
参考链接

13、如何处理404页面

如何触发404页面?
比如你的域名是http://localhost:8080/,当你进入一个没有声明/匹配的路由页面时就会跳到404页面,
比如访问了http://localhost:8080/无此页面,就会跳到404页面,如果没有声明一个404页面,那就会跳到一个空白页面。

解决方案
在router.js中 路由是从上到下执行的 只需要在最后一行把path写成 * 并且指定一个404.vue页面即可
项目启动页指的是: 当你进入www.baidu.com,会自动跳转到login登录页。

// 路由懒加载
const firstPage = r => require.ensure([], () => r(require('@/components/firstPage.vue')), 'firstPage')
const login = r => require.ensure([], () => r(require('@/components/login.vue')), 'login')

const router = new Router({
    mode: 'history',
    routes: [
        {
            path: '/login',
            name: '登录',
            component: login
        },
        
        {
            path: '/',
            name: '首页',
            component: firstPage
        },

        {
            path: '*',
            name: '/404',
            component: resolve => require(['@/components/404.vue'], resolve),
        },

    ]


});

参考视频链接

14、Vue 页面权限控制的两种种方法

场景如下: 如果一个网站有不同的角色,比如 管理员 和 普通用户 ,要求不同的角色能访问的页面是不一样的。
这个时候我们就可以 把所有的页面都放在路由表里 ,只要 在访问的时候判断一下角色权限 。如果有权限就让访问,没有权限的话就拒绝访问,跳转到401页面

方法一: vue-router 在构建路由时提供了元信息 meta 配置接口我们可以在元信息中添加路由对应的权限,然后在路由守卫中检查相关权限,控制其路由跳转

案例1:可以在每一个路由的 meta 属性里,将能访问该路由的角色添加到 roles 里。用户每次登陆后,将用户的角色返回。然后在访问页面时,把路由的 meta 属性和用户的角色进行对比,如果用户的角色在路由的 roles 里,那就是能访问,如果不在就拒绝访问。
路由信息:

routes: [
  {
    path: '/login',
    name: 'login',
    meta: {
      roles: ['admin', 'user']
    },
    component: () => import('../components/Login.vue')
  },
  {
    path: 'home',
    name: 'home',
    meta: {
      roles: ['admin']
    },
    component: () => import('../views/Home.vue')
  },
]

页面控制:

//假设有两种角色:admin 和 user 
//从后台获取的用户角色
const role = 'user'
//当进入一个页面是会触发导航守卫 router.beforeEach 事件
router.beforeEach((to,from,next)=>{
 if(to.meta.roles.includes(role)){
 next() //放行
 }esle{
 next({path:"/404"}) //跳到404页面
 }
})

案例2:可以在多个路由下面添加这个权限标识,达到控制的目的。只要一切换页面,就需要看有没有这个权限,所以可以在最大的路由下 main.js 中配置

// router.js
// 路由表元信息
[
 {
  path: '',
  redirect: '/home'
 },
 {
  path: '/home',
  meta: {
   title: 'Home',
   icon: 'home'
  }
 },
 {
  path: '/userCenter',
  meta: {
   title: '个人中心',
   requireAuth: true // 在需要登录的路由的meta中添加响应的权限标识
  }
 }
]
 
// 在守卫中访问元信息
function gaurd (to, from, next) {
 // to.matched.some(record => record.meta.requireAuth)
 // 可在此处
}
// router.js
// 路由表元信息
[
 {
  path: '',
  redirect: '/home'
 },
 {
  path: '/home',
  meta: {
   title: 'Home',
   icon: 'home'
  }
 },
 {
  path: '/userCenter',
  meta: {
   title: '个人中心',
   requireAuth: true // 在需要登录的路由的meta中添加响应的权限标识
  }
 }
]

// 在守卫中访问元信息
function gaurd (to, from, next) {
 // to.matched.some(record => record.meta.requireAuth)
 // 可在此处
}

方法二:将控制权限交给后端,后端根据登录人的不同,返回给前端不同的前端路由路径,并将这些路径渲染到菜单上,

14、如何根据权限进行按钮级别控制

参考文档链接
参考视频链接

15、Vue中v-if和v-for为何不能连用?

实质在vue2中是v-for优先执行,会创建对应的dom节点,如果v-if为false,会删除这个dom节点;这样创建后再删除,会造成页面卡顿。

16、v-model原理?

比如:v-modelinput元素上时:
第一行的代码其实只是第二行的语法糖

<input v-model="sth" />
<input v-bind:value="sth" v-on:input="sth = $event.target.value" />

详情如下:
在这里插入图片描述

17、slot插槽

插槽分为如下

  • 匿名插槽: 不写name属性时的使用方法就叫匿名插槽,其时所谓的匿名插槽是有名字的 他的名字是default
  • 具名插槽: 可以通过v-slot:插槽的名字 , 给指定的插槽定义内容 ,v-slot:名字可以简写为 #名字
  • 作用域插槽: 插槽也可以通过自定义属性预备插槽需要使用的数据 而这种插槽就叫做作用域插槽,

18、computed、watch、method区别

1、三者的加载顺序不同
computed是在HTML DOM加载后马上执行的,如赋值;(属性将被混入到 Vue 实例)

methods:则必须要有一定的触发条件才能执行,如点击事件

watch:它用于观察Vue实例上的数据变动。最初绑定的时候是不会执行的,要等到监听的值第一次被改变时才执行监听计算。

默认加载的时候: 先computed再watch,不执行methods;

触发某一事件后: 先computed再methods再到watch

2、computed计算属性是基于它们的依赖进行缓存的。可以同时操作多个字段
3、watch可以实现异步请求相关的方法,只能操作一个字段
参考视频

19、watch的参数选项以及其含义

选项:deep
为了发现对象内部值的变化,可以在选项参数中指定 deep: true。注意监听数组的变更不需要这么做。(监听对象必须设置deep: true)

选项:immediate
在选项参数中指定 immediate: true 将立即以表达式的当前值触发回调,注意在带有 immediate 选项时,你不能在第一次回调时取消侦听给定的 property。
参考文档详情

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值