本文首发于微信公众号「程序员面试官」前端
Vue框架部分咱们会涉及一些高频且有必定探讨价值的面试题,咱们不会涉及一些很是初级的在官方文档就能查看的纯记忆性质的面试题,好比:vue
首先,上述类型的面试题在文档中可查,没有比官方文档更权威的答案了,其次这种问题没有太大价值,除了考察候选人的记忆力,最后,这种面试题只要用过vue的都知道,没有必要占用咱们的篇幅.node
咱们的问题并很少,可是难度可能会高一些,若是你真的搞懂了这些问题,在绝大多数状况下会有触类旁通的效果,能够说基本能拿下Vue相关的全部重要知识点了.程序员
MVVM 模式,顾名思义即 Model-View-ViewModel 模式。它萌芽于2005年微软推出的基于 Windows 的用户界面框架 WPF ,前端最先的 MVVM 框架 knockout 在2010年发布。面试
Model 层: 对应数据层的域模型,它主要作域模型的同步。经过 Ajax/fetch 等 API 完成客户端和服务端业务 Model 的同步。在层间关系里,它主要用于抽象出 ViewModel 中视图的 Model。算法
View 层:做为视图模板存在,在 MVVM 里,整个 View 是一个动态模板。除了定义结构、布局外,它展现的是 ViewModel 层的数据和状态。View 层不负责处理状态,View 层作的是 数据绑定的声明、 指令的声明、 事件绑定的声明。vuex
ViewModel 层:把 View 须要的层数据暴露,并对 View 层的 数据绑定声明、 指令声明、 事件绑定声明 负责,也就是处理 View 层的具体业务逻辑。ViewModel 底层会作好绑定属性的监听。当 ViewModel 中数据变化,View 层会获得更新;而当 View 中声明了数据的双向绑定(一般是表单元素),框架也会监听 View 层(表单)值的变化。一旦值变化,View 层绑定的 ViewModel 中的数据也会获得自动更新。vue-cli
优势:npm
缺点:数组
Vue 实例有一个完整的生命周期,也就是从开始建立、初始化数据、编译模版、挂载Dom -> 渲染、更新 -> 渲染、卸载等一系列过程,咱们称这是Vue的生命周期。
生命周期 | 描述 |
---|---|
beforeCreate | 组件实例被建立之初,组件的属性生效以前 |
created | 组件实例已经彻底建立,属性也绑定,但真实dom尚未生成,$el 还不可用 |
beforeMount | 在挂载开始以前被调用:相关的 render 函数首次被调用 |
mounted | el 被新建立的 vm.$el 替换,并挂载到实例上去以后调用该钩子 |
beforeUpdate | 组件数据更新以前调用,发生在虚拟 DOM 打补丁以前 |
update | 组件数据更新以后 |
activited | keep-alive专属,组件被激活时调用 |
deadctivated | keep-alive专属,组件被销毁时调用 |
beforeDestory | 组件销毁前调用 |
destoryed | 组件销毁后调用 |
官方实例的异步请求是在mounted生命周期中调用的,而实际上也能够在created生命周期中调用。
Vue组件通讯的方法以下:
props/$emit+v-on
: 经过props将数据自上而下传递,而经过$emit和v-on来向上传递信息。$attrs/$listeners
: Vue2.4中加入的$attrs/$listeners
能够进行跨级的组件通讯还有一些用solt插槽或者ref实例进行通讯的,使用场景过于有限就不赘述了。
computed:
computed
是计算属性,也就是计算值,它更多用于计算值的场景computed
具备缓存性,computed的值在getter执行后是会缓存的,只有在它依赖的属性值改变以后,下一次获取computed的值时才会从新调用对应的getter来计算computed
适用于计算比较消耗性能的计算场景watch:
props
$emit
或者本组件的值,当数据变化时来执行回调进行后续操做小结:
利用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的优点以下:
Object.defineProperty
不具有的Object.defineProperty
只能遍历对象属性直接修改Object.defineProperty的优点以下:
响应式系统简述:
优势:
缺点:
详细实现见虚拟DOM原理?
考点: 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的变化侦测原理
前置知识: 依赖收集、虚拟DOM、响应式系统
根本缘由是Vue与React的变化侦测方式有所不一样
React是pull的方式侦测变化,当React知道发生变化后,会使用Virtual Dom Diff进行差别检测,可是不少组件其实是确定不会发生变化的,这个时候须要用shouldComponentUpdate进行手动操做来减小diff,从而提升程序总体的性能.
Vue是pull+push的方式侦测变化的,在一开始就知道那个组件发生了变化,所以在push的阶段并不须要手动控制diff,而组件内部采用的diff方式其实是能够引入相似于shouldComponentUpdate相关生命周期的,可是一般合理大小的组件不会有过量的diff,手动优化的价值有限,所以目前Vue并无考虑引入shouldComponentUpdate这种手动优化的生命周期.
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.