前端攻城狮必须收藏的vue面试题+详解答案

Source

vue面试题+详解答案

1 MVC 和 MVVM 区别

MVC

MVC 全名是 Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,一种软件设计典范

  • Model(模型):是应用程序中用于处理应用程序数据逻辑的部分。通常模型对象负责在数据库中存取数据
  • View(视图):是应用程序中处理数据显示的部分。通常视图是依据模型数据创建的
  • Controller(控制器):是应用程序中处理用户交互的部分。通常控制器负责从视图读取数据,控制用户输入,并向模型发送数据

在这里插入图片描述

  • MVC 的思想:一句话描述就是 Controller 负责将 Model 的数据用 View 显示出来,换句话说就是在
    Controller 里面把 Model 的数据赋值给 View。

MVVM

MVVM 新增了 VM 类

  • ViewModel 层:做了两件事达到了数据的双向绑定一是将【模型】转化成【视图】,即将后端传递的数据转化成所看到的页面。实现的方式是:数据绑定。二是将【视图】转化成【模型】,即将所看到的页面转化成后端的数据。实现的方式是:DOM事件监听。
    在这里插入图片描述
  • MVVM 与 MVC 最大的区别就是:它实现了 View 和 Model 的自动同步,也就是当 Model
    的属性改变时,我们不用再自己手动操作 Dom 元素,来改变 View 的显示,而是改变属性后该属性对应 View层显示会自动改变(对应Vue数据驱动的思想)
  • 整体看来,MVVM 比 MVC 精简很多,不仅简化了业务与界面的依赖,还解决了数据频繁更新的问题,不用再用选择器操作 DOM 元素。因为在MVVM 中,View 不知道 Model 的存在,Model 和 ViewModel 也观察不到 View,这种低耦合模式提高代码的可重用性
	注意:Vue 并没有完全遵循 MVVM 的思想 这一点官网自己也有说明

在这里插入图片描述
那么问题来了 为什么官方要说 Vue 没有完全遵循 MVVM 思想呢?

  • 严格的 MVVM 要求 View 不能和 Model 直接通信,而 Vue 提供了$refs 这个属性,让 Model 可以直接操作 View,违反了这一规定,所以说 Vue 没有完全遵循 MVVM。

2 为什么 data 是一个函数

  • 组件中的 data 写成一个函数,数据以函数返回值形式定义,这样每复用一次组件,就会返回一份新的,data类似于给每个组件实例创建一个私有的数据空间,让各个组件实例维护各自的数据。而单纯的写成对象形式,就使得所有组件实例共用了一份data,就会造成一个变了全都会变的结果.

3 Vue 组件通讯有哪几种方式

  • props 和$emit 父组件向子组件传递数据是通过 prop 传递的,子组件传递数据给父组件是通过$emit 触发事件来做到的
  • $parent,$children 获取当前组件的父组件和当前组件的子组件
  • $attrs$listeners A->B->C。Vue 2.4 开始提供了$attrs$listeners
    来解决这个问题
  • 父组件中通过 provide 来提供变量,然后在子组件中通过 inject
    来注入变量。(官方不推荐在实际业务中使用,但是写组件库时很常用)
  • $refs 获取组件实例
  • envetBus 兄弟组件数据传递 这种情况下可以使用事件总线的方式
  • vuex 状态管理

4 Vue 的生命周期方法有哪些 一般在哪一步发请求

  • beforeCreate 在实例初始化之后,数据观测(data observer) 和 event/watcher
    事件配置之前被调用。在当前阶段 data、methods、computed 以及 watch 上的数据和方法都不能被访问
  • created 实例已经创建完成之后被调用。在这一步,实例已完成以下的配置:数据观测(data observer),属性和方法的运算,watch/event 事件回调。这里没有$el,如果非要想与 Dom 进行交互,可以通过 vm.$nextTick 来访问 Dom
  • beforeMount 在挂载开始之前被调用:相关的 render 函数首次被调用。
  • mounted 在挂载完成后发生,在当前阶段,真实的 Dom 挂载完毕,数据完成双向绑定,可以访问到 Dom 节点
  • beforeUpdate 数据更新时调用,发生在虚拟 DOM重新渲染和打补丁(patch)之前。可以在这个钩子中进一步地更改状态,这不会触发附加的重渲染过程
  • updated 发生在更新完成之后,当前阶段组件 Dom已完成更新。要注意的是避免在此期间更改数据,因为这可能会导致无限循环的更新,该钩子在服务器端渲染期间不被调用。
  • beforeDestroy 实例销毁之前调用。在这一步,实例仍然完全可用。我们可以在这时进行善后收尾工作,比如清除计时器。
  • destroyed Vue 实例销毁后调用。调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁。 该钩子在服务器端渲染期间不被调用。
  • activated keep-alive 专属,组件被激活时调用
  • deactivated keep-alive 专属,组件被销毁时调用
	异步请求在哪一步发起?
  • 可以在钩子函数 created、beforeMount、mounted 中进行异步请求,因为在这三个钩子函数中,data
    已经创建,可以将服务端端返回的数据进行赋值。
  • 如果异步请求不需要依赖 Dom 推荐在 created 钩子函数中调用异步请求,因为在 created
    钩子函数中调用异步请求有以下优点:
  • 能更快获取到服务端数据,减少页面 loading 时间; ssr 不支持 beforeMount 、mounted
    钩子函数,所以放在 created 中有助于一致性;

