>首页> IT >

环球微动态丨2022年vue高频面试题分享(附答案分析)

时间:2022-08-01 20:04:15       来源:转载
本篇文章给大家总结一些值得收藏的2022年精选vue高频面试题(附答案)。有一定的参考价值,有需要的朋友可以参考一下,希望对大家有所帮助。

Vue-router 导航守卫有哪些

全局前置/钩子:beforeEach、beforeResolve、afterEach路由独享的守卫:beforeEnter组件内的守卫:beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave

(学习视频分享:vue视频教程)


(相关资料图)

为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

push();pop();shift();unshift();splice();sort();reverse();

由于只针对了以上 7 种方法进行了 hack 处理,所以其他数组的属性也是检测不到的,还是具有一定的局限性。

v-for 为什么要加 key

如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试就地修改/复用相同类型元素的算法。key 是为 Vue 中 vnode 的唯一标记,通过这个 key,我们的 diff 操作可以更准确、更快速

更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。

更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快

如何从真实DOM到虚拟DOM

涉及到Vue中的模板编译原理,主要过程:

将模板转换成ast树,ast用对象来描述真实的JS语法(将真实DOM转换成虚拟DOM)

优化树

ast树生成代码

为什么Vue采用异步渲染呢?

Vue是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能,Vue会在本轮数据更新后,在异步更新视图。核心思想nextTick

dep.notify()通知 watcher进行更新,subs[i].update依次调用 watcher 的updatequeueWatcher将watcher 去重放入队列, nextTick(flushSchedulerQueue)在下一tick中刷新watcher队列(异步)。

为什么vue组件中data必须是一个函数?

对象为引用类型,当复用组件时,由于数据对象都指向同一个data对象,当在一个组件中修改data时,其他重用的组件中的data会同时被修改;而使用返回对象的函数,由于每次返回的都是一个新对象(Object的实例),引用地址不同,则不会出现这个问题。

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 为什么要用 vm.$set() 解决对象新增属性不能响应的问题 ?你能说说如下代码的实现原理么?

1)Vue为什么要用vm.$set() 解决对象新增属性不能响应的问题

Vue使用了Object.defineProperty实现双向数据绑定

在初始化实例时对属性执行 getter/setter 转化

属性必须在data对象上存在才能让Vue将它转换为响应式的(这也就造成了Vue无法检测到对象属性的添加或删除)

所以Vue提供了Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)

2)接下来我们看看框架本身是如何实现的呢?

