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

温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
面经原文内容
📍面试公司:美团
🕐面试时间:近期
💻面试岗位:前端暑期实习一面
❓面试问题:
- useEffect和useLayoutEffect的区别是什么
- useEffect依赖数组是什么作用
- 依赖数组多个变量,其中一个变化是否会执行副作用
- useEffect清理函数的作用是什么,为何会产生内存泄漏
- React组件间通信有哪几种方式
- useState是同步还是异步执行
- 对象函数与箭头函数this指向相关代码分别输出什么
- 前端缓存相关:区分协商缓存和强缓存、如何在浏览器查看资源是否命中缓存、什么缓存返回304、强缓存与协商缓存分别需要配置哪些HTTP字段
- 从浏览器输入域名到页面渲染完整流程是什么
- HTML解析中遇到JS、CSS资源加载是否阻塞解析
- 前端页面性能指标有哪些,FCP如何计算、如何采集统计FCP数据、全页面渲染结束时间如何监听
- Promise.all、Promise.race、Promise.allSettled、Promise.any的区别与适用场景
- 异步代码执行顺序相关代码输出结果
- 仿微信聊天列表卡片布局如何实现
- CSS position有哪些取值及各自含义
- rem、em、vw单位含义
- 编写函数查找字符串第一个不重复字符下标,无则返回-1
来源:牛客网 不想打工的加菲猫很慵懒
💡 木木有话说(刷前先看)
美团这场面试,是一份"React原理+网络缓存+性能指标+CSS布局"全面覆盖的经典面经。,非常适合暑期实习面试前系统自查。
📝 美团前端暑期实习一面·深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|---|
| 面试风格 | 基础全面型 + 原理追问型 + 实战代码型 |
| 难度评级 | ⭐⭐⭐⭐(四星,覆盖面广,原理细节多) |
| 考察重心 | React Hooks原理、组件通信、前端缓存、浏览器渲染、性能指标、Promise并发、CSS布局 |
| 特殊之处 | 无项目深挖,纯技术基础考察,适合检验知识体系完整性 |
🔍 逐题深度解析
一、useEffect和useLayoutEffect的区别
| 维度 | useEffect | useLayoutEffect |
|---|---|---|
| 执行时机 | 浏览器绘制后(异步) | 浏览器绘制前(同步) |
| 阻塞渲染 | 否 | 是 |
| 使用场景 | 多数副作用(数据获取、订阅、日志) | 需要测量DOM、同步修改样式(防止闪烁) |
| SSR支持 | 完全支持 | 有警告(需跳过) |
javascript
// useLayoutEffect典型场景:测量DOM尺寸
useLayoutEffect(() => {
const height = divRef.current.offsetHeight
setHeight(height) // 绘制前更新,避免闪烁
}, [])
二、useEffect依赖数组的作用
作用:控制副作用函数的执行时机。
| 依赖数组 | 执行时机 |
|---|---|
| 无(不传) | 每次渲染后都执行 |
[](空数组) |
仅组件挂载时执行一次 |
[a, b] |
首次挂载 + a或b变化时执行 |
三、依赖数组多个变量,其中一个变化是否会执行副作用
答案 :会。只要依赖数组中任意一个变量的值发生变化(浅比较),副作用函数就会重新执行。
javascript
useEffect(() => {
console.log('count或name变化了')
}, [count, name])
// count变化 → 执行;name变化 → 执行
四、useEffect清理函数的作用及内存泄漏
作用:在组件卸载或下次执行副作用前清理上一次的副作用。
常见清理场景:
- 清除定时器(
clearInterval/clearTimeout) - 取消订阅(
eventBus.off) - 取消请求(
AbortController.abort())
内存泄漏原因:未清理的定时器、订阅、DOM事件监听器在组件卸载后仍然存在,导致无法被垃圾回收。
javascript
useEffect(() => {
const timer = setInterval(() => {}, 1000)
return () => clearInterval(timer) // 清理函数
}, [])
五、React组件间通信方式
| 方式 | 适用场景 |
|---|---|
props / emit |
父子组件直接通信 |
Context |
跨多级组件(祖先→后代) |
| 状态管理(Redux/Zustand/Pinia) | 全局共享状态 |
eventBus |
任意组件通信(不推荐大型项目) |
ref |
父组件调用子组件方法 |
props.children / 插槽 |
内容分发 |
六、useState是同步还是异步执行
答案 :异步执行(批量更新)。
原理 :React会将多个setState合并到一个更新批次中,在事件循环末尾统一执行,以优化性能。
javascript
const [count, setCount] = useState(0)
setCount(1)
console.log(count) // 输出0,不是1
获取最新值的方法 :使用函数式更新或useEffect监听变化。
七、对象函数与箭头函数this指向
箭头函数 :this静态绑定,定义时继承外层作用域。
普通函数 :this动态绑定,调用时决定。
javascript
const obj = {
name: 'Tom',
normal: function() { console.log(this.name) },
arrow: () => { console.log(this.name) }
}
obj.normal() // 'Tom'(this指向obj)
obj.arrow() // undefined(this指向外层,通常是window)
八、前端缓存机制
强缓存:
- 缓存有效期内直接使用本地缓存,不发请求
- 状态码:
200 (from disk cache)/200 (from memory cache) - 字段:
Cache-Control: max-age=3600(优先级高)、Expires
协商缓存:
- 缓存过期后向服务端验证,返回304则使用缓存
- 状态码:
304 Not Modified - 字段:请求头
If-None-Match(对应ETag)、If-Modified-Since(对应Last-Modified)
浏览器查看 :Chrome DevTools → Network → 查看Size列((disk cache)/(memory cache))和Status(304)
九、浏览器输入域名到页面渲染的完整流程
- DNS解析(域名→IP)
- TCP三次握手
- TLS握手(HTTPS)
- 发送HTTP请求
- 服务端处理并返回响应
- 浏览器解析HTML → DOM树
- 解析CSS → CSSOM树
- 合成渲染树
- 布局(Layout/Reflow)
- 分层(Layer)
- 绘制(Paint)
- 分块(Tiling)
- 光栅化(Rasterize)
- 合成(Composite)
- 页面显示
十、HTML解析中JS、CSS资源加载是否阻塞解析
| 资源 | 阻塞情况 |
|---|---|
| CSS | 不阻塞HTML解析,但阻塞后续JS执行和渲染 |
| 同步JS | 阻塞HTML解析(下载+执行) |
async JS |
异步下载,不阻塞解析,下载完立即执行 |
defer JS |
异步下载,不阻塞解析,DOMContentLoaded前执行 |
十一、前端页面性能指标与FCP采集
核心指标:
- FCP(First Contentful Paint):首次内容绘制,≤1.8s
- LCP(Largest Contentful Paint):最大内容绘制,≤2.5s
- INP(Interaction to Next Paint):交互延迟,≤200ms
- CLS(Cumulative Layout Shift):累积布局偏移,≤0.1
- TTFB(Time To First Byte):首字节时间,≤800ms
FCP计算:从开始加载到任意文本/图片/Canvas绘制完成的时间。
采集方法:
javascript
new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-contentful-paint') {
console.log('FCP:', entry.startTime)
}
}
}).observe({ entryTypes: ['paint'] })
全页面渲染结束时间 :load事件(window.onload)或LCP。
十二、Promise并发方法区别
| 方法 | 返回时机 | 适用场景 |
|---|---|---|
Promise.all |
全部成功或任一失败 | 所有请求都必要 |
Promise.allSettled |
全部完成(无论成功/失败) | 需要知道每个结果 |
Promise.race |
第一个完成 | 超时控制 |
Promise.any |
第一个成功 | 多个备用服务 |
javascript
Promise.all([p1, p2]) // 全成功则成功,一失败则失败
Promise.allSettled([p1, p2]) // 全部完成,返回状态数组
Promise.race([p1, p2]) // 第一个完成的
Promise.any([p1, p2]) // 第一个成功的
十三、异步代码执行顺序
javascript
console.log('1')
setTimeout(() => console.log('2'), 0)
Promise.resolve().then(() => console.log('3'))
console.log('4')
// 输出:1,4,3,2
十四、仿微信聊天列表卡片布局
Flex实现:
css
.chat-item {
display: flex;
align-items: center;
padding: 12px;
gap: 12px;
}
.avatar {
width: 48px;
height: 48px;
border-radius: 50%;
flex-shrink: 0;
}
.content {
flex: 1;
min-width: 0;
}
.name {
font-weight: bold;
}
.message {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
}
.time {
flex-shrink: 0;
font-size: 12px;
color: #999;
}
十五、CSS position取值
| 取值 | 含义 |
|---|---|
static |
默认,文档流 |
relative |
相对自身偏移,占位保留 |
absolute |
绝对定位,相对最近的非static祖先 |
fixed |
相对视口固定 |
sticky |
粘性定位,滚动到阈值时固定 |
十六、rem、em、vw单位
| 单位 | 参照物 |
|---|---|
| rem | 根元素(html)字体大小 |
| em | 当前元素字体大小 |
| vw | 视口宽度的1% |
css
html { font-size: 16px; }
.box { width: 10rem; } /* 160px */
.child { font-size: 1.2em; } /* 1.2倍父元素字体 */
.container { width: 50vw; } /* 视口宽度的50% */
十七、查找字符串第一个不重复字符下标
javascript
function firstUniqChar(s) {
const map = new Map()
for (let i = 0; i < s.length; i++) {
map.set(s[i], (map.get(s[i]) || 0) + 1)
}
for (let i = 0; i < s.length; i++) {
if (map.get(s[i]) === 1) return i
}
return -1
}
📚 知识点速查表
| 知识点 | 核心要点 |
|---|---|
| useEffect vs useLayoutEffect | 绘制后/前,测量DOM用LayoutEffect |
| 依赖数组 | 控制执行时机,空数组仅挂载执行 |
| 清理函数 | 清除定时器/订阅,防止内存泄漏 |
| 组件通信 | props、Context、状态管理、eventBus、ref |
| useState | 异步批量更新,函数式更新获取最新值 |
| 箭头函数this | 静态绑定,定义时继承外层 |
| 缓存 | 强缓存(Cache-Control)、协商缓存(ETag)、304 |
| 浏览器渲染 | DNS→TCP→请求→解析→布局→绘制→合成 |
| 性能指标 | FCP/LCP/INP/CLS,PerformanceObserver采集 |
| Promise并发 | all/allSettled/race/any区别 |
| CSS布局 | Flex聊天卡片、position取值、rem/em/vw |
| 算法 | 哈希表统计频率,两次遍历 |
📌 最后一句:
美团这场一面,是一场"基础扎实度"的全面体检。从React Hooks原理到缓存机制,从浏览器渲染到性能指标采集,从Promise并发到CSS布局,没有偏题怪题,每一道都是必须掌握的核心知识。