Vue 面试题
基础题
参考答案:
Vue 2 使用 Object.defineProperty 遍历对象属性,将其转换为 getter/setter 实现响应式。
局限性:
- 无法监听对象新增属性和删除属性(需用
Vue.set/Vue.delete)。 - 无法监听数组索引和
length变化(对数组方法做了改写)。 - 初始化时需递归遍历所有属性,性能开销大。
Vue 3 使用 Proxy 代理整个对象:
- 可监听新增、删除属性。
- 可监听数组索引和
length变化。 - 支持 Map、Set。
- 初始化无需递归,访问到嵌套对象时才做响应式转换(lazy reactivity)。
const state = reactive({ count: 0 });
state.count++; // 触发依赖更新
state.newKey = 'value'; // 同样可被监听
评分维度:
- 能说出两种实现方式(30%)
- 能说明 Vue 2 局限(30%)
- 能说明 Vue 3 优势并举例(40%)
参考答案:
虚拟 DOM 是用 JavaScript 对象描述真实 DOM 结构的技术。状态变化时,框架先比较新旧虚拟 DOM,再最小化更新真实 DOM。
Vue Diff 特点:
- 同层比较:只比较同一层级节点,不跨层级。
- 使用 key:通过 key 识别节点身份,实现复用、移动、删除。
- Vue 2 双端比较:从新旧节点数组两端同时比较,减少移动次数。
- Vue 3 快速 Diff:引入最长递增子序列(LIS)优化移动节点,静态节点提升、PatchFlag 标记动态节点减少比较范围。
示例:列表中 key 使用稳定 ID,能让 Vue 正确识别元素移动而不是全部销毁重建。
评分维度:
- 能解释虚拟 DOM(30%)
- 能说明 Diff 特点(40%)
- 能说明 key 的作用(30%)
参考答案:
Composition API 是 Vue 3 引入的新的逻辑组织方式,相比 Options API 有以下优势:
- 按逻辑组织代码:同一功能的 data、methods、computed、watch 可写在一起,而不是分散在不同 option 中。
- 更好的逻辑复用:通过自定义组合式函数(Composables)抽取可复用逻辑,替代 mixin 的命名冲突和来源不明问题。
- 更好的 TypeScript 支持:函数式 API 更利于类型推断和类型安全。
- 更灵活的代码组织:没有选项限制,可自由组合响应式 API。
- Tree Shaking 友好:只用到的 API 才会被打包。
示例:把获取用户列表的逻辑封装成 useUserList,在多个组件中复用。
// composables/useUserList.js
import { ref, onMounted } from 'vue';
export function useUserList() {
const users = ref([]);
const loading = ref(false);
const fetchUsers = async () => {
loading.value = true;
users.value = await api.getUsers();
loading.value = false;
};
onMounted(fetchUsers);
return { users, loading, fetchUsers };
}
// 组件中使用
const { users, loading } = useUserList();
评分维度:
- 能说出按逻辑组织代码(25%)
- 能说明复用性提升(25%)
- 能提到 TS 支持与 Tree Shaking(30%)
- 能写出 Composable 示例(20%)
参考答案:
| 特性 | ref | reactive |
|---|---|---|
| 数据类型 | 任意类型(推荐基本类型) | 对象/数组/Map/Set |
| 访问方式 | .value |
直接访问属性 |
| 重新赋值 | 可直接替换整个值 | 替换对象会丢失响应式 |
| 解构 | 不会丢失响应式 | 解构会丢失响应式 |
| 使用场景 | 基本类型、需要重新赋值的值 | 复杂对象 |
示例:
const count = ref(0);
count.value++;
const user = reactive({ name: 'Tom' });
user.name = 'Jerry';
注意:reactive 对象解构会丢失响应式,应用 toRefs 转换后再解构。
评分维度:
- 能说出核心区别(40%)
- 能说明使用场景(30%)
- 能提醒 reactive 的局限性(30%)
参考答案:
| 特性 | Hash 模式 | History 模式 |
|---|---|---|
| URL 形式 | /#/path |
/path |
| 服务端配置 | 不需要 | 需要配置 fallback 到 index.html |
| 兼容性 | 更好,兼容旧浏览器 | 依赖 HTML5 History API |
| SEO | 较差 | 更好 |
| 刷新行为 | 不会发送 # 后内容到服务端 | 会向服务端请求当前路径 |
History 模式需要 Nginx/服务器配置:
location / {
try_files $uri $uri/ /index.html;
}
选择:对 SEO 和 URL 美观有要求用 History;需要零服务端配置或兼容旧环境用 Hash。
评分维度:
- 能区分两种模式(40%)
- 能说明服务器配置差异(30%)
- 能给出选型建议(30%)
进阶题
参考答案:
Vue 3 响应式系统基于 Proxy + effect + dep:
- 依赖收集(track):当响应式对象属性被读取时,如果当前有活跃的 effect,则将该 effect 收集到该属性对应的 dep 中。
- 触发更新(trigger):当属性被修改时,dep 通知所有收集到的 effect 重新执行。
- effect 管理:
effect函数执行时会把自己设为全局活跃 effect,执行完清除。
const state = reactive({ count: 0 });
effect(() => {
console.log(state.count); // track
});
state.count++; // trigger,重新执行 effect
依赖存储结构:WeakMap<Target, Map<Key, Set<Effect>>>,使用 WeakMap 避免内存泄漏。
评分维度:
- 能解释 effect 和 dep(30%)
- 能说明 get 时收集依赖(30%)
- 能说明 set 时触发更新并提到 WeakMap(40%)
参考答案:
| 特性 | computed | methods |
|---|---|---|
| 缓存 | 基于依赖缓存 | 每次调用都执行 |
| 适用场景 | 依赖稳定、计算较复杂 | 事件处理、需要参数 |
| 性能 | 依赖不变时无重复计算 | 重复执行 |
| 写法 | 像属性一样访问 | 函数调用 |
示例:
const fullName = computed(() => firstName.value + ' ' + lastName.value);
function greet() {
return 'Hello ' + fullName.value;
}
在模板中频繁使用且依赖不变时,优先用 computed;需要传参或响应事件用 methods。
评分维度:
- 能说明缓存特性(40%)
- 能说明使用场景(30%)
- 能举例说明(30%)
参考答案:
nextTick 用于在下次 DOM 更新循环结束后执行回调。Vue 的 DOM 更新是异步的,修改响应式数据后不会立即更新真实 DOM。
示例:
import { nextTick } from 'vue';
const count = ref(0);
async function increment() {
count.value++;
console.log(document.getElementById('num').textContent); // 可能还是旧值
await nextTick();
console.log(document.getElementById('num').textContent); // 已更新
}
原理:Vue 将同一事件循环中的状态变更缓冲,在微任务阶段统一更新 DOM,nextTick 就是把回调放到这个微任务之后。
评分维度:
- 能解释 nextTick 作用(30%)
- 能说明异步更新机制(40%)
- 能写出使用方式(30%)
参考答案:
Pinia 是 Vue 官方推荐的新一代状态管理库,相比 Vuex 4 的改进:
- API 更简洁:没有 mutations,直接修改 state。
- 更好的 TypeScript 支持:类型推断更自然,无需复杂模块声明。
- Composition API 风格:与 Vue 3 更契合。
- 模块化更简单:每个 store 是独立模块,无需嵌套命名空间。
- 体积更小:核心代码更少。
- 支持 SSR:与 Nuxt 3 深度集成。
- Devtools 支持:提供时间旅行、状态检查等能力。
示例:
export const useUserStore = defineStore('user', {
state: () => ({ name: 'Tom' }),
getters: { upperName: (state) => state.name.toUpperCase() },
actions: { updateName(name) { this.name = name; } }
});
评分维度:
- 能说出 4 个以上改进(40%)
- 能说明 mutations 变化(30%)
- 能提到 TS 支持(30%)
参考答案:
| 特性 | v-if | v-show |
|---|---|---|
| 渲染行为 | 条件为 false 时不渲染元素 | 始终渲染,通过 display: none 控制显示 |
| 切换开销 | 高(创建/销毁组件) | 低(仅 CSS 切换) |
| 初始渲染开销 | 可能更低(条件为 false 时不渲染) | 更高(无论如何都渲染) |
| 适用场景 | 不频繁切换 | 频繁切换 |
注意:
v-if是“真正的”条件渲染,会触发组件生命周期。v-show只是视觉隐藏,组件状态保留。
评分维度:
- 能区分渲染行为(40%)
- 能说明开销差异(30%)
- 能给出使用建议(30%)
高级题
参考答案:
Vue 3 编译器进行了大量优化:
- 静态节点提升(hoistStatic):把不依赖状态的节点提升到渲染函数外部,避免每次渲染重新创建。
- 静态属性提升:静态 class、style 等属性同样提升。
- PatchFlag 标记动态节点:编译时标记哪些节点是动态的,Diff 时只比较动态部分。
- Block Tree:把整个模板划分为 Block,只追踪动态子节点,减少遍历范围。
- 事件监听缓存(cacheHandlers):内联事件函数缓存,避免子组件不必要的更新。
- SVG 优化:对 SVG 内容进行静态提升和更精确的更新。
这些优化让 Vue 3 在大型、复杂模板下性能显著提升。对比 Vue 2 的全量 Diff,Vue 3 编译时会标记动态节点,渲染时只对比动态部分。
<!-- 编译后:只有 msg 会被标记为动态节点 -->
<template>
<div>
<h1>Static Title</h1>
<p>{{ msg }}</p>
</div>
</template>
评分维度:
- 能说出 3 个以上优化(35%)
- 能解释静态节点提升(25%)
- 能说明 PatchFlag 作用(25%)
- 能对比 Vue 2 全量 Diff(15%)
参考答案:
大型 Vue 应用设计要点:
- 技术栈:Vue 3 + TypeScript + Vite,使用
<script setup>和 Composition API。 - 状态管理:Pinia 管理全局状态,局部状态优先使用 ref/reactive。
- 路由:Vue Router 4,按功能模块拆分路由配置,使用路由懒加载。
- 代码组织:按业务领域(features)划分目录,避免按类型平铺。
- 组件分层:基础组件(UI)+ 业务组件 + 页面组件。
- 逻辑复用:把公共逻辑封装为 Composables。
- 请求层:封装统一的 Axios/fetch 请求层,处理错误、鉴权、缓存。
- 工程化:ESLint、Prettier、单元测试、E2E、CI/CD。
- 可观测性:错误监控、性能监控、埋点。
评分维度:
- 能说出 5 个以上设计点(40%)
- 能说明代码组织方式(30%)
- 能说明状态管理与工程化(30%)
参考答案:
Vue 3 响应式系统虽然比 Vue 2 强大,但仍有局限:
- reactive 不能直接替换整个对象:
user = newObj会丢失响应式,应修改属性或使用 ref。 - reactive 对象解构会丢失响应式:需用
toRefs转换。 - ref 需要
.value:在 JS 中访问略显繁琐。 - 对原始值有限制:Proxy 只能代理对象,基本类型必须用 ref 包装。
- 某些内置对象需特殊处理:如 Date、自定义类可能需要额外处理。
- 大型对象性能:深层嵌套对象首次访问时仍有响应式转换开销。
最佳实践:基本类型和需要重新赋值的用 ref;复杂对象用 reactive 但避免替换和解构。
评分维度:
- 能说出 3 个以上局限(40%)
- 能说明解构问题(30%)
- 能给出解决方案(30%)
参考答案:
Teleport 允许把组件的一部分模板渲染到 DOM 中的其他位置,类似 React Portal。常用于模态框、Toast、Tooltip 等需要脱离父组件布局上下文的场景。
示例:
<Teleport to="body">
<div class="modal">Modal Content</div>
</Teleport>
特点:
- 逻辑上仍属于当前组件,props、事件、生命周期正常。
- 渲染目标 DOM 节点需在挂载时存在。
- 可用于解决
z-index、overflow: hidden等层叠上下文问题。
评分维度:
- 能解释 Teleport(40%)
- 能说明使用场景(30%)
- 能写出基本用法(30%)
参考答案:
Vue 性能优化应从渲染、加载、运行时多个层面入手:
- 减少不必要渲染:使用
computed、v-once、v-memo、合理拆分组件。 - 异步组件:
defineAsyncComponent实现路由/弹窗懒加载。 - 虚拟列表:长列表使用 vue-virtual-scroller 等库。
- keep-alive:缓存不常销毁的组件状态。
- 合理使用 key:列表使用稳定 ID,避免索引 key。
- 避免过度响应式:大数据、临时对象避免转为响应式。
- 路由懒加载:
component: () => import('./Page.vue')。 - 构建优化:Tree Shaking、Gzip/Brotli、CDN、图片压缩。
- 使用 Profiler:Vue DevTools 定位性能瓶颈。
评分维度:
- 能说出 4 种以上方法(40%)
- 能解释原理(30%)
- 能结合实际选择(30%)
补充题
参考答案:
Provide/Inject 用于祖先组件向后代组件传递数据,避免 props 层层传递。
示例:
// 祖先
const theme = ref('dark');
provide('theme', theme);
// 后代
const theme = inject('theme');
注意:
- 适合低频更新的全局数据,如主题、语言、用户信息。
- 默认不是响应式,注入 ref/reactive 后才能响应。
- 2.x 中 Provide/Inject 不是响应式的;3.x 中配合响应式 API 使用。
评分维度:
- 能解释概念(40%)
- 能说明使用场景(30%)
- 能写出基本示例并提到响应式(30%)
参考答案:
自定义指令用于直接操作 DOM,适合聚焦、权限、水印等场景。
示例:
const vFocus = {
mounted: (el) => el.focus()
};
// 使用
<input v-focus />
Vue 3 指令钩子:
created:绑定元素属性前。beforeMount:元素挂载前。mounted:元素挂载后(最常用)。beforeUpdate、updated:组件更新前后。beforeUnmount、unmounted:卸载前后。
评分维度:
- 能说出指令钩子(40%)
- 能写出示例(30%)
- 能说明适用场景(30%)
参考答案:
Suspense 用于处理异步依赖,在异步组件或异步 setup 完成前显示 fallback 内容。
示例:
<Suspense>
<template #default>
<AsyncComponent />
</template>
<template #fallback>
<Loading />
</template>
</Suspense>
特点:
- 可与异步组件
<AsyncComponent>配合使用。 - Vue 3 中 Suspense 是实验性特性,但已在实际项目中广泛使用。
- 提供了声明式的加载状态管理,替代手动
v-if控制 loading。
评分维度:
- 能解释概念(40%)
- 能说明与异步组件配合(30%)
- 能写出基本用法(30%)
参考答案:
| 维度 | Vue | React |
|---|---|---|
| 定位 | 渐进式框架 | 视图库 |
| 模板 | 模板语法(HTML-like) | JSX |
| 响应式 | 自动追踪依赖 | 手动 setState / useState |
| 状态管理 | 官方 Pinia / Vuex | Redux / Zustand / Jotai 等第三方 |
| 路由 | 官方 Vue Router | React Router 第三方 |
| 学习曲线 | 相对平缓 | 相对陡峭 |
共同点:
- 都基于组件化、虚拟 DOM、单向数据流。
- 都支持 Hooks / Composition API 风格的逻辑复用。
- 都有庞大的生态和社区。
选择:Vue 适合快速上手、模板清晰的项目;React 适合高度灵活、生态丰富的项目。
评分维度:
- 能说出 3 个以上区别(40%)
- 能客观评价(30%)
- 能说明共同点(30%)
参考答案:
Vue 3 在性能、TypeScript 支持、Composition API、Tree Shaking 等方面有显著提升,是Vue 生态的未来方向。
建议:
- 新项目:优先使用 Vue 3 + Vite + TypeScript +
<script setup>。 - 存量 Vue 2 项目:评估迁移成本,可采用渐进式迁移或官方迁移构建(Vue 2.7 提供了部分 Composition API)。
- 团队学习:重点掌握 Composition API、响应式原理、新生命周期和 Pinia。
不应盲目迁移,应结合业务需求、技术债和团队能力做决策。
评分维度:
- 能说明 Vue 3 优势(40%)
- 能说明迁移考虑(30%)
- 能给出建议(30%)
参考答案:
Vue 3.4 主要新特性:
- defineModel:简化
v-model实现,无需手动声明 props 和 emits。 - v-bind 同名简写:
<Child :title :user />等价于<Child :title="title" :user="user" />。 - 响应式系统优化:computed 更高效,watchEffect 清理逻辑更可靠。
- 类型推断改进:减少 TypeScript 类型错误和
any。
示例:
<script setup>
const modelValue = defineModel();
</script>
<template>
<input v-model="modelValue" />
</template>
评分维度:
- 能说出 3 个以上新特性(40%)。
- 能解释 defineModel 用法(30%)。
- 能说明 v-bind 同名简写(30%)。
参考答案:
Vapor Mode 是 Vue 正在开发的全新编译策略,放弃虚拟 DOM,编译为更细粒度的 DOM 操作指令。
优势:
- 更小的包体积。
- 更低的内存占用。
- 更快的更新性能(特定场景)。
特点:
- 不需要修改业务代码。
- 通过编译器选项开启(实验阶段)。
- 将进一步缩小 Vue 与 Svelte 等无虚拟 DOM 方案的差距。
评分维度:
- 能解释 Vapor Mode 概念(30%)。
- 能说明与传统虚拟 DOM 的区别(30%)。
- 能说出优势和当前状态(40%)。
参考答案:
Vue Router Data Loaders 是路由级数据加载方案,让数据获取逻辑从组件中分离出来。
示例:
import { defineLoader } from "vue-router";
export const useUserData = defineLoader(async (route) => {
const res = await fetch(`/api/users/${route.params.id}`);
if (!res.ok) throw new Error("User not found");
return res.json();
});
组件中使用:
<script setup>
const { data, error, isLoading } = useUserData();
</script>
解决问题:
- 数据加载逻辑分散。
- 导航等待体验难以统一。
- SSR 数据预取困难。
评分维度:
- 能解释 Data Loaders 概念(30%)。
- 能写出 defineLoader 示例(40%)。
- 能说明解决的问题(30%)。
领域编号:E07 Vue
最后更新:2026-06-24