Code前端首页关于Code前端联系我们

Vue3 有哪些核心新特性?

terry 2周前 (10-03) 阅读数 60 #Vue
文章标签 Vue3;核心新特性

p>现在前端圈里,Vue3 已经成了面试必考点,不管是校招、社招,面试官总会揪着 Vue3 的特性、原理、实战优化这些问题问,要是没把这些吃透,面试时很容易卡壳,这篇文章把 Vue3 高频面试题拆成一个个常见问题,结合实际开发场景给你讲明白,帮你面试时能条理清晰回答~
问题:Vue3 相比 Vue2 新增了哪些关键特性,能解决什么问题?

回答时可以从功能、性能、底层重构这几个维度展开:

组合式 API(Composition API),Vue2 用“选项式”(data、methods、computed 拆分)写代码时,一个功能的逻辑会分散在不同选项里,比如用户信息模块,变量在 data,方法在 methods,计算属性在 computed,维护时像“拼碎片”,组合式 API 允许把同一功能的逻辑封装到一起(比如用 setup 里的 ref、reactive 组织代码),复用性和可读性直接起飞,大型项目里优势特别明显。

然后是 Teleport 组件,它解决了“组件嵌套导致 DOM 结构受限”的痛点,比如写弹窗组件时,默认 DOM 会嵌套在父组件里,可能被父级的 overflow: hidden 或 z-index 影响,Teleport 能把组件渲染到指定 DOM 节点(body 下),做模态框、下拉菜单时再也不用纠结样式穿透了。

还有 Suspense 组件(虽处于实验阶段,但面试常提),它让异步组件加载更丝滑,比如页面里多个异步组件,Suspense 能统一处理“加载中”“加载失败”状态,不用每个组件自己写 loading 逻辑,省了不少重复代码。

性能层面,Vue3 做了不少优化:静态提升(Static Hoisting) 会把不依赖响应式数据的节点“提”到渲染函数外,避免每次渲染重复创建;Patch Flag 给动态节点打标记,更新时只处理有变化的部分,减少比对开销。

响应式系统重构,Vue2 用 Object.defineProperty 劫持属性,对数组、对象新增属性支持差(比如数组 push 不触发更新,得用 $set),Vue3 改用 ES6 Proxy,能监听整个对象,数组索引修改、对象增删属性都能精准捕获,响应式彻底多了。

组合式 API 怎么理解?和选项式 API 有啥区别?

问题:面试总问“组合式 API 和选项式 API 的差异”,得从哪些角度答?

要先理解设计初衷:选项式 API(Vue2 那套)适合小项目,把数据、方法、计算属性按“选项”分类,结构清晰,但项目一复杂,一个功能的逻辑就散在不同选项里,比如用户信息管理,data 存 userInfo,methods 写 fetchUser、updateUser,computed 搞 userFullName,维护时得来回翻代码,像“分散的逻辑碎片”。

组合式 API(基于 setup 函数)是把 同一功能的逻辑聚合,还是用户管理场景,能把 userInfo 定义、获取、更新逻辑全塞一个函数里,甚至抽成自定义 Hook(useUser),看代码时,找功能相关逻辑不用跳来跳去,复用也方便——把 useUser 复制到其他组件就能用。

选项式 API 里 this 指向容易踩坑(比如箭头函数里 this 不是组件实例),组合式 API 基本不用 this,变量和方法通过 setup 返回后直接用,更直观,不过选项式 API 没被淘汰,Vue3 支持两种写法:小项目用选项式更轻量,中大型项目用组合式更易维护。

ref 和 reactive 怎么选?toRef、toRefs 有啥用?

问题:这几个响应式 API 容易混淆,面试常考使用场景和区别,咋讲清楚?

先拆分 ref 和 reactive:ref 用来包装基本类型(字符串、数字、布尔这些),因为基本类型没法被 Proxy 直接代理,ref 内部用带 value 的对象包起来(const count = ref(0),修改得写 count.value++),但如果用 ref 包对象/数组,内部其实会调用 reactive 处理,ref 更灵活,基本、引用类型都能包。

