Vue2.0面试全攻略

540 阅读7分钟

1、谈一下你对MVVM的理解

MVVM 是 Model-View-ViewModel 的缩写。

  • Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。
  • View 代表UI 组件,它负责将数据模型转化成UI 展现出来。
  • ViewModel 监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步ViewModel的对象,连接ModelView

在MVVM架构下,ViewModel 之间并没有直接的联系,而是通过ViewModel进行交互,ModelViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上

2、请说一下Vue响应式数据的原理

  1. 就是利用Object.defineProperty数据劫持 和观察者模式实现的
  2. 在数据初始化的时候吧data里面的数据进行转换,因为Object.defineProperty里面有一个get和set方法 get可以返回被劫持的属性的结果,而set可以修改被劫持的属性的结果。
  3. 每个数据创建一个Dep被观察者,用到数据的地方就被称为观察者watcher
  4. 对模板进行编译compiler提取里面所有的需要数据的地方变成watcher把watch加入到对应的Dep的观察者列表中
  5. 当data中的数据发生改变的时候,由于被劫持了 所以vue的内部是知道数据改变了,然后就调用对应的dep去通知对应的观察者列表中的所有观察者,观察者得到通知后立刻去更新视图

3、vue中如何检测数组变化

使用函数劫持的方式,重写了数组的方法,Vue 将 data 中的数组,进行了原型链重写。指向了重新定义的数组原型方法,这样当调用数组API的时候,可以通知依赖更新。如果数组中包含着引用类型。会对数组中的引用类型再次进行监控。

4、为何vue采用异步渲染

如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能考虑,Vue会在本轮数据更新后,再去异步更新试图。

5、nextTick的实现原理

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

6、Vue中computed的特点

默认的computed也是一个watcher是具备缓存的,当依赖属性发生变化时才会更新试图

7、Watch中的deep:true是如何实现的

当用户指定了watch中的deep属性为true时,如果当前监控值是数组类型,会对对象中的每一项进行求值,此时会将当前的watcher存入到对应属性的依赖中,这样数组中对象发生变化时也会通知数据更新

8、vue的生命周期

什么时候被调用:

  1. beforeCreate:在实例初始化之后,数据观测(data observer)之前被调用。
  2. created:实例已经创建完成之后被调用,在这一步,实例已经完成以下配置:数据观测(data obwerver),属性和方法的运算,watch/event事件回调。这里没有#el。
  3. beforeMount:在挂载开始之前被调用,相关的render函数首次被调用。
  4. mounted:el被新创建的vm.$el替换,并挂载到实例上去之后调用该钩子。
  5. beforeUpdate:数据更新时调用,发生在虚拟DOM重新渲染和打补丁之前。
  6. updated:由于数据更改导致的虚拟DOM重新渲染和打补丁,在这之后会调用该钩子。
  7. beforeDestroy:实例销毁之前调用,在这一步,实例仍然完全可用。
  8. destroyed:vue实例销毁后调用,调用后,Vue实例指示的所有东西都会解绑定,所有的事件监听器会被移除,所有的子实例也会被销毁,该钩子在服务器端渲染期间不被调用。

生命周期内部可以做什么事:

  1. created:实例已经创建完成,因为他是最早触发的原因可以进行一些数据资源的请求。
  2. mounted:实例已经挂载完成,可以进行一些DOM操作
  3. beforeUpdate:可以在这个钩子中进一步的更改状态,这个不会触发附加的重渲染过程
  4. updated:可以执行依赖于DOM的操作,然而在大多数情况下,应该避免在此期间更改状态,因为这可能会导致更新无限循环。该钩子在服务器端渲染期间不被调用
  5. beforeDestroy:可以执行一些优化操作,清空定时器,解除绑定事件

9、ajax请求放在哪个生命周期合适呢?

  • 在created的时候,视图中的dom并没有渲染出来,所以此时如果直接去操作dom节点,无法找到相关的元素。
  • 在mounted中,由于此时dom已经渲染出来了,所以可以直接操作dom节点

一般情况下都放到mounted中,保证逻辑的统一性,因为生命周期是同步执行的,ajax是异步执行的

10、何时需要用到 beforeDestroy

  • 可能在当前页面里使用了$on方法,那需要在组件销毁前解绑
  • 清除自己定义的定时器
  • 解除事件的绑定 scroll  mousemove

