前端面试提问①

记录了一下前端面试的提问,整合了一下每道题。记录了些自己目前的理解。
一、Vue 3 核心知识
1. Vue 3 的响应式是怎样工作的?
我会沿着“读取数据、建立依赖、修改数据、通知更新”这条线来解释。对响应式对象,Vue 3 使用 Proxy 拦截属性访问和修改。响应式副作用执行期间读取属性会建立依赖关系,相关属性发生变化时再通知依赖更新。这里我还会补充一句:组件更新经过调度与批处理,不是每次赋值都立即操作 DOM。
追问:ref 也是把基本类型交给 Proxy 吗?
复习到这里,我会把 ref 和 reactive 分开记。ref 通过 .value 的 getter/setter 收集与触发依赖;普通 ref 接收对象时,会把对象转换为深层响应式对象。reactive 面向对象、数组和受支持的集合类型,不能直接接收数字或字符串作为响应式目标。这也是我对照 Vue 响应式原理 时重点确认的区别。
import { ref, reactive, nextTick } from 'vue'
const count = ref(0)const form = reactive({ name: '', address: '' })
async function increment() { count.value += 1 await nextTick() // 此时再读取依赖本次状态变化的 DOM}追问:Vue 2 与 Vue 3 有什么差异?
Vue 2 对对象属性使用 Object.defineProperty,新增、删除属性以及直接修改数组索引或 length 存在检测限制,通常需要 Vue.set、Vue.delete 或 splice 等方式。Vue 3 的 Proxy 改善了这些限制,但仍应通过代理对象修改数据;直接修改原始对象不会触发代理拦截。
2. setup 的作用是什么?
我理解 setup 的价值,主要在于能按业务功能组织代码。例如一个查询功能的状态、计算属性、监听和生命周期逻辑可以放在一起,阅读时更容易顺着业务走。setup 是组合式 API 的入口,早于选项式 beforeCreate 执行,不能通过 this 访问组件实例。复习时我也会注意,组合式与选项式 API 可以共存。
普通 setup 接收 props 和 context,context 提供 attrs、slots、emit、expose 等能力。<script setup> 是编译语法糖,顶层绑定可直接用于模板。defineProps、defineEmits 是编译宏,不需要导入;ref、computed 等运行时 API 仍需导入,除非项目配置了自动导入插件。参见 script setup 文档。
追问:解构 props 一定会丢失响应式吗?
普通 JavaScript 解构基本类型属性会取得当时的值,可使用 toRef、toRefs 或 getter 保留读取关系。但 Vue 3.5 及以上对同一 <script setup> 内从 defineProps 解构的变量提供了编译支持。传入 watch 或外部函数时仍要留意是否传递了 getter,不能忽略版本和使用位置。参见 响应式 Props 解构。
3. computed、watch 和 watchEffect 如何选择?
我通常先判断自己是在“计算一个值”,还是“执行一件事”。订单金额汇总、按钮是否可操作这类派生值,我会用 computed;状态变化后需要请求接口、写缓存,我会用 watch;依赖简单且适合自动收集时,再考虑 watchEffect。
computed 的 getter 应尽量保持纯计算。watch 的深度监听可能增加遍历成本,监听明确字段通常更容易维护。异步监听还要处理旧请求晚返回的问题,不能只讨论“数据是否变了”。
二、CSS 选择器与层叠
4. 常用选择器有哪些?
我把常用选择器按匹配方式列在一起。复习时会结合页面结构看它们选中了谁,而不只是记住符号的名字。
| 类型 | 示例 | 含义 |
|---|---|---|
| 基础 | *、div、.card、#app | 通配、标签、类、ID |
| 属性 | [type="text"]、[class*="box"] | 匹配属性或属性值 |
| 后代、子代 | .card p、.card > p | 所有后代、直接子元素 |
| 兄弟 | h2 + p、h2 ~ p | 紧邻的后续兄弟、所有匹配的后续兄弟 |
| 伪类 | :hover、:focus、:nth-child(2)、:not(.disabled) | 状态或结构条件 |
| 伪元素 | ::before、::after、::first-letter | 指定元素的某个部分 |
追问:nth-child 和 nth-of-type 有什么区别?
前者按兄弟元素中的位置匹配,后者按同类型兄弟元素中的位置匹配。容器中混有多种标签时,结果可能不同。
5. 为什么不能只背“important 大于 ID 大于 class”?
我现在会先解释层叠条件,再比较选择器。浏览器先考虑来源、重要性和层叠层等因素,再在可比较的声明之间比较选择器优先级。!important 是声明的重要性标记,不是选择器权重。
在相同可比较条件下,优先级按 ID、类/属性/伪类、类型/伪元素三组比较;通配符和组合符不增加权重。:where() 的优先级为零,:is()、:not()、:has() 的参数会影响其优先级。权重相同还要继续考虑作用域邻近性、声明顺序等因素。参见 MDN:选择器优先级。
追问:组件库样式覆盖不生效怎么办?
遇到这种情况,我会先在开发者工具中查看实际命中的规则、加载顺序、scoped 属性和 Teleport 后的 DOM 位置,再选择组件属性、主题变量或局部深度选择器处理。先看清规则为什么没生效,才知道应该改哪里。
三、页面加载与性能
6. 用户说页面很慢,你先做什么?
我会先确认慢在首次访问、切换页面、接口返回还是交互响应,再决定从哪里查。用 Network 看资源体积、缓存和请求瀑布,用 Performance 看主线程长任务和渲染,再结合 LCP、INP、CLS 判断体验问题。我把排查线索和对应措施整理成了下面这张表。
| 问题证据 | 优化方向 | 验证重点 |
|---|---|---|
| 首屏图片过大 | 合理尺寸、WebP/AVIF、CDN | 图片传输量和 LCP |
| 初始 JS 太大 | 路由/组件拆分、按需加载、移除无用依赖 | 下载、解析与执行耗时 |
| 相同资源反复下载 | 检查 Cache-Control、ETag | 缓存命中与重复传输 |
| 列表节点过多 | 分页或虚拟列表 | 节点数、滚动和交互耗时 |
| DOM 读写交错 | 集中读写、避免重复布局计算 | 布局次数和长任务 |
| 字体拖慢显示 | 子集化、合理回退字体 | 字体体积、文字显示与偏移 |
Gzip/Brotli 适合压缩文本资源。首屏关键图片通常不应懒加载;非首屏图片才适合延迟加载。骨架屏改善等待感受,但不会直接缩短接口耗时。
7. defer、async 和预加载怎么用?
我会先看脚本之间有没有执行顺序要求。对普通外部脚本,defer 允许并行下载,在文档解析完成后按顺序执行;async 下载完成后尽快执行,不保证脚本间顺序,适合独立脚本。模块脚本默认具有类似 defer 的行为。
preload 用于当前页面明确需要的关键资源;prefetch 面向后续可能用到的资源;preconnect 提前建立到目标来源的连接。预加载过多会争抢带宽,应以实际请求链路为依据。
追问:HTTP/2、SSR 能解决所有慢页面吗?
不能。HTTP/2 多路复用减少应用层串行等待,但 TCP 丢包仍可能影响同一连接上的流。SSR 按请求生成 HTML,SSG 通常在构建时生成 HTML;二者都要权衡数据时效、生成成本和客户端交互脚本。现代协议下也不应为了减少请求数,把所有代码合成一个巨型文件。
四、技术选型如何回答
8. 面试官问“你了解哪些新技术”,怎样避免堆名词?
我会从项目里的 Vue、Vite、uni-app 和 Astro 讲起,再补充正在了解的技术。实际用过的部分,我会结合代码解释;还停留在学习阶段的部分,就说明它解决什么问题、有哪些限制。下面这张表是我给自己整理的复习提纲。
| 技术方向 | 可以讨论的价值 | 需要交代的边界 |
|---|---|---|
| Vue 组合式 API、Teleport | 逻辑复用、弹层挂载 | 状态归属、DOM 位置与样式 |
| React Transitions、Suspense | 更新优先级与异步 UI 边界 | 依赖版本和框架集成,不能泛化为任意请求自动挂起 |
| Svelte、Solid | 编译优化或细粒度更新 | 避免无实测依据的性能排名 |
| Vite、Rspack、Turbopack | 开发反馈与构建效率 | 确认具体版本、插件和配置兼容性 |
| WebAssembly | 部分计算密集任务 | 数据传递成本,不能直接替代所有页面逻辑 |
容器查询、:has() | 基于容器尺寸或关系选择布局 | 目标运行环境是否支持 |
| TanStack Query | 服务端状态缓存与重新获取 | 不等于自动解决所有防抖、鉴权与业务状态问题 |
| uni-app / uni-app x | 跨端开发 | 产品路线和渲染机制不能混为一谈 |
整理构建工具这部分时,我也会回头看项目的版本。例如我的管理端声明 Vite 6.4.1,讨论实现细节时就应结合这一版本,后续升级再重新确认。
五、HTTP 状态码
9. 高频状态码怎么区分?
我按成功、重定向、客户端错误和服务端错误来记,再把容易混淆的状态码放在一起比较。
| 状态码 | 含义 |
|---|---|
| 200 / 201 / 204 | 成功 / 创建成功 / 成功且无响应正文 |
| 301 / 302 | 永久 / 临时重定向 |
| 304 | 条件请求确认资源未修改,可复用缓存 |
| 400 / 401 / 403 | 请求有问题 / 需要认证 / 拒绝访问 |
| 404 / 405 | 资源未找到 / 方法不被允许 |
| 429 | 请求过于频繁 |
| 500 | 服务端遇到未预期情况 |
| 502 | 网关收到无效上游响应 |
| 503 | 服务暂时无法处理请求 |
| 504 | 网关等待上游响应超时 |
这些是 HTTP 层语义,不能仅凭 502 断言后端一定宕机。参见 MDN:HTTP 响应状态码。
追问:接口返回 200,就代表业务成功吗?
我会结合管理端的请求封装回答这道题:HTTP 状态与响应体里的业务 code 是两个层次。项目里还会读取 res.data.code,所以排查时,我会一起看 HTTP status、业务 code、响应内容和请求链路。
六、上传报错实战
10. Axios ERR_NETWORK、ERR_CONNECTION_RESET 怎么排查?
看到这类报错,我会先分清浏览器给出的现象。ERR_NETWORK 表示未获得可正常交付给 JavaScript 的响应,可能涉及 CORS、连接异常或其他浏览器网络限制;ERR_CONNECTION_RESET 表示连接被重置。仅凭这两个字符串,我还不能确定是哪台服务或哪段配置导致。
我参考的资料没有展开后续排查过程,因此这里补上自己的排查顺序。真遇到上传失败时,我会逐项收集证据,再判断原因。
- 检查 Network 中请求 URL、方法、预检请求、响应头和失败时间,确认开发代理前缀是否正确。
- 对比小文件、大文件与普通接口,观察是否存在体积或持续时间阈值。
- 检查代理的请求体限制、超时、上游连接和后端 multipart 配置,并对照同一时刻的日志。
- 检查上传参数:字段名、FormData、鉴权头是否正确。浏览器发送 FormData 时通常让浏览器生成带 boundary 的 Content-Type。
- 分别验证直连后端和经过代理的路径。直连成功只能缩小问题范围,还需要比较路径、协议和请求头。
追问:把 Axios timeout 调大就可以了吗?
我会先确认失败是否真的来自客户端超时。只有超时确实过短时,调大 timeout 才可能有效;它不能修复文件体积限制、CORS、代理主动断开或服务异常。对于上传,我也会把前端提示与服务端校验分开考虑:文件类型、大小和资源归属仍需在服务端独立检查。
七、面试前快速复述
最后,我把这轮复习压缩成七条提醒,方便面试前再过一遍:
- 响应式:对象代理与 ref 的 value 访问器要区分,更新有批处理。
- setup:按功能组织逻辑,分清编译宏、运行时 API 和自动导入。
- CSS:先看层叠条件,再比较权重,结合实际 DOM 排查。
- 性能:先定位瓶颈,再提出措施,用相同条件的指标验证。
- 技术选型:解释收益、成本与版本边界,坦诚说明实践范围。
- HTTP:区分传输状态、业务状态和没有正常响应的网络错误。
- 上传:沿浏览器、代理、后端逐层取证,不凭一条报错锁定原因。
支持与分享
如果这篇文章对你有帮助,欢迎分享给更多人或打赏支持!





























