化身面试官出 30+ Vue 面试题,超级干货
之前一直没有总结 Vue 相关的知识,看了挺多别人总结的,其实也能快速知道一些,但是遇到真正的面试,发现自己的知识都还是是碎片化的,觉得不行,得总结一下了。
不知道大伙是不是已经在准备春招面试了呢,准备得咋样了呢,面试的 Vue 复习得怎么样了呢?如果你感觉在 vue 这方面还比较薄弱的话,不如来做一做这套模拟面试吧,看看大伙能不能打个满分,祝你顺利,:cherry_blossom: 答案仅供参考 :cherry_blossom: 欢迎访问 GitHub 仓库 [1] ,目前已经有 552
道大厂真题了,涵盖各类前端的真题—— 进入正题化身为面试官 ——:telephone_receiver::telephone_receiver: 电话拨通中,咳咳喂,听得到吗,听得到是吧 :blush:,那面试开始了,你先做个自我介绍吧 。。。在你自我介绍的时候呢,我就看看你做过的项目,技术栈什么的。第一个问题,先摸个底:
了解过(用过)react 或者 angular 吗,他们有什么区别?
答案
Vue
借鉴了 angular
的模板和数据绑定技术,又借鉴了 react
的组件化和虚拟 DOM
技术。
:no_mouth: 对 Vue
比较熟一些是吧~(这里只说 Vue
假设你就只熟练 Vue
)
那首先谈谈你对 Vue 的理解吧?
答案
官网介绍: cn.vuejs.org/index.html [2]关键点: 渐进式 JavaScript
框架、核心库加插件、动态创建用户界面(异步获取后台数据,数据展示在界面) 特点: MVVM
模式;代码简洁体积小,运行效率高,适合移动 PC 端开发;本身只关注 UI
(和 react
相似),可以轻松引入 Vue
插件或其他的第三方库进行开发。
:cherry_blossom: 思考一下自己所说的那些点,自己都非常清楚明白吗?下面呢我就根据你对 vue
的理解,接着谈谈:
你刚刚说到了 MVVM,能详细说说吗?
答案
全称: Model-View-ViewModel
, Model
表示数据模型层。 view
表示视图层, ViewModel
是 View
和 Model
层的桥梁,数据绑定到 viewModel
层并自动渲染到页面中,视图变化通知 viewModel
层更新数据。
:no_mouth: 摸底差不多,问基础了,响应式数据得知道吧,问一问
vue 是如何实现响应式数据的呢?(响应式数据原理):exclamation:
答案
Vue2: Object.definProperty
重新定义 data
中所有的属性, Object.definProperty
可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。具体的过程:首先 Vue 使用 initData
初始化用户传入的参数,然后使用 new Observer
对数据进行观测,如果数据是一个对象类型就会调用 this.walk(value)
对对象进行处理,内部使用 defineReactive
循环对象属性定义响应式变化,核心就是使用 Object.definProperty
重新定义数据。 :cherry_blossom: 刚刚如果你说了对象的检测,然后又没说清楚数组的处理的话,我就会问下面这个问题
那 vue 中是如何检测数组变化的呢?
答案
数组就是使用 object.defineProperty
重新定义数组的每一项,那能引起数组变化的方法我们都是知道的, pop
、 push
、 shift
、 unshift
、 splice
、 sort
、 reverse
这七种,只要这些方法执行改了数组内容,我就更新内容就好了,是不是很好理解。
-
是用来函数劫持的方式,重写了数组方法,具体呢就是更改了数组的原型,更改成自己的,用户调数组的一些方法的时候,走的就是自己的方法,然后通知视图去更新。
-
数组里每一项可能是对象,那么我就是会对数组的每一项进行观测,(且只有数组里的对象才能进行观测,观测过的也不会进行观测)
vue3:改用 proxy
,可直接监听对象数组的变化。
那你说说 Vue 的事件绑定原理吧
答案
-
原生
DOM
的绑定:Vue 在创建真实 DOM 时会调用createElm
,默认会调用invokeCreateHooks
。会遍历当前平台下相对的属性处理代码,其中就有updateDOMLListeners
方法,内部会传入add()
方法 -
组件绑定事件,原生事件,自定义事件;组件绑定之间是通过 Vue 中自定义的
$on
方法实现的。
(可以理解为:组件的 nativeOnOn
等价于 普通元素 on 组件的 on 会单独处理)
v-model 中的实现原理及如何自定义 v-model :exclamation:
答案
v-model
可以看成是 value+input
方法的语法糖(组件)。原生的 v-model
,会根据标签的不同生成不同的事件与属性。解析一个指令来。自定义:自己写 model
属性,里面放上 prop
和 event
:+1: 还行哟~知道响应式数据和数据绑定问完了,接着问问渲染呗:
为什么 Vue 采用异步渲染呢?
答案
Vue
是组件级更新,如果不采用异步更新,那么每次更新数据都会对当前组件进行重新渲染,所以为了性能, Vue
会在本轮数据更新后,在异步更新视图。核心思想 nextTick
。 dep.notify()
通知 watcher 进行更新, subs[i].update
依次调用 watcher 的 update
, queueWatcher
将 watcher 去重放入队列, nextTick( flushSchedulerQueue
)在下一 tick 中刷新 watcher 队列(异步)。
:cherry_blossom: 接着追问,要是你 nextTick 都能讲得很清楚的话那基本你是明白了。
了解 nextTick 吗?
答案
异步方法,异步渲染最后一步,与 JS 事件循环联系紧密。主要使用了宏任务微任务( setTimeout
、 promise
那些),定义了一个异步方法,多次调用 nextTick
会将方法存入队列,通过异步方法清空当前队列。
可以的可以的,先问你个生命周期,我再想想怎么难住你 🙂
说说 Vue 的生命周期吧 :exclamation:
答案
什么时候被调用?
-
beforeCreate :实例初始化之后,数据观测之前调用
-
created:实例创建万之后调用。实例完成:数据观测、属性和方法的运算、
watch/event
事件回调。无
$el
. -
beforeMount:在挂载之前调用,相关
render
函数首次被调用 -
mounted:了被新创建的
vm.$el
替换,并挂载到实例上去之后调用改钩子。 -
beforeUpdate:数据更新前调用,发生在虚拟 DOM 重新渲染和打补丁,在这之后会调用改钩子。
-
updated:由于数据更改导致的虚拟 DOM 重新渲染和打补丁,在这之后会调用改钩子。
-
beforeDestroy:实例销毁前调用,实例仍然可用。
-
destroyed:实例销毁之后调用,调用后,Vue 实例指示的所有东西都会解绑,所有事件监听器和所有子实例都会被移除
每个生命周期内部可以做什么?
-
created:实例已经创建完成,因为他是最早触发的,所以可以进行一些数据、资源的请求。
-
mounted:实例已经挂载完成,可以进行一些 DOM 操作。
-
beforeUpdate:可以在这个钩子中进一步的更改状态,不会触发重渲染。
-
updated:可以执行依赖于 DOM 的操作,但是要避免更改状态,可能会导致更新无线循环。
-
destroyed:可以执行一些优化操作,清空计时器,解除绑定事件。
ajax 放在哪个生命周期?:一般放在 mounted
中,保证逻辑统一性,因为生命周期是同步执行的, ajax
是异步执行的。单数服务端渲染 ssr
同一放在 created
中,因为服务端渲染不支持 mounted
方法。 什么时候使用 beforeDestroy? :当前页面使用 $on
,需要解绑事件。清楚定时器。解除事件绑定, scroll mousemove
。
父子组件生命周期调用顺序(简单)
答案
渲染顺序:先父后子,完成顺序:先子后父更新顺序:父更新导致子更新,子更新完成后父销毁顺序:先父后子,完成顺序:先子后父
Vue 组件通信 :exclamation:
答案
-
父子间通信:父亲提供数据通过属性
props
传给儿子;儿子通过$on
绑父亲的事件,再通过$emit
触发自己的事件(发布订阅) -
利用父子关系
$parent
、$children
,
获取父子组件实例的方法。
-
父组件提供数据,子组件注入。
provide
、inject
,插件用得多。 -
ref
获取组件实例,调用组件的属性、方法 -
跨组件通信
Event Bus
(Vue.prototype.bus=newVue)其实基于 bus = new Vue)其实基于 bus=newVue)其实基于 on 与$emit -
vuex
状态管理实现通信
Vuex 工作原理
答案
官网: vuex.vuejs.org/zh/ [3]
-
Vuex 是一个专为 Vue.js 应用程序开发的 状态管理模式 。
-
状态自管理应用包含以下几个部分:
-
state,驱动应用的数据源;
-
view,以声明方式将 state 映射到视图;
-
actions,响应在 view 上的用户输入导致的状态变化。下图单向数据流示意图:
-
vuex,多组件共享状态,因-单向数据流简洁性很容易被破坏:
-
多个视图依赖于同一状态。
-
来自不同视图的行为需要变更同一状态。