11、vue中的v-if和v-show的区别

  • v-if如果条件不成立不会渲染当前指令所在节点的dom元素
  • v-show只是切换当前dom的显示隐藏

12、为什么v-for和v-if不能连用

v-for会比v-if的优先级高一些,如果连用的话,会把v-if给每个元素都添加,会造成性能问题

13、用vnode来描述一个dom结构

虚拟几点就是用一个对象来来描述真实的dom元素。

function c(tag,data,...children){
    let key = data.key;
    delete data.key;
    children = children.map(child => {
        if(typeof child === 'object'){
            return child;
        }else{
           return vnode(undefined,undefined,undefined,undefined,child)
        }
    })
    return vnode(tag,data,key,children)
}
function vnode(tag,data,key,children,text){
    return {tag,data,key,children,text}
}

14、简述vue中diff算法原理

  • 先同级比较,在比较子节点
  • 先判断一方有儿子一方没儿子的情况
  • 比较都有儿子的情况
  • 递归比较子节点

15、v-for中为什么要用key

key主要是用来做dom diff的,例如只是将列表的顺序调换位置,可以通过key比对出来,只会做一个移动的操作,并不会有销毁的操作。v-for尽量不使用索引做为key,因为索引删除之前和之后是一样的,删之前是0,1,2,删之后变成0,1。

16、组件中data为什么是一个函数

同一个组件被复用多次,会创建多个实例,这些实例用的都是同一个构造函数,如果data是一个对象的话,那么所有组件都共享了同一个对象,为了保证组件的数据独立性要求每个组件必须通过data函数返回一个对象作为组件的状态

为什么new Vue({data:{}})可以是一个对象,不是一个函数,因为它不会复用

17、Vue中的事件绑定原理

Vue中的事件绑定分为两种,一种是原生的事件绑定,还有一种是组件的事件绑定。

  • 原生dom事件绑定,采用的是addEventListener实现
  • 组件事件绑定采用$on方法 

18、v-model中的实现原理及如何自定义v-model

组件的v-modelvalue+input方法的语法糖

编译时,不同的标签解析出的内容不一样例如checkbox等

19、Vue中v-html会导致哪些问题

  • 可能会导致XSS攻击
  • v-html会替换掉标签内部的子元素

20、Vue父子组件生命周期调用顺序

  • 组件的调用顺序都是先父后子,渲染完成的顺序是先子后父
  • 组件的销毁操作是先父后子,销毁完成的顺序是先子后父

渲染顺序:父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted
更新顺序:父beforeUpdate->子beforeUpdate->子updated->父updated
销毁顺序:父beforeDestroy->子beforeDestroy->子destroy->父destroy

21、Vue组件如何通信

  • 父子之间通信 父->子通过props、子->父通过$on、$emit

  • 获取父子实例的方式$parent、$childeren

  • 在父组件中提供数据子组件进行消费Provide、inject

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

  • Event、Bus实现跨组件通信

  • Vues状态管理实现通信

22、为什么要用异步组件

如果组件功能多,打包出来的体积会变大,我们可以采用异步组件的方式来加载组件。主要依赖import()这个语法,可以实现文件的分割加载。

23、什么是作用域插槽

  1. 插槽:
  • 创建组件虚拟节点时,会将组件的儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类{a:[vnode],b[vnode]}
  • 渲染组件时,会拿对应的slot属性的节点进行替换操作。(插槽的作用域为父组件)
  1. 作用域插槽
  • 作用域插槽在解析的时候,不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。(插槽的作用域为子组件)

24、谈谈你对keep-alive的了解

keep-alive可以实现组件的缓存,当组件切换时不会对当前组件进行卸载,常用的两个属性include、exclude,2个生命周期activated、deactivated

25、Vue中常见的性能优化

  1. 不要将所有数据都放在data中,data中的数据都会增加gettersetter,会收集对应的watch

  2. vue在v-for时给每项元素绑定事件需要用事件代理

  3. SPA页面使用keep-alive缓存组件

  4. 拆分组件(提高复用性,增加代码可维护性,减少不必要的渲染)

  5. v-if当值为false时,内部指令不会执行,具有阻断功能,很多情况下使用v-if代替v-show

  6. key保证唯一性(默认vue会采用就地复用策略)

  7. Object.freeze冻结数据

  8. 合理使用路由懒加载、异步组件

  9. 数据持久化的问题(防抖、节流)