Vue高级面试题
Vue高级面试题
1. Vue 3 相比 Vue 2 的核心改进有哪些?
答案:
- 使用
Proxy重构响应式,解决Object.defineProperty无法监听数组、新增和删除属性的问题。 - 全新 Composition API,解决大型项目中 Options API 碎片化的问题。
- 支持 TypeScript 全量类型推导。
- 打包体积更小,支持 Tree Shaking。
- 性能提升:更新更快,减少无用渲染。
- 新增
Teleport、Suspense、Fragment,支持多根节点。 - 全新异步组件、自定义渲染器。
2. Vue 3 Proxy 相比 Vue 2 defineProperty 有哪些优势?
答案:
- 可监听数组变化,包括增删、下标修改和
length变化。 - 可监听对象新增、删除属性。
- 可监听
Map、Set集合类型。 - 一次性代理整个对象,无需递归遍历所有属性。
- 支持更多拦截行为,例如
has、deleteProperty等。
3. Vue 3 响应式原理整体流程?
答案:
数据初始化 → 使用 reactive 创建 Proxy 代理 → 组件渲染触发 getter 收集依赖 → 数据修改触发 setter → trigger 触发组件更新 → patch 更新 DOM。
4. ref 和 reactive 的区别?
答案:
ref:支持基本类型和引用类型。reactive:主要用于处理对象、数组等引用类型。ref通过.value访问,底层对象依然会通过reactive转换为响应式对象。reactive使用Proxy代理,无需通过.value访问。ref的本质:基本类型无法被Proxy代理,所以需要包装成对象。
5. 为什么 ref 需要 .value?
答案:
Proxy 只能代理对象,无法代理字符串、数字等基本类型。ref 会将原始值包装成 { value: xxx } 对象,从而实现响应式。
6. toRef 和 toRefs 的区别?
答案:
toRef:单独取出对象某个属性生成ref,并与原对象保持关联。toRefs:批量将reactive对象所有属性转换为ref。- 重点:不会丢失响应式,解构赋值时很有用。
7. reactive 解构为什么会丢失响应式?
答案:
解构赋值取出的是原始值,脱离了 Proxy 代理对象,因此不再具备响应式。必须使用 toRefs 或 toRef 进行解构。
8. computed 缓存原理?
答案:
computed 内部有 dirty 标记:
- 依赖未变化:
dirty = false,直接返回缓存值。 - 依赖变化:
dirty = true,重新计算。 - 所以多次访问时只会执行一次计算。
9. computed 和 watch 的区别?
答案:
computed:依赖缓存、同步、纯计算,不支持异步副作用。watch:无缓存,支持异步操作,监听变化后执行副作用。
10. watch、watchEffect 的区别?
答案:
watch:需要手动指定监听源,可获取新值和旧值,默认不立即执行。watchEffect:自动收集依赖,立即执行,无法获取oldValue。
11. watchEffect 如何停止监听?
答案:
watchEffect 会返回一个 stop 函数,手动调用即可停止监听;组件卸载时,监听也会自动停止。
js
const stop = watchEffect(() => {
console.log('监听中')
})
stop()
12. Vue 3 生命周期和 Vue 2 的区别?
答案:
- 组合式 API 使用
onMounted、onUpdated等函数式生命周期。 - 取消
beforeCreate和created,相关逻辑可以直接写在setup最顶部。 - 新增
onRenderTracked、onRenderTriggered等调试钩子。
13. setup 执行时机?
答案:
setup 在 beforeCreate 之前执行,此时组件实例尚未创建,无法访问 this。
14. setup 中为什么没有 this?
答案:
setup 执行时机早于组件实例创建,this 不存在;同时,Composition API 的设计理念就是脱离 this。
15. Vue 3 组件通信方式有哪些?
答案:
- 父子通信:
props、defineProps - 子传父:
emit、defineEmits - 父组件访问子组件:
expose、defineExpose - 跨级通信:
provide、inject - 全局属性:
globalProperties - 状态管理:Pinia
- 事件总线:
mitt,替代 Vue 2 的 EventBus
16. defineProps、defineEmits 原理?
答案:
defineProps 和 defineEmits 是 Vue 3 的编译期宏函数,无需导入,仅在 <script setup> 语法糖中生效,编译阶段会被直接解析,无运行时开销。
17. provide/inject 响应式如何保证?
答案:
provide 传递 ref 或 reactive 数据即可保持响应式;普通数据不会自动响应。也可以通过 readonly 防止子组件篡改。
js
provide('state', readonly(state))
18. Vue 3 为什么废弃 EventBus?
答案:
Vue 2 的 EventBus 基于 Vue 实例,多组件混用容易造成事件残留、重复监听和内存泄漏。Vue 3 不再提供基于 Vue 实例的事件总线,推荐使用独立的轻量事件总线,例如 mitt。
19. Pinia 相比 Vuex 的优势?
答案:
- 去除
mutations,可以直接修改state。 - 完整的 TypeScript 类型推导。
- 模块扁平化,无需嵌套
modules。 - 体积更小,API 更简洁。
- 支持模块化和热更新。
20. Vue 3 diff 算法优化(核心高级题)
答案:
Vue 3 使用最长递增子序列优化移动逻辑:
- 头尾对比预处理。
- 生成新老节点的
key映射表。 - 通过最长递增子序列,最小化 DOM 移动次数。
- 相比 Vue 2 的乱序 diff,性能有较大提升。
21. Vue 3 虚拟 DOM 相比 Vue 2 有哪些优化?
答案:
PatchFlag:静态标记,只更新动态节点。hoistStatic:静态节点提升,不参与更新。cacheHandlers:事件缓存。- 减少 VDOM 创建开销。
22. PatchFlag 原理?
答案:
编译时给动态节点打标记,例如文本、class、style 和 props。diff 时只对比标记过的动态内容,静态内容直接跳过,极大提升 diff 速度。
23. Vue 3 为什么可以多根节点?
答案:
Vue 3 编译阶段支持 Fragment。Fragment 是一个虚拟根节点,不渲染真实 DOM,所以支持多根模板。
24. Teleport 原理和使用场景?
答案:
Teleport 可以将组件 DOM 挂载到任意 DOM 位置,脱离父组件层级、overflow、z-index 等限制。
常用于弹窗、悬浮层、全局提示等场景。
25. Suspense 的作用?
答案:
Suspense 用于处理异步组件加载,加载中显示 fallback,加载完成后渲染组件;常用于路由懒加载和异步组件。
26. Vue 3 异步组件写法?
答案:
js
const MyComp = defineAsyncComponent(() => import('./comp.vue'))
27. Vue 3 样式穿透 :deep() 原理?
答案:
scoped 样式会给当前组件 DOM 添加唯一的 data-v-xxx 属性,从而限制样式作用范围。
:deep() 让内部选择器不携带当前组件的 scoped 唯一属性,从而可以修改子组件内部样式。
css
:deep(.child-class) {
color: red;
}
28. Vue 3 性能优化实战手段?
答案:
v-once:静态节点只渲染一次。v-memo:缓存动态节点。- 拆分组件,减少无效渲染。
- 使用
computed缓存计算结果。 - 使用异步组件和路由懒加载。
- 大型列表使用虚拟列表。
29. v-memo 的作用?
答案:
v-memo 可以缓存模板节点。当依赖值不变时,跳过整段 diff,适合长列表和复杂模板,可以明显提升渲染性能。
30. Vue 3 响应式丢失场景及解决方案?
答案:
- 解构
reactive对象会丢失响应式。
解决方案:使用toRefs或toRef。 - 直接替换
reactive整个对象会导致响应式丢失。
解决方案:使用Object.assign,或者使用ref管理整个对象。 - 普通变量接收
ref值会丢失响应式。
解决方案:必须保留.value。 - 函数参数解构可能导致响应式丢失。
解决方案:提前使用toRefs。
暂无评论,快来发表第一条评论吧