5 v-if 和 v-show 的区别

  • v-if 在编译过程中会被转化成三元表达式,条件不满足时不渲染此节点。
  • v-show 会被编译成指令,条件不满足时控制样式将对应节点隐藏 (display:none)

使用场景

v-if 适用于在运行时很少改变条件,不需要频繁切换条件的场景
v-show 适用于需要非常频繁切换条件的场景

6 说说 vue 的内置指令
在这里插入图片描述
7 怎样理解 Vue 的单向数据流

数据总是从父组件传到子组件,子组件没有权利修改父组件传过来的数据,只能请求父组件对原始数据进行修改。这样会防止从子组件意外改变父级组件的状态,从而导致你的应用的数据流向难以理解。

注意:在子组件直接用 v-model 绑定父组件传过来的 prop 这样是不规范的写法 开发环境会报警告

如果实在要改变父组件的 prop 值 可以再 data 里面定义一个变量 并用 prop 的值初始化它 之后用$emit 通知父组件去修改

8 computed 和 watch 的区别和运用的场景

  • computed 是计算属性,依赖其他属性计算值,并且 computed 的值有缓存,只有当计算值变化才会返回内容,它可以设置getter 和 setter。
  • watch 监听到值的变化就会执行回调,在回调中可以进行一些逻辑操作。

9 v-if 与 v-for 为什么不建议一起使用

  • v-for 和 v-if 不要在同一个标签中使用,因为解析时先解析 v-for 再解析v-if。如果遇到需要同时使用时可以考虑写成计算属性的方式。

10 Vue2.0 响应式数据的原理

  • 整体思路是数据劫持+观察者模式 对象内部通过 defineReactive 方法,使用 Object.defineProperty
    将属性进行劫持(只会劫持已经存在的属性),数组则是通过重写数组方法来实现。当页面使用对应属性时,每个属性都拥有自己的 dep属性,存放他所依赖的 watcher(依赖收集),当属性变化后会通知自己对应的 watcher 去更新(派发更新)。

代码如下

class Observer {
    
      
  // 观测值
  constructor(value) {
    
      
    this.walk(value);
  }
  walk(data) {
    
      
    // 对象上的所有属性依次进行观测
    let keys = Object.keys(data);
    for (let i = 0; i < keys.length; i++) {
    
      
      let key = keys[i];
      let value = data[key];
      defineReactive(data, key, value);
    }
  }
}
// Object.defineProperty数据劫持核心 兼容性在ie9以及以上
function defineReactive(data, key, value) {
    
      
  observe(value); // 递归关键
  // --如果value还是一个对象会继续走一遍odefineReactive 层层遍历一直到value不是对象才停止
  //   思考?如果Vue数据嵌套层级过深 >>性能会受影响
  Object.defineProperty(data, key, {
    
      
    get() {
    
      
      console.log("获取值");

      //需要做依赖收集过程
      return value;
    },
    set(newValue) {
    
      
      if (newValue === value) return;
      console.log("设置值");
      //需要做派发更新过程
      value = newValue;
    },
  });
}
export function observe(value) {
    
      
  // 如果传过来的是对象或者数组 进行属性劫持
  if (
    Object.prototype.toString.call(value) === "[object Object]" ||
    Array.isArray(value)
  ) {
    
      
    return new Observer(value);
  }
}

