2024年 Vue经典面试题 -- 必问知识点 --(包含答案)

2.Vuex响应式数据/双向绑定原理


Vue 数据双向绑定主要是指:数据变化更新视图,视图变化更新数据。其中,View变化更新Data,可以通过事件监听的方式来实现,所以 Vue数据双向绑定的工作主要是如何根据Data变化更新View

简述

当你把一个普通的 JavaScript 对象传入 Vue 实例作为 data 选项,Vue 将遍历此对象所有的 property,并使用 Object.defineProperty 把这些 property 全部转为 getter/setter。

这些 getter/setter 对用户来说是不可见的,但是在内部它们让 Vue 能够追踪依赖,在 property 被访问和修改时通知变更。

每个组件实例都对应一个 watcher 实例,它会在组件渲染的过程中把“接触”过的数据 property 记录为依赖。之后当依赖项的 setter 触发时,会通知 watcher,从而使它关联的组件重新渲染。

深入理解:

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

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

订阅者 Watcher:Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁 ,主要的任务是订阅 Observer 中的属性值变化的消息,当收到属性值变化的消息时,触发解析器 Compile 中对应的更新函数。每个组件实例都有相应的 watcher 实例对象,它会在组件渲染的过程中把属性记录为依赖,之后当依赖项的 setter 被调用时,会通知 watcher 重新计算,从而致使它关联的组件得以更新——这是一个典型的观察者模式

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

Vue 双向数据绑定原理

数据的双向绑定是通过数据劫持结合发布订阅实现的。利用Object.defineproperty()方法将各个属性的getter和setter方法改写。当数据发生改变的时候,通知订阅者,并触发对应的回调函数,重新渲染数据。主要是实现以下几点:

(1) 数据观察者Observer,观察每个属性,当发生变化的时候,得到最新值并通知订阅者。

(2) 指令编译器Compile,对每个元素的节点进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数。

(3) 监听者Watcher,是数据观察者Observer和编译器Compile的桥梁。能订阅每个数据属性变化,当收到变化通知后,执行指令绑定的相应回调,从而更新视图。

vue会遍历data中的所有属性,利用Object.definedProperty这个方法给data中每一个属性增加监听者,即给每一个属性增加get方法和set方法,当数据发生变化时,会触发set方法,当获取数据时,会触发get方法;

数据劫持

Object.defineProperty()

控制一个对象属性的一些特有操作,比如读写权、是否可以枚举

通过get和set方法对data里的每个属性进行劫持

发布者-订阅者模式

监听器Observer 用来劫持并监听所有属性,如果有变动的,就通知订阅者。

订阅者Watcher 可以收到属性的变化通知并执行相应的函数,从而更新视图

解析器Compile 可以扫描和解析每个节点的相关指令,并根据初始化模板数据以及初始化相应的订阅器

订阅器Dep 订阅者(watcher)是有很多个,所以我们需要有一个消息订阅器Dep来专门收集这些订阅者

每一个对象属性中存储了一个 Dep,Dep 中收集了该对象对应的 watcher,当该属性值发生变化,会触发的 Dep 中保存的 watcher 进行遍历(notify),进而更新视图。

3.你知道Vue3.x响应式数据原理吗?


  • Vue3.x改用Proxy替代Object.defineProperty

  • 因为Proxy可以直接监听对象和数组的变化,并且有多达13种拦截方法。并且作为新标准将受到浏览器厂商重点持续的性能优化。

  • Proxy只会代理对象的第一层,Vue3是怎样处理这个问题的呢?

  • 判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理, 这样就实现了深度观测。

  • 监测数组的时候可能触发多次get/set,那么如何防止触发多次呢?我们可以判断key是否为当前被代理对象target自身属性,也可以判断旧值与新值是否相等,只有满足以上两个条件之一时,才有可能执行trigger。

Proxy 与 Object.defineProperty 优劣对比

Proxy 的优势如下:

Proxy 可以直接监听对象而非属性;

Proxy 可以直接监听数组的变化;

Proxy 有多达 13 种拦截方法,不限于 apply、ownKeys、deleteProperty、has 等等是 Object.defineProperty 不具备的;

