前言
大家好,我是木斯佳。
在这个春节假期,当大家都在谈论返乡、团圆与休息时,作为一名技术人,我的思考却不由自主地转向了行业的「冬」与「春」。
相信很多人都感受到了,在AI浪潮的席卷之下,前端领域的门槛在变高,纯粹的"增删改查"岗位正在肉眼可见地减少。曾经热闹非凡的面经分享,如今也沉寂了许多。但我们都知道,市场的潮水退去,留下的才是真正在踏实准备、努力沉淀的人。学习的需求,从未消失,只是变得更加务实和深入。
正值春节,也是复盘与规划的好时机。结合CSDN这次「春节代码贺新年」活动所提倡的"用技术视角记录春节、复盘成长",我决定在这个假期持续更新专栏,帮助年后参加春招的同学。
这个专栏的初衷很简单:拒绝过时的、流水线式的PDF引流贴,专注于收集和整理当下最新、最真实的前端面试资料。
我会在每一份面经和八股文的基础上,尝试从面试官的角度去拆解问题背后的逻辑,而不仅仅是提供一份静态的背诵答案。无论你是校招还是社招,目标是中大厂还是新兴团队,只要是真实发生、有价值的面试经历,我都会在这个专栏里为你沉淀下来。
温馨提示:市面上的面经鱼龙混杂,甄别真伪、把握时效,是我们对抗内卷最有效的武器。
在这个假期,让我们一起充电,为下一个技术春天做好准备。

