Vue面试题82题
1、vue开发中常用的指令有哪些?
-
条件渲染:v-if、 v-else、 v-else-if、 v-show(注意,v-show 不支持 <template> 元素,也不支持 v-else)
-
列表渲染:v-for
-
监听事件:v-on
-
表单输入绑定:v-model
-
其他:v-bind、v-html、v-text
v-if vs v-show: 以下是vue官方文档的描述。#17
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 相比之下,v-show 就简单得多——不管初始条件是什么,元素总是会被渲染,并且只是简单地基于 CSS 进行切换。 一般来说,v-if 有更高的切换开销,而 v-show 有更高的初始渲染开销。因此,如果需要非常频繁地切换,则使用 v-show 较好;如果在运行时条件很少改变,则使用 v-if 较好。
- 手段:v-if是动态的向DOM树内添加或者删除DOM元素;v-show是通过设置DOM元素的display样式属性控制显隐;
- 编译过程:v-if切换有一个局部编译/卸载的过程,切换过程中合适地销毁和重建内部的事件监听和子组件;v-show只是简单的基于css切换;
- 编译条件:v-if是惰性的,如果初始条件为假,则什么也不做;只有在条件第一次变为真时才开始局部编译; v-show是在任何条件下,无论首次条件是否为真,都被编译,然后被缓存,而且DOM元素保留;
- 性能消耗:v-if有更高的切换消耗;v-show有更高的初始渲染消耗;
- 使用场景:v-if适合运营条件不大可能改变;v-show适合频繁切换。
2、vuediff算法的原理
https://juejin.cn/post/7010594233253888013 https://juejin.cn/post/7045976871116210213
点题收敛
Vue的Virtual DOM实现了一种高效的diff算法,能够快速比较两个虚拟DOM树的差异,然后只更新有必要更新的部分,从而提高渲染性能。
原理大概总结
dff算法的实现原理是从根节点开始逐层遍历新旧两个虚拟DOM树,比较节点的类型、属性和子节点等内容,如果节点有差异,则记录该差异,并将其添加到一个差异队列中。在比较完成后,对于存在差异的节点,根据其类型进行相应的更新操作,比如替换节点、修改属性、移动节点位置等等。
需要注意的是,为了提高diff算法的性能,现代前端框架往往采用一些优化技巧,比如只比较同层级的节点、使用key值进行优化等,从而进一步提高渲染性能。
Diff 算法的优化
1. 「分层diff」 只比较同一层级,不跨级比较
2. 「组件的Type(Tagname)不一致」 比较标签名
3. 「列表组件的Key不一致」 比较 key
key的作用
在Vue的虚拟DOM算法中,key的作用主要有两个:
- 优化组件的渲染,减少DOM操作
当列表中的数据发生变化时,Vue会使用diff算法来比较旧的虚拟DOM树和新的虚拟DOM 树,找出需要更新的节点,然后进行DOM操作。如果列表中每一项都没有key,那么默认情况下使用节点的索引作为key,这样做会导致在对列表中的数据进行删除、添加等操作时,所有后面的节点都需要重新创建和渲染,这样会导致性能上的浪费。
当我们使用key时,Vue会通过比较新旧节点的key值来判断哪些节点需要更新,哪些节点需要删除和添加。这样可以大大减少DOM操作的次数,提高页面的性能。
- 标识每个节点的唯一性,提高虚拟DOM算法的效率
当有节点需要被移动时,Vue会根据节点的key值来判断是否是同一个节点。如果节点没有key,那么Vue会根据节点的标签名称、属性和子节点来判断是否为同一个节点,这样会导致效率上的低下。
使用key可以唯一标识每个节点,提高虚拟DOM算法的效率,从而减少页面的渲染时间。
export function isSameVNodeType(n1: VNode, n2: VNode): boolean {
// 比较类型和key是否一致()
return n1.type === n2.type && n1.key === n2.key
}
详细回答对比过程
Vue的diff算法具体实现如下:
首先比较新旧节点的标签名,如果不同,直接替换成新节点。如果标签名相同,则比较节点的属性和事件,如果不同,直接修改成新节点。
如果节点相同,则比较节点的子节点,如果子节点有不同,则继续比较子节点,直到所有子节点比较完成。
如果子节点也相同,则不需要更新,直接退出比较。
追问继续答,不追问就不用答了 --- 这是vue2的
在进行比较时,Vue.js使用了两个指针来遍历新旧节点树,分别为新节点的开始指针和旧节点的开始指针,分别向右移动比较。如果新节点的开始指针在旧节点的开始指针的左侧,则新节点需要插入到旧节点的前面。如果新节点的开始指针在旧节点的开始指针的右侧,则说明旧节点需要删除。如果两个指针指向的节点相同,则说明节点不需要更新,直接跳过比较。在比较完成后,Vue.js会生成一份差异表,记录哪些节点需要更新、删除、插入等操作 ,然后根据差异表进行DOM更新。
- 新的头和老的头对比
- 新的尾和老的尾对比
- 新的头和老的尾对比
- 新的尾和老的头对比
vue3---双端对比算法以及静态标记
- 头和头比
- 尾和尾比
- 基于最长递增子序列进行移动/添加/删除
看个例子,比如
- 老的 children:[ a, b, c, d, e, f, g ]
- 新的 children:[ a, b, f, c, d, e, h, g ]
- 先进行头和头比,发现不同就结束循环,得到 [ a, b ]
- 再进行尾和尾比,发现不同就结束循环,得到 [ g ]
- 再保存没有比较过的节点 [ f, c, d, e, h ],并通过 newIndexToOldIndexMap 拿到在数组里对应的下标,生成数组 [ 5, 2, 3, 4, -1 ],-1 是老数组里没有的就说明是新增
- 然后再拿取出数组里的最长递增子序列,也就是 [ 2, 3, 4 ] 对应的节点 [ c, d, e ]
- 然后只需要把其他剩余的节点,基于 [ c, d, e ] 的位置进行移动/新增/删除就可以了
使用最长递增子序列可以最大程度的减少 DOM 的移动,达到最少的 DOM 操作
静态标记
Vue 3对静态标记做了重大升级,使用基于ES6 Proxy 的新API来实现静态标记,以提高性能和可维护性。以下是Vue 3和Vue 2静态标记之间的主要区别
- 新的模板编译器:Vue 3中的模板编译器支持将所有静态节点提取到一个单独的渲染函数中,这样可以避免在每次渲染中重复读取静态节点的开销。
- 静态提升:Vue 3中新的编译器还提供了一种静态提升技术,可以自动将动态节点转换为静态节点。静态提升意味着只要生成渲染函数一次,就可以在整个组件的生命周期内重复使用该渲染函数,从而显著减少渲染时间和内存占用。
- 固化:Vue 3中的静态标记被称为“固化”,因为它使用ES6 Proxy是实现,而不是Vue 2中使用的Object.defineProperty。这种技术可以避免由于属性添加和删除而造成的运行时性能问题,而且因为ES6 Proxy是全新编写的,所以它可以避免一些Vue 2中静态标记的限制。
3、vue mixin解决了什么问题,原理以及缺点?
1. mixin和 mixins 区别
/* minxin 用于全局混入,会影响到每个组件实例,通常插件都是这样做初始化的
* mixin 使我们能够为 Vue 组件编写可插拔和可重用的功能
* 如果你要在 mixin 中定义生命周期 hook,那么它在执行时将优化于组件自已的 hook
*
*/
Vue.mixin({
beforeCreate() {
// ...逻辑
// 这种方式会影响到每个组件的 beforeCreate 钩子函数
}
})
// mixins
/* 是一种分发 Vue 组件中可复用功能的非常灵活的方式
* mixins 是一个 js 对象
* 它可以包含我们组件中 script 项中的任意功能选项
* mixins 混入的钩子函数会先于组件内的钩子函数执行,并且在遇到同名选项的时候也会有选择性的进行合并。
*/
import { myMixins } from "@/mixins/myMixins.js";
export default {
mixins: [myMixins],
/* mixins: {
components:{},
data() {
return {}
},
created() {
console.log('xxx from mixins')
}
} */
}
2. 简述 mixin、extends 的覆盖逻辑
(1)mixin 和 extends
**相同点:**mixin 和 extends均是用于合并、拓展组件的,两者均通过 mergeOptions 方法实现合并。
不同点:
a. mixins 接收一个混入对象的数组,其中混入对 象可以像正常的实例对象一样包含实例选项,这些选项会被合并到最终的选项中。Mixin 钩子按照传入顺序依次调用,并在调用组件自身的钩子之前被调用。
b. extends 类似于mixin,相当于继承,但是只是继承 options Api 中的内容,不继承 template 模板。主要是为了便于扩展单文件组件,接收一个对象或构造函数。

