Vue面试常考题总结——持续更新
文章目录
-
Vue面试常考题
-
常规篇
-
掌握MVVM模式的核心概念
-
单页面应用(SPA)有哪些优点和缺点?
-
v-show与v-if的主要区别是什么?它们各自适用于何种场景?
-
在使用循环组件时为何避免同时使用v-for和v-if?
-
为什么在v-for中必须指定key属性?
-
为何不建议在组件索引中直接使用index作为key?
-
组件生命周期相关知识有哪些?
-
如何实现组件之间的双向数据绑定?
-
了解computed属性与watch属性的工作原理及其应用场景
-
数据绑定的基本逻辑是什么?为什么data必须是一个函数?
-
如何实现组件间props的有效传递?
-
如何抽取并复用多个组件中的共性逻辑?
-
在什么情况下应该采用异步加载组件的方式?
-
对keep-alive组件的工作原理需有清晰理解
-
需要明确为什么Vue会采用单向数据流而非双向流
-
高级篇
-
- 展述组件渲染及更新机制
-
通过vnode构建DOM架构
-
解析响应式编程基础
-
探讨Data API的核心功能模块
-
分析Vue在数组变化事件处理中的实现细节
-
评估差异算法的性能特征
-
概述差异计算的基本步骤
-
解析Vue异步渲染机制及其时间戳函数的作用
-
A/A测试框架在性能调优中的应用
-
A/A测试框架的设计理念与应用场景
-
VUEX篇
-
- 在哪些场景下适合使用Vuex?
- action与mutation之间的主要区别是什么?
- 在VUEX框架中实现mutation模块是否支持异步操作存在疑问。
- 相较于单纯依赖全局对象的解决方案,在使用VUEX时有何独特优势?
-
路由篇
-
- hash模式和history模式各自的工作原理是什么?两者的区别在哪里?
- Vue-roter的异步加载机制(懒加载)
- 在Vue-router中,导航守卫有哪些功能特点?
Vue面试常考题
常规篇
对MVVM的理解
- MVVM 的数据驱动视图则聚焦于业务逻辑与数据开发,并通过双向绑定机制实现对视图的动态更新。
- Model-View-ViewModel 的缩写体系中:Model 代表数据模型;View 表示 UI 组件;ViewModel 则负责将 Model 与 View 联系在一起。
- 数据会被绑定至 ViewModel 层并在页面渲染时自动展示;当视图发生变更时会触发 ViewModel 更新以反映最新状态。
SPA单页面的优缺点
-
含义
-
SPA(单页应用)只在Web页面初始化时预加载对应的HTML、JavaScript和CSS文件。当Web页面初始化完成后,SPA不会因用户的任何行为而触发页面重新渲染或跳转;相反地,则通过路由重定向实现HTML内容的变化,并通过UI与用户交互以避免因内容变化而导致的页面刷新。
-
核心优势*
-
用户体验优秀且快速响应;内容变更无需重新加载页面以避免不必要的跳转或重复渲染
-
基于这一点;SPA模式减少了服务器端的压力
-
前端负责交互逻辑实现;后端处理数据逻辑清晰
-
主要缺陷
-
初次加载耗时较多的主要原因是技术上的限制:为实现该单页面应用的功能以及展示效果,在初始化页面时必须一次性加载JavaScript、CSS和HTML文件;此外还需要根据具体需求按需加载部分网页。
-
由于无法利用浏览器的前进/后退功能来导航到不同的网页(因为所有内容都集中在一个页面上),因此必须手动管理堆栈以实现路由切换。
-
由于所有内容都集中在一个网页中以动态方式呈现(而非静态构建),因此其在SEO方面存在一定的劣势。
v-show 和 v-if的区别
- 根据条件判断结果决定是否进行组件的渲染;
- 其功能类似于DOM中的显示与隐藏操作;
- 当页面首次加载时会自动执行该组件的显示逻辑;
- 组件不会被销毁也不会被重新渲染;
- 只有当满足特定条件时才会执行相应的初始化操作;
- 否则会导致组件被销毁;
- 下次需要重新呈现该组件时必须进行相应操作;
- 如果场景中频繁需要切换显示状态,则更适合采用v-show组件;相反地,在仅需单次展示的情况下更适合使用v-if。
为什么 v-for 和 v-if 不建议用在一起
每当同一个节点同时包含v-for和v-if时,在优先级上v-for高于v-if这一事实意为着在每个循环中都会执行多个条件判断操作(即每个v-for循环中都会运行一次)。若所处理的数据量极大而实际显示的数据却非常有限,则这会导致明显的性能消耗。
为了应对这种情况,在开发过程中建议采用computed机制,并对数据进行筛选处理以减少不必要的计算
为何在v-for中使用key
- key的核心作用是快速更新虚拟DOM。
- diff算法通过tag和key来判断是否是sameNode。
- 优化渲染效率以减少操作次数并提高整体性能。
- 当Vue采用v-for遍历已显示的组件列表时,默认会使用'原地复用'策略。在这种情况下如果数据项顺序发生变化则系统将保持现有布局而不会重新排列DOM元素以匹配新索引位置而是会简单复制现有元素并在相应索引位置展示已显示的内容。
为什么不建议用index作为key
由于在数组操作中对"index"属性进行无意义的操作并不会带来实质性的功能提升,在任何情况下无论数组顺序如何变化,"index"始终按照0,1,2的顺序排列会导致Vue重复使用旧子节点并产生不必要的工作开销
生命周期
生命周期函数:
在创建之前发生的事情有四个阶段:pre-Creat、pre-Mount、pre-Update和pre-Destroy. 在预热之前发生的事情也有四个阶段:pre-Creat、pre-Mount、pre-Update和post-Destroy. 在初始化之前发生的事情也有四个阶段:post-Creat、post-Mount、post-Update和post-Destroy. 在销毁之前发生的事情也有四个阶段:post-Creat、post-Mount、post-Update和post-Destroy.
what stage can you access the DOM operation: In the mounted stage, Vue will attach the compiled template to the page.
接口请求通常会在哪些生命周期阶段进行处理? 通常可以在created、beforeMount或mounted阶段进行处理。这是因为在这三个钩子函数中已经准备好接收来自服务端返回的数据,并将其赋值给当前组件的数据属性。 但是建议优先选择在created钩子函数处执行异步操作,在此阶段执行异步操作具有以下优势:
- 该方法显著提升了从服务端获取数据的速度,并且降低了页面加载时间(两者效果相近);
- 由于服务器-side rendering (SSR) 不支持 beforeMount 和 mounted 钩子函数,在 mount 端点处设置 hook functions 可有助于保持一致性和稳定性。
描述Vue组件生命周期(父子组件):
页面创建:
- 父组件先创建
- 然后等到子组件创建和挂载完成
- 父组件才挂载完成
数据更新:
父组件随后将更新其数据
当子组件完成全部更新流程后
父组件将在完成所有设置之后执行update操作
何时需要使用beforeDestory:
- 解除self-defined event.$off绑定关系以避免潜在内存泄漏问题
- 清空定时器
- 解除与窗口滚动操作相关的自定义DOM时间绑定
组件通讯(常用)
- 父子组件props/this.$emit
- 自定义事件event.on/event.emit/event.$off
- vuex
双向数据绑定v-model的实现原理
-input元素的value绑定到data中某个属性(例如value = this.name)
- 该组件会被配置为绑定input事件,并将this.name属性设置为$event.target.value。
- 每当数据发生更新时, 系统会重新渲染视图。
omputed和watch,说说他们的使用场景
- computed有缓存,data不变则不会重新计算。
- cpmputed在v-model绑定的时候需要有get(),set()
- watch需要设置deep为true才能深度监听
- watch监听引用类型,拿不到oldVal,因为同一个引用类型指针相同,修改值之后指向新的val,就拿不到旧的val。
- 使用场景
- 当我们需要进行数值计算,并且依赖于其它数据时,应该使用 computed,因为可以利用 computed 的缓存特性,避免每次获取值时,都要重新计算
- 当我们需要在数据变化时执行异步或开销较大的操作时,应该使用 watch,使用watch选项允许我们执行异步操作 ( 访问一个 API ),限制我们执行该操作的频率,并在我们得到最终结果前,设置中间状态。这些都是计算属性无法做到的。
为何data必须是一个函数
- 定义了一个组件构造器(类),它在每个地方使用的时候都会生成一个实例。
- 对象为引用类型的属性在复用组件时会共享同一个data对象。因此,在修改该data时会同步影响到所有其他依赖这个data的对象。
- 使用返回独立的对象函数则避免了这个问题。
如何将组件所有props传递给子组件
- $props
//其实用场景不多
<user :"$prpps"></user>
抽离多个组件中的相同逻辑
- mixin
-
提取公共逻辑以统一管理共性功能
-
mixin并非完美方案会存在一些问题例如:
- 变量来源模糊不够直观不利于阅读体验
- 多个mixin使用可能导致名称冲突影响可维护性
- mixins与组件之间可能出现复杂的关联关系提升系统复杂度
-
Vue3提出的composition API 旨在解决这些问题
-
何时使用异步加载组件
- 加载大组件的时候
- 路由异步加载
- Vue常见性能优化之一
对keep-alive组件的了解
- 缓存相关的功能模块
- 频繁切换静态文件时无需重复渲染
- Vue中常见的性能优化手段之一
- keep-alive是Vue内置的一个核心功能模块,能够使嵌套在其中的子模块维持其状态信息,从而避免了重新渲染的问题,其主要特性包括:
- 通常与路由和动态属性组合使用,主要用于实现状态缓存
- 支持include和exclude属性,两者都支持字符串或正则表达式.其中include表示只有名称匹配的模块会被缓存,而exclude表示任何名称匹配的模块都不会被缓存.需要注意的是排除机制会在包含机制之上
- 配备activated和deactivated两个钩子函数,当某个模块被激活时会触发activated钩子函数,而当该模块从 DOM 中移除时会触发deactivated钩子函数。
//include()包含的组件缓存);exclude(排除的组件不缓存)
<keep-alive include='include_components' exclude='exclude_components'>
<component>
<!-- 该组件是否缓存取决于include和exclude属性 -->
</component>
</keep-alive>
子组件为什么无法更改其父组件传递过来的Prop?/如何解析Vue单向数据传输机制
- Vue采用了单向数据传输机制,在这种机制下,父组件的属性更新会传递到子组件,但逆过程无法实现。
- 为了避免意外的修改父组件的状态,从而使得整个应用的数据流动无法被直观追踪。
- 如果违背了这一原则,在系统架构变得较为复杂的场景下,digging(调试)的成本将大幅上升。
高级篇
描述组件渲染和更新的过程
-
初始渲染流程
-
解析模板为 render 模板(在开发环境中完成)[注:使用 Vue-Loader]
-
响应式地监听 data 属性的 getter 和 setter 方法
-
执行 render 函数以生成 vnode 对象并执行 patch 操作于 elem 对象
-
更新流程
-
数据被修改时会触发setter(此前已由gettr捕获)
-
重新运行渲染函数以生成新的Vnode
-
path(vnode, newVnode)
- 非阻塞渲染
- $nextTick
- 整合data的修改为单次批量处理视图
- 优化DOM操作频率以提升性能
用vnode描述一个DOM结构
<div id='div1' class='container'>
<p>vdom</p>
<ul style='font-size: 20px'>
<li>a</li>
</ul>
</div>
//转为vnode结构:
<script>
let vnode = {
tag: 'div',
props: {
className: 'container',
id: 'div1'
},
children: [
{
tag: 'p'
children: 'vdom'
},
{
tag: 'ul',
props: {style: 'font-size: 20px'},
children: [
{
tag: 'li',
children: 'a'
}
]
}
]
}
</script>
描述响应式原理
检测数据的变化情况;收集各视图所需的原始数据信息;当发生数据显示变更时;自动触发相关视图的数据同步流程,并执行相应的更新操作。三个步骤对应的专业术语分别为:数据库复制 / 数据复制机制(DB replication);依赖解析与收集(dependency analysis and collection);发布订阅模型(pub-sub model)。
监听data的核心API是什么
- Object.defineProperty
- 缺点
- 在进行深度监听时需层层递进直至底部层次;每次操作都会带来较大的计算负担。
- 不具备内置的支持来直接追踪新增或删除的属性值;必须借助
Vue.set或Vue.delete方法才能实现。 - 不具备内置的支持来直接监控数组的变化;在实现时需采取额外的技术手段。
vue如何监听数组变化
Define the prototype using Object.defineProperty to dynamically bind and enable event sourcing functionality, and override common methods such as push and pop to support this binding relationship. Proxies inherently support event sourcing for dynamic properties of arrays.
diff算法的时间复杂度
计算数diff的时间复杂度为O(n^3), 其原因是由于需要先遍历两棵树, 接着还需要进行排序操作. 当节点数量达到1000时, 则需要进行约一亿次计算量, 这样的算法在实际应用中并不实用. 因此众多技术专家将VDM的差异计算算法优化至线性时间复杂度O(n).
vdom的diff算法优化方案:
- 仅限于在同一层级进行比较
- 当tag不同时,则直接删除并重建而不进行深入对比
- 若两者具有相同的tag和key,则视为同一个节点而不进行进一步对比
简述diff算法过程
- 修改elem至指定的vnode,并获取该路径。
- 在指定路径中新增节点到当前列表,并删除多余节点。
- 更新children节点的重要性和相关性考量。
Vue为何是异步渲染,$nextTick的用处
- 异步渲染提高渲染性能
- $nextTick在DOM更新完之后,触发回调
Vue常见性能优化
以下是基于给定规则对原文的改写结果
对SSR的了解
SSR即Service-侧渲染,在前端框架如Vue中实现的方式是将前端视图层的组件标签进行逻辑层面的转译生成对应的DOM结构,并将其传递至后端处理以完成显示效果。随后会将生成的DOM结构以DOM节点形式反馈至前端展示区域以供用户体验。
-
优势:
- 更好的SEO;
- 首屏加载速度更快
-
缺点如下:
-
开发环境受到限制,在服务端渲染中仅支持beforeCreate和created两个钩子作为可执行 hook
-
在引入外部扩展库时需要采取特别措施,并且要求服务端渲染的应用程序运行于Node.js环境中
-
增加更多的服务端负载压力
VUEX篇
什么情况下用Vuex
如果项目很简单,则无需使用Vuex框架;采用一个简单的Store方案即可。
在构建中型以上单页应用时,则能够更加有效地实现组件外部的状态管理。
action和mutation的区别
- 在action中可以支持异步操作,在mutation中则无法执行异步操作。
- mutation每次只能执行一个操作。
- action支持整合多个不同的mutation。
为什么 Vuex 的 mutation 中不能做异步操作
每次mutation执行完成后都会对应生成一个新的状态变更。因此,在这种情况下, devtools就能够捕获当前的状态并存储下来,从而完成time-travel的功能。然而, 如果一个mutation支持异步操作,则会失去同步更新的能力,就无法确定状态是在哪个时间点被更新的,从而给调试带来诸多不便。
Vuex和单纯的全局对象有什么区别
VUE采用响应式存储机制。当Vue组件从Store获取状态时,若Store的状态发生变更,则组件将随之实现高效的更新机制。无法直接修改Store中的状态信息。要修改Store中的状态信息,则必须通过显式提交Mutation操作。这种方法不仅简化了对Store状态的监控流程,并且还提供了实用的工具辅助我们更深入地分析和优化我们的应用系统。
路由篇
hash模式和history模式实现原理分别是什么,他们的区别是什么
-
hash默认值
- 表示为#符号
- 虽然在URL编码中用于标识哈希值的起始位置,并且通常用
$标签来获取哈希地址。 - 虽然在URL编码中用于标识哈希值的起始位置,并且通常用
$标签来获取哈希地址。 - 在这种模式下,只有请求头中的哈希相关参数会被包含到HTTP请求体里。
- 因此对于后端处理逻辑来说,
-
H5的历史记录(需依赖服务端支持)
-
主要采用HTML5标准发布的pushState和replaceState两个API来实现历史记录功能。这些API能够更新URL而不必发起HTTP请求;通过监听URL变化可实时更新页面内容。
-
在历史模式下,默认情况下前端应用必须与实际向后端发送的URL保持一致
-
主要体现在两者的展示方式上有明显差异。
-
在展示过程中,在url路径上采用#号作为分隔符的方式(即hash模式)会包含#号标识符。
-
刷新页面时,在技术层面实现方面存在显著差异:具体来说,在采用hash模式时系统会正常加载到对应的具体页面;而在history未做特殊处理的情况下,则会导致访问失败(返回404错误),因此建议在后端统一将所有相关资源进行重定向配置以确保访问路径的一致性。
-
兼容性方面需要注意的是,在某些旧版本浏览器或者特定条件下(如IE浏览器),该技术仍能提供良好的用户体验。
vue-roter异步加载(懒加载)
{
path: '/',
component: () => import('./app.vue')
}
Vue-router 导航守卫有哪些
-
全局前置/钩子:
- beforeEach
- beforeResolve
- afterEach
-
负责处理路由独享事务的核心组件:beforeEnter *
-
内部组件中的守护者:
- 在进入路径前进行检查:beforeRouteEnter
- 在处理路径更新前进行验证:beforeRouteUpdate
- 在离开路径时进行校验:beforeRouteLeave