Proxy 返回的是一个新对象,我们可以只操作新的对象达到目的,而 Object.defineProperty 只能遍历对象属性直接修改;

Proxy 作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利;

Object.defineProperty 的优势如下:

兼容性好,支持 IE9,而 Proxy 的存在浏览器兼容性问题,而且无法用 polyfill 磨平,因此 Vue 的作者才声明需要等到下个大版本( 3.0 )才能用 Proxy 重写。

**4.**Vuex 是什么?


运用到了js设计模式中的单例模式,单例模式想要做到的是,不管我们尝试去创建多少次,它都只给你返回第一次所创建的那唯一的一个实例。

  • Vuex 是一个专为 Vue.js 应用程序开发的状态管理模式。每一个 Vuex 应用的核心就是 store(仓库)。“store” 基本上就是一个容器,它包含着你的应用中大部分的状态 ( state )。

  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  • 改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化。

Vuex 使用单一状态树,用一个对象就包含了全部的应用层级状态。至此它便作为一个“唯一数据源 (SSOT)”而存在。这也意味着,每个应用将仅仅包含一个 store 实例。单一状态树让我们能够直接地定位任一特定的状态片段,在调试的过程中也能轻易地取得整个当前应用状态的快照。 ——Vuex官方文档

主要包括以下几个模块:

State:定义了应用状态的数据结构,可以在这里设置默认的初始状态。

Getter:允许组件从 Store 中获取数据,mapGetters 辅助函数仅仅是将 store 中的 getter 映射到局部计算属性。

Mutation:是唯一更改 store 中状态的方法,且必须是同步函数。

Action:用于提交 mutation,而不是直接变更状态,可以包含任意异步操作。

Module:允许将单一的 Store 拆分为多个 store 且同时保存在单一的状态树中。

5. 什么情况下使用 Vuex?


  • 如果应用够简单,最好不要使用 Vuex,一个简单的 store 模式即可

  • 需要构建一个中大型单页应用时,使用Vuex能更好地在组件外部管理状态

**6.**Vuex和单纯的全局对象有什么区别?


  • Vuex 的状态存储是响应式的。当 Vue 组件从 store 中读取状态的时候,若 store 中的状态发生变化,那么相应的组件也会相应地得到高效更新。

  • 不能直接改变 store 中的状态。改变 store 中的状态的唯一途径就是显式地提交 (commit) mutation。这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

**7.**为什么 Vuex 的 mutation 中不能做异步操作?


  • Vuex中所有的状态更新的唯一途径都是mutation,异步操作通过 Action 来提交 mutation实现,这样使得我们可以方便地跟踪每一个状态的变化,从而让我们能够实现一些工具帮助我们更好地了解我们的应用。

  • 每个mutation执行完成后都会对应到一个新的状态变更,这样devtools就可以打个快照存下来,然后就可以实现 time-travel 了。如果mutation支持异步操作,就没有办法知道状态是何时更新的,无法很好的进行状态的追踪,给调试带来困难。

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


computed:是计算属性,依赖其它属性值,并且 ****computed 的值有****缓存,只有它依赖的属性值发生改变,下一次获取 computed 的值时才会重新计算 computed 的值;

watch:****没有缓存性,更多的是「观察」的作用,****类似于某些数据的监听回调 ,每当监听的数据变化时都会执行回调进行后续操作;当我们需要深度监听对象中的属性时,可以打开deep:true选项,这样便会对对象中的每一项进行监听

运用场景

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

当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用watch选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。

9. Vue2.x组件通信有哪些方式?


父子组件通信

  • 事件机制(**父->子props,子->父 o n 、 ∗ ∗ ∗ ∗ on、**** onemit****)

  • 获取父子组件实例 p a r e n t 、 parent、 parentchildren

  • Ref 获取实例的方式调用组件的属性或者方法

  • Provide、inject (不推荐使用,组件库时很常用)

兄弟组件通信

eventBus 这种方法通过一个空的 Vue实例作为中央事件总线(事件中心),用它来触发事件和监听事件,从而实现任何组件间的通信,包括父子、隔代、兄弟组件

