Vue3 高频面试题,这些知识点面试前一定要搞懂
组合式 API 和选项式 API 有啥区别?该怎么选?
很多同学刚接触 Vue3 时,会纠结用组合式 API(Composition API)还是选项式 API(Options API),简单说,选项式是“按功能拆分”,把数据、方法、生命周期分散在 data、methods、computed 这些选项里;组合式是“按逻辑聚合”,通过 setup 函数把相关逻辑(比如获取用户信息、处理表单提交)集中在一块,用 ref、reactive 这些 API 组织代码。
选的时候看场景:如果是新手写小项目,选项式更直观,不用记新 API;但中大型项目、逻辑复杂的页面(比如表单校验 + 异步请求 + 权限控制),组合式优势就来了——它能通过「自定义 Hook」复用逻辑(比如把请求用户信息的逻辑封装成 useUser,多个组件直接 import),还能避免选项式里 mixins 命名冲突、逻辑来源模糊的问题,组合式对 TypeScript 更友好,类型推导一步到位,写 TS 项目优先选它。
Vue3 响应式原理和 Vue2 有啥不一样?Proxy 好在哪?
Vue2 靠 Object.defineProperty 实现响应式:遍历对象每个属性,给属性加 getter/setter 劫持读写,但有不少痛点——比如新增属性、删除属性监听不到(Vue2 要写 $set、$delete),数组下标修改也监听不到(得用 splice 这类重写的方法)。
Vue3 换成了 Proxy,直接代理整个对象,能监听更多操作:新增属性、删除属性、数组长度变化都能感知到,不用再手动调 $set。Proxy 是“懒代理”,访问对象属性时才去处理,不像 Object.defineProperty 要递归遍历所有属性(大对象性能差),举个例子,一个有 100 个属性的对象,Vue2 初始化时要遍历 100 次加劫持,Vue3 只在访问某个属性时才处理,性能和灵活性都提升了。
setup 函数是干啥的?执行时机和参数咋理解?
setup 是组合式 API 的入口,在组件创建前执行(比 beforeCreate 还早),负责准备组件的响应式数据、方法这些,它接收两个参数:
props:父组件传的属性,是“响应式”的!但注意不能直接解构,否则会失去响应性(const { msg } = props后,msg就不是响应式了,得用toRefs(props)解构)。context:包含attrs(父组件非props属性)、slots(插槽)、emit(触发自定义事件)、expose(暴露方法给父组件)这些,是“非响应式”的,解构了也没事。
setup 返回的对象或函数,会变成模板里能直接用的变量/方法,比如返回 { count, increment },模板里就能写 {{ count }} 和 @click="increment"。
ref 和 reactive 区别是啥?咋选场景?
一句话总结:ref 更灵活,reactive 更“整”。
ref既能包基本类型(字符串、数字、布尔),也能包对象数组,它内部靠value属性实现响应式(const count = ref(0),改的时候要count.value++)。reactive只能代理对象/数组,而且不能直接解构(解构后变成普通对象,失去响应性),它适合给“整个对象”做响应式,比如用户信息对象const user = reactive({ name: '张三', age: 18 })。
场景建议:基本类型优先用 ref;对象/数组如果需要“整体替换”(user = { name: '李四' }),用 ref 包对象更方便;如果只是修改对象内部属性(user.age++),用 reactive 更简洁。
computed 和 watch 在 Vue3 咋用?和 Vue2 有啥区别?
先看 computed:Vue3 里是个函数,导入后用 const 全名 = computed(() => 依赖值计算),还支持“可写计算属性”(传对象,配 get 和 set),比如做购物车总价:
const total = computed(() => goodsList.reduce((sum, item) => sum + item.price, 0))
再看 watch:Vue3 有 watch 和 watchEffect 两个 API。watch 要“指定监听源”(ref、reactive 的属性、getter 函数),支持配置 immediate(立即执行)、deep(深度监听)。watchEffect 更“懒”,自动收集依赖,执行时用到啥就监听啥,适合处理副作用(比如请求数据)。
和 Vue2 区别:Vue2 的 computed、watch 是写在选项里的,Vue3 能在 setup 或自定义 Hook 里用,更灵活。watchEffect 是 Vue3 新增的,写异步逻辑、自动依赖收集特别香。
Composition API 比 mixins 好在哪?
mixins 是 Vue2 里复用逻辑的方案,但坑很多:命名冲突(多个 mixins 有同名方法,后引入的会覆盖)、依赖模糊(组件里用了某个方法,分不清是自己写的还是 mixins 来的)、逻辑分散(数据和方法散在各个 mixins,维护时要跳来跳去)。
Composition API 用「自定义 Hook」解决这些问题:比如把“获取用户信息 + 处理用户状态”的逻辑封装成 useUser(),组件里 import { useUser } from './hooks' 后,直接用返回的 userInfo、updateUser,逻辑来源清晰,还能传参控制(useUser(123) 指定用户 ID),而且和 TS 结合更丝滑,类型能自动推导,不用像 mixins 那样手动声明类型。
Teleport 组件是干啥的?项目里咋用?
Teleport 直译是“传送门”,作用是把组件里的内容“传送”到页面任意 DOM 节点(body 标签里),最典型的场景是模态框、下拉菜单——如果它们被嵌套在有 overflow: hidden 或 z-index 层级低的父组件里,样式会乱掉,用 Teleport 把弹窗内容直接挂到 body 下,就能避免这些问题。
用法很简单:
<template>
<button @click="showModal = true">打开弹窗</button>
<Teleport to="body">
<div v-if="showModal" class="modal">
我是弹窗内容,现在渲染到 body 里啦!
</div>
</Teleport>
</template>
注意 to 可以是 CSS 选择器(#app、.container)或 DOM 元素,Teleport 内部的组件和父组件上下文是通的,能正常用 props、emit 这些。
Suspense 组件咋理解?适合啥场景?
Suspense 是 Vue3 里处理异步组件加载的“占位组件”,能优雅展示“加载中”“加载错误”状态,比如路由懒加载时,组件还没下载完,就显示个 Loading 动画;或者组件依赖异步数据(比如请求接口后渲染),也能用 Suspense 控制加载状态。
用法示例:
<template>
<Suspense>
<template #default>
<AsyncComponent /> <!-- 异步组件,比如用 defineAsyncComponent 定义 -->
</template>
<template #fallback>
<div>加载中...</div>
</template>
</Suspense>
</template>
场景建议:路由组件懒加载、依赖异步数据的组件(比如图表组件要等接口返回数据才渲染),不过要注意,Suspense 现在需要结合错误边界(onErrorCaptured 钩子)处理加载失败的情况,避免页面崩掉。
Vue3 自定义指令咋实现?和 Vue2 有啥区别?
Vue3 自定义指令的钩子函数更贴近组件生命周期,
created:指令绑定到元素前调用(还没创建 DOM)beforeMount:元素挂载到 DOM 前mounted:元素挂载后(常用,比如做自动聚焦)beforeUpdate:组件更新前updated:组件更新后beforeUnmount:元素卸载前unmounted:元素卸载后
注册方式分全局和局部:
// 全局指令:main.js
app.directive('focus', {
mounted(el) {
el.focus() // 挂载后自动聚焦
}
})
// 局部指令:组件内
directives: {
focus: {
mounted(el) {
el.focus()
}
}
}
和 Vue2 区别:Vue2 的钩子是 bind(绑定)、inserted(插入 DOM)、update 这些,Vue3 改名后更直观,和组件生命周期的命名逻辑统一了。
Vue3 性能优化做了哪些改进?开发咋用这些特性?
Vue3 从编译、响应式、打包三方面做了优化:
-
编译优化:
- 静态提升:把不变的节点(比如纯文本、无指令的元素)编译后复用,减少创建开销。
<div>固定文案</div>会被标记为静态节点,只创建一次。 - 补丁标记:给动态节点加更细的标记(
v-if和v-for节点区分开),Diff 时能快速跳过静态节点,减少比对时间。 - 事件缓存:内联事件处理函数(
@click="() => doSomething")会被自动缓存,避免每次渲染都创建新函数。
- 静态提升:把不变的节点(比如纯文本、无指令的元素)编译后复用,减少创建开销。
-
响应式优化:用
Proxy懒代理对象,访问属性时才处理响应式,不用像 Vue2 那样递归遍历所有属性,大对象性能更好。 -
Tree-shaking:Vue3 的 API 是 ES 模块导出,打包时只包含用到的部分(比如只用了
ref就不会打包reactive),体积更小。
开发时咋用?比如写模板时,把静态内容尽量提取到组件外;事件处理函数用内联写法(Vue3 会自动缓存);配合 Vite 打包,利用其基于 ESM 的快速启动和热更新。
Pinia 和 Vuex 咋选?核心区别是啥?
Pinia 是 Vue 官方现在主推的状态管理库,比 Vuex 更轻量、简洁,核心区别:
- 语法层面:Vuex 需要定义
mutations(必须同步)、actions(异步)、getters,结构繁琐;Pinia 没有mutations,只有state、getters、actions(actions 能同步也能异步),用defineStore定义 Store,写法更自由。 - TS 支持:Pinia 对 TypeScript 更友好,状态、方法的类型能自动推导,不用像 Vuex 那样手动写很多类型声明。
- 体积和性能:Pinia 体积更小(不到 Vuex 的 1/3),性能更好,没有 Vuex 里复杂的模块嵌套逻辑。
新项目直接选 Pinia 就行,简单高效;老项目如果用 Vuex 且不想迁移,继续用也没问题。
Vite 和 Vue CLI 构建 Vue3 项目有啥不同?为啥推荐 Vite?
核心差异在构建原理:
- Vue CLI 基于 Webpack,开发时要把所有代码打包后再启动,项目大了启动慢、热更新也慢;
- Vite 基于 ES 模块(ESM),开发时不打包,直接按需加载浏览器支持的 ESM 文件,启动秒开,热更新也是毫秒级。
Vite 打包用 Rollup,比 Webpack 更高效,体积更小;配置也更简洁,不用写一堆 loader、plugin,Vue3 官方模板默认用 Vite,就是因为开发体验好太多,Webpack 生态更成熟,如果项目需要复杂的自定义打包逻辑(比如老项目迁移、特殊 loader),暂时用 Vue CLI 也能过渡,但长远看 Vite 是趋势。
Vue2 项目迁 Vue3 要注意啥?
迁移分“语法兼容”和“代码重构”两步:
-
Breaking Change 处理:
- 过滤器(
filter)移除:改用方法或计算属性。{{ msg | uppercase }}改成{{ toUppercase(msg) }}。 - 实例方法移除:
$on、$off、$once没了,事件总线得换第三方库(mitt)。 - 生命周期改名:
beforeDestroy→beforeUnmount,destroyed→unmounted。 v-model语法:Vue3 里v-model等价于modelValue+@update:modelValue,还支持多个v-model(<Child v-model:name="name" v-model:age="age" />)。
- 过滤器(
-
代码重构:
- 逐步替换组件:先用 Vue 官方的「迁移构建工具」(
@vue/compat),让 Vue3 兼容 Vue2 API,再慢慢把组件改成组合式 API。 - 第三方库兼容:检查 UI 库、请求库等是否支持 Vue3,不支持的话得找替代方案。
- 逐步替换组件:先用 Vue 官方的「迁移构建工具」(
如果项目不大,直接用组合式 API 重写更彻底;项目大就用迁移工具分步来,风险更低。
Vue3 结合 TypeScript 有啥实践技巧?
Vue3 对 TS 支持比 Vue2 好太多,这些技巧能少踩坑:
-
组件定义:用
defineComponent包裹组件,让 TS 推导组件选项类型。import { defineComponent } from 'vue' export default defineComponent({ props: { msg: String }, setup() { /* ... */ } }) -
响应式数据类型:
ref用泛型(ref<number>(0)),reactive用接口(interface User { name: string }; const user = reactive<User>({ name: '' }))。 -
Props 类型:用 TS 语法声明
defineProps<{ msg: string; count?: number }>(),还能结合withDefaults设默认值:withDefaults(defineProps<{ msg: string; count?: number }>(), { count: 0 }) -
Emits 类型:用
defineEmits<{ (e: 'change', value: number): void }>()明确事件和参数类型,避免传错参数。 -
自定义 Hook:给 Hook 的参数、返回值写清楚类型,
function useFetch<T>(url: string): Ref<T | null> { ... },复用时代码提示更友好。
Vue3 的 SSR 和 Vue2 比有啥改进?开发要注意啥?
Vue3 对 SSR(服务端渲染)的支持更高效,主要体现在:
- 异步处理更丝滑:结合 Composition API 和 Suspense,能优雅处理异步组件、异步数据加载(比如组件里用
await请求数据,Suspense 自动处理加载状态)。 - 性能提升:基于 Vite 的 SSR 方案启动更快,打包更优,内存占用更低。
开发时要注意这些点:
- 避免浏览器 API:服务端渲染时没有
window、document,DOM 操作要放到onMounted等客户端钩子中。 - 样式处理:如果用 CSS-in-JS 或 scoped CSS,要确保服务端能正确注入样式,避免客户端 Hydration 时样式错乱。
- 状态序列化:服务端渲染的状态要传递到客户端(比如用
window.__INITIAL_STATE__),否则客户端重新请求数据会导致界面闪烁(Hydration Mismatch)。
Nuxt3 基于 Vue3 和 Vite,把 SSR 配置简化了,想做服务端渲染的项目可以优先考虑。
Vue3 面试绕不开「响应式原理变化」「组合式 API 深度」「新组件(Teleport/Suspense)」「性能优化」「生态工具(Pinia/Vite)」这些核心点,把这些问题吃透,不仅面试更有底气,实际开发时也能更高效地驾驭框架~
版权声明
本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。
code前端网

