Vue 面试真题汇总
Vue 组件初始化的各个阶段都做了什么?
Section titled “Vue 组件初始化的各个阶段都做了什么?”Vue 组件初始化主要分为创建阶段和挂载阶段。
创建阶段包括 beforeCreate(初始化组件实例基本属性、事件系统,此时无法访问 data、methods 等)和 created(完成数据观测、响应式系统建立、props 初始化,可访问组件数据但无法访问 DOM)。
挂载阶段包括 beforeMount(模板编译完成,生成虚拟 DOM,但真实 DOM 未创建)和 mounted(组件挂载到 DOM 完成,可访问真实 DOM 元素和子组件实例)。渲染函数首次调用发生在 beforeMount 和 mounted 之间,此时会触发响应式变量的依赖收集,建立组件与响应式数据的依赖关系。
在 Vue3 组合式 API 中,setup 函数在所有生命周期钩子之前执行,响应式数据在 setup 中定义,但依赖收集要等到渲染函数执行时才开始。
说说Vue2和Vue3的区别
Section titled “说说Vue2和Vue3的区别”Vue3相比Vue2有以下主要区别:
响应式系统:Vue2使用Object.defineProperty实现响应式,只能监听对象属性的变化,对数组和新增属性支持不好;Vue3使用Proxy实现,可以监听整个对象的所有操作,包括属性的增删改查。
组合式API:Vue2主要使用选项式API(data、methods、computed等),代码按选项类型分散;Vue3引入组合式API(setup函数),相关逻辑可以组织在一起,提高代码复用性和可维护性。
性能优化:Vue3在编译时进行了大量优化,如静态提升、补丁标记、树摇优化等,运行时性能比Vue2提升1.3-2倍,包体积减少约41%。
TypeScript支持:Vue3从底层用TypeScript重写,提供了更好的类型推导和类型安全,开发体验更佳。
生命周期变化:Vue3在组合式API中使用onMounted、onUpdated等钩子函数,替代了Vue2的mounted、updated等选项。
| 对比维度 | Vue2 | Vue3 |
|---|---|---|
| 响应式原理 | Object.defineProperty | Proxy |
| API风格 | 选项式API | 组合式API + 选项式API |
| TypeScript支持 | 需要额外配置 | 原生支持 |
| 包体积 | 较大 | 减少41% |
| 性能 | 基准性能 | 提升1.3-2倍 |
了解过Vue2的diff算法吗,能简单说说吗?
Section titled “了解过Vue2的diff算法吗,能简单说说吗?”Vue2的diff算法是虚拟DOM更新的核心机制,主要采用双端比较策略来高效地找出新旧虚拟DOM树的差异。
核心流程:
- 同层比较:只比较同一层级的节点,不跨层级比较,降低算法复杂度
- 双端比较:设置四个指针(旧头、旧尾、新头、新尾),进行四种比较方式:
- 旧头 vs 新头:相同则直接复用
- 旧尾 vs 新尾:相同则直接复用
- 旧头 vs 新尾:相同则移动到尾部
- 旧尾 vs 新头:相同则移动到头部
- key值查找:四种比较都失败时,通过key在旧节点中查找匹配节点
- 创建和删除:找不到匹配节点则创建新节点,剩余旧节点则删除
优势:通过双端比较能够快速处理常见的DOM操作场景(如列表头尾插入、反转等),避免不必要的节点移动,提高更新效率。
时间复杂度:理想情况下为O(n),最坏情况下为O(n²)。
说说 Vue2 和 Vue3 diff 算法的区别?
Section titled “说说 Vue2 和 Vue3 diff 算法的区别?”Vue2和Vue3的diff算法在核心思路和优化策略上有显著差异:
Vue2 diff算法:采用双端比较策略,通过头头、尾尾、头尾、尾头四种方式进行节点比较。当四种比较都无法匹配时,会遍历旧节点列表查找相同key的节点。时间复杂度在最坏情况下为O(n²)。
Vue3 diff算法:引入了最长递增子序列算法,显著提升了性能。主要优化包括:
- 静态标记:编译时标记动态节点,跳过静态节点的比较
- 最长递增子序列:找出不需要移动的节点序列,减少DOM操作
- 块级更新:将动态内容提取到Block中,只对变化的部分进行diff
性能对比:Vue3的diff算法在处理大量节点时性能提升明显,特别是在节点顺序变化较少的场景下,能够将时间复杂度优化到接近O(n)。
| 对比项 | Vue2 | Vue3 |
|---|---|---|
| 核心策略 | 双端比较 | 最长递增子序列 |
| 静态优化 | 无 | 静态标记跳过 |
| 时间复杂度 | O(n²) | 接近O(n) |
| DOM操作次数 | 较多 | 显著减少 |
vue 是如何做样式隔离的?
Section titled “vue 是如何做样式隔离的?”Vue主要通过Scoped CSS和CSS Modules两种方式实现样式隔离:
Scoped CSS:
- 在
<style scoped>标签中编写的样式会被自动添加唯一的属性选择器 - 编译时为组件的每个元素添加
data-v-xxx属性,样式选择器也会相应添加该属性 - 实现原理:通过PostCSS插件在构建时转换CSS选择器,确保样式只作用于当前组件
- 优点:使用简单,无需额外配置;缺点:会增加CSS选择器的特异性
CSS Modules:
- 通过
<style module>启用,将CSS类名转换为局部作用域的唯一标识符 - 类名会被编译成哈希值,避免全局污染
- 在模板中通过
$style.className的方式使用样式类 - 优点:完全隔离,性能更好;缺点:使用相对复杂
深度选择器:
- 在Scoped CSS中使用
:deep()、::v-deep或>>>穿透样式隔离 - 用于修改子组件的样式,但要谨慎使用避免破坏封装性
全局样式:
- 使用
:global()选择器或单独的<style>标签定义全局样式 - 适用于重置样式、第三方组件样式覆盖等场景
Vue3 的组合式API相较于 Option API 有哪些优点?
Section titled “Vue3 的组合式API相较于 Option API 有哪些优点?”组合式API相比选项式API具有以下显著优势:
逻辑组织更清晰:
- 选项式API按选项类型分散代码(data、methods、computed分离),相关逻辑被拆分到不同区域
- 组合式API可以将相关的响应式数据、计算属性、方法组织在一起,提高代码的内聚性和可读性
更好的代码复用:
- 选项式API主要通过mixins实现复用,但存在命名冲突、来源不明确等问题
- 组合式API通过自定义Hook(composables)实现逻辑复用,更加灵活且类型安全
更强的TypeScript支持:
- 选项式API在TypeScript中类型推导较弱,需要额外的类型声明
- 组合式API提供完整的类型推导,IDE智能提示更准确,开发体验更佳
更灵活的逻辑抽象:
- 选项式API受固定结构限制,难以进行复杂的逻辑抽象
- 组合式API可以根据业务需求自由组织代码结构,支持更复杂的逻辑封装
更好的Tree-shaking支持:
- 组合式API按需引入响应式API,未使用的功能可以被构建工具移除
- 有助于减少最终打包体积
| 对比维度 | 选项式API | 组合式API |
|---|---|---|
| 逻辑组织 | 按选项类型分散 | 按功能逻辑聚合 |
| 代码复用 | mixins(有局限性) | composables(更灵活) |
| TypeScript支持 | 类型推导较弱 | 完整类型推导 |
| 学习成本 | 较低 | 相对较高 |
| 适用场景 | 简单组件 | 复杂逻辑组件 |
能说下 Vue 是怎么实现双向绑定的吗?
Section titled “能说下 Vue 是怎么实现双向绑定的吗?”Vue的双向绑定是通过响应式系统和指令系统协同实现的,核心原理如下:
响应式数据劫持:
- Vue2:使用
Object.defineProperty劫持对象属性的getter/setter,在getter中收集依赖,在setter中触发更新 - Vue3:使用
Proxy代理整个对象,可以监听属性的增删改查等所有操作
依赖收集与派发更新:
- 组件渲染时访问响应式数据,触发getter进行依赖收集,建立数据与组件的关联关系
- 数据变化时触发setter,通知所有依赖该数据的组件重新渲染
v-model指令实现:
v-model本质上是语法糖,等价于:value和@input的组合- 表单元素变化时触发input事件,更新绑定的数据
- 数据变化时通过响应式系统更新表单元素的value
双向绑定流程:
- 数据到视图:数据变化 → 触发setter → 通知依赖 → 重新渲染 → 更新DOM
- 视图到数据:用户输入 → 触发事件 → 执行事件处理器 → 更新数据 → 触发响应式更新
关键组件:
- Observer:负责将数据转换为响应式对象
- Dep:依赖收集器,管理依赖关系
- Watcher:观察者,当数据变化时执行相应的更新操作
| 版本 | 数据劫持方式 | 优势 | 局限性 |
|---|---|---|---|
| Vue2 | Object.defineProperty | 兼容性好 | 无法监听数组索引、对象新增属性 |
| Vue3 | Proxy | 功能完整,性能更好 | IE不支持 |
平时项目中性能优化怎么做的,能简单说说吗?
Section titled “平时项目中性能优化怎么做的,能简单说说吗?”Vue项目性能优化主要从以下几个维度进行:
组件层面优化:
- 使用
v-memo缓存复杂的渲染结果,避免不必要的重新计算 - 合理使用
v-show和v-if:频繁切换用v-show,条件渲染用v-if - 组件懒加载:使用
defineAsyncComponent实现按需加载 - 避免在模板中使用复杂表达式,改用计算属性
数据响应式优化:
- 使用
shallowRef和shallowReactive处理大型数据结构 - 通过
markRaw标记不需要响应式的对象 - 合理使用
readonly避免不必要的响应式转换 - 大列表使用虚拟滚动技术
路由和代码分割:
- 路由懒加载:
component: () => import('./Component.vue') - 按功能模块进行代码分割,减少首屏加载时间
- 使用
Suspense组件优化异步组件加载体验
构建优化:
- 开启Gzip压缩和资源压缩
- 使用CDN加速静态资源加载
- 图片懒加载和格式优化(WebP等)
- 合理配置缓存策略
监控和分析:
- 使用Vue DevTools分析组件性能
- 通过Performance API监控关键指标
- 定期进行Bundle分析,识别体积过大的依赖
| 优化类型 | 具体措施 | 效果 |
|---|---|---|
| 渲染优化 | v-memo、计算属性 | 减少重复计算 |
| 加载优化 | 懒加载、代码分割 | 提升首屏速度 |
| 数据优化 | shallow API、虚拟滚动 | 降低内存占用 |
| 资源优化 | 压缩、CDN、缓存 | 减少网络传输 |