面经原文内容
📍面试公司:有赞
🕐面试时间:2026-1-13
💻面试岗位:前端
❓面试问题:
一面
40分钟
-
1.项目难点亮点深挖(思路和解决办法)
-
2.浏览器渲染,display、visibility这些内容对元素的重排、重绘的影响
-
3.setTimeout、Promise、requestAnimationFrame这些api的区别
-
4.react中通过useState维护了一个对象数组,如果要修改这个对象数组的某个值,应该如何实现?
-
5.通过不可变数据(让口头说源代码怎么写😂),通过扩展运算符
如果对象的赋值,如果不是浅拷贝,使用深拷贝的话有哪些影响
-
6.重渲染层面来说,深拷贝有什么影响吗
面试的时候说id一样就不会重新渲染,后面复盘的时候试验了一下,都会重新渲染,除非用memo去包裹子组件,memo默认是浅拷贝,深拷贝一样会重新渲染
-
7.屏幕共享
-
8.考察一段ts代码的错误原因
-
9.手写题:url参数解析
二面
30分钟
- 1.为什么选择前端作为职业发展规划
- 2.实习(2段分别说明)
说一下第一段实习一个公司的规模和业务
实习离职的原因
实习对自己的成长
实习项目难点亮点追问 - 3.ai生成代码的比例
- 4.用过哪些ai编程工具
- 5.这些工具用下来,评价是什么
- 6.prompt的优化技巧有哪些
上一个问说可以在prompt中写入一些输出的例子,提问这样做有什么负面影响
ai的上下文工程有哪些
上一个问题说到了上下文压缩,问有哪些常见的压缩手段 - 7.国内外主流的大模型有哪些
- 8.项目追问
- 9.前端性能优化有哪些方向,这些方向下有哪些技术手段
- 10.用户反馈h5页面,滚动会掉帧,但是没有办法复现,根据你的经验考虑可能是哪些方面的问题
- 11.内存泄漏(面试忘记了)
- 12.js长任务(long task)会导致
dom过多 - 13.上一段实习遇到的协作的问题是如何解决的
上一段实习mt对你的评价如何
在上一段时间的工作节奏如何
对未来工作节奏自己有什么预期
在工作之外,大概一周会花多少时间学习一些技术方面的问题
了解一下最近学习的技术 - 14.问langchain的deepresearch了解吗
- 15.对于新的工作机会会关注哪些点
转正薪资的期望
来源: 牛客网 26想要offer
📝 有赞前端一面二面HR面·面经深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|---|
| 公司定位 | 有赞 - 零售科技SaaS服务商 |
| 面试流程 | 一面(技术深度)+ 二面(技术广度+AI)+ HR面(综合素质) |
| 难度评级 | ⭐⭐⭐(三星,偏重实战与认知) |
| 考察重心 | 渲染原理、状态管理、AI工具链、性能优化、协作能力 |
| 典型有赞风格 | 务实、关注落地、重视AI工程化能力、HR面细致入微 |
💡 面经关键点:
有赞的前端面试,非常看重实战能力 和AI工具运用能力
他们希望招到"能用AI提效,又能解决复杂交互问题"的工程师
🎨 浏览器渲染·重排重绘深度解析
问题:display、visibility对元素的重排、重绘的影响
✅ 完整答案:
1. 基础概念
| 概念 | 定义 | 成本 |
|---|---|---|
| 重排(Reflow) | 计算元素几何位置(尺寸、位置) | 高 |
| 重绘(Repaint) | 绘制元素外观(颜色、背景) | 中 |
| 合成(Composite) | 图层合并 | 低 |
2. 属性对比
| 属性 | 效果 | 是否占位 | 是否触发重排 | 是否触发重绘 |
|---|---|---|---|---|
display: none |
元素消失 | 否 | ✅ 是(影响周围元素) | ✅ 是 |
visibility: hidden |
元素不可见 | 是 | ❌ 否 | ✅ 是 |
visibility: visible |
元素可见 | 是 | ❌ 否 | ✅ 是 |
opacity: 0 |
透明 | 是 | ❌ 否 | ✅ 是(可优化) |
opacity: 1 |
不透明 | 是 | ❌ 否 | ✅ 是 |
3. 代码验证
javascript
// 触发重排的操作
- 添加/删除元素
- 改变元素尺寸(width/height/padding/margin)
- 改变字体大小
- 改变窗口大小
- 获取布局信息(offsetHeight/getComputedStyle)
// 只触发重绘的操作
- 改变颜色
- 改变背景
- 改变visibility
- 改变opacity(如果元素独立图层)
// 优化示例
// ❌ 多次触发重排
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';
// ✅ 合并操作
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';
// 或者使用class
element.classList.add('new-style');
4. 面试官追问:如何减少重排?
javascript
// 1. 批量DOM操作
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const li = document.createElement('li');
li.textContent = `Item ${i}`;
fragment.appendChild(li);
}
list.appendChild(fragment); // 一次重排
// 2. 离线操作
const clone = list.cloneNode(true);
// 修改clone
list.parentNode.replaceChild(clone, list);
// 3. 使用transform替代top/left
// ❌
element.style.top = '100px';
// ✅
element.style.transform = 'translateY(100px)';
// 4. 图层提升
will-change: transform;
transform: translateZ(0);
⏱️ 定时器与动画API·深度对比
问题:setTimeout、Promise、requestAnimationFrame的区别
✅ 完整对比:
| API | 类型 | 执行时机 | 优先级 | 适用场景 |
|---|---|---|---|---|
| setTimeout | 宏任务 | 指定延迟后 | 低 | 延迟执行、轮询 |
| Promise.then | 微任务 | 当前宏任务末尾 | 高 | 异步结果处理 |
| requestAnimationFrame | 渲染前 | 下一次重绘前 | 最高(渲染相关) | 动画、流畅更新 |
1. 执行时序图
宏任务(setTimeout)
↓
执行同步代码
↓
清空微任务(Promise.then)
↓
requestAnimationFrame(如果有)
↓
渲染(重排/重绘)
↓
下一个宏任务
2. 代码验证
javascript
console.log('1: 同步');
setTimeout(() => {
console.log('2: setTimeout');
}, 0);
Promise.resolve().then(() => {
console.log('3: Promise');
});
requestAnimationFrame(() => {
console.log('4: requestAnimationFrame');
});
console.log('5: 同步');
// 输出顺序:
// 1: 同步
// 5: 同步
// 3: Promise(微任务)
// 4: requestAnimationFrame(渲染前)
// 2: setTimeout(下一个宏任务)
3. 性能对比
javascript
// 动画实现对比
// ❌ setTimeout动画(不精准)
let start = null;
function step() {
const now = Date.now();
if (!start) start = now;
const progress = (now - start) / 2000;
element.style.transform = `translateX(${progress * 100}px)`;
if (progress < 1) {
setTimeout(step, 16); // 约60fps
}
}
// ✅ requestAnimationFrame动画(精准)
function step() {
const now = performance.now();
if (!start) start = now;
const progress = (now - start) / 2000;
element.style.transform = `translateX(${progress * 100}px)`;
if (progress < 1) {
requestAnimationFrame(step); // 跟随屏幕刷新率
}
}
4. 面试官追问:setTimeout(fn, 0) 真的是0ms吗?
不是,HTML5规范规定最小延迟为4ms(嵌套超过5层时)
⚛️ React状态管理·对象数组修改
问题:useState维护对象数组,如何修改某个值?
✅ 完整答案:
1. 基础实现(浅拷贝)
javascript
const [items, setItems] = useState([
{ id: 1, name: 'Apple', count: 10 },
{ id: 2, name: 'Banana', count: 5 },
{ id: 3, name: 'Orange', count: 8 }
]);
// 修改id为2的count
const updateItem = (id, newCount) => {
setItems(prevItems =>
prevItems.map(item =>
item.id === id
? { ...item, count: newCount } // 浅拷贝,只修改需要改的
: item
)
);
};
// 删除
const deleteItem = (id) => {
setItems(prevItems => prevItems.filter(item => item.id !== id));
};
// 添加
const addItem = (newItem) => {
setItems(prevItems => [...prevItems, newItem]);
};
2. 深拷贝 vs 浅拷贝
javascript
// 浅拷贝
const shallowCopy = { ...item, count: 20 };
// 只拷贝一层,嵌套对象还是引用
// 深拷贝
const deepCopy = JSON.parse(JSON.stringify(item));
// 或者用lodash: _.cloneDeep(item)
// 修改
setItems(prevItems =>
prevItems.map(item =>
item.id === id
? JSON.parse(JSON.stringify({ ...item, count: newCount })) // 深拷贝
: item
)
);
3. 重渲染影响(候选人复盘的关键!)
javascript
// 候选人说的:id一样就不会重新渲染 ❌
// 实际:只要setState传入新对象,就会触发重新渲染 ✅
// 为什么?
React的重新渲染基于:
1. state是否变化(引用比较)
2. 默认情况下,只要父组件渲染,所有子组件都重新渲染
// 子组件示例
const Item = ({ item }) => {
console.log('Item渲染:', item.id);
return <div>{item.name}: {item.count}</div>;
};
// 即使item内容没变,只要父组件的items引用变了,所有Item都会重新渲染
// 优化方案:React.memo
const Item = React.memo(({ item }) => {
console.log('Item渲染:', item.id);
return <div>{item.name}: {item.count}</div>;
}, (prevProps, nextProps) => {
// 自定义比较逻辑
return prevProps.item.id === nextProps.item.id
&& prevProps.item.count === nextProps.item.count;
});
// 或者用useMemo
const itemElements = useMemo(() =>
items.map(item => <Item key={item.id} item={item} />),
[items] // 只有items变化时才重新计算
);
4. 深拷贝对重渲染的影响
javascript
// 场景:修改一个item
const newItems = prevItems.map(item =>
item.id === id
? JSON.parse(JSON.stringify({ ...item, count: newCount })) // 深拷贝
: item // 浅拷贝,引用不变
);
// 结果:
- 修改的item:新对象 → 该子组件重新渲染
- 其他item:引用没变 → 如果用了React.memo,不会重新渲染
- 但如果父组件传递了新的items数组,所有子组件都会收到新的props引用
// 最佳实践:
1. 使用不可变数据(Immer)
2. 配合React.memo + useCallback
3. 列表项使用唯一key
📝 TS代码错误排查·类型系统
问题:考察一段ts代码的错误原因
✅ 常见TS错误类型:
typescript
// 1. 类型不匹配
let str: string = 123; // ❌ Type 'number' is not assignable to type 'string'
// 2. 可选链问题
interface User {
name: string;
age?: number;
}
const user: User = { name: 'Tom' };
const age = user.age.toString(); // ❌ Object is possibly 'undefined'
// ✅ 正确
const age = user.age?.toString();
// 3. 类型断言滥用
const input = document.getElementById('input') as HTMLInputElement;
// 如果元素不存在,会报错
// ✅ 正确
const input = document.getElementById('input');
if (input instanceof HTMLInputElement) {
// 类型收窄
}
// 4. 索引签名
interface Dictionary {
[key: string]: string;
}
const dict: Dictionary = {
name: 'Tom',
age: 18 // ❌ Type 'number' is not assignable to type 'string'
};
// 5. 泛型约束
function getLength<T>(arg: T): number {
return arg.length; // ❌ Property 'length' does not exist on type 'T'
}
// ✅ 正确
function getLength<T extends { length: number }>(arg: T): number {
return arg.length;
}
🔗 URL参数解析·手写题
问题:实现URL参数解析
✅ 完整实现:
javascript
/**
* 解析URL参数为对象
* 输入:'https://example.com?name=Tom&age=18&city=Beijing'
* 输出:{ name: 'Tom', age: '18', city: 'Beijing' }
*
* 进阶:处理特殊字符、重复参数、数组格式
*/
function parseUrlParams(url) {
// 方案1:基础版
const params = {};
// 获取?后面的部分
const queryString = url.split('?')[1];
if (!queryString) return params;
// 分割参数
const pairs = queryString.split('&');
pairs.forEach(pair => {
const [key, value] = pair.split('=');
params[decodeURIComponent(key)] = decodeURIComponent(value || '');
});
return params;
}
// 方案2:进阶版(处理数组、重复key)
function parseUrlParamsAdvanced(url) {
const params = {};
const urlObj = new URL(url); // 现代浏览器API
for (const [key, value] of urlObj.searchParams.entries()) {
// 处理数组格式:key[]
if (key.endsWith('[]')) {
const arrayKey = key.slice(0, -2);
if (!params[arrayKey]) {
params[arrayKey] = [];
}
params[arrayKey].push(value);
}
// 处理重复key
else if (params.hasOwnProperty(key)) {
if (!Array.isArray(params[key])) {
params[key] = [params[key]];
}
params[key].push(value);
}
else {
params[key] = value;
}
}
return params;
}
// 方案3:正则表达式版
function parseUrlParamsRegex(url) {
const params = {};
const regex = /[?&]([^=#]+)=([^&#]*)/g;
let match;
while (match = regex.exec(url)) {
const key = decodeURIComponent(match[1]);
const value = decodeURIComponent(match[2]);
if (params.hasOwnProperty(key)) {
if (!Array.isArray(params[key])) {
params[key] = [params[key]];
}
params[key].push(value);
} else {
params[key] = value;
}
}
return params;
}
// 测试
console.log(parseUrlParams('https://example.com?name=Tom&age=18&city=Beijing'));
// { name: 'Tom', age: '18', city: 'Beijing' }
console.log(parseUrlParamsAdvanced('https://example.com?hobby[]=reading&hobby[]=coding&name=Tom&name=Jerry'));
// { hobby: ['reading', 'coding'], name: ['Tom', 'Jerry'] }
🤖 AI编程工具·深度评估
问题:用过哪些AI编程工具?评价如何?
✅ 完整对比:
| 工具 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| GitHub Copilot | 代码补全准、上下文理解强 | 付费、可能生成不安全代码 | 日常开发 |
| Cursor | IDE集成好、支持整个文件 | 不够稳定 | 重构、修改 |
| 通义灵码 | 中文友好、免费 | 代码质量一般 | 中文场景 |
| CodeGeeX | 免费、支持多语言 | 补全准确度低 | 简单代码 |
| ChatGPT | 解释代码、设计方案 | 需要手动复制 | 方案设计 |
评价维度:
javascript
// 1. 代码质量
- 生成的代码是否符合规范
- 是否有安全隐患
- 是否考虑边界情况
// 2. 上下文理解
- 能否理解项目结构
- 能否复用已有代码风格
// 3. 效率提升
- 重复代码生成速度
- 调试辅助能力
// 4. 学习成本
- 是否需要专门学习prompt技巧
- 是否影响原有工作流
📝 Prompt优化技巧·深度解析
问题:prompt的优化技巧有哪些?
✅ 完整总结:
1. 基础技巧
javascript
// ❌ 不好的prompt
"写一个排序函数"
// ✅ 好的prompt
"用JavaScript写一个快速排序函数,输入是数字数组,输出是排序后的数组,需要处理空数组和边界情况"
// 技巧1:明确角色
"你是一个资深前端工程师,请帮我..."
// 技巧2:给出上下文
"在React项目中,使用TypeScript,需要实现一个自定义hook用于..."
// 技巧3:分步提问
"第一步:设计数据结构;第二步:实现核心逻辑;第三步:添加错误处理"
2. 高级技巧
javascript
// 技巧4:提供示例
"请按照以下格式输出:
输入:'hello world'
输出:'Hello World'"
// 技巧5:约束输出
"请用中文回答,代码需要包含注释,不需要解释原理"
// 技巧6:思维链(Chain of Thought)
"让我们一步步思考:
1. 问题是什么?
2. 需要哪些数据?
3. 核心算法是什么?
4. 边界情况怎么处理?"
// 技巧7:Few-shot(少量示例)
"例如:
输入:[1, 2, 3] 输出:[2, 4, 6]
输入:[0, -1, -2] 输出:[0, -2, -4]
现在请处理:输入:[5, 10, 15]"
3. 负面影响(面试官追问)
javascript
// 提供示例的负面影响
// 1. 限制创造性
示例可能让AI只模仿示例,不考虑其他可能
// 2. 引入偏见
示例如果有错误或不良风格,AI会学习
// 3. 增加token消耗
长示例占用上下文窗口,可能忽略重要指令
// 4. 过度拟合
AI可能只生成类似示例的代码,不灵活
// 解决方案:
- 提供多样化示例
- 明确说"这只是参考,请根据实际情况调整"
- 平衡示例数量和指令清晰度
📦 上下文工程·压缩手段
问题:上下文压缩有哪些常见手段?
✅ 完整总结:
1. 为什么需要上下文压缩?
- 大模型上下文窗口有限(如GPT-4 32K/128K)
- 长文档可能超过限制
- token消耗 = 成本
2. 压缩手段
| 手段 | 原理 | 适用场景 |
|---|---|---|
| 摘要提取 | 提取关键信息 | 长文档 |
| 分块+检索 | 只取相关块 | RAG |
| 关键词提取 | 保留重要词 | 搜索 |
| 结构化压缩 | JSON/XML转schema | 结构化数据 |
| 代码简化 | 移除注释、空格 | 代码分析 |
3. 实现示例
javascript
// 摘要提取
async function compressText(text, maxTokens) {
const response = await callLLM(`
请将以下文本压缩到${maxTokens}token以内,
保留关键信息,保持原意:
${text}
`);
return response;
}
// 分块+检索
function retrieveRelevantChunks(document, query, chunkSize = 1000) {
// 1. 分块
const chunks = splitIntoChunks(document, chunkSize);
// 2. 向量化(embedding)
const embeddings = await embedChunks(chunks);
// 3. 检索最相关的
const queryEmbedding = await embedText(query);
const relevantChunks = findTopK(chunks, embeddings, queryEmbedding, 3);
return relevantChunks;
}
// 结构化压缩
function compressJSON(data) {
// 只保留需要的字段
const compressed = data.map(item => ({
id: item.id,
name: item.name,
// 移除不需要的字段
}));
return JSON.stringify(compressed);
}
🚀 前端性能优化·完整体系
问题:前端性能优化有哪些方向?技术手段有哪些?
✅ 完整体系:
1. 加载优化
| 方向 | 手段 | 效果 |
|---|---|---|
| 网络优化 | CDN、HTTP/2、Gzip | 减少传输时间 |
| 资源优化 | 压缩图片、Tree Shaking | 减小体积 |
| 缓存优化 | 强缓存、协商缓存 | 减少请求 |
| 预加载 | Preload、Prefetch | 提前加载 |
| 懒加载 | 图片懒加载、路由懒加载 | 按需加载 |
javascript
// 预加载示例
<link rel="preload" href="font.woff2" as="font">
<link rel="prefetch" href="next-page.js">
// 路由懒加载
const Home = lazy(() => import('./pages/Home'));
2. 渲染优化
| 方向 | 手段 | 效果 |
|---|---|---|
| 减少重排 | 批量DOM操作、transform代替位置 | 减少计算 |
| 图层提升 | will-change、transform: translateZ(0) | GPU加速 |
| 虚拟列表 | 只渲染可视区域 | 大数据量 |
| 时间切片 | requestIdleCallback | 避免卡顿 |
| Web Worker | 耗时计算放在后台 | 主线程不阻塞 |
javascript
// 虚拟列表
function VirtualList({ data, height, itemHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const startIdx = Math.floor(scrollTop / itemHeight);
const endIdx = Math.min(
startIdx + Math.ceil(height / itemHeight),
data.length
);
const visibleData = data.slice(startIdx, endIdx);
return (
<div onScroll={e => setScrollTop(e.target.scrollTop)}>
<div style={{ height: data.length * itemHeight }}>
{visibleData.map(item => (
<div style={{ position: 'absolute', top: startIdx * itemHeight }}>
{item}
</div>
))}
</div>
</div>
);
}
3. 运行时优化
| 方向 | 手段 | 效果 |
|---|---|---|
| 防抖节流 | 限制高频操作 | 减少计算 |
| 事件委托 | 统一事件处理 | 减少监听器 |
| 长任务拆分 | requestIdleCallback | 避免卡顿 |
| 内存管理 | 及时清理引用 | 避免内存泄漏 |
| 代码拆分 | 按需加载 | 减少首屏JS |
javascript
// 长任务拆分
function processLargeArray(array) {
const chunkSize = 100;
let index = 0;
function processChunk() {
const end = Math.min(index + chunkSize, array.length);
for (let i = index; i < end; i++) {
// 处理数据
processItem(array[i]);
}
index = end;
if (index < array.length) {
// 继续下一块
requestIdleCallback(processChunk, { timeout: 1000 });
}
}
requestIdleCallback(processChunk);
}
📱 H5滚动掉帧·问题排查
问题:用户反馈H5滚动掉帧,无法复现,可能原因?
✅ 排查思路:
1. 常见原因分析
| 类别 | 具体原因 | 排查方法 |
|---|---|---|
| 渲染性能 | DOM过多、重排频繁 | 检查DOM树、布局变化 |
| JS执行 | 长任务阻塞、滚动监听 | Performance面板 |
| 内存 | 内存泄漏、频繁GC | Memory面板 |
| 图片 | 大图加载、懒加载失效 | 检查图片大小 |
| 设备差异 | 低端机性能不足 | 模拟降级设备 |
| 网络 | 资源加载慢 | 弱网模拟 |
2. 排查工具
javascript
// 1. Performance面板
- 录制滚动操作
- 查看FPS曲线
- 定位长任务
// 2. 代码埋点
window.addEventListener('scroll', () => {
const start = performance.now();
// 使用requestAnimationFrame检测帧率
requestAnimationFrame(() => {
const duration = performance.now() - start;
if (duration > 16) { // 超过60fps的帧时间
console.warn('卡顿检测:', duration);
// 上报
}
});
});
// 3. 模拟低端机
Chrome DevTools → Performance → CPU降速
Network → 网络降速
3. 解决方案
javascript
// 1. 滚动事件防抖
let ticking = false;
window.addEventListener('scroll', () => {
if (!ticking) {
requestAnimationFrame(() => {
handleScroll();
ticking = false;
});
ticking = true;
}
});
// 2. 使用passive事件
window.addEventListener('scroll', handler, { passive: true });
// 3. 减少重排
// 使用transform代替top/left
// 读取样式放在一起
// 4. 虚拟列表(数据量大时)
// 5. 图片优化
<img loading="lazy" src="image.jpg">
🎁 附:有赞面试复习清单
| 知识点 | 掌握程度 | 重点方向 |
|---|---|---|
| 渲染原理 | ⭐⭐⭐⭐ | 重排重绘、优化手段 |
| 定时器API | ⭐⭐⭐ | setTimeout/Promise/RAF对比 |
| React状态 | ⭐⭐⭐⭐ | 不可变数据、memo优化 |
| TS类型 | ⭐⭐⭐ | 常见错误、类型收窄 |
| URL解析 | ⭐⭐⭐ | 多种实现、边界处理 |
| AI工具链 | ⭐⭐⭐⭐ | 工具对比、prompt技巧 |
| 上下文压缩 | ⭐⭐⭐ | 压缩手段、原理 |
| 性能优化 | ⭐⭐⭐⭐⭐ | 加载/渲染/运行时优化 |
| 问题排查 | ⭐⭐⭐⭐ | 掉帧问题、内存泄漏 |
📌 最后一句:
有赞的面试,是一场技术深度 + AI认知 + 综合素质 的全面考察。
他们不只要一个能写代码的人,
他们要一个能用AI提效、能解决复杂问题、能和团队愉快协作的人。