边肖想和大家分享一下Vue必要的面试问题。相信大部分人还是不太了解。因此,我想分享这篇文章供你参考。希望你看完这篇文章后收获多多。让我们一起来看看。
序
在Vue框架中,我们会涉及到一些具有一定讨论价值的高频面试问题,但不会涉及到一些在官方文档中可以查看的非常初级的纯记忆面试问题,比如:
vue常用修饰语?
vue-cli项目中常用的npm命令有哪些?
vue中保活组件的作用?
首先,上面提到的面试问题可以在文献中找到,没有比官方文献更权威的答案。其次,这类问题除了考查考生的记忆力外,没有太大的价值。最后,只要用过vue的人都知道这种面试问题,就没必要占用我们的空间。
我们问题不多,但难度可能更高。如果你真的理解了这些问题,在大多数情况下,会起到举一反三的效果。可以说,你基本上可以拿下所有与Vue相关的重要知识点。
你怎么理解MVVM?
什么是MVVM?
顾名思义,MVVM模式就是模型-视图-视图模型模式。它起源于微软2005年推出的基于Windows的用户界面框架WPF,最早的MVVM框架淘汰赛是在2010年发布的。
模型层:对应数据层的域模型,主要是同步域模型。通过Ajax/fetch API同步客户端和服务器的业务模型。在层间关系中,主要用于抽象视图模型中视图的模型。
视图层:作为视图模板存在。在MVVM,整个视图是一个动态模板。除了定义结构和布局,它还显示视图模型层的数据和状态。视图层不负责处理状态,而是负责声明数据绑定、指令和事件绑定。
ViewModel层:公开了View所需的层数据,负责View层的数据绑定声明、指令声明和事件绑定声明,即处理View层的具体业务逻辑。视图模型的底层将很好地监控绑定属性。当视图模型中的数据改变时,视图层将被更新。当在视图中声明数据(通常是表单元素)的双向绑定时,框架还将监控视图层(表单)的值的变化。一旦值发生变化,视图层绑定的视图模型中的数据将自动更新。
MVVM的优势和劣势?
优势:
将视图与模型分开,减少代码耦合,提高视图或逻辑的可重用性。例如,视图可以独立于模型进行更改和修改,视图模型可以绑定到不同的“视图”。当视图改变时,模型不能保持不变,当模型改变时,视图也可以保持不变。您可以将一些视图逻辑放在一个视图模型中,让许多视图重用这个视图逻辑。
提高可测试性: ViewModel的存在可以帮助开发人员更好地编写测试代码。
自动更新dom:采用双向绑定,数据更新后视图自动更新,将开发人员从繁琐的手工dom中解放出来。
缺点:
Bug很难调试。由于双向绑定模式,当您看到接口异常时,可能是您的视图代码中有错误,或者模型代码有问题。数据绑定使一个位置的bug可以快速转移到其他位置,而要找到最初的问题并不那么容易。另外,数据绑定的声明是以指令方式写在View的模板中的,所以没有办法中断调试。
大模块中的模型也会很大,虽然使用起来很方便,也很容易保证数据的一致性。当时如果长时间持有的话,不释放内存的话会耗费更多的内存。
对于大型图形应用程序,视图状态很多,构建和维护ViewModel的成本会相对较高。
你对Vue的生命周期有什么理解?
生命周期是什么?
一个Vue实例有一个完整的生命周期,即从创建开始、初始化数据、编译模板、挂载Dom-渲染、更新-渲染、卸载等一系列过程。我们称之为Vue的生命周期。
每个生命周期的作用。
在创建Create组件实例之前的生命周期描述的开始,创建的组件实例被完全创建,并且在组件的属性生效之前绑定了属性,但是真正的dom还没有生成。在Mount开始之前调用$el之前$ El不可用:第一次调用相关的render函数。装载的el被新创建的vm替换。$el,并且在它装载到实例上之后,在更新组件数据之前调用钩子,这发生在虚拟DOM中。
补丁之前 update 组件数据更新之后 activited keep-alive专属,组件被激活时调用 deadctivated keep-alive专属,组件被销毁时调用 beforeDestory 组件销毁前调用 destoryed 组件销毁后调用 生命周期示意图
异步请求适合在哪个生命周期调用?
官方实例的异步请求是在mounted生命周期中调用的,而实际上也可以在created生命周期中调用。
Vue组件如何通信?
Vue组件通信的方法如下:
-
props/$emit+v-on: 通过props将数据自上而下传递,而通过$emit和v-on来向上传递信息。
-
EventBus: 通过EventBus进行信息的发布与订阅
-
vuex: 是全局数据管理库,可以通过vuex管理全局的数据流
-
$attrs/$listeners: Vue2.4中加入的$attrs/$listeners可以进行跨级的组件通信
-
provide/inject:以允许一个祖先组件向其所有子孙后代注入一个依赖,不论组件层次有多深,并在起上下游关系成立的时间里始终生效,这成为了跨组件通信的基础
还有一些用solt插槽或者ref实例进行通信的,使用场景过于有限就不赘述了。
computed和watch有什么区别?
computed:
-
computed是计算属性,也就是计算值,它更多用于计算值的场景
-
computed具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算
-
computed适用于计算比较消耗性能的计算场景
watch:
-
更多的是「观察」的作用,类似于某些数据的监听回调,用于观察props $emit或者本组件的值,当数据变化时来执行回调进行后续操作
-
无缓存性,页面重新渲染时值不变化也会执行
小结:
-
当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed
-
如果你需要在某个数据变化时做一些事情,使用watch来观察这个数据变化
Vue是如何实现双向绑定的?
利用Object.defineProperty劫持对象的访问器,在属性值发生变化时我们可以获取变化,然后根据变化进行后续响应,在vue3.0中通过Proxy代理对象进行类似的操作。
// 这是将要被劫持的对象 const data = { name: '', }; function say(name) { if (name === '古天乐') { console.log('给大家推荐一款超好玩的游戏'); } else if (name === '渣渣辉') { console.log('戏我演过很多,可游戏我只玩贪玩懒月'); } else { console.log('来做我的兄弟'); } } // 遍历对象,对其属性值进行劫持 Object.keys(data).forEach(function(key) { Object.defineProperty(data, key, { enumerable: true, configurable: true, get: function() { console.log('get'); }, set: function(newVal) { // 当属性值发生变化时我们可以进行额外操作 console.log(`大家好,我系${newVal}`); say(newVal); }, }); }); data.name = '渣渣辉'; //大家好,我系渣渣辉 //戏我演过很多,可游戏我只玩贪玩懒月 复制代码
详细实现见Proxy比defineproperty优劣对比?
Proxy与Object.defineProperty的优劣对比?
Proxy的优势如下:
-
Proxy可以直接监听对象而非属性
-
Proxy可以直接监听数组的变化
-
Proxy有多达13种拦截方法,不限于apply、ownKeys、deleteProperty、has等等是Object.defineProperty不具备的
-
Proxy返回的是一个新对象,我们可以只操作新的对象达到目的,而Object.defineProperty只能遍历对象属性直接修改
-
Proxy作为新标准将受到浏览器厂商重点持续的性能优化,也就是传说中的新标准的性能红利
Object.defineProperty的优势如下:
-
兼容性好,支持IE9
详细实现见Proxy比defineproperty优劣对比?
你是如何理解Vue的响应式系统的?
响应式系统简述:
-
任何一个 Vue Component 都有一个与之对应的 Watcher 实例。
-
Vue 的 data 上的属性会被添加 getter 和 setter 属性。
-
当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
-
data 被改动时(主要是用户操作), 即被写, setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新。
虚拟DOM的优劣如何?
优点:
-
保证性能下限: 虚拟DOM可以经过diff找出最小差异,然后批量进行patch,这种操作虽然比不上手动优化,但是比起粗暴的DOM操作性能要好很多,因此虚拟DOM可以保证性能下限
-
无需手动操作DOM: 虚拟DOM的diff和patch都是在一次更新中自动进行的,我们无需手动操作DOM,极大提高开发效率
-
跨平台: 虚拟DOM本质上是JavaScript对象,而DOM与平台强相关,相比之下虚拟DOM可以进行更方便地跨平台操作,例如服务器渲染、移动端开发等等
缺点:
-
无法进行极致优化: 在一些性能要求极高的应用中虚拟DOM无法进行针对性的极致优化,比如VScode采用直接手动操作DOM的方式进行极端的性能优化
虚拟DOM实现原理?
-
虚拟DOM本质上是JavaScript对象,是对真实DOM的抽象
-
状态变更时,记录新树和旧树的差异
-
最后把差异更新到真正的dom中
详细实现见虚拟DOM原理?
既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟DOM进行diff检测差异?
考点: Vue的变化侦测原理
前置知识: 依赖收集、虚拟DOM、响应式系统
现代前端框架有两种方式侦测变化,一种是pull一种是push
pull: 其代表为React,我们可以回忆一下React是如何侦测到变化的,我们通常会用setStateAPI显式更新,然后React会进行一层层的Virtual Dom Diff操作找出差异,然后Patch到DOM上,React从一开始就不知道到底是哪发生了变化,只是知道「有变化了」,然后再进行比较暴力的Diff操作查找「哪发生变化了」,另外一个代表就是Angular的脏检查操作。
push: Vue的响应式系统则是push的代表,当Vue程序初始化的时候就会对数据data进行依赖的收集,一但数据发生变化,响应式系统就会立刻得知,因此Vue是一开始就知道是「在哪发生变化了」,但是这又会产生一个问题,如果你熟悉Vue的响应式系统就知道,通常一个绑定一个数据就需要一个Watcher,一但我们的绑定细粒度过高就会产生大量的Watcher,这会带来内存以及依赖追踪的开销,而细粒度过低会无法精准侦测变化,因此Vue的设计是选择中等细粒度的方案,在组件级别进行push侦测的方式,也就是那套响应式系统,通常我们会第一时间侦测到发生变化的组件,然后在组件内部进行Virtual Dom Diff获取更加具体的差异,而Virtual Dom Diff则是pull操作,Vue是push+pull结合的方式进行变化侦测的.
Vue为什么没有类似于React中shouldComponentUpdate的生命周期?
考点: Vue的变化侦测原理
前置知识: 依赖收集、虚拟DOM、响应式系统
根本原因是Vue与React的变化侦测方式有所不同
React是pull的方式侦测变化,当React知道发生变化后,会使用Virtual Dom Diff进行差异检测,但是很多组件实际上是肯定不会发生变化的,这个时候需要用shouldComponentUpdate进行手动操作来减少diff,从而提高程序整体的性能.
Vue是pull+push的方式侦测变化的,在一开始就知道那个组件发生了变化,因此在push的阶段并不需要手动控制diff,而组件内部采用的diff方式实际上是可以引入类似于shouldComponentUpdate相关生命周期的,但是通常合理大小的组件不会有过量的diff,手动优化的价值有限,因此目前Vue并没有考虑引入shouldComponentUpdate这种手动优化的生命周期.
Vue中的key到底有什么用?
key是为Vue中的vnode标记的唯一id,通过这个key,我们的diff操作可以更准确、更快速
diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行比对,然后超出差异.
diff程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较,这四种比较方式就是首、尾、旧尾新头、旧头新尾.
-
准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug.
-
快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度O(n),Map的时间复杂度仅仅为O(1).
以上是“Vue必备面试题有哪些”这篇文章的所有内容,感谢各位的阅读!相信大家都有了一定的了解,希望分享的内容对大家有所帮助,如果还想学习更多知识,欢迎关注行业资讯频道!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/35988.html