Vue面试问题集锦(个人用)

244 阅读10分钟

1、Vue 的响应式原理中 Object.defineProperty 有什么缺陷?为什么在 Vue3.0 采用了 Proxy,抛弃了 Object.defineProperty?

Object.defineProperty无法监控到数组下标的变化,导致通过数组下标添加元素,不能实时响应;Object.defineProperty只能劫持对象的属性,从而需要对每个对象,每个属性进行遍历,
如果,属性值是对象,还需要深度遍历。Proxy可以劫持整个对象,并返回一个新的对象。Proxy不仅可以代理对象,还可以代理数组。还可以代理动态增加的属性。

2、Vue 的父组件和子组件生命周期钩子执行顺序是什么

加载渲染过程父beforeCreate->父created->父beforeMount->子beforeCreate->子created->子beforeMount->子mounted->父mounted子组件更新过程父beforeUpdate->子beforeUpdate->子updated->父updated父组件更新过程父beforeUpdate->父updated销毁过程父beforeDestroy->子beforeDestroy->子destroyed->父destroyed

3、谈谈你对对vuex的理解

当面试官问我们对vuex的理解的时候,我们不能只说“vuex是一个专为vue.js应用程序开发的状态管理模式”,尽量不要让面试官连续追问(总会问到自己不会的然后说你实力还差那么点来可以得降薪),应该从三个方面一次性去说清楚:

1.vuex是什么?

2.vuex的核心概念;

  • vuex的属性;
  • vuex的数据传递流程;

3.为什么要用vuex?

  对于问题一,我们按官网的描述即可。vuex是一个专为vue.js应用程序开发的状态管理模式(它采用集中式存贮管理应用的所有组件的状态,并以相应的规则保证状态以一种可预测的方式发生变化)。

  对于问题二,vuex五大核心属性:state,getter,mutation,action,module

  • state:存储数据,存储状态;在根实例中注册了store 后,用 this.$store.state 来访问;对应vue里面的data;存放数据方式为响应式,vue组件从store中读取数据,如数据发生变化,组件也会对应的更新。
  • getter:可以认为是 store 的计算属性,它的返回值会根据它的依赖被缓存起来,且只有当它的依赖值发生了改变才会被重新计算。
  • mutation:更改 Vuex 的 store 中的状态的唯一方法是提交 mutation。
  • action:包含任意异步操作,通过提交 mutation 间接更变状态。
  • module:将 store 分割成模块,每个模块都具有state、mutation、action、getter、甚至是嵌套子模块。

对于vuex的数据传递流程,如下图所示:

当组件进行数据修改的时候我们需要调用dispatch来触发actions里面的方法。actions里面的每个方法中都会有一个commit方法,当方法执行的时候会通过commit来触发mutations里面的方法进行数据的修改。mutations里面的每个函数都会有一个state参数,这样就可以在mutations里面进行state的数据修改,当数据修改完毕后,会传导给页面。页面的数据也会发生改变。

  对于问题三,由于传参的方法对于多层嵌套的组件将会非常繁琐,并且对于兄弟组件间的状态传递无能为力。我们经常会采用父子组件直接引用或者通过事件来变更和同步状态的多份拷贝。以上的这些模式非常脆弱,通常会导致代码无法维护。所以我们需要把组件的共享状态抽取出来,以一个全局单例模式管理。在这种模式下,我们的组件树构成了一个巨大的“视图”,不管在树的哪个位置,任何组件都能获取状态或者触发行为!另外,通过定义和隔离状态管理中的各种概念并强制遵守一定的规则,我们的代码将会变得更结构化且易维护。

4、Vue实现数据双向绑定的原理

vue实现数据双向绑定主要是采用数据劫持结合发布-订阅者模式的方式,通过Object.defineProperty()来劫持各个属性的setter,getter,在数据变动时发布消息给订阅者,触发相应的监听回调。当把一个普通的javascript对象传给Vue实例作为它的data选项时,Vue将遍历它的属性,用Object.defineProperty将它们转换为getter/setter。用户看不到getter/settter,但在内部Vue追踪依赖,在属性被访问和修改时通知变化。

vue的数据双向绑定 将MVVM作为数据绑定的入口,整合Observer,compile和Watcher三者,通过Observer来监听自己的model的数据变化,通过Compile来解析编译模板指令,最终利用watcher搭起observer和compile之间的通讯桥梁,达到数据变化--》视图更新;视图交互变化--》model变更双向绑定的效果。

5、对于MVVM的理解

MVVM是Model-VIew-ViewModel的缩写。Model代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑。View代表UI组件,它负责将数据模型转换成UI展现出来。

