首页 > 其他分享 >3-Vue高频面试题

3-Vue高频面试题

时间:2022-11-13 11:25:25浏览次数:67  
标签:面试题 Vue DOM 更新 组件 高频 数据 节点

1. 你怎样理解Vue

Vue通过MVVM思想实现数据的双向绑定,数据驱动页面视图。

Vue不是一个MVVM框架,它是一个视图层框架。

  • Vue是数据驱动的框架,我们不必纠结于DOM元素的获取与操作,变动数据时,Vue的底层会自动的帮助我们更新视图层。(VM这一层不必我们去操作)
  • JQuery中我们是面向DOM开发,Vue是面向数据开发(Model层最重要,其次View层)

MV-VM的理解

  • Model-View-ViewModel
    • Model - 数据层,如计数器案例中的obj或是从网络上请求的数据
    • View - 视图层,DOM层
    • ViewModel - 视图模型 - 是Model与View沟通的桥梁
      • 一方面实现Data Binding,数据绑定,将Model的改变反应到View中;
      • 一方面实现DOM Listening,监听到DOM中的事件,并根据情况更改对应Data
image-20221021152114948

Vue是怎么实现VM层的

  • 虚拟DOM
  • Object.defineProperty
    • 当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setterObject.defineProperty 是 ES5 中的特性,这也就是 Vue 不支持 IE8 以及更低版本浏览器的原因。

2. Vue响应式原理

Vue数据双向绑定是通过采用数据劫持结合发布者-订阅者模式的方式来实现的。通过Object.defineProperty()来劫持各个属性的setter,getter。修改触发set方法赋值,获取触发get方法取值,在数据变动时发布消息给订阅者,触发相应的回调并通过数据劫持发布信息。

Vue 主要通过以下 4 个步骤来实现数据双向绑定的:

  • 实现一个监听器 Observer:对数据对象进行遍历,包括子属性对象的属性,利用 Object.defineProperty() 对属性都加上 setter 和 getter。这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化。

  • 实现一个解析器 Compile:解析 Vue 模板指令,将模板中的变量都替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,调用更新函数进行数据更新。

  • 实现一个订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。

  • 实现一个订阅器 Dep:订阅器采用发布-订阅 设计模式,用来收集订阅者 Watcher,对监听器 Observer 和 订阅者 Watcher 进行统一管理。

Vue响应式原理

3. v-model双向绑定的原理

v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据。

Object.defineproperty()重新定义(set方法)对象设置属性值和(get方法)获取属性值的操纵来实现的。

  1. 实现一个监听器Observer,用来劫持并监听所有属性,如果有变动的,就通知订阅者
  2. 实现一个订阅者Watcher,可以收到属性的变化通知并执行相应的函数,从而更新视图。
  3. 实现一个解析器Compile,可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器。

4. Vue不能检测数组和对象的变化

因为 Vue 会在初始化实例时对 property 执行 getter/setter 转化,所以 property 必须在 data 对象上存在才能让 Vue 将它转换为响应式的。Vue官网解释

4.1 如何解决上述问题?

对于对象:

Vue.set(vm.someObject, 'b', 2)

也可以使用 vm.$set 实例方法,这也是全局 Vue.set 方法的别名:

this.$set(this.someObject,'b',2)
this.$delete(this.someObject,'b') 删除旧属性

添加多个属性:( 直接使用Object.assign()_.extend()无法触发更新 )

应该用原对象与要混合进去的对象的 property 一起创建一个新的对象。

// 代替 `Object.assign(this.someObject, { a: 1, b: 2 })`
this.someObject = Object.assign({}, this.someObject, { a: 1, b: 2 })

对于数组:

Vue 不能检测以下数组的变动:

  1. 当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue
  2. 当你修改数组的长度时,例如:vm.items.length = newLength
// Vue.set或vm.$set
Vue.set(vm.items, indexOfItem, newValue)
vm.$set(vm.items, indexOfItem, newValue)

// Array.prototype.splice
vm.items.splice(indexOfItem, 1, newValue)

//解决第2点,splice若只设置一个参数,则从该位置开始删除后面所有值
vm.items.splice(newLength)

4.2 Vue.set方法是如何实现的?

  • vue给对象和数组本身都增加了dep属性
  • 当给对象新增不存在的属性的时候,就会触发对象依赖的watcher去更新
  • 当修改数组索引的时候,就调用数组本身的splice方法去更新数组

5. 生命周期函数

5.1 什么是生命周期(简述Vue的生命周期)

  • 生命周期函数是一些钩子函数,在某个时间会被Vue源码内部进行回调;

每个组件都可能会经历从创建、挂载、更新、卸载等一系列的过程,在这个过程中的某一个阶段,用于可能会想要添加一些属于自己的代码逻辑(比如组件创建完后就请求一些服务器数据)

  • beforeCreate 在实例初始化之后,数据观测observer 和event、watcher事件配置之前被调用

  • created 实例已经创建完成,在这一步,以下配置被完成

    • 数据观测

    • 属性和方法的运算

    • 初始化事件:watch/event时间回调

    • $el尚未生成

  • beforeMount 在挂载之前被调用,render尚未被调用

  • mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用

    • DOM渲染在mounted中就已经完成了
  • beforeUpdate

  • updated

  • beforeDestroy 实例销毁之前调用

  • destroyed 实例销毁之后调用,调用后Vue实例的所有东西都会被解绑,所有的事件监听会被移除,子实例被销毁,该钩子在服务端渲染期间不被调用

