目录
前言
最近要准备面试了,所以重新总结一下最新的经典Vue面试题解析。 这里是已经写完的一部分,后续面试过程中还会这一篇更新其他部分。
相关学习资源
首页置顶的相关网站-思维导图-体系课
一般慕课网上有的课程我都买了,有需要的可以私信哈
01-Vue组件之间通信方式有哪些
vue是组件化开发框架,所以对于vue应用来说组件间的数据通信非常重要。此题主要考查大家vue基本功,对于vue基础api运用熟练度。另外一些边界知识如provide/inject/$attrs则提现了面试者的知识广度。
思路分析:
1.总述知道的所有方式
2.按组件关系阐述使用场景
回答范例:
1.组件通信常用方式有以下8种:
props
$emit/$on(vue3废弃)$children(vue3废弃)/$parent
$attrs/$listeners(vue3废弃)
ref
$root
eventbus
vuex
注意vue3中废弃的几个API
https://v3-migration.vuejs.org/breaking-changes/children.html
https://v3-migration.vuejs.org/breaking-changes/listeners-removed.html
https://v3-migration.vuejs.org/breaking-changes/events-api.html#overview
2.根据组件之间关系讨论组件通信最为清晰有效
- 父子组件
props/$emit/$parent/ref/$attrs
- 兄弟组件
$parent/$root/eventbus/vuex
- 跨层级关系
eventbus/vuex/provide+inject
02-v-if和v-for哪个优先级更高?
分析:
此题考查常识,文档中曾有详细说明v2|v3;也是一个很好的实践题目,项目中经常会遇到,能够看出面试者api熟悉程度和应用能力。
思路分析:
1.先给出结论
2.为什么是这样的,说出细节
3.哪些场景可能导致我们这样做,该怎么处理
4.总结,拔高
个人面试技巧(适用任何面试问题):
- 结合做过的项目
三个方面
1.首先说出答案
2.尽量遵循WWWW (4W)回答方法,where,when,what,why
3.讲人话:在哪里,在什么时候,在什么事情,为什么这样做
4.以及有什么可以优化的
回答范例:
- 实践中
不应该把v-for和v-if放一起
- 在
vue2中,v-for的优先级是高于v-if
,把它们放在一起,输出的渲染函数中可以看出会先执行循环再判断条件,哪怕我们只渲染列表中一小部分元素,也得在每次重渲染的时候遍历整个列表,这会比较浪费;另外需要注意的是在vue3中则完全相反,v-if的优先级高于v-for
,所以v-if执行时,它调用的变量还不存在,就会导致异常 - 通常有两种情况下导致我们这样做:- 为了过滤列表中的项目 (比如 v-for=“user in users” v-if=“user.isActive”)。此时定义一个计算属性 (比如 activeUsers),让其返回过滤后的列表即可(比如users.filter(u=>u.isActive))。- 为了避免渲染本应该被隐藏的列表 (比如 v-for=“user in users” v-if=“shouldShowUsers”)。此时把 v-if 移动至容器元素上 (比如 ul、ol)或者外面包一层template即可。
- 文档中明确指出
永远不要把 v-if 和 v-for 同时用在同一个元素上
,显然这是一个重要的注意事项。 - 源码里面关于代码生成的部分,能够清晰的看到是先处理v-if还是v-for,顺序上vue2和vue3正好相反,因此产生了一些症状的不同,但是不管怎样都是不能把它们写在一起的。
03-能说一说双向绑定使用和原理吗?
题目分析:
双向绑定是vue的特色之一,开发中必然会用到的知识点,然而此题还问了实现原理,升级为深度考查。
思路分析:
- 给出双绑定义
- 双绑带来的好处
- 在哪使用双绑
- 使用方式、使用细节、vue3变化
- 原理实现描述
回答范例:
- vue中双向绑定是一个指令
v-model
,可以绑定一个响应式数据到视图,同时视图中变化能改变该值。 - v-model是语法糖,默认情况下相当于
:value和@input
。使用v-model可以减少大量繁琐的事件处理代码,提高开发效率。 - 通常在表单项上使用v-model,还可以在自定义组件上使用,表示某个值的输入和输出控制。
- 通过
<input v-model="xxx">
的方式将xxx的值绑定到表单元素value上;对于checkbox,可以使用true-value和false-value指定特殊的值,对于radio可以使用value指定特殊的值;对于select可以通过options元素的value设置特殊的值;还可以结合.lazy,.number,.trim
对v-mode的行为做进一步限定;v-model用在自定义组件上时又会有很大不同,vue3中它类似于sync修饰符
,最终展开的结果是modelValue属性和update:modelValue
事件;vue3中我们甚至可以用参数形式指定多个不同的绑定,例如v-model:foo和v-model:bar,非常强大! - v-model是一个指令,它的神奇魔法实际上是vue的编译器完成的。我做过测试,包含v-model的模板,转换为渲染函数之后,实际上还是是value属性的绑定以及input事件监听,事件回调函数中会做相应变量更新操作。编译器根据表单元素的不同会展开不同的DOM属性和事件对,比如text类型的input和textarea会展开为value和input事件;checkbox和radio类型的input会展开为checked和change事件;select用value作为属性,用change作为事件。
04-Vue中如何扩展一个组件
此题属于实践题,考察大家对vue常用api使用熟练度,答题时不仅要列出这些解决方案,同时最好说出他们异同。
答题思路:
- 按照逻辑扩展和内容扩展来列举,
- 逻辑扩展有:
mixins、extends、composition api
; - 内容扩展有
slots
;
- 分别说出他们使用方法、场景差异和问题。
- 作为扩展,还可以说说vue3中新引入的composition api带来的变化
回答范例:
- 常见的组件扩展方法有:mixins,slots,extends等
- 混入mixins是分发 Vue组件中可复用功能的非常灵活的方式。混入对象可以包含任意组件选项。当组件使用混入对象时,所有混入对象的选项将被混入该组件本身的选项。
- 插槽主要用于vue组件中的内容分发,也可以用于组件扩展。
子组件Child
<div><slot>这个内容会被父组件传递的内容替换</slot></div>
父组件Parent
<div><Child>来自老爹的内容</Child></div>
如果要精确分发到不同位置可以使用具名插槽,如果要使用子组件中的数据可以使用作用域插槽。
- 混入的数据和方法不能明确判断来源且可能和当前组件内变量产生命名冲突,
vue3中引入的composition api
,可以很好解决这些问题,利用独立出来的响应式模块可以很方便的编写独立逻辑并提供响应式的数据,然后在setup选项中组合使用,增强代码的可读性和维护性。例如:
// 复用逻辑1functionuseXX(){}// 复用逻辑2functionuseYY(){}// 逻辑组合const Comp ={setup(){const{xx}=useXX()const{yy}=useYY()return{xx, yy}}}
05-子组件可以直接改变父组件的数据么,说明原因
分析
这是一个实践知识点,组件化开发过程中有个
单项数据流原则
,不在子组件中修改父组件是个常识问题。
参考文档:https://staging.vuejs.org/guide/components/props.html#one-way-data-flow
思路
- 讲讲单项数据流原则,表明为何不能这么做
- 举几个常见场景的例子说说解决方案
- 结合实践讲讲如果需要修改父组件状态应该如何做
回答范例
- 所有的 prop 都使得其父子之间形成了一个
单向下行绑定
:父级 prop 的更新会向下流动到子组件中,但是反过来则不行。这样会防止从子组件意外变更父级组件的状态,从而导致你的应用的数据流向难以理解。另外,每次父级组件发生变更时,子组件中所有的 prop 都将会刷新为最新的值。这意味着你不
应该在一个子组件内部改变 prop。如果你这样做了,Vue 会在浏览器控制台中发出警告。
const props =defineProps(['foo'])// ❌ 下面行为会被警告, props是只读的!
props.foo ='bar'
- 实际开发过程中有两个场景会想要修改一个属性:
这个 prop 用来传递一个初始值;这个子组件接下来希望将其作为一个本地的 prop 数据来使用
。在这种情况下,最好定义一个本地的 data,并将这个 prop 用作其初始值:
const props =defineProps(['initialCounter'])const counter =ref(props.initialCounter)
这个 prop 以一种原始的值传入且需要进行转换
。在这种情况下,最好使用这个 prop 的值来定义一个计算属性:
const props =defineProps(['size'])// prop变化,计算属性自动更新const normalizedSize =computed(()=> props.size.trim().toLowerCase())
- 实践中如果确实想要改变父组件属性应该
emit一个事件让父组件去做这个变更
。注意虽然我们不能直接修改一个传入的对象或者数组类型的prop,但是我们还是能够直接改内嵌的对象或属性。
06-Vue要做权限管理该怎么做?控制到按钮级别的权限怎么做?
分析
综合实践题目,实际开发中经常需要面临权限管理的需求,考查实际应用能力。
权限管理一般需求是两个:页面权限和按钮权限,从这两个方面论述即可。
思路
- 权限管理需求分析:页面和按钮权限
- 权限管理的实现方案:分后端方案和前端方案阐述
- 说说各自的优缺点
回答范例
- 权限管理一般需求是页面权限和按钮权限的管理
- 具体实现的时候分后端和前端两种方案:
- 前端方案会
把所有路由信息在前端配置
,通过路由守卫要求用户登录,用户登录后根据角色过滤出路由表
。比如我会配置一个asyncRoutes
数组,需要认证的页面在其路由的meta中添加一个roles字段,等获取用户角色之后取两者的交集,若结果不为空则说明可以访问。此过滤过程结束,剩下的路由就是该用户能访问的页面,最后通过router.addRoutes(accessRoutes)方式动态添加路由
即可。 - 后端方案会
把所有页面路由信息存在数据库
中,用户登录的时候根据其角色查询得到其能访问的所有页面路由信息
返回给前端,前端再通过addRoutes动态添加路由信息
- 按钮权限的控制通常会
实现一个指令
,例如v-permission
,将按钮要求角色通过值传给v-permission指令
,在指令的moutned钩子
中可以判断当前用户角色和按钮是否存在交集
,有则保留按钮,无则移除按钮。
- 纯前端方案的优点是实现简单,不需要额外权限管理页面,但是维护起来问题比较大,有新的页面和角色需求就要修改前端代码重新打包部署;服务端方案就不存在这个问题,通过专门的角色和权限管理页面,配置页面和按钮权限信息到数据库,应用每次登陆时获取的都是最新的路由信息,可谓一劳永逸!
服务端返回的路由信息如何添加到路由器中?
// 前端组件名和组件映射表const map ={//xx: require('@/views/xx.vue').default // 同步的⽅式xx:()=>import('@/views/xx.vue')// 异步的⽅式}// 服务端返回的asyncRoutesconst asyncRoutes =[{path:'/xx',component:'xx',...}]// 遍历asyncRoutes,将component替换为map[component]functionmapComponent(asyncRoutes){
asyncRoutes.forEach(route=>{
route.component = map[route.component];if(route.children){
route.children.map(child=>mapComponent(child))}})}mapComponent(asyncRoutes)
07 - 说一说你对vue响应式理解?
分析
这是一道必问题目,但能回答到位的比较少。如果只是看看一些网文,通常没什么底气,经不住面试官推敲,但像我们这样即看过源码还造过轮子的,回答这个问题就会比较有底气啦。
答题思路:
- 啥是响应式?
- 为什么vue需要响应式?
- 它能给我们带来什么好处?
- vue的响应式是怎么实现的?
- 有哪些优缺点?
- vue3中的响应式的新变化
回答范例:
- 所谓数据响应式就是
能够使数据变化可以被检测并对这种变化做出响应的机制
。 - MVVM框架中要解决的一个核心问题是连接数据层和视图层,通过
数据驱动
应用,数据变化,视图更新,要做到这点的就需要对数据做响应式处理,这样一旦数据发生变化就可以立即做出更新处理。 - 以vue为例说明,通过数据响应式加上虚拟DOM和patch算法,开发人员只需要操作数据,关心业务,完全不用接触繁琐的DOM操作,从而大大提升开发效率,降低开发难度。
- vue2中的数据响应式会根据数据类型来做不同处理,如果是对象则采用
Object.defineProperty()的方式定义数据拦截,当数据被访问或发生变化时,我们感知并作出响应;如果是数组则通过覆盖数组对象原型的7个变更方法
,使这些方法可以额外的做更新通知,从而作出响应。这种机制很好的解决了数据响应化的问题,但在实际使用中也存在一些缺点:比如初始化时的递归遍历会造成性能损失;新增或删除属性时需要用户使用Vue.set/delete
这样特殊的api才能生效;对于es6中新产生的Map、Set这些数据结构不支持等问题。 - 为了解决这些问题,vue3重新编写了这一部分的实现:利用ES6的
Proxy
代理要响应化的数据,它有很多好处,编程体验是一致的,不需要使用特殊api,初始化性能和内存消耗都得到了大幅改善;另外由于响应化的实现代码抽取为独立的reactivity
包,使得我们可以更灵活的使用它,第三方的扩展开发起来更加灵活了。
知其所以然
vue2响应式:
https://github1s.com/vuejs/vue/blob/HEAD/src/core/observer/index.js#L135-L136
vue3响应式:
https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/reactive.ts#L89-L90https://github1s.com/vuejs/core/blob/HEAD/packages/reactivity/src/ref.ts#L67-L68
08 - 说说你对虚拟 DOM 的理解?
分析
现有框架几乎都引入了虚拟 DOM 来对真实 DOM 进行抽象,也就是现在大家所熟知的 VNode 和 VDOM,那么为什么需要引入虚拟DOM 呢?围绕这个疑问来解答即可!
思路
- vdom是什么
- 引入vdom的好处
- vdom如何生成,又如何成为dom
- 在后续的diff中的作用
回答范例
- 虚拟dom顾名思义就是虚拟的dom对象,它本身就是一个
JavaScript
对象,只不过它是通过不同的属性去描述一个视图结构。 - 通过引入vdom我们可以获得如下好处:
将真实元素节点抽象成 VNode,有效减少直接操作 dom 次数,从而提高程序性能方便实现跨平台
- vdom如何生成?在vue中我们常常会为组件编写模板 - template, 这个模板会被编译器 - compiler编译为渲染函数,在接下来的挂载(mount)过程中会调用render函数,返回的对象就是虚拟dom。但它们还不是真正的dom,所以会在后续的patch过程中进一步转化为dom。
- 挂载过程结束后,vue程序进入更新流程。如果某些响应式数据发生变化,将会引起组件重新render,此时就会生成新的vdom,和上一次的渲染结果diff就能得到变化的地方,从而转换为最小量的dom操作,高效更新视图。
9 - 你了解diff算法吗?
分析
必问题目,涉及vue更新原理,比较考查理解深度。
思路
- diff算法是干什么的
- 它的必要性
- 它何时执行
- 具体执行方式
- 拔高:说一下vue3中的优化
回答范例
1.Vue中的diff算法称为patching算法,虚拟DOM要想转化为真实DOM就需要通过patch方法转换。
2.Vue 2.x中为了降低Watcher粒度,每个组件只有一个Watcher与之对应,此时就需要引入patching算法才能精确找到发生变化的地方并高效更新。
3.vue中diff执行的时刻是组件内响应式数据变更触发实例执行其更新函数时,更新函数会再次执行render函数获得最新的虚拟DOM,然后执行patch函数,并传入新旧两次虚拟DOM,通过比对两者找到变化的地方,最后将其转化为对应的DOM操作。
4.patch过程是一个递归过程,遵循深度优先、同层比较的策略;
5.vue2和vue3的虚拟dom的区别和更新如下:
vue2的vdom
vue3的vdom
10 - 你知道哪些vue3新特性
分析
官网列举的最值得注意的新特性:https://v3-migration.vuejs.org/
也就是下面这些:
Composition API
SFC Composition API语法糖
Teleport传送门
Fragments片段
Emits选项
自定义渲染器
SFC CSS变量
Suspense
以上这些是api相关,另外还有很多框架特性也不能落掉。
回答范例
- api层面Vue3新特性主要包括:Composition API、SFC Composition API语法糖、Teleport传送门、Fragments 片段、Emits选项、自定义渲染器、SFC CSS变量、Suspense
- 另外,Vue3.0在框架层面也有很多亮眼的改进:
- 更快 - 虚拟DOM重写- 编译器优化:静态提升、patchFlags、block等- 基于Proxy的响应式系统
- 更小:更好的摇树优化
- 更容易维护:TypeScript + 模块化
- 更容易扩展 - 独立的响应化模块- 自定义渲染器
未完待续,持续更新ing~
2022-8-27
版权归原作者 不停喝水 所有, 如有侵权,请联系我们删除。