Java 工程师师需要掌握的前端知识
Vue 高频面试20题(Vue2/Vue3 通用)
1. v-if 和 v-show 的区别?
- 原理:
v-if条件为false时直接销毁DOM,条件成立再重建DOM,会触发组件生命周期;v-showDOM一直存在,只是切换CSSdisplay:none,不会销毁DOM。 - 性能:
v-if适合很少切换的场景,初始false开销小;v-show适合频繁显示隐藏(弹窗、下拉)。 - 其它:
v-if支持<template/>标签;v-show不支持template。
注意:不要把v-if和v-for写在同一个标签上,v-for优先级更高,会造成不必要循环。
2. computed 和 watch 的区别?
- computed(计算属性):有缓存,依赖不变就不会重新执行;用于根据已有数据派生新数据,必须return结果,尽量不要写异步、副作用代码。模板可直接当做变量使用。
- watch(侦听器):无缓存;监听数据变化,适合执行异步请求、复杂业务逻辑、定时器等副作用,可以不返回值。
- 选型:简单数据计算优先computed;数据变化后要做异步操作用watch。
3. ref 和 reactive 的区别?如何选择?
- ref:支持基础类型(数字/字符串/布尔)和对象;在script中访问需要
.value;模板自动解包不用.value;可以直接整体替换。 - reactive:只支持对象/数组,不支持基础类型;返回Proxy代理对象,不需要
.value;不能直接整体重新赋值,否则丢失响应式;解构会丢失响应式。 - 推荐:基础类型必须ref;日常开发优先ref;表单完整对象可以用reactive,注意不要解构。
4. v-for 中 key 的作用,为什么不推荐用 index 作为 key?
key是VNode唯一标识,diff算法依靠key识别节点,用来复用、移动、删除DOM,提升diff对比效率,保证列表渲染正确性。
不推荐index做key:当数组插入、删除、排序时,index会发生变化,diff会错误复用DOM节点,造成表单输入状态错乱、动画错位等问题。
最佳实践:使用业务唯一id做key;只有列表永远不会增删排序时,才可以临时用index。
5. Vue3响应式原理,对比Vue2有什么差异?
Vue3:基于ES6 Proxy + Reflect。Proxy拦截对象的get/set/deleteProperty操作;get收集依赖,set触发副作用更新。 优点:可以监听数组下标修改、对象新增/删除属性,原生支持Map、Set;不需要重写数组方法。缺点:不兼容IE。
Vue2:Object.defineProperty,只能监听对象已存在属性,无法监听新增属性、数组下标修改;需要重写7个数组方法实现数组响应式。
6. nextTick 的作用和使用场景?
Vue更新DOM是异步批处理机制,修改响应式数据不会立刻更新DOM。nextTick等待下一次DOM更新完成后执行回调。 场景:
- 修改数据后,立刻获取更新后的DOM元素、DOM尺寸;
- 通过ref获取更新后的组件实例。 Vue3组合式API中nextTick返回Promise,可以配合async/await。
7. Vue组件通信方式有哪些?
- 父子组件:父传子
props;子传父emit;父通过ref获取子组件实例; - 跨多层组件:
provide / inject; - 全局任意组件:Pinia(Vue3)/Vuex(Vue2)状态管理;
- 简单跨组件事件:mitt(Vue3替代旧的事件总线);
- 属性透传:
$attrs,透传属性给后代组件。
8.Vue3中父子组件通信怎么实现?
- 父传子:props + defineProps 父组件通过属性传递数据;子组件使用
defineProps接收。props是单向数据流,子组件不能直接修改props。 - 子传父:emit + defineEmits 子组件通过
defineEmits声明事件,调用emit触发自定义事件,携带参数传给父组件,父组件监听事件拿到数据。 - 父获取子实例:ref + defineExpose 父组件绑定ref拿到子组件实例;子组件通过
defineExpose显式暴露属性/方法,父组件才能访问。 - v-model 双向绑定(语法糖) Vue3支持多v-model,本质是props + emit的封装。
v-model:xxx等价于:modelValue+@update:modelValue。 - $attrs 属性透传 父组件属性未被props接收时,自动透传给子组件根元素,可用于传递属性。
规范:优先props/emit;简单场景用v-model;父需要调用子方法用ref+defineExpose。
9. keep-alive的作用,以及对应的生命周期钩子?
<keep-alive/>是Vue内置组件,用来缓存组件实例,组件切换时不会销毁,保留页面状态,避免重复请求接口。 属性:include 指定缓存组件;exclude排除缓存;max设置最大缓存数量。
专属生命周期(只有被keep-alive缓存才触发):
- onActivated:组件被激活进入页面;
- onDeactivated:组件离开失活。
缓存组件不会重复执行onMounted,页面清理逻辑写在onDeactivated。
10. Vue Router导航守卫有哪些?导航流程简单说下?
导航守卫用来做路由鉴权、权限校验、页面离开提示。
- 全局守卫:
beforeEach全局前置;beforeResolve全局解析;afterEach全局后置; - 路由独享守卫:路由配置中的
beforeEnter; - 组件内守卫:
onBeforeRouteLeave离开当前组件;onBeforeRouteUpdate路由参数变化复用组件。
导航核心流程:触发跳转 → 离开组件守卫 → 全局beforeEach → 路由独享beforeEnter → 解析异步组件 → 组件内进入守卫 → beforeResolve → 更新URL → afterEach → DOM挂载。
11. Pinia 和 Vuex 的区别,Vue3为什么推荐Pinia?
Vuex:state/getters/mutations/actions;mutation强制同步修改state;模块嵌套需要命名空间;TS类型支持差。
Pinia:移除mutation,可直接修改state;异步逻辑写在action;每个store独立文件,天然隔离;TS类型推断友好;代码样板更少;可以在组件外直接import使用store;支持store之间互相调用。 Vue3推荐Pinia,API简洁,类型友好,保留devtools调试能力。
12. Vue3 v-model原理,对比Vue2 v-model有什么变化?
v-model本质是语法糖。 Vue2:组件v-model等价于 :value="val" + @input="val=$event";一个组件只能有一个v-model。
Vue3:组件 v-model:name="val" 等价于 :modelValue="val" + @update:modelValue="val=$event";支持多个v-model绑定不同字段;Vue3.4+ defineModel宏简化v-model开发。
13. watch 和 watchEffect 的区别?
watch:需要手动指定监听源;默认惰性执行(初始不会触发回调);回调可以拿到新值、旧值。
watchEffect:自动收集回调内部依赖,不用手动声明监听源;立即执行一次;无法获取旧值。 选型:需要新旧值对比用watch;只需要依赖变化自动执行逻辑,不需要旧值用watchEffect。
14. provide 和 inject 的使用场景,有什么注意点?
上层组件通过provide提供数据;深层后代组件使用inject获取,跨多层传参,避免一层层props透传。 适用场景:主题配置、全局组件配置、深层嵌套组件共享数据。 ⚠️注意:inject拿到的数据默认不是响应式;如果需要响应式,provide传递ref/reactive对象;不适合大型全局状态管理。
15. Vue项目性能优化有哪些手段?
打包阶段
- 路由懒加载,异步组件分包;
- 第三方库按需引入,Tree-Shaking;静态资源CDN;压缩代码。
运行时
- v-for使用正确key;避免v-if和v-for写同一标签;
- 长列表使用虚拟滚动;
- keep-alive缓存页面;合理使用v-if/v-show;
- shallowRef / shallowReactive / markRaw减少深层代理开销;
- 事件防抖、节流;图片懒加载。
其他
减少不必要渲染;组件拆分;监控页面性能指标。
16. 插槽是什么?作用域插槽解决什么问题?
插槽让父组件可以自定义子组件内部模板内容。
- 默认插槽:基础内容分发;
- 具名插槽:多个插槽,分配不同位置模板;
- 作用域插槽:子组件把自身数据传递给父组件模板,父组件拿到子组件数据渲染UI。
场景:高度可复用组件,比如表格组件,组件负责逻辑,父组件自定义单元格渲染。
17. Teleport 组件解决什么问题?适用场景?
Teleport可以把组件内的DOM挂载到任意DOM节点(如body),不受父组件overflow:hidden、z-index层级限制。 适用场景:弹窗、抽屉、tooltip悬浮层;业务逻辑写在组件内部,但DOM挂载到body,防止父组件样式截断浮层。
18. 自定义指令、组件、组合式函数(composable)三者怎么区分?
答案:
- 自定义指令:直接操作底层DOM;适合DOM原生行为:权限控制、点击外部关闭、防抖;不能封装业务状态;钩子mounted/updated/beforeUnmount。
- 组件:拥有模板、状态、生命周期,产出UI视图。
- 组合式函数composable:封装状态逻辑,不操作DOM;返回响应式变量与方法;setup中导入复用逻辑,替代mixin。
一句话区分:操作DOM写自定义指令;输出UI写组件;复用状态逻辑写组合式函数。
19. 为什么不推荐mixin,Vue3用什么替代?
mixin存在很多缺陷:同名变量/方法会发生覆盖冲突;隐式依赖,很难追溯变量来源;多个mixin合并时代码难以维护;TS类型支持差。
Vue3推荐组合式函数(composable)替代mixin:普通函数,内部使用ref/computed,返回状态和方法;导入使用,变量来源清晰,无命名冲突,可以传参,类型友好。
20. Vue透传属性 $attrs 和 inheritAttrs?
父组件传入子组件,但子组件没有通过props接收的属性,会自动透传到子组件根DOM元素,这就是$attrs,事件也会透传。
inheritAttrs默认true;设置inheritAttrs:false关闭自动透传;可以手动使用v-bind="$attrs"绑定到组件内任意元素,而不是根元素。 常用于高阶组件封装,把属性向下传递给内部原生DOM。