一般在created里做网络请求,在mounted做数据挂载(mounted阶段才有真实的DOM

life-cycle

5.2 生命周期相关题目

1)Vue 实例的 data 属性,可以在哪些生命周期中获取到?(B/C/D)

A. beforeCreate B. created C. beforeMount D. mounted


6. 虚拟DOM和diff算法

vue基于虚拟DOM做更新,diff又是其核心部分,因此常被问道,

6.1 Vue为什么要用虚拟Dom

  • 虚拟dom就是用js对象来描述真实Dom,是对真实Dom的抽象
  • 由于直接操作Dom性能低,但是js层的操作效率高,可以将Dom操作转化成对象操作。最终通过diff算法比对差异进行更新Dom
  • 虚拟Dom不依赖真实平台环境,可以实现跨平台

6.2 Vue的diff算法原理是什么?

Vue的diff算法是平级比较,不考虑跨级比较的情况。内部采用深度递归的方式+双指针方式比较

  • 先比较两个节点是不是相同节点
  • 相同节点比较属性,复用老节点
  • 先比较儿子节点,考虑老节点和新节点儿子的情况
  • 优化比较:头头、尾尾、头尾、尾头
  • 比对查找,进行复用

回答范例:

  1. diff算法是虚拟DOM技术的产物,vue里面实际叫做patch,它的核心实现来自于snabbdom;通过新旧虚拟DOM作对比(即patch),将变化的地方转换为DOM操作

  2. 在vue 1中是没有patch的,因为界面中每个依赖都有专门的watcher负责更新,这样项目规模变大就会成为性能瓶颈,vue 2中为了降低watcher粒度,每个组件只有一个watcher,但是当需要更新的时候,怎样才能精确找到发生变化的地方?这就需要引入patch才行。

  3. 组件中数据发生变化时,对应的watcher会通知更新并执行其更新函数,它会执行渲染函数获取全新虚拟dom:newVnode,此时就会执行patch比对上次渲染结果oldVnode和新的渲染结果newVnode。

  4. patch过程遵循深度优先、同层比较的策略;两个节点之间比较时,如果它们拥有子节点,会先比较子节点;比较两组子节点时,会假设头尾节点可能相同先做尝试,没有找到相同节点后才按照通用方式遍历查找;查找结束再按情况处理剩下的节点;借助key通常可以非常精确找到相同节点,因此整个patch过程非常高效。

6.3 key值的作用

  • 使用key值可以提高渲染效率

  • 用于管理可复用的元素。diff算法可以判断两个节点是否是相同节点,Vue 会尽可能高效地渲染元素,会复用已有元素而不是从头开始渲染

  • 尽量不要采用索引作为key

image-20221113110228053

7. 组件通信

7.1 父子组件通信

  • 父传子:props

    在开发中很常见的就是父子组件之间通信,比如父组件有一些数据,需要子组件来进行展示

  • 子传父:$emit事件

    当子组件有一些事件发生的时候,比如在组件中发生了点击,父组件需要切换内容时,就要用到子传父。

7.2 其他通信

  • 祖先组件通过provide提供,子孙组件通过inject注入变量
  • 兄弟组件:eventBus 数据传递
  • Vuex
  • $ref获取实例

8. nextTick作用与实现原理

这道题考查对vue异步更新队列的理解,有一定深度。

官方定义:Vue.nextTick( [callback, context] )

nextTick是一个微任务。

  • nextTick中的回调是在下次Dom更新循环结束之后执行的延迟回调
  • 可以用于获取更新后的Dom
  • Vue中的数据更新是异步的,使用nextTick可以保证用户定义的逻辑在更新之后执行

用法:在下次 DOM 更新循环结束之后执行延迟回调。在修改数据之后立即使用这个方法,获取更新后的 DOM。

// 修改数据
vm.msg = 'Hello'
// DOM 还没有更新
Vue.nextTick(function () {
// DOM 更新了
})

回答范例:

  1. nextTick是Vue提供的一个全局API,由于vue的异步更新策略导致我们对数据的修改不会立刻体现在dom变化上,此时如果想要立即获取更新后的dom状态,就需要使用这个方法

  2. Vue 在更新 DOM 时是异步执行的。只要侦听到数据变化,Vue 将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更。如果同一个 watcher 被多次触发,只会被推入到队列中一次。这种在缓冲时去除重复数据对于避免不必要的计算和 DOM 操作是非常重要的。nextTick方法会在队列中加入一个回调函数,确保该函数在前面的dom操作完成后才调用。

  3. 所以当我们想在修改数据后立即看到dom执行结果就需要用到nextTick方法。

  4. 比如,我在干什么的时候就会使用nextTick,传一个回调函数进去,在里面执行dom操作即可。

  5. 我也有简单了解nextTick实现,它会在callbacks里面加入我们传入的函数,然后用timerFunc异步方式调用它们,首选的异步方式会是Promise。这让我明白了为什么可以在nextTick中看到dom操作结果。


