vue高频面试题(面试路上踩过的坑)
Posted abdm-989
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了vue高频面试题(面试路上踩过的坑)相关的知识,希望对你有一定的参考价值。
mvvm 双向绑定,采用**数据劫持结合发布者-订阅者模式**的方式,通过 `Object.defineProperty()` 来劫持各个属性的 setter、getter,在数据变动时发布消息给订阅者,触发相应的监听回调。
![双向绑定原理](?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
**几个要点:**
1、实现一个数据监听器 Observer,能够对数据对象的所有属性进行监听,如有变动可拿到最新值并通知订阅者
2、实现一个指令解析器 Compile,对每个元素节点的指令进行扫描和解析,根据指令模板替换数据,以及绑定相应的更新函数
3、实现一个 Watcher,作为连接 Observer 和 Compile 的桥梁,能够订阅并收到每个属性变动的通知,执行指令绑定的相应回调函数,从而更新视图
4、mvvm 入口函数,整合以上三者
**具体步骤:**
1. 需要 observe 的数据对象进行递归遍历,包括子属性对象的属性,都加上 setter 和 getter
这样的话,给这个对象的某个值赋值,就会触发 setter,那么就能监听到了数据变化
2. compile 解析模板指令,将模板中的变量替换成数据,然后初始化渲染页面视图,并将每个指令对应的节点绑定更新函数,添加监听数据的订阅者,一旦数据有变动,收到通知,更新视图
3. Watcher 订阅者是 Observer 和 Compile 之间通信的桥梁,主要做的事情是:
* 在自身实例化时往属性订阅器(dep)里面添加自己
* 自身必须有一个 update() 方法
* 待属性变动 dep.notice() 通知时,能调用自身的 update() 方法,并触发 Compile 中绑定的回调,则功成身退。
4. MVVM 作为数据绑定的入口,整合 Observer、Compile 和 Watcher 三者,通过Observer来监听自己的 model 数据变化,通过 Compile 来解析编译模板指令,最终利用 Watcher 搭起 Observer 和 Compile 之间的通信桥梁,达到数据变化 -> 视图更新;视图交互变化(input) -> 数据 model 变更的双向绑定效果。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 描述下 vue 从初始化页面--修改数据--刷新页面 UI 的过程?
当 Vue 进入初始化阶段时,一方面 Vue 会遍历 data 中的属性,并用 Object.defineProperty 将它转化成 getter/setter 的形式,实现数据劫持(暂不谈 Vue3.0 的 Proxy);另一方面,Vue 的指令编译器 Compiler 对元素节点的各个指令进行解析,初始化视图,并订阅 Watcher 来更新试图,此时 Watcher 会将自己添加到消息订阅器 Dep 中,此时初始化完毕。
当数据发生变化时,触发 Observer 中 setter 方法,立即调用 Dep.notify(),Dep 这个数组开始遍历所有的订阅者,并调用其 update 方法,Vue 内部再通过 diff 算法,patch 相应的更新完成对订阅者视图的改变。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 你是如何理解 Vue 的响应式系统的?
[图片上传失败...(image-b4defe-1573527366002)]
响应式系统简述:
* 任何一个 Vue Component 都有一个与之对应的 Watcher 实例
* Vue 的 data 上的属性会被添加 getter 和 setter 属性
* 当 Vue Component render 函数被执行的时候, data 上会被 触碰(touch), 即被读, getter 方法会被调用, 此时 Vue 会去记录此 Vue component 所依赖的所有 data。(这一过程被称为依赖收集)
* data 被改动时(主要是用户操作), 即被写, setter 方法会被调用, 此时 Vue 会去通知所有依赖于此 data 的组件去调用他们的 render 函数进行更新
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 虚拟 DOM 实现原理
* 虚拟DOM本质上是javascript对象,是对真实DOM的抽象
* 状态变更时,记录新树和旧树的差异
* 最后把差异更新到真正的dom中
> 详细实现见 [面试官: 你对虚拟DOM原理的理解?](https://user-gold-cdn.xitu.io/2019/8/1/16c49afec13e0416)
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 既然 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的视图更新机制对比](https://blog.csdn.net/csdn_haow/article/details/89915908)
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vue 中 key 值的作用?
当 Vue.js 用 v-for 正在更新已渲染过的元素列表时,它默认用“**就地复用**”策略。如果数据项的顺序被改变,Vue 将不会移动 DOM 元素来匹配数据项的顺序, 而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。**key 的作用主要是为了高效的更新虚拟DOM**。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vue 的生命周期
[vue生命周期详解](https://juejin.im/post/5c6d48e36fb9a049eb3c84ff)
1. `beforeCreate`和`created`
2. `beforeMount`和`mounted`
3. `beforeUpdate`和`updated`
4. `beforeDestory`和`destoryed`
5. `activated`和`deactivated`
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vue 组件间通信有哪些方式?
[Vue 组件间通信六种方式](https://juejin.im/post/5cde0b43f265da03867e78d3)
1. props/$emit
2. $emit/$on
3. vuex
4. $attrs/$listeners
5. provide/inject
6. $parent/$children 与 ref
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### watch、methods 和 computed 的区别?
* watch 为了监听某个响应数据的变化。computed 是自动**监听依赖值**的变化,从而动态返回内容,主要目的是简化模板内的复杂运算。所以区别来源于用法,只是需要动态值,那就用 computed ;需要知道值的改变后执行业务逻辑,才用 watch。
* methods是一个方法,它可以接受参数,而computed 不能,computed 是可以缓存的,methods 不会。computed 可以依赖其他 computed,甚至是其他组件的 data。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### vue 中怎么重置 data?
使用Object.assign(),vm.$data可以获取当前状态下的data,vm.$options.data可以获取到组件初始化状态下的data。
```
Object.assign(this.$data, this.$options.data())
```
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 组件中写 name 选项有什么作用?
1. 项目使用 keep-alive 时,可搭配组件 name 进行缓存过滤
2. DOM 做递归组件时需要调用自身 name
3. vue-devtools 调试工具里显示的组见名称是由vue中组件name决定的
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### vue-router 有哪些钩子函数?
官方文档:[vue-router钩子函数](https://router.vuejs.org/zh/guid ... E%E5%AE%88%E5%8D%AB)
* 全局前置守卫 `router.beforeEach`
* 全局解析守卫 `router.beforeResolve`
* 全局后置钩子 `router.afterEach`
* 路由独享的守卫 `beforeEnter`
* 组件内的守卫 `beforeRouteEnter`、`beforeRouteUpdate`、`beforeRouteLeave`
[前端路由简介以及vue-router实现原理](https://juejin.im/post/5b10b46df265da6e2a08a724)
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### `route` 和 `router` 的区别是什么?
`route`是“路由信息对象”,包括`path`,`params`,`hash`,`query`,`fullPath`,`matched`,`name`等路由信息参数。
`router`是“路由实例对象”,包括了路由的跳转方法(`push`、`replace`),钩子函数等。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### 说一下 Vue 和 React 的认识,做一个简单的对比
**1.监听数据变化的实现原理不同**
* Vue 通过 getter/setter 以及一些函数的劫持,能精确快速的计算出 Virtual DOM 的差异。这是由于它在渲染过程中,会跟踪每一个组件的依赖关系,不需要重新渲染整个组件树。
* React 默认是通过比较引用的方式进行的,如果不优化,每当应用的状态被改变时,全部子组件都会重新渲染,可能导致大量不必要的 VDOM 的重新渲染。
Vue 不需要特别的优化就能达到很好的性能,而对于 React 而言,需要通过 PureComponent/shouldComponentUpdate 这个生命周期方法来进行控制。如果你的应用中,交互复杂,需要处理大量的 UI 变化,那么使用 Virtual DOM 是一个好主意。如果你更新元素并不频繁,那么 Virtual DOM 并不一定适用,性能很可能还不如直接操控 DOM。
为什么 React 不精确监听数据变化呢?这是因为 Vue 和 React 设计理念上的区别,Vue 使用的是可变数据,而 React 更强调数据的不可变。
**2.数据流的不同**
![cmd-markdown-logo](https://upload-images.jianshu.io ... imageView2/2/w/1240)
* Vue 中默认支持双向绑定,组件与 DOM 之间可以通过 v-model 双向绑定。但是,父子组件之间,props 在 2.x 版本是单向数据流
* React 一直提倡的是单向数据流,他称之为 onChange/setState()模式。
不过由于我们一般都会用 Vuex 以及 Redux 等单向数据流的状态管理框架,因此很多时候我们感受不到这一点的区别了。
**3.模板渲染方式的不同**
在表层上,模板的语法不同
* React 是通过 JSX 渲染模板
* 而 Vue 是通过一种拓展的 html 语法进行渲染
在深层上,模板的原理不同,这才是他们的本质区别:
* React 是在组件 JS 代码中,通过原生 JS 实现模板中的常见语法,比如插值,条件,循环等,都是通过 JS 语法实现的
* Vue 是在和组件 JS 代码分离的单独的模板中,通过指令来实现的,比如条件语句就需要 v-if 来实现
对这一点,我个人比较喜欢 React 的做法,因为他更加纯粹更加原生,而 Vue 的做法显得有些独特,会把 HTML 弄得很乱。举个例子,说明 React 的好处:react 中 render 函数是支持闭包特性的,所以我们 import 的组件在 render 中可以直接调用。但是在 Vue 中,由于模板中使用的数据都必须挂在 this 上进行一次中转,所以我们 import 一个组件完了之后,还需要在 components 中再声明下,这样显然是很奇怪但又不得不这样的做法。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vue 的 nextTick 的原理是什么?
**1. 为什么需要 nextTick**
Vue 是异步修改 DOM 的并且不鼓励开发者直接接触 DOM,但有时候业务需要必须对数据更改--刷新后的 DOM 做相应的处理,这时候就可以使用 Vue.nextTick(callback)这个 api 了。
**2. 理解原理前的准备**
首先需要知道事件循环中宏任务和微任务这两个概念(这其实也是面试常考点)。请阅大佬文章--[彻底搞懂浏览器 Event-loop](https://juejin.im/post/5c947bca5188257de704121d)
常见的宏任务有 script, setTimeout, setInterval, setImmediate, I/O, UI rendering
常见的微任务有 process.nextTick(Nodejs),Promise.then(), MutationObserver;
**3. 理解 nextTick**
而 nextTick 的原理正是 vue 通过异步队列控制 DOM 更新和 nextTick 回调函数先后执行的方式。如果大家看过这部分的源码,会发现其中做了很多 isNative()的判断,因为这里还存在兼容性优雅降级的问题。可见 Vue 开发团队的深思熟虑,对性能的良苦用心。
如果你比较了解了前面的事件循环原理,推荐你看看这篇文章 请阅大佬文章--[全面解析 Vue.nextTick 实现原理](https://mp.weixin.qq.com/s/mCcW4OYj3p3471ghMBylBw)
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vuex 有哪几种属性?
有五种,分别是 `State`、`Getter`、`Mutation`、`Action`、`Module`
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### vue 首屏加载优化
##### 1. 把不常改变的库放到 index.html 中,通过 cdn 引入
![index.html](?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
然后找到 build/webpack.base.conf.js 文件,在 module.exports = { } 中添加以下代码
```
externals: {
‘vue‘: ‘Vue‘,
‘vue-router‘: ‘VueRouter‘,
‘element-ui‘: ‘ELEMENT‘,
},
```
这样 webpack 就不会把 vue.js, vue-router, element-ui 库打包了。声明一下,我把 main.js 中对 element 的引入删掉了,不然我发现打包后的 app.css 还是会把 element 的 css 打包进去,删掉后就没了。
然后你打包就会发现 vendor 文件小了很多~
##### 2. vue 路由的懒加载
`import`或者`require`懒加载。你打包就会发现,多了很多 1.xxxxx.js;2.xxxxx.js 等等,而 vendor.xxx.js 没了,剩下 app.js 和 manifest.js,而且 app.js 还很小,我这里是 100k 多一点。
##### 3. 不生成 map 文件
找到 config/index.js,修改为 `productionSourceMap: false`
##### 4. vue 组件尽量不要全局引入
##### 5. 使用更轻量级的工具库
##### 6. 开启gzip压缩
这个优化是两方面的,前端将文件打包成.gz文件,然后通过nginx的配置,让浏览器直接解析.gz文件。
##### 7. 首页单独做服务端渲染
如果首页真的有瓶颈,可以考虑用 node 单独做服务端渲染,而下面的子页面仍用 spa 单页的方式交互。
这里不推荐直接用 nuxt.js 服务端渲染方案,因为这样一来增加了学习成本,二来服务端的维护成本也会上升,有时在本机测试没问题,在服务端跑就有问题,为了省心,还是最大限度的使用静态页面较好。
参考链接:
[vue首屏加载优化](https://www.jianshu.com/p/df198914331b)
[vue项目首屏加载优化实战](https://www.cnblogs.com/mianbaodaxia/p/10751453.html)
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### Vue 3.0 有没有过了解?
关于Vue 3.0有幸看过尤大的关于3.0版本的[RFC Vue Function-based API RFC](https://zhuanlan.zhihu.com/p/68477600)。大致说了三个点,第一个是关于提出的新API `setup()`函数,第二个说了对于Typescript的支持,最后说了关于替换`Object.defineProperty`为 Proxy 的支持。
详细说了下关于Proxy代替带来的性能上的提升,因为传统的原型链拦截的方法,无法检测对象及数组的一些更新操作,但使用Proxy又带来了浏览器兼容问题。
[回到顶部](https://www.cnblogs.com/chenwenhao/p/11258895.html#_labelTop)
### vue-cli 替我们做了哪些工作?
首先需要知道 vue-cli 是什么?它是基于 Vue.js 进行快速开发的完整系统,也可以理解成是很多 npm 包的集合。其次,vue-cli 完成的功能有哪些?
> .vue 文件 --> .js 文件
> ES6 语法 --> ES5 语法
> Sass,Less,Stylus --> CSS
> 对 jpg,png,font 等静态资源的处理
> 热更新
> 定义环境变量,区分 dev 和 production 模式
> ...
如果开发者需要补充或修改默认设置,需要在 package.json 同级下新建一个 vue.config.js 文件
以上是关于vue高频面试题(面试路上踩过的坑)的主要内容,如果未能解决你的问题,请参考以下文章
前端面试Vue 高频原理篇+详细解答,还有105道vue面试题集合