export function set (target: Array | Object, key: any, val: any): any {  // target 为数组    if (Array.isArray(target) && isValidArrayIndex(key)) {    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误    target.length = Math.max(target.length, key)    // 利用数组的splice变异方法触发响应式      target.splice(key, 1, val)    return val  }  // key 已经存在,直接修改属性值    if (key in target && !(key in Object.prototype)) {    target[key] = val    return val  }  const ob = (target: any).__ob__  // target 本身就不是响应式数据, 直接赋值  if (!ob) {    target[key] = val    return val  }  // 对属性进行响应式处理  defineReactive(ob.value, key, val)  ob.dep.notify()  return val}

我们阅读以上源码可知,vm.$set 的实现原理是:

如果目标是数组,直接使用数组的 splice 方法触发相应式;

如果目标是对象,会先判读属性是否存在、对象是否是响应式,

最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理

Vue3.0 和 2.0 的响应式原理区别

Vue3.x 改用 Proxy 替代 Object.defineProperty。因为 Proxy 可以直接监听对象和数组的变化,并且有多达 13 种拦截方法。

相关代码如下

import { mutableHandlers } from "./baseHandlers"; // 代理相关逻辑import { isObject } from "./util"; // 工具方法export function reactive(target) {  // 根据不同参数创建不同响应式对象  return createReactiveObject(target, mutableHandlers);}function createReactiveObject(target, baseHandler) {  if (!isObject(target)) {    return target;  }  const observed = new Proxy(target, baseHandler);  return observed;}const get = createGetter();const set = createSetter();function createGetter() {  return function get(target, key, receiver) {    // 对获取的值进行放射    const res = Reflect.get(target, key, receiver);    console.log("属性获取", key);    if (isObject(res)) {      // 如果获取的值是对象类型,则返回当前对象的代理对象      return reactive(res);    }    return res;  };}function createSetter() {  return function set(target, key, value, receiver) {    const oldValue = target[key];    const hadKey = hasOwn(target, key);    const result = Reflect.set(target, key, value, receiver);    if (!hadKey) {      console.log("属性新增", key, value);    } else if (hasChanged(value, oldValue)) {      console.log("属性值被修改", key, value);    }    return result;  };}export const mutableHandlers = {  get, // 当获取属性时调用此方法  set, // 当修改属性时调用此方法};

Vue模版编译原理知道吗,能简单说一下吗?

简单说,Vue的编译过程就是将template转化为render函数的过程。会经历以下阶段:

生成AST树优化codegen

首先解析模版,生成AST语法树(一种用JavaScript对象的形式来描述整个模板)。 使用大量的正则表达式对模板进行解析,遇到标签、文本的时候都会执行对应的钩子进行相关处理。

Vue的数据是响应式的,但其实模板中并不是所有的数据都是响应式的。有一些数据首次渲染后就不会再变化,对应的DOM也不会变化。那么优化过程就是深度遍历AST树,按照相关条件对树节点进行标记。这些被标记的节点(静态节点)我们就可以跳过对它们的比对,对运行时的模板起到很大的优化作用。

编译的最后一步是将优化后的AST树转换为可执行的代码

MVVM的优缺点?

优点:

分离视图(View)和模型(Model),降低代码耦合,提⾼视图或者逻辑的重⽤性: ⽐如视图(View)可以独⽴于Model变化和修改,⼀个ViewModel可以绑定不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。你可以把⼀些视图逻辑放在⼀个ViewModel⾥⾯,让很多view重⽤这段视图逻辑提⾼可测试性: ViewModel的存在可以帮助开发者更好地编写测试代码⾃动更新dom: 利⽤双向绑定,数据更新后视图⾃动更新,让开发者从繁琐的⼿动dom中解放

缺点:

Bug很难被调试: 因为使⽤双向绑定的模式,当你看到界⾯异常了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得⼀个位置的Bug被快速传递到别的位置,要定位原始出问题的地⽅就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的⼀个⼤的模块中model也会很⼤,虽然使⽤⽅便了也很容易保证了数据的⼀致性,当时⻓期持有,不释放内存就造成了花费更多的内存对于⼤型的图形应⽤程序,视图状态较多,ViewModel的构建和维护的成本都会⽐较⾼。

Vue data 中某一个属性的值发生改变后,视图会立即同步执行重新渲染吗?

不会立即同步执行重新渲染。Vue 实现响应式并不是数据发生变化之后 DOM 立即变化,而是按一定的策略进行 DOM 的更新。Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化, Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。

如果同一个watcher被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。然后,在下一个的事件循环tick中,Vue 刷新队列并执行实际(已去重的)工作。

diff算法

时间复杂度:个树的完全 diff算法是一个时间复杂度为 O(n*3),vue进行优化转化成 O(n)

理解:

最小量更新, key很重要。这个可以是这个节点的唯一标识,告诉 diff算法,在更改前后它们是同一个DOM节点

扩展 v-for为什么要有 key,没有 key会暴力复用,举例子的话随便说一个比如移动节点或者增加节点(修改DOM),加 key只会移动减少操作DOM。只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。只进行同层比较,不会进行跨层比较。

diff算法的优化策略:四种命中查找,四个指针

旧前与新前(先比开头,后插入和删除节点的这种情况)旧后与新后(比结尾,前插入或删除的情况)旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)

--- 问完上面这些如果都能很清楚的话,基本O了 ---

以下的这些简单的概念,你肯定也是没有问题的啦?

Vue的优点

轻量级框架:只关注视图层,是一个构建数据的视图集合,大小只有几十 kb;简单易学:国人开发,中文文档,不存在语言障碍 ,易于理解和学习;双向数据绑定:保留了 angular的特点,在数据操作方面更为简单;组件化:保留了 react的优点,实现了 html的封装和重用,在构建单页面应用方面有着独特的优势;视图,数据,结构分离:使数据的更改更为简单,不需要进行逻辑代码的修改,只需要操作数据就能完成相关操作;虚拟DOM:dom操作是非常耗费性能的,不再使用原生的 dom操作节点,极大解放 dom操作,但具体操作的还是 dom不过是换了另一种方式;运行速度更快:相比较于 react而言,同样是操作虚拟 dom,就性能而言, vue存在很大的优势。

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

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