9. 全局导航钩子函数应用场景

router.beforeEach(全局前置守卫)是页面加载之前,在每次路由改变的时候执行一遍。
router.afterEach(全局后置守卫),与上相反,是页面加载之后。

应用场景:

  1. 可进行一些页面跳转前处理,例如判断需要登录的页面进行拦截,做登录跳转。
  2. 进入页面登录判断、管理员权限判断、浏览器判断

10. computed | watch 区别

  • computed的属性是具备缓存的,依赖的值不发生变化,对其取值时计算属性方法不会重复执行
  • watch是监控值的变化,当值发生改变的时候,会调用回调函数

追问:computed 和 watch 应用场景?

computed:当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算。(computed+缓存)

watch:当我们需要在数据变化时执行的操作时使用(如调用其它函数)

追问:能使用箭头函数定义computed和watch吗?

不能,因为箭头函数绑定了父级作用域的上下文,所以 this 将不会按照期望指向 Vue 实例,为undefined


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

new Vue()实例中,data 可以是对象,因为new Vue是一个单例模式,不会有任何的合并操作,所以根实例不必校验data一定是一个函数。

但在 vue 组件中,data 必须是一个函数。因为组件是可以复用的,如果组件 data 是一个对象,那么子组件中的 data 属性值会互相污染。因为JS 里对象是引用关系,在合并时会指向同一个地址。 而如果是函数的时候,合并的时候调用,会产生两个空间,每个实例可以维护一份被返回对象的独立的拷贝。

关键词: 复用 + 污染 + 函数返回 + 数据拷贝


其他

1. public和assets文件的异同

1. 相同点
文件夹中的资源在html中使用都是可以的。

2. 不同点

  • public中的文件,是不会经过编译的,打包后会生成dist文件夹,public中的文件只是复制一遍。因此,public建议放一些外部第三方,自己的文件放在assets,别人的放public中。

  • 若把图片放在assets和public中,html页面都可以使用,但是在动态绑定中,assets路径的图片会加载失败(因为webpack使用的是commenJS规范,必须使用require才可以。

    • 使用assets下面的资源,在js中使用的话,路径要经过webpack中的file-loader编译,路径不能直接写。

    • 使用public文件下面的资源,是不会被webpack处理的,它们会被直接复制到最终的打包目录下面,且必须使用绝对路径来引用这些文件。


标签:面试题,Vue,DOM,更新,组件,高频,数据,节点
From: https://www.cnblogs.com/lin77/p/16885622.html

相关文章

  • vue.js3:用html2canvas把html转canvas图片([email protected] / [email protected])
    一,安装html2canvas1,官网:https://html2canvas.hertzen.com/如图:代码站:https://github.com/niklasvh/html2canvas2,通过npm安装liuhongdi@lhdpc:/data......
  • Vue.js -- 动态组件&异步组件
    动态组件根据数据的变化,动态切换组件的显示。点击切换组件首先定义两个子组件//子组件app.component('myInput',{template:`......
  • I++与++I 面试题
    程序员面试宝典第28页,书中的答案是9,49而正确的答案应该是,12,423*4=126*7=421#include<stdio.h>2#defineproduct(x)((x)*(x))3intmain(intargc,char......
  • vue监视实操案例-根据导航条点击面包屑发生改动
    监视部分代码: $route(to,From){console.log(to,From);if(to.path=="/Home"){this.path="";this.name="";}else......
  • 谈一谈 vuex 的运行机制
    Vuex提供数据(state)来驱动视图(vuecomponents),通过dispath派发actions,在其中可以做一些异步的操作,然后通过commit来提交mutations,最后mutations来更改state。 ......
  • 我们如何在 vue 应用我们的权限
    权限可以分为用户权限和按钮权限;用户权限,让不同的用户拥有不同的路由映射,具体实现方法:1.初始化路由实例的时候,只把静态路由规则注入,不要注入动态路由规则;2.......
  • Vue生命周期及组件
    目录Vue生命周期钩子钩子函数的由来生命周期钩子函数生命周期图示Vue生命周期钩子钩子函数的由来每个Vue实例在被创建时都要经过一系列的初始化过程——例如,需要......
  • vue3.2 setup语法糖,多个API解释
    前言在vue3中删除了vue2中的data函数,因此,vue3.0要在template中使用某些变量就必须在最后return出来,多次声明变量,不太方便,也不太友好。而在vue3.2版本之后,我们只需在......
  • 12-使用Vue与axios改造jquery原页面
    改造List页面1引入文件<!--1引入vuejsaxios文件--><scripttype="text/javascript"src="js/vue.js"></script><scripttype="text/javascript"src="js/axios.j......
  • 11-SpringBoot2整合Vue最简入门
    vuejs入门环境搭建》1:导入文件<scripttype="text/javascript"src="js/vue.js"></script><scripttype="text/javascript"src="js/axios.js"></script>》2:准备app视......