reactive 专门处理对象或数组这类引用类型,返回 Proxy 代理后的对象,直接改属性就能触发更新(const state = reactive({ name: '张三' }),state.name = '李四' 会更新),但 reactive 有俩限制:不能直接赋值整个对象(state = { ... } 会让响应式失效,Proxy 被替换了),也不能处理基本类型。

再看 toRef 和 toRefs:toRef 是把响应式对象的某个属性,转成带 value 的 ref,比如有个 reactive 对象 user = { name: '张三', age: 18 },想把 name 单独抽成 ref 还保持响应式,就用 const nameRef = toRef(user, 'name'),修改 nameRef.value 会同步到 user.name,反过来也一样,场景是组件传参时,父组件传了个 reactive 对象,子组件只需要其中一个属性,用 toRef 能保持响应式连接。

toRefs 是把整个 reactive 对象的所有属性,转成 ref 组成的对象,比如解构 reactive 对象时,直接 const { name, age } = user 会失去响应式(解构后成普通变量了),用 const { name, age } = toRefs(user),这样 name 和 age 都是 ref,修改它们的 value 会同步到原对象,setup return 时,常用 toRefs 把 reactive 数据解构后返回,模板里就不用写 .value 了(模板里用 ref 会自动解包)。

Vue3 响应式原理和 Vue2 有啥不一样?

问题:面试官爱问响应式实现细节,得对比着讲清楚优缺点。

Vue2 用 Object.defineProperty 劫持属性:遍历对象每个属性,用 defineProperty 加 getter 和 setter,读取时触发 getter 收集依赖,修改时触发 setter 通知更新,但这有局限:

  • 对数组处理不彻底:Object.defineProperty 监听不到数组索引和 length 变化,Vue2 重写了数组原型(Array.prototype.push),但像 arr[0] = 1 这种改索引的操作,还是没法触发更新,得用 $set。
  • 新增/删除对象属性检测不到:给对象新增 key,Object.defineProperty 拦不住,Vue2 新增属性得用 $set,删除用 $delete。

Vue3 改用 ES6 的 Proxy 代理整个对象,优势很明显:

  • 能监听对象所有变化,包括新增、删除,还有数组索引修改、length 变化(arr.push、arr[0] = 1 都能触发更新)。
  • 懒代理特性:Proxy 不用像 Object.defineProperty 那样遍历所有属性,初始化时只代理对象,属性访问时再拦截,性能更好(尤其是大对象场景)。
  • 支持 Map、Set 等复杂数据结构(虽然项目里少用,但原理上支持)。

但 Proxy 兼容性差,IE 完全不支持(Vue3 放弃 IE 支持),而 Vue2 的 Object.defineProperty 能兼容到 IE9,Vue3 用 Proxy 换来了更强的响应式能力,代价是放弃旧浏览器。

Vue3 做了哪些性能优化?项目里怎么落地优化?

问题:性能优化是面试重点,得从框架层面和实战层面讲。

先讲 Vue3 框架自身的优化:

  1. 编译时优化

    • 静态提升(Static Hoisting):编译器把不依赖响应式数据的节点(比如纯静态的 div、文本)“提”到渲染函数外,每次渲染不用重新创建这些节点,减少开销,比如多个地方用的静态文本,只会创建一次。
    • Patch Flag:给动态节点打标记,更新时只处理带标记的节点,比如一个 div 里只有文本是动态的,编译器会给这个 div 加标记,更新时只检查文本有没有变,不用整个 div 比对,减少 diff 时间。
    • 缓存事件处理函数:@click 绑定的函数,编译后会被缓存,避免每次渲染重新创建函数。
  2. 响应式优化:用 Proxy 替代 Object.defineProperty,减少初始化性能消耗(不用遍历所有属性),响应式触发也更高效。

  3. 渲染器优化:重构虚拟 DOM 渲染逻辑,减少不必要的节点比对和操作。