问虚拟 DOM
吧,看你能不能讲清楚从真实 DOM
到虚拟 DOM
,再和我说说 diff
如何从真实 DOM 到虚拟 DOM
答案
涉及到 Vue 中的模板编译原理,主要过程:
-
将模板转换成
ast
树,ast
用对象来描述真实的 JS 语法(将真实 DOM 转换成虚拟 DOM) -
优化树
-
将
ast
树生成代码
用 VNode 来描述一个 DOM 结构
答案
虚拟节点就是用一个对象来描述一个真实的 DOM 元素。首先将 template
(真实 DOM)先转成 ast
, ast
树通过 codegen
生成 render
函数, render
函数里的 _c
方法将它转为虚拟 dom
diff 算法
答案
时间复杂度:个树的完全 diff
算法是一个时间复杂度为 O(n*3)
,vue 进行优化转化成 O(n)
。 理解:
-
最小量更新,
key
很重要。这个可以是这个节点的唯一标识,告诉diff
算法,在更改前后它们是同一个 DOM 节点 -
v-for key key key
-
只有是同一个虚拟节点才会进行精细化比较,否则就是暴力删除旧的,插入新的。
-
只进行同层比较,不会进行跨层比较。
diff 算法的优化策略:四种命中查找,四个指针
-
旧前与新前(先比开头,后插入和删除节点的这种情况)
-
旧后与新后(比结尾,前插入或删除的情况)
-
旧前与新后(头与尾比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧后之后)
-
旧后与新前(尾与头比,此种发生了,涉及移动节点,那么新前指向的节点,移动到旧前之前)
— 问完上面这些如果都能很清楚的话,基本 O 了 —以下的这些简单的概念,你肯定也是没有问题的啦 😉
Computed watch 和 method
答案
computed:默认 computed
也是一个 watcher
具备缓存,只有当依赖的数据变化时才会计算, 当数据没有变化时, 它会读取缓存数据。如果一个数据依赖于其他数据,使用 computed
watch :每次都需要执行函数。 watch
更适用于数据变化时的异步操作。如果需要在某个数据变化时做一些事情,使用 watch。 method :只要把方法用到模板上了,每次一变化就会重新渲染视图,性能开销大
v-if 和 v-show 区别
答案
-
v-if
如果条件不成立不会渲染当前指令所在节点的 DOM 元素 -
v-show
只是切换当前 DOM 的显示与隐藏
v-for 和 v-if 为什么不能连用
答案
v-for
会比 v-if
的优先级更高,连用的话会把 v-if
的每个元素都添加一下,造成性能问题。
v-html 会导致哪些问题(简单)
答案
-
XSS
攻击 -
v-html
会替换标签内部的元素
描述组件渲染和更新过程
答案
渲染组件时,会通过 vue.extend()
方法构建子组件的构造函数,并进行实例化。最终手动调用 $mount()
进行挂载。更新组件时会进行 patchVnode
流程,核心就是 diff
算法。
组件中的 data 为什么是函数
答案
避免组件中的数据互相影响。同一个组件被复用多次会创建多个实例,如果 data
是一个对象的话,这些实例用的是同一个构造函数。为了保证组件的数据独立,要求每个组件都必须通过 data
函数返回一个对象作为组件的状态。
为什么要使用异步组件?
答案
-
节省打包出的结果,异步组件分开打包,采用 jsonp 的方式进行加载,有效解决文件过大的问题。
-
核心就是包组件定义变成一个函数,依赖
import()
语法,可以实现文件的分割加载。
详细的看官方文档: cn.vuejs.org/v2/guide/co… [4]
action 与 mutation 的区别
答案
-
mutation
是同步更新,
$watch
严格模式下会报错 -
action
是同步操作,可以获取数据后调用
mutation
提交最终数据
插槽与作用域插槽的区别
插槽
答案
-
创建组件虚拟节点时,会将组件儿子的虚拟节点保存起来。当初始化组件时,通过插槽属性将儿子进行分类
{a:[vnode],b[vnode]}
-
渲染组件时会拿对应的
slot
属性的节点进行替换操作。(插槽的作用域为父组件)
作用域插槽
答案
-
作用域插槽在解析的时候不会作为组件的孩子节点。会解析成函数,当子组件渲染时,会调用此函数进行渲染。
-
普通插槽渲染的作用域是父组件,作用域插槽的渲染作用域是当前子组件。
vue 中相同逻辑如何抽离
答案
其实就是考察 vue.mixin
用法,给组件每个生命周期,函数都混入一些公共逻辑。
谈谈对 keep-alive 的了解
答案
keep-alive
可以实现组件的缓存,当组件切换时不会对当前组件进行卸载。常用的 2 个属性 include/exclude
,2 个生命周期 activated
, deactivated
Vue 性能优化
答案
编码优化:
-
事件代理
-
keep-alive
-
拆分组件
-
key
保证唯一性 -
路由懒加载、异步组件
-
防抖节流
Vue 加载性能优化
-
第三方模块按需导入(
babel-plugin-component
) -
图片懒加载
用户体验
-
app-skeleton
骨架屏 -
shellap
p 壳 -
pwa
SEO 优化
-
预渲染
最后的话
:rocket::rocket: 觉得不错的朋友可以 :star:️ 关注我,后续会持续更新~:star:️:star:️ 最后祝各位正在准备秋招补招和春招的小伙伴面试顺利~,收割 offer,我们一起加油吧 !还有就是 新年快乐 :heart: :heart: ~
作者:HearLing,链接:https://juejin.cn/post/6930897845369356295
参考资料
GitHub 仓库: https://github.com/Chocolate1999/Front-end-learning-to-organize-notes/issues
cn.vuejs.org/index.html: https://cn.vuejs.org/index.html
vuex.vuejs.org/zh/: https://vuex.vuejs.org/zh/
cn.vuejs.org/v2/guide/co…: https://cn.vuejs.org/v2/guide/components-dynamic-async.html#%E5%BC%82%E6%AD%A5%E7%BB%84%E4%BB%B6