11 vue3.0 用过吗 了解多少

  • 响应式原理的改变 Vue3.x 使用 Proxy 取代 Vue2.x 版本的 Object.defineProperty
  • 组件选项声明方式 Vue3.x 使用 Composition API setup 是 Vue3.x 新增的一个选项, 他是组件内使用Composition API 的入口。
  • 模板语法变化 slot 具名插槽语法 自定义指令 v-model 升级
  • 其它方面的更改 Suspense 支持 Fragment(多个根节点)和 Protal(在 dom其他部分渲染组建内容)组件,针对一些特殊的场景做了处理。 基于 treeshaking 优化,提供了更多的内置功能。

这里其他关于Vue3.0的相关内容查看我的历史文章

12 Vue 的父子组件生命周期钩子函数执行顺序

  • 加载渲染过程 父 beforeCreate->父 created->父 beforeMount->子 beforeCreate->子created->子 beforeMount->子 mounted->父 mounted
  • 子组件更新过程父 beforeUpdate->子 beforeUpdate->子 updated->父 updated
  • 父组件更新过程 父 beforeUpdate->父 updated
  • 销毁过程父 beforeDestroy->子 beforeDestroy->子 destroyed->父 destroyed

13 虚拟 DOM 是什么 有什么优缺点

  • 由于在浏览器中操作 DOM 是很昂贵的。频繁的操作 DOM,会产生一定的性能问题。这就是虚拟 Dom 的产生原因。Vue2 的 Virtual DOM 借鉴了开源库 snabbdom 的实现。Virtual DOM 本质就是用一个原生的 JS 对象去描述一个 DOM节点,是对真实 DOM 的一层抽象。

优点:

  • 保证性能下限: 框架的虚拟 DOM 需要适配任何上层 API 可能产生的操作,它的一些 DOM操作的实现必须是普适的,所以它的性能并不是最优的;但是比起粗暴的 DOM 操作性能要好很多,因此框架的虚拟 DOM至少可以保证在你不需要手动优化的情况下,依然可以提供还不错的性能,即保证性能的下限;

  • 无需手动操作 DOM: 我们不再需要手动去操作 DOM,只需要写好 View-Model 的代码逻辑,框架会根据虚拟 DOM 和数据双向绑定,帮我们以可预期的方式更新视图,极大提高我们的开发效率;

  • 跨平台: 虚拟 DOM 本质上是 JavaScript 对象,而 DOM 与平台强相关,相比之下虚拟 DOM可以进行更方便地跨平台操作,例如服务器渲染、weex 开发等等。

缺点:

  • 无法进行极致优化: 虽然虚拟 DOM + 合理的优化,足以应对绝大部分应用的性能需求,但在一些性能要求极高的应用中虚拟 DOM无法进行针对性的极致优化。
  • 首次渲染大量 DOM 时,由于多了一层虚拟 DOM 的计算,会比 innerHTML 插入慢。

14 v-model 原理

  • v-model 只是语法糖而已
  • v-model 在内部为不同的输入元素使用不同的 property 并抛出不同的事件:
  • text 和 textarea 元素使用 value property 和 input 事件;
  • checkbox 和 radio 使用 checked property 和 change 事件;
  • select 字段将 value 作为 prop 并将 change 作为事件

15 v-for 为什么要加 key

  • 如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。key 是为 Vue 中vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速
  • 更准确:因为带 key 就不是就地复用了,在sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。
  • 更快速:利用 key 的唯一性生成map 对象来获取对应节点,比遍历方式更快

16 vue-router 路由钩子函数是什么 执行顺序是什么

  • 路由钩子的执行流程, 钩子函数种类有:全局守卫、路由守卫、组件守卫

完整的导航解析流程:

  • 导航被触发。
  • 在失活的组件里调用 beforeRouteLeave 守卫。
  • 调用全局的 beforeEach 守卫。
  • 在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。
  • 在路由配置里调用 beforeEnter。
  • 解析异步路由组件。
  • 在被激活的组件里调用 beforeRouteEnter。
  • 调用全局的 beforeResolve 守卫 (2.5+)。
  • 导航被确认。
  • 调用全局的 afterEach 钩子。
  • 触发 DOM 更新。
  • 调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

17 使用过 Vue SSR 吗?说说 SSR

  • SSR 也就是服务端渲染,也就是将 Vue 在客户端把标签渲染成 HTML 的工作放在服务端完成,然后再把 html 直接返回给客户端。

优点:

  • SSR 有着更好的 SEO、并且首屏加载速度更快

缺点:

  • 开发条件会受到限制,服务器端渲染只支持 beforeCreate 和 created
    两个钩子,当我们需要一些外部扩展库时需要特殊处理,服务端渲染应用程序也需要处于 Node.js 的运行环境。 服务器会有更大的负载需求

