Vue 面试题
Vue 面试题
MVVM软件架构设计模式
- 定义:
MVVM是Model-View-ViewModel的缩写,它是一种用于构建用户界面的软件架构设计模式。 - 它的核心目标是 “实现数据与视图的自动同步”,从而让开发者从繁琐的 DOM 操作中解放出来,更加专注于数据和业务逻辑。
- 核心机制:数据驱动视图,数据与视图的双向绑定
vue首屏优化方案
硬件方面
- 增加网络带宽
cdn加速 服务器就近部署
软件方面
- 路由懒加载
- 静态资源压缩
- 拆包 懒加载部分资源
Vue 2和Vue 3的核心区别
核心区别可以概括为“底层重构”和“上层创新”。
底层重构
- 响应式系统重写:从
Object.defineProperty到Proxy,解决了数组索引、长度修改、对象属性增删的监听问题,性能也更优。
上层创新
Composition API(组合式API):这是最大的范式转变。Options API(声明式 API) 在逻辑简单时很清晰,但逻辑复杂后,同一功能相关的代码(数据、方法、计算属性、生命周期)会分散在各处。Composition API通过纯函数(setup或<script setup>)将相关逻辑组织在一起,极大地提升了复杂逻辑的可读性、可复用性和可测试性。自定义组合函数(composables)是代码复用的利器。
ref和retive的区别
ref 和 reactive 都是创建响应式数据的核心 API,但设计目的和使用场景不同:
- 数据类型:
ref可以包装任何类型,而reactive只处理对象类型。 - 访问方式:
ref需要通过.value访问(模板中自动解包),reactive直接访问属性。 - 重新赋值:
ref的.value可以被完全替换且保持响应性,而reactive直接重新赋值会丢失响应性
适用场景
ref:更适合原始值、可能被替换的数据、组合式函数返回值。reactive:更适合需要深度响应式的复杂对象、相关状态的分组管理。
ref为什么要.value
ref 需要 .value 是 Vue 3 响应式系统设计中一个深思熟虑的权衡。
- 技术层面:它是解决
JavaScript原始值无法被直接代理(修改)的唯一方案。通过创建包装对象,**Vue获得了追踪变化的能力 **。 - 设计层面:
.value作为ref容器的统一访问接口,保证了API的一致性,并明确区分了ref(有.value)和reactive(直接访问属性)的使用场景。 - 体验层面:
Vue通过模板编译时的自动解包,消除了模板中的.value,让开发者获得近乎直接的编程体验,同时在脚本逻辑中保留 .value以维持明确性和类型安全。
ref 能在全局替换时保持响应式,而 reactive 不能
根本原因在于两者的响应式实现层级不同
ref 是容器级的响应式
- 它创建一个固定的包装对象,响应式系统追踪的是这个包装对象的
.value属性。 - 当你替换
.value时,包装对象本身没变,响应式连接保持不变。 - 新值如果是对象,会被自动转换为响应式。
reactive 是对象属性级的响应式
- 它创建原始对象的
Proxy代理,响应式系统追踪的是原始对象的具体属性。 - 当你直接给
reactive变量赋新值时,实际上是把变量指向了新对象,原来的Proxy代理丢失了,响应式连接也就断了。
设计差异
ref采用 "盒模型":盒子固定,内容可换reactive采用 "包装纸模型":包装纸和对象绑定,换对象就没了包装纸
手写一个 ref 的实现
- 包装器模式:
ref本质上是一个包装对象,通过.value属性来访问内部值。 - 依赖收集:在
getter中收集当前正在执行的副作用(effect),建立响应式依赖关系。 - 触发更新:在
setter中比较新旧值,如果不同,则触发所有收集到的依赖重新执行。 - 对象处理:如果内部值是对象,需要递归或代理使其也具有响应式。
- 标志位:通过
__v_isRef等标志位让系统能够识别这是一个ref对象,从而实现模板中的自动解包等特性。
如何在计算属性中请求接口
在 Vue 3 中,不建议在计算属性中直接请求接口,因为计算属性的核心设计是同步的、无副作用的纯函数。 总结:在计算属性中请求的接口请求的数据不是实际的数据 是Promise对象 而且在网络请求时不能立即更新
计算属性的设计原则:
纯函数:相同的输入应该产生相同的输出,不应该有副作用(如网络请求、
DOM操作等)。同步返回:计算属性必须立即返回计算结果,不能返回
Promise。缓存机制:基于依赖的缓存是其核心特性。
正确的解决 使用 watch + ref(最常用)
通过watch监听数据变化,并在其中进行网络请求,请求完毕后将结果赋值到ref中.
如何使监听器只监听一次
最新推荐(Vue 3.4+)
使用 watch 的 once: true 选项:
watch(source, callback, { once: true })
通用方法(所有 Vue 3 版本)
const stop = watch(source, (...args) => {
callback(...args)
stop() // 在回调中立即停止
})
watch和计算属性的区别
计算属性 (computed) 和监听器 (watch) 在 Vue 中是两个根本不同的概念,它们的核心区别在于:
设计目的不同:
computed用于派生数据:基于现有响应式数据计算出一个新值watch用于监听数据源的变化,执行特定操作或副作用
缓存机制不同
computed有强缓存:依赖不变时直接返回缓存值,避免重复计算watch无缓存:每次数据源变化时都执行回调
异步支持不同
computed必须是同步操作,不能包含异步代码watch可以包含异步代码,如网络请求
返回值不同
computed返回一个ref对象,可以访问内部值watch返回一个取消监听的函数,用于停止监听
简单记忆
computed:回答"是什么"的问题(这个值是什么?)watch:回答"做什么"的问题(当这个变化时,要做什么?)
watch和watchEffect的区别
| 特性 | watch | watchEffect |
|---|---|---|
| 设计目的 | 监听特定数据源的变化,执行副作用 | 自动追踪依赖变化,执行副作用 |
| 依赖收集 | 显式声明要监听的数据源 | 自动收集回调中用到的响应式依赖 |
| 执行时机 | 默认惰性,数据变化时才执行(可配置 immediate) | 立即执行,并在依赖变化时重新执行 |
| 访问新旧值 | ✅ 可以获取变化前后的值 | ❌ 只能获取当前值,不知道具体哪个依赖变了 |
| 性能优化 | 可以精确控制监听范围 | 自动追踪,可能追踪到不需要的依赖 |
| 代码风格 | 声明式、精确控制 | 命令式、自动化 |
Vite和webpack的区别
Vite 和 Webpack 都是优秀的构建工具,但它们的设计理念和适用场景有本质区别:
Webpack:全能但复杂
- 核心理念:『打包一切』,预先构建完整的依赖图和
bundle - 优势:功能极其全面,插件生态丰富,适合复杂、定制化要求高的大型项目
- 劣势:随着项目规模增大,开发体验急剧下降(启动慢、热更新慢)
Vite:快速而现代
- 核心理念:『按需编译』,利用浏览器原生ES模块,需要什么编译什么
- 优势:开发体验极佳(秒级启动、毫秒级热更新),配置简单,面向现代浏览器
- 劣势:插件生态相对较新,对老旧浏览器支持需要额外配置
关键技术差异:
- 开发服务器:
Webpack先打包后服务,Vite直接服务按需编译 - 热更新:
Webpack重新打包依赖链,Vite只更新单个文件 - 生产构建:
Webpack自研打包器,Vite使用Rollup - 配置复杂度:
Webpack配置复杂但灵活,Vite配置简单但约定优先