再讲 项目实战中的优化(结合 Vue3 特性):

  • 合理使用响应式 API:不需要响应式的变量,别用 ref/reactive,比如纯展示的静态数据,用普通变量就行,减少响应式追踪开销。
  • 组件级优化
    • 缓存组件实例,避免重复创建销毁(Tab 切换场景)。
    • 对频繁更新的组件,用 defineOptions({ inheritAttrs: false }) 减少不必要的属性继承,或者用 v-once 让组件只渲染一次(如果内容静态)。
  • 异步组件与代码分割:用 defineAsyncComponent 定义异步组件,配合 Suspense 做加载态,webpack/vite 会自动代码分割,减少首屏加载体积。
  • 依赖优化:用 vite 的按需导入(Element Plus 用 unplugin-vue-components 自动按需引入组件),减少打包体积。
  • 性能监测:用 Vue DevTools 里的性能面板,记录组件渲染时间,定位渲染频繁的组件,针对性优化(比如用 memo 缓存计算结果,或用 shallowRef 减少深层响应式追踪)。

Vue3 生态工具都有哪些更新?(Vite、Pinia、Vue Router 4)

问题:现在面试也爱考生态,得把常用工具的变化讲清楚。

生态工具是 Vue3 技术栈的重要部分,几个核心工具变化很大:

Vite 替代 Webpack(作为推荐构建工具)

Vue2 时代主流是 Webpack,但 Vite 用 ES 模块原生支持(浏览器支持 import 语句)和 Rollup 打包,开发时启动速度飞快(不用等打包,直接启动服务),热更新(HMR)也更丝滑,比如改个组件,Vite 能精准更新,不用整个页面刷新,生产环境用 Rollup 打包,体积更小,Vue 官方脚手架 create-vue 默认用 Vite,所以得了解 Vite 的配置(vite.config.js 里的 alias、plugins)、环境变量(import.meta.env)这些。

Pinia 替代 Vuex 成为状态管理新宠

Vuex 是 Vue2 经典状态管理,但写法繁琐(要定义 mutations、actions、modules 等),Pinia 是 Vue 团队成员开发的,更轻量、简洁:

  • 不需要 mutations,修改状态直接在 actions 里写(或直接修改,因为 Pinia 支持非严格模式)。
  • 语法和组合式 API 无缝衔接,比如在 store 里用 ref、reactive 定义状态,用 computed 做计算属性,用函数写逻辑。
  • 自动代码分割,每个 store 独立打包,按需加载。
    现在新项目基本用 Pinia,所以得掌握 defineStore 的用法,怎么定义状态、动作,怎么在组件里使用 store。

Vue Router 4 的变化

Vue Router 4 是适配 Vue3 的版本,有这些关键更新:

  • 路由组件用组合式 API:useRouter、useRoute 替代 this.$router、this.$route,在 setup 里更方便。
  • 路由配置支持 Composition API:可以在路由守卫里用组合式逻辑。
  • 动态路由匹配更灵活,比如用 defineAsyncComponent 做异步路由组件。
  • 移除了一些旧 API, 的 tag 属性改成 custom 插槽,更灵活但也需要适应。

这些生态工具的更新,本质是让 Vue3 技术栈更高效、简洁,面试时得体现对整个生态的了解,而不只是框架本身。

Vue3 中怎么处理跨组件通信?

问题:组件通信是面试经典题,Vue3 有哪些新方式?

Vue3 里组件通信方式更丰富了,传统方式还能用,也有新方法:

  1. Props / Emits:最基础的父子通信,和 Vue2 类似,但 Vue3 里 emits 可以在组件选项里显式声明(defineEmits),更规范,还能做参数校验。

  2. Provide / Inject:跨多层级组件通信,Vue3 里可以用组合式 API 的 provide 和 inject,而且能结合响应式,比如父组件 provide('theme', ref('dark')),子组件 inject('theme') 后,修改 theme.value 能触发子组件更新。

  3. mitt 等事件总线:Vue2 里常用 EventBus,Vue3 没有内置事件总线,所以可以用第三方库 mitt,原理是创建一个事件发射器,$on 监听,$emit 触发,$off 销毁,适合兄弟组件或无关联组件通信。

  4. Pinia 状态管理:全局状态用 Pinia 管理,组件里通过 store 读写状态,适合复杂场景下的多组件共享数据。

  5. Teleport + 状态管理:比如弹窗组件用 Teleport 渲染到 body,同时通过 Pinia 或 Provide/Inject 传递状态,控制弹窗的显示隐藏。