18 你都做过哪些 Vue 的性能优化

  • 对象层级不要过深,否则性能就会差
  • 不需要响应式的数据不要放到 data 中(可以用 Object.freeze() 冻结数据)
  • v-if 和 v-show 区分使用场景
  • computed 和 watch 区分使用场景
  • v-for 遍历必须加 key,key 最好是 id 值,且避免同时使用 v-if
  • 大数据列表和表格性能优化-虚拟列表/虚拟表格
  • 防止内部泄漏,组件销毁后把全局变量和事件销毁
  • 图片懒加载
  • 路由懒加载
  • 第三方插件的按需引入
  • 适当采用 keep-alive 缓存组件
  • 防抖、节流运用
  • 服务端渲染 SSR or 预渲染

19 nextTick 使用场景和原理

  • nextTick 中的回调是在下次 DOM 更新循环结束之后执行的延迟回调。在修改数据之后立即使用这个方法,获取更新后的DOM。主要思路就是采用微任务优先的方式调用异步方法去执行 nextTick 包装的方法( 在整个DOM元素渲染完成后再进行其他逻辑 )

20 keep-alive 使用场景和原理

  • keep-alive 是 Vue 内置的一个组件,可以实现组件缓存,当组件切换时不会对当前组件进行卸载。
  • 常用的两个属性 include/exclude,允许组件有条件的进行缓存。
  • 两个生命周期 activated/deactivated,用来得知当前组件是否处于活跃状态

21 Vue.set 方法原理

  • 了解 Vue 响应式原理的同学都知道在两种情况下修改数据 Vue 是不会触发视图更新的
  • 在实例创建之后添加新的属性到实例上(给响应式对象新增属性)
  • 直接更改数组下标来修改数组的值
  • Vue.set 或者说是$set 原理如下
  • 因为响应式数据 我们给对象和数组本身都增加了__ob__属性,代表的是 Observer 实例。当给对象新增不存在的属性
  • 首先会把新的属性进行响应式跟踪 然后会触发对象__ob__的 dep 收集到的 watcher 去更新,当修改数组索引时我们调用数组本身的 splice 方法去更新数组
//举例
		//定义的对象 //里面的字段 // 值
this$set(this.form,data,'1')

22 Vue 修饰符有哪些

事件修饰符

  • .stop 阻止事件继续传播
  • .prevent 阻止标签默认行为
  • .capture 使用事件捕获模式,即元素自身触发的事件先在此处处理,然后才交由内部元素进行处理
  • .self 只当在 event.target 是当前元素自身时触发处理函数
  • .once 事件将只会触发一次
  • .passive 告诉浏览器你不想阻止事件的默认行为

v-model 的修饰符

  • .lazy 通过这个修饰符,转变为在 change 事件再同步
  • .number 自动将用户的输入值转化为数值类型
  • .trim 自动过滤用户输入的首尾空格

键盘事件的修饰符

  • .enter .tab .delete (捕获“删除”和“退格”键) .esc .space .up .down .left .right

系统修饰键

  • .ctrl .alt .shift .meta

鼠标按钮修饰符

  • .left .right .middle

23 能说下 vue-router 中常用的路由模式实现原理吗

hash 模式

  • location.hash 的值实际就是 URL 中#后面的东西 它的特点在于:hash 虽然出现 URL 中,但不会被包含在 HTTP
    请求中,对后端完全没有影响,因此改变 hash 不会重新加载页面。
  • 可以为 hash 的改变添加监听事件
window.addEventListener("hashchange", funcRef, false);
  • 每一次改变 hash(window.location.hash),都会在浏览器的访问历史中增加一个记录利用 hash
    的以上特点,就可以来实现前端路由“更新视图但不重新请求页面”的功能了
  • 特点:兼容性好但是不美观

history 模式

  • 利用了 HTML5 History Interface 中新增的 pushState() 和 replaceState() 方法。这两个方法应用于浏览器的历史记录站,在当前已有的 back、forward、go的基础之上,它们提供了对历史记录进行修改的功能。这两个方法有个共同的特点:当调用他们修改浏览器历史记录栈后,虽然当前 URL改变了,但浏览器不会刷新页面,这就为单页应用前端路由“更新视图但不重新请求页面”提供了基础。
  • 特点:虽然美观,但是刷新会出现 404 需要后端进行配置

希望此文章对你有帮助