Vue面试题
本文最后更新于 2025年10月31日 中午
React 和 Vue 的 diff 比较
🎯 React 的 Diff 算法
- 思想:提出了一套通用的“调和(Reconciliation)”算法。
- 规则:
- 同级比较:只比较同层节点,不跨层。
- 不同类型 → 直接替换:比如
<div>→<span>,直接卸载重建。 - 同类型 → 递归比较子节点。
- 列表 diff:通过 key 来判断节点是否复用,否则可能导致整个列表重建。
- 特点:借助 Fiber 架构,更新过程可被中断和恢复,更适合大规模应用的性能优化。
👉 React diff 更强调 通用性,需要开发者手动优化(比如 key 的使用)。
🎯 Vue 的 Diff 算法
- 思想:Vue 的 diff 更“收敛”,基于双端比较策略,主要用于模板生成的虚拟 DOM 更新。
- 规则:
- 同样是 同层比较,不跨层。
- 子节点比较时,Vue 使用 双指针法(从头尾往中间对比),尽量复用节点,减少 DOM 操作。
- 处理列表时,同样依赖 key,但 Vue 内部会尽量移动/复用节点,减少销毁重建。
- 特点:因为 Vue 模板更可控,它的 diff 算法做了更多的优化假设,性能上通常比 React 更高效。
👉 Vue diff 更强调 工程化的优化,利用模板编译结果做静态标记(Vue3 里尤其明显)。
总结
React 和 Vue 都是基于虚拟 DOM 的 diff,但思路有区别:
React 的 diff 更通用,基于 Fiber 架构,同层比较,如果节点类型不同就直接替换,同类型则递归对子节点 diff,列表更新依赖 key。它强调灵活性,但需要开发者注意优化。
Vue 的 diff 更工程化,基于双端比较算法,能更高效地移动和复用节点;另外 Vue 编译阶段会做静态标记,比如 Vue3 的静态提升,可以跳过不变的部分,从而减少 diff 的开销。
总体来说,React 更通用,Vue 更收敛和高效。
Vue3 对比 Vue2
Vue2 的响应式是基于 Object.defineProperty,而 Vue3 使用 Proxy 实现,能够更好地处理新增属性和深层对象。
在编程方式上,Vue2 主要是 Options API,而 Vue3 引入了 Composition API,逻辑更清晰,也更方便复用。
此外,Vue3 的性能有提升,打包体积更小,对 TypeScript 的支持也更好。
虽然我还没有完整的 Vue3 项目经验,但我平时会关注 Vue3 的特性,并且相信凭借 Vue2 的经验,可以很快上手 Vue3。
其它参考:https://juejin.cn/post/6892295955844956167
Vue 优点
- 轻量级框架。只关注视图层,体积小。
- 组件化。把单页应用中的模块拆分为一个个单独组件,清晰明了。
- 响应式数据绑定。自动对页面某些数据的变化做出同步响应。
- 使用虚拟 DOM 算法。使用虚拟 DOM 配合 diff 算法,提高性能。
为什么 data 必须是函数
对象为引用类型,若 data 是对象,当复用组件时,同样的组件的 data 都指向同一个数据对象,使用相同的内存地址,导致数据混用:当在一个组件中修改 data 时,其他同样的组件的 data 会同时被修改;若 data 是函数,当复用组件时,都会调用函数,返回是一个新对象,使用不同的内存地址,数据不会混用。
Vue 组件通信
参考:https://juejin.cn/post/6999687348120190983
- 父组件向子组件传递数据通过 props 传递
- 子组件向父组件传递数据通过 $emit 触发事件
- 获取当前组件的父组件 $parent、根组件 $root(不推荐)
- 获取当前组件的子组件 $children、组件实例 $refs (不推荐)
- bus 事件总线
- Vuex 状态管理
Vue2.x 组件通信共有 12 种:
- props
- $refs
- $root
- $emit / v-on
- $children / $parent
- $attrs / $listeners
- provide / inject
- .sync
- v-model
- EventBus
- Vuex
- slot
父子组件通信可以用:
- props
- $refs
- $root
- $emit / v-on
- $children / $parent
- $attrs / $listeners
- provide / inject
- .sync
- v-model
- EventBus
- Vuex
- slot
兄弟组件通信可以用:
- $parent
- EventBus
- Vuex
跨层级组件通信可以用:
- $root
- $attrs / $listeners
- provide / inject
- EventBus
- Vuex
v-if 和 v-show
v-if 确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建,首次渲染时,v-if 条件为真才会在 DOM 树上,v-if 条件为假则什么也不做,不会在 DOM 树上,直到条件变为真才会在 DOM 树上;对应,v-show 无论首次渲染时是否真假都会在 DOM 树上,如果条件不为真,其对应 CSS 为dispaly: none。
因此,操作元素时,v-if 更消耗性能,因为 v-if 在使用过程中有 DOM 的添加和删除,v-show 在使用过程中只是操作 CSS。v-if 适用于在运行时很少改变条件、不会频繁切换条件的场景。v-show 适用于在运行时经常改变条件、需要频繁切换条件的场景。
computed 和 watch
计算属性 computed
- 支持缓存,依赖其他属性值,只有其依赖值的数据发生改变,才会触发相应的操作;
- 不支持异步;
- computed 属性值会默认走缓存,计算属性是基于它们的响应式依赖进行缓存的,也就是基于 data 中声明过或者父组件传递的 props 中的数据通过计算得到的值;
- 如果一个属性是由其他属性计算而来的,这个属性依赖其他属性,是一个多对一或者一对一,一般用 computed;
- 如果 computed 属性属性值是函数,那么默认会走 get 方法;函数的返回值就是属性的属性值;在 computed 中的,属性都有一个 get 和一个 set 方法,当数据变化时,调用 set 方法;
- 适用于在模板渲染中,某个值是依赖了其它的响应式对象甚至是计算属性计算而来。
侦听属性 watch
- 不支持缓存,监听到值的变化,就会直接触发相应的操作;
- 支持异步;
- 监听的函数接收两个参数,第一个参数是最新的值;第二个参数是输入之前的值;
- 当一个属性发生变化时,需要执行对应的操作,一对多;
- 监听数据必须是 data 中声明过或者父组件传递过来的 props 中的数据,当数据变化时,触发其他操作,函数有两个参数;
- 适用于观测某个值的变化去完成一段复杂的业务逻辑。
Vue 指令
- v-text
- v-html
- v-show
- v-if
- v-else
- v-else-if
- v-for
- v-on
- v-bind
- v-model
- v-slot
- v-pre
- v-cloak
- v-once
使用 v-for 时 key 的作用
当 Vue 用 - v-for 正在更新已渲染过的元素列表时使用“就地复用”策略。如果数据项的顺序被改变,Vue 将不是移动 DOM 元素来匹配数据项的改变,而是简单复用此处每个元素,并且确保它在特定索引下显示已被渲染过的每个元素。
key 属性用于方便 Vue 跟踪每个节点的身份,从而重用和重新排序现有元素,需要为每项提供一个唯一 key 属性。key 属性只能为 Strig 或 Number 类型。如果不使用 key,Vue 会使用一种最大限度减少动态元素并且尽可能的尝试修复/再利用相同类型元素的算法。使用 key,Vue 会基于 key 的变化重新排列元素顺序,并且会移除 key 不存在的元素。
$nextTick 使用场景和原理
Vue 实现响应式并不是数据发生变化后 DOM 立即变化,而是按照一定的策略来进行 DOM 更新。$nextTick 是在下次 DOM 更新循环结束之后执行延迟回调,在修改数据之后使用 $nextTick,则可以在回调中获取更新后的 DOM。跟全局方法 Vue.nextTick 一样,不同的是回调的 this 自动绑定到调用它的实例上。
Vue 数据双向绑定原理
参考:https://juejin.cn/post/6844903903822086151
Demo:https://github.com/xuekeven/js-learn_vue2/tree/main/15.vue原理高级教程/响应性/4.mini-observer.html
1、实现一个监听器 Observer ,用来劫持并监听所有属性,如果属性发生变化,就通知订阅者;
2、实现一个订阅器 Dep,用来收集订阅者,对监听器 Observer 和 订阅者 Watcher 进行统一管理;
3、实现一个订阅者 Watcher,可以收到属性的变化通知并执行相应的方法,从而更新视图;
4、实现一个解析器 Compile,可以解析每个节点的相关指令,对模板数据和订阅器进行初始化。