完整的导航解析流程:

导航被触发。

在失活的组件里调用 beforeRouteLeave 守卫。

调用全局的 beforeEach 守卫。

在重用的组件里调用 beforeRouteUpdate 守卫 (2.2+)。

在路由配置里调用 beforeEnter。

解析异步路由组件。

在被激活的组件里调用 beforeRouteEnter。

调用全局的 beforeResolve 守卫 (2.5+)。

导航被确认。

调用全局的 afterEach 钩子。

触发 DOM 更新。

调用 beforeRouteEnter 守卫中传给 next 的回调函数,创建好的组件实例会作为回调函数的参数传入。

Vue.js的template编译

简而言之,就是先转化成AST树,再得到的render函数返回VNode(Vue的虚拟DOM节点),详细步骤如下:

$nextTick 是什么?

Vue 实现响应式并不是在数据发生后立即更新 DOM,使用 vm.$nextTick是在下次 DOM 更新循环结束之后立即执行延迟回调。在修改数据之后使用,则可以在回调中获取更新后的 DOM

说说Vue的生命周期吧

什么时候被调用?

beforeCreate :实例初始化之后,数据观测之前调用created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、watch/event事件回调。无$el.beforeMount:在挂载之前调用,相关render函数首次被调用mounted:了被新创建的vm.$el替换,并挂载到实例上去之后调用改钩子。beforeUpdate:数据更新前调用,发生在虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用改钩子。beforeDestroy:实例销毁前调用,实例仍然可用。destroyed:实例销毁之后调用,调用后,Vue实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除

每个生命周期内部可以做什么?

created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。mounted:实例已经挂载完成,可以进行一些DOM操作。beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。updated:可以执行依赖于DOM的操作,但是要避免更改状态,可能会导致更新无线循环。destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。

ajax放在哪个生命周期?:一般放在mounted中,保证逻辑统一性,因为生命周期是同步执行的,ajax是异步执行的。单数服务端渲染ssr同一放在created中,因为服务端渲染不支持mounted方法。 什么时候使用beforeDestroy?:当前页面使用$on,需要解绑事件。清楚定时器。解除事件绑定,scroll mousemove

Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?

受现代 JavaScript 的限制 ,Vue 无法检测到对象属性的添加或删除。由于 Vue 会在初始化实例时对属性执行 getter/setter 转化,所以属性必须在 data 对象上存在才能让 Vue 将它转换为响应式的。但是 Vue 提供了 Vue.set (object, propertyName, value) / vm.$set (object, propertyName, value)来实现为对象添加响应式属性,那框架本身是如何实现的呢?

我们查看对应的 Vue 源码:vue/src/core/instance/index.js

export function set (target: Array | Object, key: any, val: any): any {  // target 为数组    if (Array.isArray(target) && isValidArrayIndex(key)) {    // 修改数组的长度, 避免索引>数组长度导致splcie()执行有误    target.length = Math.max(target.length, key)    // 利用数组的splice变异方法触发响应式      target.splice(key, 1, val)    return val  }  // key 已经存在,直接修改属性值    if (key in target && !(key in Object.prototype)) {    target[key] = val    return val  }  const ob = (target: any).__ob__  // target 本身就不是响应式数据, 直接赋值  if (!ob) {    target[key] = val    return val  }  // 对属性进行响应式处理  defineReactive(ob.value, key, val)  ob.dep.notify()  return val}

我们阅读以上源码可知,vm.$set 的实现原理是:

如果目标是数组,直接使用数组的 splice 方法触发相应式;如果目标是对象,会先判读属性是否存在、对象是否是响应式,最终如果要对属性进行响应式处理,则是通过调用 defineReactive 方法进行响应式处理( defineReactive 方法就是 Vue 在初始化对象时,给对象属性采用 Object.defineProperty 动态添加 getter 和 setter 的功能所调用的方法)

(学习视频分享:web前端开发、编程基础视频)

以上就是2022年vue高频面试题分享(附答案分析)的详细内容,更多请关注php中文网其它相关文章!

关键词: 进行响应 数据绑定 生命周期