vue 面试题
生命周期API
| Vue2 | Vue3 |
|---|---|
beforeCreate 挂载前 | ❌setup(替代) |
created 挂载中 | ❌setup(替代) |
beforeMount | onBeforeMount |
mounted | onMounted |
beforeUpdate | onBeforeUpdate |
updated | onUpdated |
beforeDestroy | onBeforeUnmount |
destroyed | onUnmounted |
errorCaptured | onErrorCaptured 子组件错误捕获(setup,render,声明周期,watch/computed) |
| - | 🎉onRenderTracked (渲染依赖了谁) 调试组件 |
| - | 🎉onRenderTriggered (谁触发了渲染)调试组件 |
keep-alive
onActivated | 当组件被插入到DOM中时调用 |
|---|---|
onDeactivated | 当组件从DOM中被移除时调用 |
vue-router
| 名字 | 作用 | 类型 |
|---|---|---|
| beforeEach | 路由跳转前 (登录鉴权/权限校验/白名单判断) | 全局 |
| beforeResolve | 在所有组件内守卫之后,确认跳转前(等待异步数据准备完成) | 全局 |
| afterEach | 页面埋点 /页面标题修改 | 全局 |
| beforeEnter | 单独页面的权限控制 | 路由独享 |
| beforeRouteEnter | 进入组件前 | 组件本身 |
| beforeRouteUpdate | 路由参数变化 | 组件本身 |
| beforeRouteLeave | 表单未保存提示/弹窗确认离开 | 组件本身 |
生命周期调用顺序
父子组件生命周期顺序:
挂载阶段(父 → 子 → 父): 父
setup->父onBeforeMount->子setup->子onBeforeMount->子onMounted->父onMounted
更新阶段(父 → 子 → 父): 父
onBeforeUpdate->子onBeforeUpdate->子onUpdated->父onUpdated
卸载阶段(父 → 子 → 父): 父
onBeforeUnmount->子onBeforeUnmount->子onUnmounted->父onUnmounted
vue-router生命周期顺序:
beforeRouteLeave(组件)router.beforeEachbeforeEnter(路由独享)beforeRouteEnter(组件)router.beforeResolverouter.afterEach
数据绑定及原理
Vue2
通过 ES5 的
Object.defindeProperty中的访问器属性中的 get 和 set 方法,data 中声明的属性都被添加了访问器属性,当读取 data 中的数据时自动调用 get 方法,当修改 data 中的数据时,自动调用 set 方法,检测到数据的变化,会通知观察者 Wacher,观察者 Wacher自动触发重新render 当前组件(子组件不会重新渲染),生成新的虚拟 DOM 树,Vue 框架会遍历并对比新虚拟 DOM 树和旧虚拟 DOM 树中每个节点的差别,并记录下来,最后,加载操作,将所有记录的不同点,局部修改到真实 DOM树上。
Object.defineProperty重新定义data中所有的属性,Object.defineProperty可以使数据的获取与设置增加一个拦截的功能,拦截属性的获取,进行依赖收集。拦截属性的更新操作,进行通知。初始化时 -> observer => defineProperty(ge/set) => getter:依赖收集 => setter:派发更新 => 触发:dep.notify() watcher.update() , 虚拟 DOM diff,patch
Vue2 更新流程
set
→ dep.notify
→ watcher.update
→ scheduler
→ patch
Vue3
Vue3.x改用Proxy替代Object.defineProperty。因为Proxy可以直接监听对象和数组的变化,并且有13种拦截方法。他可以通过判断当前Reflect.get的返回值是否为Object,如果是则再通过reactive方法做代理Proxy => reflect => effect => track/trigger
vue3更新流程
trigger
→ scheduler (job queue)
→ effect runner
→ render
→ patch
Proxy 相比于 defineProperty 的优势
| 对比点 | Vue2 | Vue3 |
|---|---|---|
| 响应式核心 | defineProperty | Proxy |
| 拦截粒度 | 属性级 | 对象级 |
| 新增/删除 属性 数组索引 | ❌ | ✅ |
| 初始化性能 | 差(递归) | 好(惰性) |
| 依赖结构 | Dep / Watcher | effect / track |
| TS 支持 | 一般 | 友好 |
| 代码体积 | 较大 | 更小 |
优化方案
- 减少入口文件体积
- 静态资源本地缓存
- 开启Gzip压缩
- 对图片进行压缩 /懒加载
- 插件提取公共代码
splitChunks - 提取组件的 CSS
- 生产禁用
SourceMap - 利用可视化分析工具查看代码体积
- 路由/组件 懒加载 keep-alive 的缓存
- 第三方插件按需引入 (vant/element,antdesign)
- 合理的定义响应式数据(data,ref,reactive)这些,防止过多的增加
getter和setter,会收集对应的watcher - 保证key值的唯一性
- 防抖、节流
- 骨架屏的使用
Vue2与Vue3的区别
| 对比 | vue2 | Vue3 |
|---|---|---|
| 响应式系统 | Object.defineProperty | Proxy |
| 虚拟 DOM & 渲染机制 | 全量 diff/双端对比 | 编译期 + 运行期双优化 /静态提升(hoist) |
| API 设计 | optionsAPI | Composition |
| this | 组件实例 | 无this |
| 响应式数据定义 | data | ref/reactive toRefs(解构) |
| 计算属性/监听器 | Computed/watch watchEffect(自动依赖收集) | |
| 声明周期变化 | Created/beforeCreated | Setup |
| 根节点 | 单根节点 | 多根节点 teleport |
| typescript支持 | 补丁的方式支持 | 原生设计 |
| 生态与工程化 | Webpack vueX | vite pinia |
| API变化 | filters $on .sync $Set $delete mixins | ref reactive readonly / shallowReactive / shallowRef |
key的作用
- key是为Vue中的vnode标记的唯一id,通过这个key,diff操作可以更准确、更快速
- diff算法的过程中,先会进行新旧节点的首尾交叉对比,当无法匹配的时候会用新节点的key与旧节点进行比对,然后超出差异.
diff程可以概括为:oldCh和newCh各有两个头尾的变量StartIdx和EndIdx,它们的2个变量相互比较,一共有4种比较方式。如果4种比较都没匹配,如果设置了key,就会用key进行比较,在比较的过程中,变量会往中间靠,一旦StartIdx>EndIdx表明oldCh和newCh至少有一个已经遍历完了,就会结束比较,这四种比较方式就是首、尾、旧尾新头、旧头新尾.
准确: 如果不加key,那么vue会选择复用节点(Vue的就地更新策略),导致之前节点的状态被保留下来,会产生一系列的bug. 快速: key的唯一性可以被Map数据结构充分利用,相比于遍历查找的时间复杂度
O(n),Map的时间复杂度仅仅为O(1).
Webpack与Vite区别
| 类型 | vite | webpack |
|---|---|---|
| 设计目标 | 极致开发体验 | 强大的模块打包能力 |
| 开发服务器 | 基于 bundle | 基于原生 ESM |
| 启动流程 | 全量构建 | 启动serve,按需加载 |
| 模块加载 | 打包后统一加载 | 浏览器按需加载 |
| 热更新 | 重新构建chunk(影响整个依赖链) | 模块及更新 |
| 打包引擎 | JS | Rollup |
| JS变异 | babel | esbuild |
| css | Style-loader css-loader file-loader 配置postCss | 原生支持 impot图片 自动读取 |
vuex与pinia的区别
| 纬度 | Vuex | Pinia |
|---|---|---|
| 设计思想 | 单一store + 模块 | 多Store天然拆分 |
| 编程方式 | Options API | compoostion API |
| 约束类型 | 强约束(必须通过mution修改state) | 少约束,组合式 可直接对state进行修改或者action修改state |
| TS类型推导 | 弱 | 强 |
| 模块互调 | 繁琐 | 直接import |
Hook的作用
hooks 是函数的一种写法。
vue3 借鉴 react hooks 开发出了 Composition API ,所以也就意味着 Composition API 也能进行自定义封装 hooks。
vue3 中的 hooks 就是函数的一种写法,就是将文件的一些单独功能的js代码进行抽离出来,放到单独的js文件中,或者说是一些可以复用的公共方法/功能。其实 hooks 和 vue2 中的 mixin 有点类似,但是相对 mixins 而言, hooks 更清楚复用功能代码的来源, 更清晰易懂。
vue3中响应状态定义方式
| API | 可接收类型 | 是否响应式 | 响应深度 | 是否需 .value | 解构是否丢失响应 | 主要用途 |
|---|---|---|---|---|---|---|
ref | 基本类型 / 对象 | ✅ | 深 | JS 中需要 | ❌ 不丢 | 单值状态 |
reactive | 对象 / 数组 | ✅ | 深 | ❌ | ✅ 会丢 | 复杂对象 |
computed | getter / getter+setter | ✅ | 深 | JS 中需要 | ❌ | 派生状态 |
readonly | 对象 | ✅(只读) | 深 | ❌ | — | 状态保护 |
shallowReactive | 对象 | ✅ | 浅 | ❌ | ✅ 会丢 | 性能优化 |
shallowRef | 任意 | ✅ | 浅 | JS 中需要 | ❌ | 大对象 |
toRef | 对象属性 | ✅ | 继承源对象 | JS 中需要 | ❌ | 单属性解构 |
toRefs | 对象 | ✅ | 继承源对象 | JS 中需要 | ❌ | 多属性解构 |
markRaw | 任意 | ❌ | 无 | ❌ | — | 跳过响应式 |
Vue 的模板是如何变成 DOM
Vue 会先把 template 编译成 render 函数,render 函数返回 虚拟 DOM,再通过 patch 过程生成真实 DOM。
template → AST → render → vnode → patch → DOM
Vue3 diff 算法有哪些优化
Vue3 在 diff 中引入了 Block Tree 和最长递增子序列(LIS),减少不必要的 DOM 移动。 PatchFlag 静态提升 Block Tree
Proxy 能监听哪些操作
get:读取属性
set:设置属性
has:in 操作符
deleteProperty:删除属性
ownKeys:Object.keys defineProperty
数组索引 / length 修改
组件通信方式
- props / emit
- Expose/ref
- Provide/inject
- Pinia/vuex
- 事件总线(mitt)
- Slot/scoped slot
- 路由参数
- 全局状态/URL
Vue Router核心原理
一句话核心答案(先说结论):
Vue Router 的核心实现原理是:监听 URL 变化 → 通过 matcher 匹配路由 → 将当前路由变成响应式状态 → 驱动 <router-view> 渲染对应组件,而整个过程不刷新页面。
监听 URL 变化:
- Hash 模式:通过
window.addEventListener('hashchange', ...)监听 URL 中#后面的部分变化,兼容性好,不包含在 HTTP 请求中。 - History 模式:利用 HTML5 的
history.pushState和history.replaceStateAPI 修改 URL,并通过popstate事件监听前进后退。 - abstract模式:利用数组栈的思想实现。Vue Router 的 abstract 模式不依赖浏览器 history,而是用一个数组 stack + index 指针,在内存中模拟浏览器历史栈,通过 push / replace / go 操作数组来完成路由前进、后退与替换,常用于 SSR 和测试环境。
React 与 Vue 详细对比
| 对比维度 | React | Vue |
|---|---|---|
| 设计理念 | 函数式编程,组件即函数 | 渐进式框架,组件即配置对象 |
| 数据流 | 单向数据流(自上而下) | 双向数据绑定(v-model) |
| 核心思想 | 声明式 UI,虚拟 DOM | 响应式系统,模板编译 |
| 更新机制 | 手动 setState 触发更新 | 自动追踪依赖,自动更新 |
| 不可变性 | 强调不可变数据 | 支持可变数据,自动处理 |
| 函数式编程 | 强烈推荐,Hooks 基于函数式 | 支持但不强制,更灵活 |
| 模板语法 | JSX(JavaScript 扩展) | HTML-like 模板语法 |
| 计算属性 | useMemo() | computed() |
| 副作用 | useEffect() | watch(), watchEffect() |
| 响应式原理 | 手动触发更新(setState) | 自动依赖追踪(Proxy/Object.defineProperty) |
| 组件缓存 | React.memo() | v-memo 或 keep-alive |
| 计算缓存 | useMemo() | computed() 自动缓存 |
| 函数缓存 | useCallback() | 自动优化 |