面试题
Vue2 Vue3 生命周期
- Vue 实例从创建到销毁的过程就是生命周期,主要钩子包括创建、挂载、更新和销毁四个阶段 。
- Vue2 常用钩子为 beforeCreate、created、beforeMount、mounted、beforeUpdate、updated、beforeDestroy、destroyed。
- Vue3 中将销毁阶段钩子更名为 beforeUnmount 和 unmounted,其余基本保持一致,但执行时机和底层实现有所优化 。
Vue 2 和 Vue 3 的响应式原理有什么不同
- Vue2 使用 Object.defineProperty 劫持对象属性的 getter 和 setter 来实现响应式,无法检测对象属性的新增或删除,以及数组索引和长度的变化 。
- Vue3 改用 Proxy 代理对象,可以直接监听对象属性的增删改查,性能更好且支持更多数据类型 。 数组处理上,Vue 2 重写了 push、pop、splice 等 7 种变异方法来触发更新,而 Vue 3 的 Proxy 能直接拦截数组索引变化
Vue 组件间如何进行通信
- 父传子:使用 props 属性,支持静态值或动态绑定,数据流是单向的 。
- 子传父:子组件通过 $emit 触发事件,父组件监听事件接收参数 。
- 跨级通信:使用 provide/inject 依赖注入,或状态管理工具如 Vuex、Pinia。
- 兄弟组件:可通过事件中心(Event Bus)或共同父组件中转,Vue 3 中推荐使用状态管理或 props/$emit 组合 。
computed 和 watch 有什么区别,应用场景是什么
computed 是计算属性,支持缓存,只有依赖数据变化时才重新计算,适合处理复杂的逻辑运算并返回结果 。 watch 是侦听器,不支持缓存,数据变化时直接执行回调,适合执行异步操作或开销较大的任务 。 Vue3相比Vue2 有哪些主要改进? 答案:
采用Composition API,更灵活的代码组织方式
基于Proxy实现的响应式系统,性能更好,支持数组索引和对象新增属性
重构了虚拟DOM,优化了渲染性能
支持Tree-shaking,减小打包体积
新增了Teleport、Suspense等组件
更好的TypeScript支持
模板语法增强,如多根节点、指令合并等
什么是setup函数?
-setup函数是Composition API的入口点,在组件创建前执行(props解析后)。它接收props和context两个参数,返回的对象会暴露给模板使用。setup函数中不能使用this,因为组件实例尚未创建完成。
ref和reactive的区别是什么?
-
数据类型 :ref用于包装基本类型数据,reactive用于包装对象类型数据
-
返回值 :ref返回的是一个带有value属性的对象,reactive返回的是原始对象的代理
-
访问方式 :访问ref包装的数据需要通过.value,在模板中会自动解包
-
修改方式 :对ref包装的对象进行修改,需要修改.value属性;对reactive包装的对象可以直接修改属性
-
响应式深度 :ref对对象类型数据会自动转换为reactive
computed和watch的区别是什么?
-
用途 :computed用于计算派生值,watch用于监听数据变化并执行副作用
-
缓存机制 :computed有缓存机制,只有依赖变化时才会重新计算;watch没有缓存
-
返回值 :computed必须有返回值,watch可以没有
-
异步操作 :computed不能执行异步操作,watch可以
-
触发时机 :computed在依赖变化时立即计算,watch默认在数据变化后执行
什么是toRef和toRefs?
-
toRef用于将响应式对象的某个属性转换为ref
-
toRefs用于将响应式对象的所有属性转换为ref对象,并返回一个包含这些ref的普通对象
-
这两个函数常用于在解构响应式对象时保持响应性
provide和inject的作用是什么?
provide和inject用于实现跨层级组件通信。父组件通过provide提供数据,子组件或孙组件通过inject注入数据。这种方式可以避免props drilling(props层层传递)的问题。
什么是作用域插槽?
作用域插槽允许子组件向父组件传递数据,父组件可以根据这些数据来渲染插槽内容。在Vue3中,作用域插槽通过v-slot指令和slotProps实现。
什么是响应式失效?如何解决?
-
响应式失效是指当修改响应式数据时,视图没有更新。常见原因包括:
-
直接修改了数组的索引或长度
-
给对象新增属性时没有使用正确的方法
-
在setup函数中返回的不是响应式数据 解决方法:
-
使用数组的push、pop等方法,或使用toRefs将数组转换为ref
-
给对象新增属性时使用Vue.set或确保对象是reactive创建的
-
确保setup函数返回的是响应式数据
Vue3中的响应式代理有哪些局限性?
无法直接检测对象的新增属性(需确保对象是 reactive 创建的或手动触发更新)
无法直接检测数组的索引和长度变化(需使用数组方法或 Vue.set 变体)
无法直接检测到Map、Set的某些变化(需手动触发更新或使用 reactive 包裹)
深度响应式可能影响性能(可用 shallowReactive 或 markRaw 优化)
Vue3的虚拟DOM有哪些改进?
优化了diff算法,减少了比较次数
支持静态提升,将静态节点提升到渲染函数之外
支持PatchFlag,标记动态节点的变化类型
支持缓存事件处理函数,避免不必要的重新创建
支持block tree,提高diff效率
如何优化Vue3的渲染性能?
使用v-once指令缓存静态内容
使用v-memo指令缓存动态内容
避免不必要的响应式数据
使用shallowReactive和shallowRef减少响应式开销
合理使用组件拆分,提高组件复用性
使用keep-alive缓存组件状态
优化大型列表渲染(如虚拟滚动)
什么是v-memo指令?
v-memo指令用于缓存模板中的一部分内容,只有当依赖的数据变化时才会重新渲染。它接收一个依赖数组,当数组中的任何一个元素变化时,才会重新渲染缓存的内容。v-memo可以用于优化频繁渲染的列表或复杂组件
Vue3中的自定义指令有哪些变化?
Vue3中的自定义指令使用钩子函数而不是对象字面量
钩子函数与组件生命周期钩子类似,包括beforeMount、mounted、beforeUpdate、updated、beforeUnmount、unmounted
指令的binding参数包含更多信息,如instance、value、oldValue等
支持传递多个值给指令
什么是Teleport组件?
Teleport组件用于将组件的内容渲染到DOM中的指定位置,而不是当前组件的位置。这对于模态框、通知等组件非常有用,可以避免CSS样式的干扰。
什么是Suspense组件?
Suspense组件用于处理异步组件的加载状态。它可以定义一个fallback内容,在异步组件加载完成前显示。Suspense还支持嵌套的异步依赖。示例:
Pinia和Vuex的区别是什么?
Pinia是Vue3推荐的状态管理库,Vuex是Vue2的官方状态管理库
Pinia没有mutations,直接通过actions修改状态
Pinia支持TypeScript,类型推断更好
Pinia的API更简洁,易于使用
Pinia支持多个store实例,Vuex通常只有一个store实例
Pinia的体积更小,性能更好
Pinia支持devtools,调试更方便
如何在Pinia中定义一个store?
可以使用defineStore函数定义一个store。它接收两个参数:store的名称和一个包含state、actions、getters的对象。state是一个函数,返回初始状态;actions是修改状态的方法;getters是计算属性。
如何在组件中使用Pinia的store?
可以使用useStore函数(需要传入store的定义)在组件中获取store实例,然后通过store实例访问state、getters和actions。
什么是状态持久化?如何实现?
状态持久化是指将状态保存到本地存储(如localStorage、sessionStorage)中,以便在页面刷新或重新打开时恢复状态。可以使用pinia-plugin-persistedstate等插件实现,也可以手动在actions中保存状态。
如何在Pinia中实现模块化?
Pinia本身支持模块化,每个store都是一个独立的模块。可以根据功能将状态分为多个store,如userStore、productStore等。
Vue Router 4有哪些变化?
支持Composition API,可以在setup中使用useRouter、useRoute等钩子
路由配置简化,不再需要new Router,而是使用createRouter
支持History API的多种模式,包括createWebHistory、createWebHashHistory、createMemoryHistory
支持动态路由匹配,如/user/:id
支持导航守卫,包括全局守卫、路由守卫、组件守卫
支持路由元信息
支持路由懒加载
什么是路由守卫?
路由守卫是Vue Router提供的一种机制,用于在路由切换过程中执行一些操作,如验证权限、加载数据等。路由守卫包括:
全局守卫 :beforeEach、beforeResolve、afterEach
路由守卫 :beforeEnter
组件守卫 :beforeRouteEnter、beforeRouteUpdate、beforeRouteLeave 示例(全局守卫)
如何实现路由懒加载?
路由懒加载是指将路由组件按需加载,而不是在初始加载时全部加载。在Vue Router 4中,可以使用动态import语法实现路由懒加载
如何优化大型列表的渲染?
使用虚拟滚动库(如vue-virtual-scroller),只渲染可视区域内的列表项
使用v-memo指令缓存列表项
避免在列表项中使用复杂的计算属性
合理设置key,避免不必要的DOM操作
懒加载列表数据,分页加载
避免在列表渲染时进行 heavy 计算
什么是组件按需加载?
组件按需加载是指将组件拆分为单独的代码块,只在需要时才加载。在Vue3中,可以使用动态import语法和defineAsyncComponent函数实现组件按需加载。这可以减小初始打包体积,提高页面加载速度。
什么是tree-shaking?Vue3是如何支持的?
tree-shaking是一种优化技术,用于移除代码中未使用的部分。Vue3的API设计支持tree-shaking,因为大多数API都是通过ES模块导出的,打包工具(如webpack、rollup)可以分析代码依赖,移除未使用的API。这可以减小打包体积。
Vue3中的TypeScript 支持如何?
Vue3本身是用TypeScript编写的,提供了更好的TypeScript支持。Composition API的设计更符合TypeScript的类型系统,setup函数、ref、reactive等API都有良好的类型推断。此外,Vue3还提供了defineComponent函数,用于定义带类型的组件。
Vue的核心特性有哪些?
答:核心特性主要是这5点:① 数据驱动视图(MVVM思想,数据变视图自动更,不用手动操作DOM);② 组件化开发(页面拆成可复用的独立组件,提升效率);③ 双向数据绑定(v-model简化表单交互);④ 虚拟DOM(减少真实DOM操作,提性能);⑤ 指令系统(v-if、v-for等简化DOM操作逻辑)。
怎么理解Vue是"渐进式框架"?
答:渐进式是指Vue核心功能轻量,能根据需求逐步加扩展功能,不用一次性全引入。比如基础开发只用核心的数据绑定、组件;需要路由加vue-router;需要状态管理加Vuex/Pinia;需要工程化加Webpack/Vite。适配从小脚本到大型应用的不同场景,学习成本也能逐步提升。
Vue实例的生命周期钩子和普通方法有啥本质区别?
答:① 触发时机:钩子由Vue在实例特定阶段自动调(比如created在实例创建后),普通方法要手动调(比如点击事件触发);② 执行环境:钩子内this指向Vue实例,不用手动绑定;③ 用途:钩子用于初始化、请求数据、DOM操作等生命周期节点,普通方法处理业务逻辑。
Vue3的生命周期钩子和Vue2有哪些区别?
答:① 命名:组合式API里钩子加"on"(created→onCreated),选项式API名称不变;② 新增:onRenderTracked(追踪渲染依赖)、onRenderTriggered(触发渲染时调),用于调试;③ 销毁钩子:beforeDestroy/destroyed改成beforeUnmount/unmounted(语义更准);④ 执行:组合式API的钩子写在setup里,无需this访问实例。
Vue Router的核心作用是什么?有哪些常用路由模式?区别是什么?
答: ① 核心:实现SPA路由跳转,URL变不刷新页面,切换组件视图; ② 模式: hash模式(基于URL#,兼容好,URL带#) history模式(基于History API,URL无#,需后端配置,否则刷新404)。
自定义指令
自定义指令通过使用下列钩子来创建,以对齐元素的生命周期,它们都是可选的:
bind - 指令绑定到元素后调用。只调用一次。 inserted - 元素插入父 DOM 后调用。 update - 当元素更新,但子元素尚未更新时,将调用此钩子。 componentUpdated - 一旦组件和子级被更新,就会调用这个钩子。 unbind - 一旦指令被移除,就会调用这个钩子。也只调用一次
js
Vue.directive('highlight', {
bind(el, binding, vnode) {
el.style.background = binding.value;
},
});