v-if 和 v-show 有什么区别?Vue 基础高频题

2026年 阅读约 8 分钟 面试指南 · 前端面试

Vue基础面试高频题精选:v-if与v-show的区别、computed与watch的选型、生命周期执行顺序、v-for中key的作用、nextTick的实现原理,分初级、中级、高级三个层次全面讲解。

一句话总结

v-if 是真实的条件渲染——切换时销毁/重建组件(惰性,初始为 false 时不渲染);v-show 只是 display 切换——初始就渲染,频繁切换开销小。computed 有缓存(依赖不变不重算),watch 无缓存(数据变化就执行,适合异步/开销大的副作用)。

初级理解

v-if vs v-show:v-if 是"真条件",元素不会渲染到 DOM;v-show 总是渲染,靠切换 CSS 控制显隐。

对比项v-ifv-show
编译产物三元表达式/分支display: none
初始渲染false 时完全不渲染(惰性)始终渲染
切换开销销毁/重建,大样式切换,极小
适用场景很少切换、条件分支少频繁切换(Tab、开关)

computed vs watch:computed 是"由已有数据算出的新数据",必须有返回值;watch 是"数据变化时执行一段逻辑",适合异步请求、复杂副作用。

<template> <!-- 频繁切换用 v-show,不常变用 v-if --> <div v-show="tab === 'a'">面板A</div> <div v-if="loaded">内容( rarely 变化)</div> </template> <script setup> // computed:派生数据,有缓存 const fullName = computed(() => `${first.value} ${last.value}`) // watch:副作用,适合异步 watch(searchText, (val) => fetchSuggest(val))</script>
一句话区分:结果能用"算出来"就用 computed,结果需要"做事"(请求/操作DOM)就用 watch。

中级深入

生命周期执行顺序:创建阶段 from 父到子,mounted 从子到父(子组件先挂载完成)。

// Vue2 → Vue3 生命周期对应关系 beforeCreate → setup() 阶段 created → setup() 阶段 beforeMount → onBeforeMount mounted → onMounted beforeUpdate → onBeforeUpdate updated → onUpdated beforeDestroy → onBeforeUnmount destroyed → onUnmounted

v-for 为什么要加 key:key 是节点的"身份证"。diff 算法用 key 判断新旧节点是否可复用。用 index 作 key 时,列表中间插入/删除元素会导致后续节点 key 全部变化,引发不必要的重新渲染,且带输入框/勾选状态的组件会出现状态错乱。

nextTick 的原理:Vue 的 DOM 更新是异步批量的——修改数据后不会立即更新 DOM,而是把更新任务放入微任务队列,本轮事件循环结束后统一执行。nextTick 就是把回调追加到这些更新任务之后,保证拿到的是更新后的 DOM。

count.value++ console.log(dom.textContent) // 旧值!DOM 还没更新 await nextTick() console.log(dom.textContent) // 新值
注意:v-for 遍历对象时 key 仍不可省略;v-if 和 v-for 同节点时,Vue3 中 v-if 优先级更高(Vue2 相反),v-if 里访问不到 v-for 的变量,这是 Vue2 升 Vue3 的常见坑。

高级拓展

computed 的缓存机制:内部创建一个 lazy 的 Watcher/Effect——初始不执行,首次访问才计算并把结果缓存;依赖变化时只是把 dirty 标记为脏,下次访问才重算。所以 computed 不依赖的响应式数据变化不会触发重算。

watch 的进阶选项:

  • deep: true:深度侦听对象内部变化(性能开销大,Vue3 中侦听 reactive 对象默认隐式开启且无法关闭)
  • immediate: true:创建时立即执行一次(替代 mounted 里的重复调用)
  • flush: 'post':回调在 DOM 更新后执行(等价于 watchPostEffect)

事件与按键修饰符的实现:@click.stop 编译为 withModifiers 包装函数,本质是高阶函数在真实事件触发前拦截处理,并不依赖任何运行时魔法。

// Vue3 组件上使用 v-model 的本质 <MyInput v-model="title" /> // 等价于: <MyInput :modelValue="title" @update:modelValue="v => title = v" /> // 组件内:defineProps(['modelValue']) + defineEmits(['update:modelValue'])
面试加分项:能说清 computed 缓存是"脏标记 + 惰性求值"、Vue3 中 v-if/v-for 优先级变化、v-model 的编译产物,说明你理解编译后的代码而不是只会 API。

实战场景

场景一:Tab 频繁切换用 v-if 导致卡顿

// 反例:每次切换都销毁重建整个面板(内含图表/表格) <ChartPanel v-if="tab === 'chart'" /> // 正例:v-show 保持实例,切换只动 display;配合 keep-alive 亦可 <ChartPanel v-show="tab === 'chart'" />

场景二:index 作 key 导致勾选状态错乱

// 列表:[{id:1,name:'A'},{id:2,name:'B'}] 已勾选第二项 // unshift 插入新元素后,index 0/1 位置内容变了,但 key(=index) 没变 // Vue 复用了旧节点 → 复选框状态跟着位置走而不是跟着数据走 <li v-for="(item, index) in list" :key="index"> <!-- 反例 --> <li v-for="item in list" :key="item.id"> <!-- 正例 -->

场景三:搜索联想用 watch 而不是 computed

watch(searchText, async (val) => { if (!val) return (list.value = []) const res = await fetchSuggest(val) // 异步副作用放 watch list.value = res }, { debounce: 300 }) // 防抖配合

面试模拟

Q:computed 和 methods 的区别?

A:computed有缓存,依赖不变时多次访问只算一次;methods 每次调用都重新执行。模板里同一数据多处使用时 computed 明显更优。另外 computed 不能传参(需要传参就封装成函数返回 computed)。

Q:Vue2 和 Vue3 的响应式为什么不同?v-if/v-for 优先级有变化吗?

A:Vue2 用 Object.defineProperty 只能劫持已存在属性,新增/删除/数组索引监听不到,需要 $set;Vue3 用 Proxy 代理整个对象,天然支持增删和索引。优先级方面:Vue2 中 v-for 高于 v-if,Vue3 中 v-if 高于 v-for,混写时语义相反,官方也不推荐同节点混用。

Q:nextTick 在 Vue2 和 Vue3 中的实现有什么不同?

A:原理一致——把回调放到微任务里排队。Vue2 有一套优雅降级(Promise → MutationObserver → setImmediate → setTimeout);Vue3 直接用 Promise.then,因为目标环境已支持 Promise。