Vue.prototype.$bus = new Vue

Vuex

跨级组件通信

Vuex

∗ ∗ ∗ ∗ ∗ ∗ ∗ ∗ a t t r s 、 ********attrs、 attrslisteners

Provide、inject

**10.**说一下v-if和v-show的区别


  • 当条件不成立时,v-if不会渲染DOM元素,v-show操作的是样式(display),切换当前DOM的显示和隐藏。

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

  • v-show 则适用于需要非常频繁切换条件的场景。

**11.**为什么 v-for 和 v-if 不建议用在一起


  • 当 v-for 和 v-if 处于同一个节点时,v-for 的优先级比 v-if 更高,这意味着 v-if 将分别重复运行于每个 v-for 循环中。如果要遍历的数组很大,而真正要展示的数据很少时,这将造成很大的性能浪费

  • 这种场景建议使用 computed,先对数据进行过滤

12. 组件中的data为什么是一个函数?


  • 一个组件被复用多次的话,也就会创建多个实例。本质上,这些实例用的都是同一个构造函数。

  • 如果data是对象的话,对象属于引用类型,会影响到所有的实例。所以为了保证组件不同的实例之间data不冲突,data必须是一个函数。

13.子组件为什么不可以修改父组件传递的Prop?/怎么理解vue的单向数据流?


  • Vue提倡单向数据流,即父级props的更新会流向子组件,但是反过来则不行。

  • 这是为了防止意外的改变父组件状态,使得应用的数据流变得难以理解。

  • 如果破坏了单向数据流,当应用复杂时,debug 的成本会非常高。

14. v-model是如何实现双向绑定的?


  • v-model是用来在表单控件或者组件上创建双向绑定的

  • 他的本质是v-bind和v-on的语法糖

  • 在一个组件上使用v-model,默认会为组件绑定名为value的prop和名为input的事件

15. nextTick的实现原理是什么?


  • 在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后立即使用 nextTick 来获取更新后的 DOM。

  • nextTick主要使用了宏任务微任务

  • 根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,如果以上都不行则采用setTimeout定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。

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

nextTick主要使用了宏任务和微任务。根据执行环境分别尝试采用Promise、MutationObserver、setImmediate,如果以上都不行则采用setTimeout定义了一个异步方法,多次调用nextTick会将方法存入队列中,通过这个异步方法清空当前队列。

理解****:(宏任务和微任务) 异步方法****

nextTick 方法主要是使用了宏任务和微任务,定义了一个异步方法.多次调用 nextTick 会将方法存入 队列中,通过这个异步方法清空当前队列。 所以这个 nextTick 方法就是异步方法

16.Vue不能检测数组的哪些变动?Vue 怎么用 vm.$set() 解决对象新增属性不能响应的问题 ?


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

当你利用索引直接设置一个数组项时,例如:vm.items[indexOfItem] = newValue

当你修改数组的长度时,例如:vm.items.length = newLength

解决办法:

第一类问题

// 法一:Vue.set

Vue.set(vm.items, indexOfItem, newValue)

// 法二:Array.prototype.splice

vm.items.splice(indexOfItem, 1, newValue)复制代码 修改原数组

第二类问题,可使用 splice:

vm.items.splice(newLength)复制代码

vm.********$set 的实现原理是:

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

总结

前端资料汇总

  • 框架原理真的深入某一部分具体的代码和实现方式时,要多注意到细节,不要只能写出一个框架。

  • 算法方面很薄弱的,最好多刷一刷,不然影响你的工资和成功率😯

  • 在投递简历之前,最好通过各种渠道找到公司内部的人,先提前了解业务,也可以帮助后期优秀 offer 的决策。

  • 要勇于说不,对于某些 offer 待遇不满意、业务不喜欢,应该相信自己,不要因为当下没有更好的 offer 而投降,一份工作短则一年长则 N 年,为了幸福生活要慎重选择!!!
    喜欢这篇文章文章的小伙伴们点赞+转发支持,你们的支持是我最大的动力!

加入社区》https://bbs.csdn.net/forums/4304bb5a486d4c3ab8389e65ecb71ac0

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值