2026 前端面试题大全:100+ 最新面经与框架真题
# 2026 前端面试题大全:100+ 最新面经与框架真题
这份 2026 前端面试题与面经按技术方向持续更新,目前收录 100+ 道题。每道题都提供 30 秒速记、真实示例代码、可交互 DEMO 和面试官追问,适合校招、社招以及中高级前端面试前集中复习。
2026 年的前端面试没有放弃 JavaScript、CSS、浏览器和网络基础,但更强调把原理放进真实项目:排查 React 闭包与渲染性能、解释 Vue 3 响应式、选择 Webpack 或 Vite、划分 Next.js Server Component,以及处理大模型流式输出和审查 AI 生成代码。
# 2026 前端面试题分类
- 基础核心:JavaScript 与浏览器、CSS 与现代布局、TypeScript、浏览器与网络进阶
- 主流框架:React 19 与 Next.js、Vue 3、Angular、Svelte 与 Nuxt
- 工程化:Webpack、Vite、Monorepo 与包工程、测试与质量
- 渲染与架构:服务端渲染与同构、微前端与模块联邦、性能与安全
- 跨端开发:Electron、React Native、Flutter、小程序、Taro 与跨端
- AI 与服务端:AI 前端高频题、AI 应用工程、Node.js 与服务端 JavaScript
如果复习时间有限,可以先按前端面试题复习路线补基础,再用本页做 2026 高频场景自测;临近面试时再结合真实公司前端面经模拟追问。
# JavaScript 与浏览器
# 微任务为什么可能让页面“卡死”?
30 秒速记
- 一轮同步任务结束后,浏览器会清空微任务队列,再决定是否渲染和执行下一个宏任务
- 微任务执行时继续递归追加微任务,队列就可能一直清不完,定时器、输入和绘制都被饿死
Promise.then、queueMicrotask都能制造这种问题;关键不是单次任务长,而是队列没有让出控制权- 修复要把长工作切片并主动让到新的任务,生产中还应设置批量上限和取消条件
微任务可能让页面“卡死”,是因为浏览器通常要清空当前微任务队列后,才有机会继续渲染或处理后续任务。 如果微任务执行时不断用 Promise.then 或 queueMicrotask 追加新微任务,队列就迟迟无法清空,绘制、输入和定时器都会等待。问题不一定是单次计算很慢,而是代码始终没有让出控制权。长工作应分批执行,并设置处理上限或取消条件,必要时切换到新的任务再继续。
# React 中的闭包为什么会读到旧状态?
30 秒速记
- 每次渲染都会创建一套新的变量与函数,旧回调闭包捕获的是那次渲染的状态快照
setInterval、异步请求和事件订阅寿命较长,最容易暴露旧闭包- 只依赖前值时用函数式更新;需要读取最新值时可用
ref,但不要用它绕过正常渲染 useEffectEvent适合“读取最新值但不希望它触发 Effect 重连”的事件逻辑,使用时要遵守当前React版本约束
React 每次渲染都会创建新的变量和函数,旧回调闭包保存的是当次渲染的状态快照,所以会读到旧状态。 这个问题常见于 setInterval、异步请求和事件订阅,因为它们可能在后续渲染完成后才执行。只根据前值更新时,我一般用函数式更新;确实要读取最新值时可以用 ref,但不应借它绕过正常渲染。若事件逻辑要读最新值、又不希望触发 Effect 重连,可考虑 useEffectEvent,同时遵守当前 React 版本的约束。
计数器只需要前值,函数式更新最直接;它接收提交时的最新状态,因此 Effect 不必依赖 count,也不会每次重建定时器。
import { useEffect, useState } from 'react'
export function Counter() {
const [count, setCount] = useState(0)
useEffect(() => {
const timer = window.setInterval(() => {
setCount((current) => current + 1)
}, 1000)
return () => window.clearInterval(timer)
}, [])
return <output>{count}</output>
}
不要机械地把每个值塞进依赖数组。先判断 Effect 是否应该随它重新同步;如果只是回调内部要读最新值,拆分事件逻辑比反复退订、重订更准确。
面试官追问
追问 1计数器页面的 setInterval 每秒执行 setCount(count + 1),界面却一直停在 1;既然回调持续运行,为什么它没有读到新的 count?
定时器回调捕获的是创建该 Effect 时那次渲染中的 count,持续执行不等于闭包会自动切换到后续渲染。这里只依赖前值,应写成 setCount(current => current + 1),让 React 在提交更新时提供最新状态。
追问 2监控面板有十几个计数器,如果把 count 加进依赖数组也能递增,为什么代码评审仍可能要求改成函数式更新?
把 count 加入依赖会在每次变化后清理并重建定时器,行为虽正确,却把“读取前值”误建模成了“重新同步外部系统”。函数式更新可让 Effect 保持空依赖,减少计时器重建及时间漂移风险。
追问 3聊天页连接只由 roomId 决定,但收到消息时要按最新 theme 显示通知;把 theme 放进依赖后每次换肤都会重连,边界该怎么拆?
连接 Effect 应只依赖真正决定连接生命周期的 roomId,主题只属于消息回调中的展示逻辑。应把读取最新主题的事件逻辑与订阅过程拆开,避免为了更新通知样式而反复退订、重订。
追问 4线上偶发出现同一房间收到两份消息,你怀疑是为解决旧闭包而频繁重建订阅,会怎样验证?
先记录 Effect 建立、清理时的 roomId 和订阅实例标识,再核对依赖变化与服务端连接日志,确认清理是否与重订一一对应。若把非生命周期值塞进依赖,任何主题或展示状态变化都可能扩大重复订阅窗口。
追问 5团队想规定所有异步回调都通过 ref.current 读取状态,避免再讨论依赖数组,这个约定是否合理?
不合理,ref.current 虽能提供最新快照,却绕开响应式数据流,修改它也不会触发渲染。仅在命令式集成或确实需要最新快照时使用;状态只基于前值更新时,函数式更新更清晰、更受 React 调度语义约束。
# 如何实现可取消、不会串结果的搜索防抖?
30 秒速记
- 防抖只减少触发频率,不能自动取消已经发出的旧请求
- 输入变化时既要清定时器,也要
abort上一次请求,避免旧响应覆盖新关键词 - 用本次请求的
AbortController绑定信号;卸载时执行同一套清理 AbortError是预期控制流,不应当成线上异常上报
搜索防抖要同时清理定时器和取消旧请求,因为防抖只能减少触发次数,不能阻止已发出的旧响应覆盖新结果。 每次输入变化时,先 clearTimeout,再调用上一个 AbortController.abort(),并为新请求绑定新的 signal;组件卸载时也复用这套清理逻辑。AbortError 属于预期的取消流程,不需要当作线上异常上报。还要处理空关键词和组合输入法;如果服务端无法真正中断请求,就比较请求序号,只让最新请求更新界面。
export function createSearch(search: (q: string, signal: AbortSignal) => Promise<void>) {
let timer: ReturnType<typeof setTimeout> | undefined
let controller: AbortController | undefined
return (query: string) => {
if (timer) clearTimeout(timer)
controller?.abort()
controller = new AbortController()
const signal = controller.signal
timer = setTimeout(async () => {
try {
await search(query, signal)
} catch (error) {
if (!(error instanceof DOMException && error.name === 'AbortError')) throw error
}
}, 300)
}
}
生产代码还要处理空关键词、组合输入法和服务端不支持中断的情况。即使网络层无法真正取消,也应比较请求序号,只允许最新请求提交 UI。
面试官追问
追问 1商品搜索页先输入 rea,请求较慢,随后输入 react,后一个请求先返回;已经做了 300ms 防抖,为什么列表仍可能被旧结果覆盖?
防抖只减少请求次数,不能保证已发请求按输入顺序返回,因此 rea 仍可能最后提交 UI。每次新输入应清除定时器并中止旧请求;即使无法真正中断网络,也要比较请求序号,只接纳最新结果。
追问 2封装 createSearch 时,新关键词到来应在定时器回调里再创建 AbortController,还是输入发生时就创建并取消旧控制器?
应在新输入发生时立即清除旧定时器、取消旧控制器,再为本次查询创建 AbortController。这样既取消尚未执行的任务,也尽早通知已经执行的搜索停止;捕获异常时只吞掉明确的 AbortError,其他错误仍需上报。
追问 3中文用户输入“北京”时,搜索框在拼音组合阶段连续触发 input,产品又要求最终文字出现后尽快搜索,事件层怎么处理?
在 compositionstart 后暂停触发搜索,在 compositionend 取得最终文本,再进入同一套防抖流程。不要把组合期间的字母当成有效查询,否则既浪费请求,也可能让中间结果在最终中文结果之后覆盖页面。
追问 4用户清空关键词后列表仍显示上一轮结果,同时开发者工具里旧请求显示已取消,你会优先检查哪些状态转换?
先确认空字符串分支是否同步清除定时器、调用 abort() 并重置列表与加载态,再检查旧 Promise 的完成逻辑是否仍会写入 UI。仅让网络请求显示取消并不够,空关键词也必须成为一次明确的最新状态。
追问 5搜索接口在服务端不支持中断,前端调用 abort() 后后台仍完成计算;这时可取消方案还能保证什么,不能保证什么?
它仍可阻止客户端继续消费旧响应,并配合请求序号避免旧数据提交 UI,但不能保证服务端停止计算。若操作带有写入副作用,还需服务端幂等键或版本校验,不能把客户端取消当成事务回滚。
# React 19 与 Next.js
# Hooks 为什么不能写在条件分支里?
30 秒速记
React用调用顺序把每次Hook与组件内部的状态槽位对应,不靠变量名匹配- 条件分支让下一次渲染的调用序列变化,后续状态会错位
- 条件应放进
Hook内部,或拆成条件渲染的子组件 - 自定义
Hook仍然要遵守同一规则,因为它最终会展开成一串Hook调用
Hooks 不能写在条件分支里,因为 React 是按调用顺序把每个 Hook 对应到组件内部状态槽位的。 某次渲染跳过一个 Hook 后,后面的调用位置就会变化,状态和副作用可能因此错位。通常把条件作为参数传进 Hook,例如用 null 表示暂不请求;也可以由父组件条件渲染子组件,让它整体挂载或卸载。自定义 Hook 也不能例外,因为它执行后仍是一串有固定顺序的 Hook 调用。
