前言
大家好,我是木斯佳。
相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。
这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。专栏快速链接

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
面经原文内容
📍面试公司:腾讯
🕐面试时间:近期,用户上传于2026-03-26
💻面试岗位:前端暑期AI面
⏱️面试时长:未提及
📝面试体验:有人说简单得很,我感觉一点也不简单
❓面试问题:
- 没自我介绍,说为什么竞聘前端岗位、做了哪些思想准备
(接下来是选择题,选择自己比较擅长的领域,我选了计网、ts;有一次换题机会) - 解释浏览器事件循环机制如何工作、阐述宏任务列和微任务列的区别
- HTTPS 相比 HTTP 的优势
- TypeScript 的 any 类型,什么情况下该避免、替代方案
- React 如何进行组件的性能分析和优化
- 浏览器提供的缓存方式及其区别(换题,这个真不会)
- 列举至少三种优化 CSS 减少首屏加载时间的方法
- 最近一个前端项目的角色和贡献(说了一下实习的时候做的东西)
- (针对项目)设计触发 UI 窗口的两种方式时如何考虑用户体验、是否针对不同用户习惯优化
- 有没有做响应式(没有,然后说了一下简单的思路)
- 要设计一个可复用的表单组件,说一下主要步骤和技术点
- 如何考虑这个组件的可扩展性和可维护性,采取什么样的策略
- 分享一下在看似普通的数据中隐藏的深刻见解(说了一下遇到的 React 状态同步的问题,然后总结了一下学到的经验)
- 有没有考虑解决问题时对性能的影响
来源:牛客网 期望去月球上班
💡 木木有话说(刷前先看)
这是一份很有特色的腾讯暑期实习面经。亮点在于面试流程设计:先让你选擅长的方向(计网/TS),给一次换题机会,整体非常灵活。题目本身不算难,但每一道都问得很深,尤其性能优化连问三道,对实习生来说确实有挑战。面试官显然不满足于"会背八股",而是想看到你真正理解原理、有过实践思考。这篇面经很适合准备大厂实习的同学对照自查。
📝 腾讯前端暑期AI面·深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|---|
| 面试风格 | 方向自选型 + 追问深度型 + 工程实践型 |
| 难度评级 | ⭐⭐⭐⭐(四星,基础扎实+深度思考+工程能力) |
| 考察重心 | 底层原理(事件循环/HTTPS)、性能优化、组件设计、工程化思维 |
| 特殊之处 | 给换题机会、连问三道性能优化、追问"数据中的深刻见解" |
🔍 逐题深度解析
一、为什么竞聘前端岗位、做了哪些思想准备
回答思路 :这不是普通的"为什么学前端",而是考察你的职业规划成熟度------不是一时兴起,而是经过思考的选择。
建议回答结构:
- 兴趣起源:从什么项目/经历开始对前端产生兴趣
- 能力匹配:你的哪些特质适合前端(设计感、逻辑思维、用户体验敏感)
- 价值认同:你认为前端在AI时代/业务中的价值是什么
- 思想准备:对前端技术迭代快、需要持续学习的认知;对工程化、性能优化的重视
加分点:结合AI浪潮,提到"前端是AI能力落地的最后一公里,我希望能做连接技术与用户的桥梁"。
二、浏览器事件循环机制
回答思路:先讲核心概念,再讲宏任务/微任务区别,最后用代码示例说明执行顺序。
核心机制:
- JavaScript是单线程,依靠事件循环实现异步
- 执行顺序:同步代码 → 清空微任务队列 → 执行一个宏任务 → 再清空微任务队列 → 循环
宏任务 vs 微任务:
| 维度 | 宏任务(MacroTask) | 微任务(MicroTask) |
|---|---|---|
| 常见API | setTimeout, setInterval, I/O, UI渲染 | Promise.then, MutationObserver, queueMicrotask |
| 执行时机 | 每轮循环执行一个 | 本轮所有同步代码执行完后一次性清空 |
| 优先级 | 低 | 高 |
javascript
console.log('1') // 同步
setTimeout(() => console.log('2'), 0) // 宏任务
Promise.resolve().then(() => console.log('3')) // 微任务
console.log('4') // 同步
// 输出顺序:1 → 4 → 3 → 2
关键理解:微任务队列是在当前宏任务执行完后、下一个宏任务开始前被清空的,所以微任务可以"插队"。
三、HTTPS相比HTTP的优势
回答思路:从安全、性能、信任三个维度展开。
核心优势:
- 加密传输(防窃听):HTTPS使用TLS/SSL对通信加密,HTTP明文传输
- 身份验证(防伪装):通过CA证书验证服务器身份,防止DNS劫持和中间人攻击
- 数据完整性(防篡改):消息摘要机制确保数据在传输过程中未被修改
- 性能优势(现代HTTPS):HTTP/2基于HTTPS,支持多路复用、头部压缩、服务器推送
- 浏览器信任:HTTP页面会被标记为"不安全",影响用户信任
补充细节:HTTPS增加一次RTT的握手时间,但可以通过会话复用、TLS 1.3(1-RTT甚至0-RTT)优化。
四、TypeScript的any类型:避免场景与替代方案
回答思路:先说明any的危害,再给出具体替代方案。
any的危害:绕过类型检查,让TypeScript失去意义;容易引入运行时错误;破坏IDE智能提示。
应该避免any的场景:
- 函数参数和返回值(应该用具体类型或泛型)
- API响应数据(应该定义interface)
- 复杂状态对象(应该用type或interface约束)
替代方案:
| 场景 | 替代方案 | 示例 |
|---|---|---|
| 类型不确定 | unknown |
const data: unknown = fetchData(),使用前需类型守卫 |
| 多种可能类型 | 联合类型 | `type ID = string |
| 对象部分属性未知 | Record<string, T> |
const cache: Record<string, User> |
| 扩展性需求 | 泛型 | function identity<T>(arg: T): T |
| 第三方库无类型 | 声明模块 | declare module 'some-lib' |
typescript
// ❌ 不好
function process(data: any) { ... }
// ✅ 好:用unknown + 类型守卫
function process(data: unknown) {
if (typeof data === 'object' && data !== null && 'id' in data) {
// 安全使用
}
}
// ✅ 好:用泛型
function process<T>(data: T): T { ... }
五、React组件性能分析与优化
回答思路:分为"如何分析"和"如何优化"两部分。
性能分析方法:
- React DevTools Profiler:录制交互,查看组件渲染次数和耗时
- Chrome Performance:分析长任务、布局抖动、重绘重排
- Why Did You Render:检测不必要的重新渲染
- React.memo 缓存命中率:通过日志统计
优化策略:
| 策略 | 具体做法 | 适用场景 |
|---|---|---|
| 避免不必要渲染 | React.memo、useMemo、useCallback |
纯展示组件、昂贵计算 |
| 虚拟滚动 | react-window | 长列表(>100项) |
| 代码分割 | React.lazy + Suspense |
路由级别、大组件 |
| 状态细化 | 拆分Context,避免大状态导致全局重渲染 | 跨组件共享状态 |
使用useTransition |
标记非紧急更新 | 输入框搜索、tab切换 |
| 列表key优化 | 使用稳定唯一ID,避免用index | 动态列表 |
javascript
// 示例:避免子组件不必要渲染
const Child = React.memo(({ data }) => {
return <div>{data}</div>
})
function Parent({ items }) {
// ✅ 使用useMemo缓存计算结果
const expensiveValue = useMemo(() => compute(items), [items])
// ✅ 使用useCallback稳定函数引用
const handleClick = useCallback(() => {}, [])
return <Child data={expensiveValue} onClick={handleClick} />
}
六、浏览器缓存方式及其区别
回答思路:核心是分清"强缓存"和"协商缓存"两类,以及它们对应的HTTP头字段。面试官想考察你对缓存策略的理解,以及如何在项目中合理配置。
回答要点:
- 分类 :浏览器缓存分为强缓存 和协商缓存两类。
- 强缓存 :本地缓存未过期,直接使用,不请求服务器。
- 相关头字段:
Expires(HTTP/1.0,绝对时间)和Cache-Control(HTTP/1.1,相对时间,优先级更高)。 - 常用
Cache-Control指令:max-age=3600(缓存1小时)、public(允许被任何中间节点缓存)、immutable(资源不变,可彻底缓存)。
- 相关头字段:
- 协商缓存 :缓存已过期,向服务器询问资源是否有变化,无变化则返回304状态码,不返回资源体。
- 相关头字段:
Last-Modified/If-Modified-Since(基于修改时间),ETag/If-None-Match(基于内容指纹,优先级更高)。
- 相关头字段:
- 缓存位置:按优先级从高到低:Service Worker → Memory Cache → Disk Cache → Push Cache(HTTP/2推送)。
javascript
// 示例:Nginx配置强缓存与协商缓存
location /static/ {
# 强缓存:图片、字体等1年
expires 1y;
add_header Cache-Control "public, immutable";
}
location / {
# 协商缓存:HTML文件,实时验证
etag on;
if_modified_since exact;
}
// 前端资源版本控制(解决强缓存更新问题)
// webpack配置输出带hash的文件名
output: {
filename: '[name].[contenthash].js',
chunkFilename: '[name].[contenthash].chunk.js',
}
面试官追问点:
- 强缓存和协商缓存的区别是什么?
强缓存不发请求,协商缓存至少发一次请求(304或200)。 Cache-Control: no-cache和no-store的区别?
no-cache表示每次都协商缓存 (验证后再用),no-store表示完全不缓存,每次都重新下载。- 如何避免强缓存导致资源更新不及时?
使用文件内容哈希(如main.a3b4c5.js),内容变化时URL变化,直接绕过缓存。
补充回答亮点 :可以结合项目实际,说明在构建时通过 contenthash 给静态资源加指纹,对HTML使用 no-cache 策略,既保证性能又确保更新及时。
七、优化CSS减少首屏加载时间(至少三种)
回答思路:关注CSS的加载、解析、渲染效率。
优化方法:
- 关键CSS内联 :将首屏必需的CSS内联到
<head>,非关键CSS异步加载
html
<head>
<style>/* 首屏关键样式 */</style>
<link rel="preload" href="non-critical.css" as="style" onload="this.onload=null;this.rel='stylesheet'">
</head>
-
压缩与合并:使用CSS压缩工具(如cssnano),合并小文件减少HTTP请求
-
移除未使用的CSS:使用PurgeCSS配合构建工具,清除未使用的样式
-
避免CSS import :
@import会阻塞并行下载,改用<link> -
使用CSS Grid/Flex替代复杂布局:减少嵌套和定位计算
-
简化选择器 :避免过深嵌套(
.a .b .c .d),浏览器解析从右向左 -
字体优化 :使用
font-display: swap避免字体闪烁,预加载关键字体
八、最近项目的角色和贡献
回答思路:用STAR法则(情境-任务-行动-结果)结构化表述。
示例:
- 情境:实习期间负责XX项目的YY模块重构
- 任务:需要解决页面首屏加载慢、代码维护性差的问题
- 行动:做了代码分割、懒加载、提取公共组件、接入性能监控
- 结果:首屏时间从2.5s降到1.2s,代码复用率提升30%
九、设计UI窗口触发方式:用户体验考量
回答思路:结合项目经验,讨论不同触发方式对用户体验的影响。
两种触发方式对比:
- 模态弹窗:强制用户聚焦,适合重要操作(删除确认、表单填写)
- 非模态浮层:不中断当前流程,适合辅助信息(提示、快捷操作)
用户体验考量:
- 场景匹配:重要决策用模态,轻量操作用非模态
- 可发现性:按钮位置、图标语义是否符合用户预期
- 可中断性:是否支持ESC关闭、点击遮罩关闭
- 键盘导航:Tab顺序、焦点管理是否合理
- 移动端适配:底部抽屉可能比居中弹窗更适合触屏
针对不同用户习惯:
- 快捷操作用户:提供快捷键、右键菜单
- 新手用户:提供引导提示、明显的关闭按钮
- 可配置:允许用户选择默认触发方式(如单击/双击)
十、响应式设计思路
用户回答"没有做过",但说了简单思路。面试官想了解你是否具备响应式设计的意识。
核心思路:
- 流体布局:使用百分比、flex、grid替代固定宽度
- 媒体查询:根据断点调整布局、字体大小
- 视口设置 :
<meta name="viewport" content="width=device-width, initial-scale=1"> - 图片响应式 :
srcset、picture元素、CSSmax-width: 100% - 移动优先 :先设计移动端样式,再用
min-width逐步增强
十一、设计可复用表单组件:主要步骤和技术点
回答思路:从需求分析到技术实现的完整链路。
主要步骤:
- 需求分析:确定需要支持的表单项类型(输入框、下拉、日期选择器等)
- 接口设计:定义props(字段配置、初始值、校验规则、提交回调)
- 状态管理:表单值、校验状态、提交状态
- 校验实现:支持同步/异步校验,提供自定义校验函数
- 布局适配:支持水平/垂直布局,栅格系统
- 无障碍支持:label关联、键盘导航、错误提示
- 文档示例:提供使用文档和多场景示例
技术点:
typescript
interface FormItem {
name: string
label: string
type: 'input' | 'select' | 'datepicker'
rules?: ValidationRule[]
props?: Record<string, any> // 透传给具体组件
}
interface FormProps {
items: FormItem[]
initialValues?: Record<string, any>
onSubmit: (values: Record<string, any>) => void
layout?: 'horizontal' | 'vertical'
}
function Form({ items, initialValues, onSubmit, layout }: FormProps) {
const [values, setValues] = useState(initialValues || {})
const [errors, setErrors] = useState({})
const handleSubmit = async () => {
// 校验逻辑
const isValid = await validate(values, items)
if (isValid) onSubmit(values)
}
return (
<form onSubmit={handleSubmit}>
{items.map(item => (
<FormItem
key={item.name}
{...item}
value={values[item.name]}
error={errors[item.name]}
onChange={val => setValues(prev => ({ ...prev, [item.name]: val }))}
/>
))}
</form>
)
}
十二、组件可扩展性和可维护性策略
回答思路:从设计模式、代码组织、文档测试等方面展开。
可扩展性策略:
- 开闭原则:对扩展开放,对修改关闭。通过props、插槽、children实现自定义
- 组合优于继承:提供基础组件,通过组合构建复杂组件
- 配置化:通过配置对象控制行为,而非不断新增props
可维护性策略:
- 单一职责:每个组件只做一件事
- 类型安全:完善的TypeScript类型定义
- 文档与示例:Storybook或文档站点,覆盖主要用例
- 单元测试:使用Jest + React Testing Library覆盖核心逻辑
- 设计系统规范:统一的命名、API风格、错误处理模式
- 版本管理:遵循语义化版本,维护CHANGELOG
十三、数据中隐藏的深刻见解
回答思路 :这是开放式问题,考察你的技术洞察力和反思能力。用户回答了React状态同步问题,是个不错的切入点。
回答示例框架:
- 现象:在XX项目中,遇到了什么问题(如状态更新后UI没变)
- 排查:通过什么方法定位(如打印日志、React DevTools)
- 根因:发现是状态引用未变导致不触发更新(或闭包陷阱)
- 见解:React的状态更新依赖引用比较,不可变数据的重要性;函数式更新的适用场景
- 推广:这个经验如何沉淀到团队(加Lint规则、写文档、复用hooks)
常见深刻见解话题:
- 为什么setState是异步的?------ 批量更新优化性能
- 为什么列表key不能用index?------ diff算法复用节点导致状态错乱
- 为什么React需要合成事件?------ 跨浏览器兼容、事件池
- 为什么虚拟DOM快?------ 减少DOM操作,批量更新
十四、解决问题时对性能的影响
回答思路:结合具体案例,说明你在解决问题时如何权衡性能和可维护性。
回答示例 :
"在之前的项目中,我需要实现一个带搜索的大列表。最初方案是前端一次性加载全部数据,然后过滤渲染。但数据量超过2000条后,每次输入都会导致明显卡顿。
我意识到这是性能问题,于是采用了两个优化:
- 虚拟滚动:使用react-window只渲染可视区域,大幅减少DOM节点
- 防抖:输入框添加300ms防抖,减少过滤计算次数
最终首屏渲染时间从800ms降到150ms,输入时帧率从20fps提升到60fps。这个经历让我意识到,写代码不能只关注功能实现,必须从一开始就考虑数据量级对性能的影响,并在方案设计时留出优化空间。"
📚 知识点速查表
| 知识点 | 核心要点 |
|---|---|
| 职业规划 | 兴趣起源、能力匹配、价值认同、持续学习准备 |
| 事件循环 | 同步→清空微任务→执行一个宏任务→循环;Promise比setTimeout先执行 |
| HTTPS优势 | 加密、身份验证、完整性、HTTP/2支持、浏览器信任 |
| TS的any | 危害:失去类型安全;替代:unknown、联合类型、泛型、Record |
| React性能优化 | Profiler分析、memo/useMemo/useCallback、虚拟滚动、代码分割 |
| 浏览器缓存 | 强缓存(Cache-Control/Expires)、协商缓存(ETag/Last-Modified)、缓存位置 |
| CSS加载优化 | 关键CSS内联、压缩合并、PurgeCSS、避免@import |
| 表单组件设计 | 配置化、校验、布局适配、TypeScript、无障碍 |
| 组件设计原则 | 开闭原则、单一职责、组合优于继承、类型安全、文档测试 |
| 技术洞察力 | 从具体问题中提炼通用原理,沉淀为团队经验 |
| 性能思维 | 方案设计阶段考虑性能,量化优化效果 |
📌 最后一句:
腾讯这场暑期AI面,看似"简单"的题目背后是对技术理解深度的层层拷问。从事件循环的微观原理,到性能优化的宏观视野,再到组件设计的工程化思维,每一题都在考察你是否具备写出生产级代码的能力。面试官对实习生的期待,早已不是"会用框架",而是"能在复杂场景下做出正确技术判断"。