(2)mergeOptions 的执行过程
该方法的作用是合并 options,options 除了存在于构造函数中,我们在 new Vue() 时传递的对象、Vue.mixin() 传递的对象、Vue.extend() 传递的对象也都是 options。
- 规范化选项(normalizeProps、normalizelnject、normalizeDirectives)
- 对未合并的选项,进行判断
if(!child._base) {
if(child.extends) {
parent = mergeOptions(parent, child.extends, vm)
}
if(child.mixins) {
for(let i = 0, l = child.mixins.length; i < l; i++){
parent = mergeOptions(parent, child.mixins[i], vm)
}
}
}
- 合并处理:根据一个通用 Vue 实例所包含的选项进行分类逐一判断合并,如 props、data、 methods、watch、computed、生命周期等,将合并结果存储在新定义的 options 对象里
- 返回合并结果 options
缺点:
- 命名冲突:使用 mixin 时,可能会出现命名冲突,特别是当不同 mixin 中具有相同名称的数据、方法或计算属性时。这可能导致意外的行为和难以调试的问题。
- 耦合性增加:使用 mixin 可能会增加组件之间的耦合性,因为它们引入了共享的逻辑和状态。这使得组件的依赖关系变得更加复杂,并且难以跟踪和理解组件之间的关系。
- 难以维护:随着项目的增长和需求的变化,mixin 可能会变得难以维护。当多个组件依赖相同的 mixin 时,对 mixin 的修改可能会对整个应用程序产生意想不到的影响,导致维护困难。
- 命名空间污染:使用 mixin 可能会导致全局作用域的污染,因为 mixin 中的数据和方法会被混入到组件中,使得全局命名空间变得混乱,增加了命名冲突的可能性。
4、vue3有哪些改变?
Composition API、 TypeScript 支持、响应式 API、Async 异步组件、Teleport、Proxy-Based Reactivity System
-
响应式系统改变:Vue2采用Object.defineProperty实现响应式系统,Vue3使用了更高效的Proxy代理对象实现响应式系统,提高了性能和稳定性。
-
数据改变检测方式改变:Vue2采用递归的方式进行数据改变检测,Vue3使用了基于Proxy的观测机制和内部追踪之间的关系,有效解决了Vue2数据检测的性能瓶颈。
-
检测属性的添加和删除;
-
检测数组索引和长度的变更;
-
支持 Map、Set、WeakMap 和 WeakSet。
-
-
生命周期改变:Vue3废除了Vue2中的beforeDestroy和Destroyed钩子函数,并新 增了两个钩子函数:beforeUnmount 和unmounted;此外,activated和deactivated这两个钩子在Vue3中被废弃,使用setup()返回的对象的onActivated和onDeactivated属性来替代。。
-
异步组件改变:Vue3使用全局函数defineAsyncComponent来定义异步组件,从而方便了异步组件的使用。
-
Teleport改变:Vue3在Teleport中添加了两个插槽,分别是空插槽和to插槽,用于在传输过程中处理内容的变化。
-
编译器改变:Vue3的编译器已被拆分为单独的包(@vue/compiler-sfc),可以进行独立地安装和使用
-
更好的TypeScript支持:Vue3对TypeScript的支持更加友好,引入了完全支持类型推断的API和更好的类型定义。
-
Composition API改变:Vue3引入了Composition API,可以更好地组织和管理组件中的代码,提高开发效率。
5、说一下generator的原理
https://juejin.cn/post/7111347194904444958 Generator是ES6引入的一种新的函数类型,它可以让函数在执行时暂停,后续又可在需要时恢复执行。Generator是一种特殊的迭代器,用于生成一系列的值。 Generator的原理可以分为以下几个步骤:
- 当调用一个Generator函数时,它并不会立即执行,而是返回一个迭代器对象(Iterator)。
- 当不断调用迭代器的next()方法时,Generator函数内部的代码会逐行执行,直到执行到第一个yield关键字时,代码会暂停,并将yield后面的表达式的值作为Generator函数返回对象的value属性值返回,此时yield表达式本身并没有执行。
- 在下一次调用next()方法时,由于上一次暂停时保存的上下文(Context)信息仍然存在,所以Generator函数内部的代码会从上一次暂停的地方继续执行,直到再次执行到yield关键字,代码会再次暂停,并将最新的yield后面的表达式的值作为Generator函数返回对象的value属性值返回。
- 通过对迭代器的不断调用next()方法,可以一步步地取出所有Generator函数中yield关键字后的表达式的值,直到函数执行结束,Generator函数返回的迭代器的done属性值变为true。
需要注意的是,Generator函数可以通过yield关键字返回任意次数的值,此外,Generator函数内部任意一处抛出异常都会导致迭代器的done属性变为true,并且抛出的异常会在外部代码中被捕获。除此之外,Generator函数还可以通过yield*关键字委托给其他Generator函数,从而实现协程的功能
只记下面的 当调用一个 Generator 函数时,它返回一个迭代器 (iterator),并不会立即执行
function* generatorFn() {
// 代码1
yield 'Iteration 1' // 生成第1个值
// 代码2
yield 'Iteration 2' // 生成第2个值
// 代码3
yield 'Iteration 3' // 生成第3个值
// 代码4
return 'Completed' // 返回结果
}
const iterator = generatorFn()
console.log(iterator) // -> Generator {}
// 调用迭代器的 next 方法时,Generator 内部的代码会逐行执行,直到执行到第一个 yield 关键字时
// 此时代码会暂停,并将 yield 关键字后面的表达式的值作为 Generator 函数返回对象的 value 属性值返回
console.log(iterator.next()) // -> { value: 'Iteration 1', done: false }
// 通过对迭代器不断调用 next 方法,可以依次取出所有 Generator 函数中的值
console.log(iterator.next()) // -> { value: 'Iteration 2', done: false }
console.log(iterator.next()) // -> { value: 'Iteration 3', done: false }
console.log(iterator.next()) // -> { value: 'Completed', done: true }
console.log(iterator.next()) // -> { value: undefined, done: true }
当 Generator 函数内部执行到 yield 语句时,函数执行的状态会保存下来,并在 yield 语句后返回一个对象,其中 value 属性就是 yield 后面表达式的值
在下一次调用 next() 方法时,Generator 函数从上一次暂停的地方继续执行,直到再次执行到 yield 语句,代码会再次暂停,并将最新的 yield 后面的表达式的值作为 Generator 函数返回对象的 value 属性值返回:
console.log(iterator.next()) // -> { value: 'Iteration 1', done: false }
console.log(iterator.next()) // -> { value: 'Iteration 2', done: false }
console.log(iterator.next()) // -> { value: 'Iteration 3', done: false }
console.log(iterator.next()) // -> { value: 'Completed', done: true }
通过对迭代器的不断调用 next() 方法,可以一步步地取出所有 Generator 函数中 yield 后面的表达式的值,直到函数执行结束,Generator 函数返回的迭代器的 done 属性值变为 true。
6. 谈谈你对vue的理解
点题收敛
Vue是一个渐进式JavaScript框架,它专注于构建用户界面:Vue的核心思想是数据驱动和组件化。通过将页面拆分成独立的组件,可以更好地管理代码,提高代码的复用性和可维护性。
追问拓展
Vue的优势在于其简单易用、灵活性高、性能卓越和扩展性强。Vue的模板语法易于理解和学习,可以快速构建交互式的Web应用程序。同时,Vue的生命周期钩子和自定义指令等功能,使得Vue可以满足各种复杂的需求。另外,Vue还提供了vuex、VueRouter等官方插件,可以进一步扩展Vue的功能。
Vue的响应式数据绑定机制是Vue最核心的特性之一。通过对数据进行劫持和监听,可以实现数据的双向绑定,即数据变化会自动更新视图,同时视图的变化也会反映到数据上。这种机制使得u的数据流非常清晰和可预测,同时也减少了开发的工作量。
总之,我认为Vue是一个优秀的JavaScript框架,它简单易用、功能强大、扩展性好,并且有着极佳的性能表现。对于前端开发人员来说,Vue是一个值得深入学习和使用的框架。
Vue.js 是一款渐进式、轻量级的前端框架,它的核心思想是【数据驱动视图】。它通过数据劫持的方式实现了响应式编程,具有高度灵活性和可组合性。它采用了虚拟DOM和异步渲染等技术,使得应用程序在性能、可维护性和开发效率方面都得到了很大的实际提升。
Vue.js 最大的特点是简洁易学、易于上手。其组件化的设计使得页面整体的结构更加清晰明了,配合指令和事件可以大大简化前端开发的复杂性。在模板语法、组件化、数据双向绑定、计算属性、虚拟DOM、自定 义指令、过滤器等方面,Vue.js都提供了方便的API支持,并且与其他主流前端库和框架兼容性良好。
此外,Vue.js在可维护性和易用性方面也非常强,有良好的文档和社区支持,为开发人员提供了强有力的工具支持,同时还提供了插件机制和可扩展性的API,以满足不同开发场景下的需求。
综上,Vue.js是一款非常优秀的前端框架,具有易学易用、高效灵活、易于维护扩展等优点,被广泛应用于实际的前端开发中,Vue是一个值得深入学习和使用的框架。
7. 双向数据绑定的原理
点题收敛
vue的数据绑定机制是通过数据劫持和发布/订阅模式实现的。当数据发生变化时,会自动更新视图,并通过虚拟DOM对比算法来提高性能。这个机制可以有效地简化开发过程,提高代码的可维护性和可读性。
如果追问深入来说

在Vue中,每个组件实例都有一个对应的响应式数据对象。当数据发生变化时,会自动更新视图。这个响应式数据对象通过数据劫持的方式实现,即通过Object.defineProperty方法来劫持数据的getter和setter方法,当数据被读取或修改时,就会触发getter或setter方法,从而实现数据的监控和响应。
在Vue中,每个组件实例都有一个对应的Watcher对象Watcher对象订阅响应式数据对象的变化。当响应式数据对象发生变化时,Watcher>