Vue框架部分我们会涉及一些高频且有一定探讨价值的面试题,我们不会涉及一些非常初级的在官方文档就能查看的纯记忆性质的面试题,比如:



*   vue常用的修饰符?

*   vue-cli 工程常用的 npm 命令有哪些?

*   vue中 keep-alive 组件的作用?



首先,上述类型的面试题在文档中可查,没有比官方文档更权威的答案了,其次这种问题没有太大价值,除了考察候选人的记忆力,最后,这种面试题只要用过vue的都知道,没有必要占用我们的篇幅.



我们的问题并不多,但是难度可能会高一些,如果你真的搞懂了这些问题,在绝大多数情况下会有举一反三的效果,可以说基本能拿下Vue相关的所有重要知识点了.



## 你对MVVM的理解?



### MVVM是什么?



MVVM 模式,顾名思义即 Model-View-ViewModel 模式。它萌芽于2005年微软推出的基于 Windows 的用户界面框架 WPF ,前端最早的 MVVM 框架 knockout 在2010年发布。



Model 层: 对应数据层的域模型,它主要做域模型的同步。通过 Ajax/fetch 等 API 完成客户端和服务端业务 Model 的同步。在层间关系里,它主要用于抽象出 ViewModel 中视图的 Model。



View 层:作为视图模板存在,在 MVVM 里,整个 View 是一个动态模板。除了定义结构、布局外,它展示的是 ViewModel 层的数据和状态。View 层不负责处理状态,View 层做的是 数据绑定的声明、 指令的声明、 事件绑定的声明。



ViewModel 层:把 View 需要的层数据暴露,并对 View 层的 数据绑定声明、 指令声明、 事件绑定声明 负责,也就是处理 View 层的具体业务逻辑。ViewModel 底层会做好绑定属性的监听。当 ViewModel 中数据变化,View 层会得到更新;而当 View 中声明了数据的双向绑定(通常是表单元素),框架也会监听 View 层(表单)值的变化。一旦值变化,View 层绑定的 ViewModel 中的数据也会得到自动更新。



[图片上传中...(image-2957b2-1573527441518-3)]



<figcaption></figcaption>



### MVVM的优缺点?



优点:



1.  分离视图(View)和模型(Model),降低代码耦合,提高视图或者逻辑的重用性: 比如视图(View)可以独立于Model变化和修改,一个ViewModel可以绑定不同的"View"上,当View变化的时候Model不可以不变,当Model变化的时候View也可以不变。你可以把一些视图逻辑放在一个ViewModel里面,让很多view重用这段视图逻辑

2.  提高可测试性: ViewModel的存在可以帮助开发者更好地编写测试代码

3.  自动更新dom: 利用双向绑定,数据更新后视图自动更新,让开发者从繁琐的手动dom中解放



缺点:



1.  Bug很难被调试: 因为使用双向绑定的模式,当你看到界面异常了,有可能是你View的代码有Bug,也可能是Model的代码有问题。数据绑定使得一个位置的Bug被快速传递到别的位置,要定位原始出问题的地方就变得不那么容易了。另外,数据绑定的声明是指令式地写在View的模版当中的,这些内容是没办法去打断点debug的

2.  一个大的模块中model也会很大,虽然使用方便了也很容易保证了数据的一致性,当时长期持有,不释放内存就造成了花费更多的内存

3.  对于大型的图形应用程序,视图状态较多,ViewModel的构建和维护的成本都会比较高



## 你对Vue生命周期的理解?



### 生命周期是什么



Vue 实例有一个完整的生命周期,也就是从开始创建、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,我们称这是Vue的生命周期。



### 各个生命周期的作用



| 生命周期 | 描述 |

| --- | --- |

| beforeCreate | 组件实例被创建之初,组件的属性生效之前 |

| created | 组件实例已经完全创建,属性也绑定,但真实dom还没有生成,`$el`还不可用 |

| beforeMount | 在挂载开始之前被调用:相关的 render 函数首次被调用 |

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