ViewModel监听模型数据的改变和控制视图行为、处理用户交互,简单理解就是一个同步View和Model的对象,连接Model和View。在MVVM架构下,View和Model之间没有直接的联系,而是通过ViewModel进行交互,Model和ViewModel之间的交互是双向的,因此View数据的变化会同步到Model中,而Model数据的变化也会立即反应到View上。ViewModel通过双向数据绑定把View层和Model层连接了起来,而View和Model之间的同步工作完全是自动的,无需认为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM,不需要关注数据状态的同步问题,复杂的数据状态维护完全。

6、虚拟 DOM 实现原理?

虚拟 DOM 的实现原理主要包括以下 3 部分:
①用 JavaScript 对象模拟真实 DOM 树,对真实 DOM 进行抽象;
②diff 算法 — 比较两棵虚拟 DOM 树的差异;
③pach 算法 — 将两个虚拟 DOM 对象的差异应用到真正的 DOM 树。

7、Vue 项目优化

1.代码层面的优化
v-if 和 v-show 区分使用场景
computed 和 watch 区分使用场景
v-for 遍历必须为 item 添加 key,且避免同时使用 v-if
长列表性能优化
事件的销毁
图片资源懒加载
路由懒加载
第三方插件的按需引入
优化无限列表性能
服务端渲染 SSR or 预渲染

2.Webpack 层面的优化
Webpack 对图片进行压缩
减少 ES6 转为 ES5 的冗余代码
提取公共代码
模板预编译
提取组件的 CSS
优化 SourceMap
构建结果输出分析
Vue 项目的编译优化

3.基础的 Web 技术的优化
开启 gzip 压缩
浏览器缓存
CDN 的使用
使用 Chrome Performance 查找性能瓶颈

8、Vue中是如何监测数组变化

原理:1、使用函数劫持的方式,重写了数组的方法

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

主要重写数组的方法:push、pop、shift、unshift、splice、sort、reverse

9、为何Vue采用异步渲染?

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

原理:dep.notify()通知watcher进行更新操作,依次调用watcher的update,将watcher去重放入到队列中,异步清空watcher队列

10、nextTick实现原理?

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

原理

nextTick(cb)调用nextTick传入cb

callbacks.push(cb)将回调函数存入数组中

timerFunc() 调用timerFunc   -- 1、尝试采用promises回调                |

                                             -- 2、尝试采用MutationObserver回调 |   =》flushcallbacks

                                             -- 3、尝试采用setImmediate回调         |         执行nextTick

                                             -- 4、最终采用setTimeout回调             |         中传入的方法

返回promise  

11、mvc和mvvm架构模式

mvc:从前端到后端交互,前端请求通过路由找到对应控制器拿到数据,数据交替是单向的。简单来讲就是前端负责页面后端负责数据

mvvm:细化了mvc的前端部分,数据直接挂到框架上面直接渲染更新。前端数据需求量的增加使其成为了一个必然的趋势,mvvm可以说是mvc前端部分的一个抽离

12、Vue中模板的编译原理

编译原理

1、vue是怎么从一个个组件变成了一个页面?怎么从组件代码变成了一个render函数?

  • 将模板转换为ast树(虚拟Dom对象,vnode树),主要是正则实现解析代码然后再提取相关数据放入对象树中
  • 优化树
  • 将ast树生成js代码

渲染及更新

1、组件如何渲染到真实DOM

  • 通过CreateElement方法将我们的组件对象生成虚拟Dom对象
  • 拿到虚拟DOM,Vue通过vue.extend()方法构建子组件的构造函数,到这里我们虚拟DOM操作就结束了,接下来实例化
  • update方法执行,我们先执行CreateCompontent方法去new一个我们传过来的虚拟DOM对象,实例化后Vue就会给这个组件内部watcher去渲染。整体就是一个渲染过程

那么更新内部就是依赖于patchvnode流程核心diff算法去做比较,然后更新界面

13、Vue采用异步渲染

1、为什么会采用异步渲染呢

如果改变的值对应的同一watcher的话,按照同步流程我们就得更新两次

那么我们实际的流程是

刷新界面数据改变的时候通知notify方法进行更新视图,然后我们会将对应的watcher附一个id标记存到一个队列中,这用我们就可以清除掉相同的watcher只保留最新的,然后渲染页面,最后通过调用nextTick方法异步清空这个存放在watcher队列,然后进行下一个组件的渲染

14、Vuex原理

Vuex实现了一个单向数据流,在全局拥有一个state存放数据,当组件要更改State中的数据时,必须通过Mutation进行,Mutation同时提供了订阅者模式供外部插件调用获取state数据的更新。而当所有异步操作或者批量的同步操作需要走Action,但Action也是无法直接修改state的,还是要通过Mutation来修改state的数据,最后根据state的变化,渲染到视图上。

15、vue中如何监听一个对象内部变化

方法1:对整个obj深层监听

watch:{
   obj:{
      handler(newValue,oldValue){
          console.log('obj changed');
      },
      deep:true,//深度遍历
      immediate:true
      //默认第一次绑定的时候不会触发watch监听,值为true时可以在最初绑定的时候执行
   },
}