不同场景选不同方式:父子通信用 Props/Emits;跨多层用 Provide/Inject;全局复杂状态用 Pinia;简单兄弟通信用事件总线。

Vue3 里的自定义指令怎么写?和 Vue2 有啥区别?

问题:自定义指令属于进阶考点,得讲清楚生命周期和用法变化。

Vue3 自定义指令的生命周期钩子变了,更贴合组合式 API 的逻辑:

Vue2 里自定义指令是这几个钩子:bind(绑定)、inserted(插入 DOM)、update(更新)、componentUpdated(组件更新完成)、unbind(解绑)。

Vue3 里改成了:created(指令绑定到元素前,还没生成 DOM)、beforeMount(绑定元素到父节点前)、mounted(绑定元素插入 DOM 后)、beforeUpdate(组件更新前)、updated(组件更新后)、beforeUnmount(组件卸载前)、unmounted(组件卸载后)

写法上,Vue3 可以用对象式或函数式定义:

  • 全局指令:app.directive('focus', { mounted(el) { el.focus() } })
  • 局部指令:组件里 directives: { focus: { mounted(el) { el.focus() } } }

如果逻辑简单,也能写成函数:app.directive('focus', (el) => { el.focus() }),这个函数会在 mounted 和 updated 时执行(类似 Vue2 里的 bind 和 update 合并)。

应用场景比如做输入框自动聚焦(v-focus)、权限控制(v-permission)、自定义滚动指令(v-scroll)等,和 Vue2 相比,生命周期命名更直观,和组件的生命周期钩子对齐,更容易理解什么时候执行逻辑。

Vue3 中怎么处理错误边界?

问题:错误边界是保证应用鲁棒性的关键,Vue3 怎么实现?

错误边界用来捕获组件渲染、事件处理中的错误,防止整个应用崩溃,Vue3 里可以用 onErrorCaptured 钩子(组合式 API)或者 errorCaptured 选项(选项式 API)。

比如用组合式 API:

<script setup>
import { onErrorCaptured } from 'vue'
onErrorCaptured((err, instance, info) => {
  // err 是错误对象
  // instance 是出错的组件实例
  // info 是错误发生的阶段("render"、"mount"、"update" 等)
  console.log('捕获到错误:', err)
  // 返回 true 继续向上传播错误,返回 false 停止传播
  return true
})
</script>

或者选项式 API:

<script>
export default {
  errorCaptured(err, instance, info) {
    console.log('捕获到错误:', err)
    return true
  }
}
</script>

错误边界组件要包裹可能出错的子组件,

<template>
  <ErrorBoundary>
    <RiskyComponent />
  </ErrorBoundary>
</template>

需要注意,onErrorCaptured 只能捕获子组件的错误,不能捕获自身的;而且异步错误(setTimeout 里的)默认捕获不到,需要结合其他方式(比如给异步操作加 try-catch),现在前端项目里,错误边界常用在页面级、模块级,捕获子组件错误后展示 fallback 内容(比如错误提示页),提升用户体验。

Vue3 支持 TS 的表现怎么样?开发中怎么结合 TS?

问题:现在前端都重视 TypeScript,得讲清楚 Vue3 对 TS 的支持。

Vue3 从架构上对 TS 更友好,主要体现在:

  1. API 设计更易类型推导:组合式 API 里的 ref、reactive、defineProps、defineEmits 等都支持泛型,比如定义 props:
    const props = defineProps<{ string;
    count?: number;
    }>()

    或者用运行时校验 + 类型:

    const props = defineProps({ String,
    count: {
     type: Number,
     required: false
    }
    } as

版权声明

本文仅代表作者观点,不代表Code前端网立场。
本文系作者Code前端网发表,如需转载,请注明页面地址。

发表评论:

◎欢迎参与讨论,请在这里发表您的看法、交流您的观点。

热门