| beforeUpdate | 组件数据更新之前调用,发生在虚拟 DOM 打补丁之前 |

| update | 组件数据更新之后 |

| activited | keep-alive专属,组件被激活时调用 |

| deadctivated | keep-alive专属,组件被销毁时调用 |

| beforeDestory | 组件销毁前调用 |

| destoryed | 组件销毁后调用 |



### 生命周期示意图



[图片上传中...(image-f987b7-1573527441518-2)]



<figcaption></figcaption>



## 异步请求适合在哪个生命周期调用?



官方实例的异步请求是在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:



1.  `computed`是计算属性,也就是计算值,它更多用于计算值的场景

2.  `computed`具有缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变之后,下一次获取computed的值时才会重新调用对应的getter来计算

3.  `computed`适用于计算比较消耗性能的计算场景



watch:



1.  更多的是「观察」的作用,类似于某些数据的监听回调,用于观察`props` `$emit`或者本组件的值,当数据变化时来执行回调进行后续操作

2.  无缓存性,页面重新渲染时值不变化也会执行



小结:



1.  当我们要进行数值计算,而且依赖于其他数据,那么把这个数据设计为computed

2.  如果你需要在某个数据变化时做一些事情,使用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优劣对比?](https://mp.weixin.qq.com/s?__biz ... 07a2edab59d8d78f#rd)



## Proxy与Object.defineProperty的优劣对比?



Proxy的优势如下:



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

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

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

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

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



Object.defineProperty的优势如下:



*   兼容性好,支持IE9



> 详细实现见[Proxy比defineproperty优劣对比?](https://mp.weixin.qq.com/s?__biz ... 07a2edab59d8d78f#rd)



## 你是如何理解Vue的响应式系统的?



[图片上传中...(image-ea78c0-1573527441518-1)]



<figcaption></figcaption>



响应式系统简述:



*   任何一个 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原理?](https://user-gold-cdn.xitu.io/2019/8/1/16c49afec13e0416)



## 既然Vue通过数据劫持可以精准探测数据变化,为什么还需要虚拟DOM进行diff检测差异?



考点: Vue的变化侦测原理



前置知识: 依赖收集、虚拟DOM、响应式系统



现代前端框架有两种方式侦测变化,一种是pull一种是push



pull: 其代表为React,我们可以回忆一下React是如何侦测到变化的,我们通常会用`setState`API显式更新,然后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).



[图片上传中...(image-ad071e-1573527441518-0)]



<figcaption></figcaption>



作者:寻找海蓝96

链接:https://juejin.im/post/5d41eec26fb9a06ae439d29f

来源:掘金

著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。




面试必备的13道可以举一反三的Vue面试题的更多相关文章

  1. 面试必备的10道MySQL题

    MySQL 事务,是我们去面试中高级开发经常会被问到的问题,很多人虽然经常使用 MySQL,SQL 语句也写得很溜,但是面试的时候,被问到这些问题,总是不知从何说起.下面我们先来了解一下什么是 MyS ...

  2. 2019前端面试系列——Vue面试题

    Vue 双向绑定原理        mvvm 双向绑定,采用数据劫持结合发布者-订阅者模式的方式,通过 Object.defineProperty()来劫持各个属性的 setter.getter,在数 ...

  3. Linux运维跳槽必备的40道面试精华题(转)

    Linux运维跳槽必备的40道面试精华题(转)   下面是一名资深Linux运维求职数十家公司总结的Linux运维面试精华,助力大家年后跳槽找个高薪好工作. 1.什么是运维?什么是游戏运维? 1)运维 ...

  4. 金九银十跳槽高峰,面试必备之 Redis + MongoDB 常问80道面试题

    前言 有着“金九银十”之称的招聘旺季已经开启,跳槽高峰期也如约而至. 本文为主要是 Redis + MongoDB 知识点的攻略,希望能帮助到大家. 内容较多,大家准备好耐心和瓜子矿泉水. Redis ...

  5. 【面试必备】常见Java面试题大综合

    一.Java基础 1.Arrays.sort实现原理和Collections.sort实现原理答:Collections.sort方法底层会调用Arrays.sort方法,底层实现都是TimeSort ...

  6. Python自动化面试必备 之 你真明白装饰器么?

    Python自动化面试必备 之 你真明白装饰器么? 装饰器是程序开发中经常会用到的一个功能,用好了装饰器,开发效率如虎添翼,所以这也是Python面试中必问的问题,但对于好多小白来讲,这个功能 有点绕 ...

  7. 可能是全网最好的MySQL重要知识点 | 面试必备

    可能是全网最好的MySQL重要知识点 | 面试必备  mp.weixin.qq.com 点击蓝色“程序猿DD”关注我 回复“资源”获取独家整理的学习资料! 标题有点标题党的意思,但希望你在看了文章之后 ...

  8. AI面试必备/深度学习100问1-50题答案解析

    AI面试必备/深度学习100问1-50题答案解析 2018年09月04日 15:42:07 刀客123 阅读数 2020更多 分类专栏: 机器学习   转载:https://blog.csdn.net ...

  9. 上位机面试必备——TCP通信灵魂二十问【下】

    上篇文章跟大家介绍了TCP通信常见的前10个面试题,没看过的小伙伴可以点击下方链接进行查看: 上位机面试必备——TCP通信灵魂二十问[上] 今天就后面的10个面试题接着做下说明:欢迎关注[dotNet ...

随机推荐

  1. May 19th, 2019. Week 21st, Sunday

    Fight for what matters to you. 为自己珍视的东西奋斗吧! We all want to make our life goals true, and we all expe ...

  2. MATLAB实例:将批量的图片保存为.mat文件

    MATLAB实例:将批量的图片保存为.mat文件 作者:凯鲁嘎吉 - 博客园 http://www.cnblogs.com/kailugaji/ 一.彩色图片 图片数据:horse.rar 1. MA ...

  3. go语言设计模式之observer

    observer.go package observer import ( "fmt" ) type Observer interface { Notify(string) } t ...

  4. KVM使用总结

    目录 一.虚拟化介绍 二.通过kvm安装centos7系统 三.常用操作 虚拟机列表 开关机 导出虚拟机 重命名 挂起&恢复 查看某个虚拟机对应的端口 kvm开机启动 console登陆(失败 ...

  5. 201871010116-祁英红《面向对象程序设计(java)》第十五周学习总结

    博文正文开头格式:(2分) 项目 内容 <面向对象程序设计(java)> https://www.cnblogs.com/nwnu-daizh/ 这个作业的要求在哪里 https://ww ...

  6. 利用Github建立博客专用图库

    0.前言 当我们写博客或者文档的时候常常需要引用图片.倘或引用图片的链接是外网的,常常会出现加载过慢的情况,并且不稳定的图片来源不方便管理.所以如果建立一个博客专用的图片仓库,统一管理维护方面就方便得 ...

  7. Python连载49-正则举例

    一.re举例 import re #查找数字 p = re.compile(r"\d+") #在字符串“ongahjeuf125”中及逆行查找,按照规则p指定的正则进行查找 m = ...

  8. react组件通信方式汇总

    父组件更新组件状态,通过props传递给子组件,子组件得到后进行更新.Timer 是倒计时子组件,集成在了OrderPay父组件里,父组件向子组件传订单数据参数order对象.如下代码: <Ti ...

  9. Anaconda更新报404:UnavailableInvalidChannel: The channel is not accessible or is invalid.error404

    Anaconda更新一直报错,修改为国内镜像也不好使,最终找到了未被屏蔽的镜像. 错误日志: UnavailableInvalidChannel: The channel is not accessi ...

  10. IT兄弟连 Java语法教程 流程控制语句 分支结构语句4

    4  嵌套if-else条件语句 嵌套if语句是作为另一个if或else语句的目标的if语句.嵌套if语句在程序设计中非常普遍.Java中,关于嵌套if语句需要记住的是,else语句总是和同一代码块中 ...