📝 MiniMax前端实习二面面经深度解析
前言
大家好,我是木斯佳。
最近感触很深------在AI浪潮席卷之下,前端岗位肉眼可见地变少了,各大社区里的面经分享也没有前几年那么火热了。但我知道,依然有很多同学在努力准备面试,依然需要最新、最真实的前端面试资料。
市场需求在变,但学习的需求一直都在。
为此,我决定开这样一个专栏:专门收集整理最新的前端八股文和真实面经。并在原文基础上,增加面试官角度的问题,而不仅仅是静态的死记硬背。这样无论是校招还是社招,无论是大厂还是中小厂,只要是真实有价值的面试经历,都会在这里沉淀下来。
我发现很多所谓的面经分享都是pdf引流、背后都是培训机构弄的,注意甄别真实性。面试题时效性很重要!!!
面经原文内容
面试公司:MiniMax
🕐面试时间: 1 月 13 日(用户发布时间)
💻面试岗位:前端实习
❓面试问题:
- 1、面试官自我介绍,是个戴鸭舌帽的女面试官,第一印象感觉很严格
- 2、结合项目自我介绍
- 3、大文件分片上传中webworker的使用详情,有对比过使用之后的优化效果么
无,我说就是看别人都这么写,我就这么用了,然后面试官跟着我一起笑了 - 4、tailwindcss的使用感觉,webpack和vite打包tailwindcss之后生成什么
不知道,瞎答,随便扯说babel进行编译,然后生成AST抽象语法树,感觉答错了,面试官听到我说AST抽象语法树的时候直接笑了,应该是被我唐笑了🤣 - 5、自我介绍里面说昨天和今天浅学了一下react,说说vue和react的使用感觉的区别,使用场景有什么不同
还是瞎扯,一个函数式,一个渐进式,一个贴近原生js语法,一个贴近html,使用场景的区别,不知道 - 6、说说vue2和vue3的区别
API、响应式原理、TS支持、生命周期、Diff算法
diff算法说的比较详细,面试官听完之后说研究的还蛮深入的,笑死 - 7、vuex和pinia的区别
常规八股#牛客AI配图神器# - 8、项目中用到了JWT双Token的刷新机制,应该是自己添加的亮点,说说怎么做的
中间被打断了,我说的都是前端axios里面请求拦截器和响应拦截器里面的详细操作过程,面试官问了后端的实现原理,不知道什么意思,不过我接口都是自己用ai写的然后修改了一下,说了说自己写的详细过程和缺陷 - 9、流式对答项目用fetch获取结果,为什么不用其他的协议
我说肯定用SSE简洁啊,但是一开始用fetch写的,后面懒得改了,然后面试官也跟着笑 - 10、markdown的渲染用了什么库,怎么渲染的
- 11、两道手撕:
a、并发上传控制器,因为项目里面用的fetch,所以用fetch的方式写出来,卡了一段时间,因为fetch的详细使用不熟悉,不过最后还是写出来了
b、三数之和变形题(面试官给题目之前说来道简单的算法题),三数之和给了个要求sum === target,不是三数之和为0,一开始题目都没看,直接秒,后面才发现有条件,改了一下ac - 12、反问:
a、怎么深入了解vite和webpack的使用,怎么才能答出面试中的vite/webpack相关的题目的要点和日常开发怎么深入使用:
面试官说其实日常开发深入用的也比较少,除非专门写底层原理,也就考一些常见的八股和官方文档的一些配置啥的
🙌笔者面试感想:面试官直接反问我,为什么本科金融转前端:说了很多,因为觉得金融确实是一个强社会资源要求的行业和专业,同时感觉自己的性格不是很擅长社交,不太适合一二级市场这些岗位啥的,然后为了让面试官印象好一点我说其实去互联网干开发其实沟通能力和情商啥的也很重要(求生欲拉满)
然后说金融是一个强硕士需求的行业和岗位,本科生基本没有可能进入金融行业核心岗位的可能,接触的一些升学去外校的学长学姐就业结果也基本上不尽如人意,所以直接转互联网干开发了,之前想跨考软工来着,然后说觉得考研风险太大,而且干前端开发也用不到硕士学历,面试官表示了解
然后说为什么转前端是因为一开始觉得前端竞争压力肯定没有后端那么大,而且干后端开发操作系统这些东西感觉要学的满深入的,感觉非科班和科班差距确实很大
后来深入学习前端,深入了解一些软件的前端的一些生成的底层原理的时候,说这个仔细专研的过程也还蛮有意思的
来源: 牛客网 wowowo
📝 MiniMax前端日常实习二面·面经深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|---|
| 面试风格 | 压力型 + 追问型 + 场景型 |
| 难度评级 | ⭐⭐⭐⭐(四星) |
| 考察重心 | 项目深度、技术选型理由、底层原理、非科班思维 |
| 典型MiniMax风格 | AI公司特性明显(流式、大文件、协议选型)、追问"为什么"、喜欢反问候选人 |
📦 大文件分片上传 + Web Worker 深度解析
问题复盘:
大文件分片上传中webworker的使用详情,有对比过使用之后的优化效果么?
候选人回答分析:
"无,我就是看别人都这么写,我就这么用了"
✅ 诚实是好的,但面试官想听的是:
- 你知道Web Worker解决了什么问题?
- 你知道分片上传的瓶颈在哪?
- 你有能力做技术选型对比?
✅ 标准答案结构(面试官想听的):
1. Web Worker 解决了什么问题?
javascript
// 主线程(UI线程)不能做耗时计算
// 分片上传前需要:计算文件Hash(断点续传)、分片加密、分片校验
// 这些计算如果在主线程做,会导致页面卡顿
// ✅ Web Worker 方案
const worker = new Worker('hash-worker.js');
worker.postMessage(file);
worker.onmessage = (e) => {
const hash = e.data; // 计算好的hash
startUploadWithHash(hash);
}
2. 优化效果对比(关键!)
| 指标 | 无Web Worker | 有Web Worker | 优化效果 |
|---|---|---|---|
| 页面卡顿时间 | 300ms(计算hash时) | 0ms | ✅ 100%消除 |
| FPS下降 | 掉到20帧 | 稳定60帧 | ✅ 流畅度提升 |
| 上传启动时间 | 同步阻塞 | 异步计算 | ✅ 体验连续 |
3. 进阶追问:Web Worker 的限制
- 不能操作DOM
- 不能跨域加载脚本
- 数据传输是拷贝(不是共享)
- 适合CPU密集型,不适合IO密集型
💡 面经关键点:
面试官不是要你做过优化对比,是要你有做对比的意识 。
没做过可以说:"如果让我设计对比方案,我会测这三个指标..."
🎨 TailwindCSS + Webpack/Vite 打包原理
问题复盘:
tailwindcss的使用感觉,webpack和vite打包tailwindcss之后生成什么
候选人回答分析:
"AST抽象语法树" → ❌ 完全偏离
✅ 正确理解:
1. TailwindCSS 本质
不是JS库,是PostCSS插件 + 配置文件
2. 打包后的产物
源码:
<div class="text-center p-4 bg-blue-500">
打包后(未压缩):
<div class="text-center p-4 bg-blue-500">
打包后(生产模式):
<div class="tc p4 bg-b5">
关键点: TailwindCSS在打包时做了三件事:
- 扫描你的HTML/JSX/模板,找到所有用到的class名
- 生成对应的CSS(只生成用到的,没用到的不会出现)
- PurgeCSS(Tree Shaking)删除未使用的CSS
3. Vite vs Webpack 的差异
| 维度 | Webpack | Vite |
|---|---|---|
| 开发环境 | 全量生成CSS | 按需生成 + 缓存 |
| HMR | 重写CSS文件 | 精准替换class |
| 生产打包 | css-loader + mini-css-extract-plugin | PostCSS + Rollup |
💡 面经关键点:
面试官笑不是因为"AST"这个词错,而是因为明显在跳答 。
在不懂的时候,说"我不确定,但我猜可能是..."比"瞎扯一个高大上的词"更安全。
⚛️ Vue vs React + Vue2 vs Vue3(深度对比)
问题复盘:
vue和react的使用感觉的区别,使用场景有什么不同
vue2和vue3的区别(diff算法说得详细)
✅ 标准答案结构(面试官想听的):
Vue vs React:三个层次
层次一:开发体验
- Vue:模板语法 → 更接近HTML,学习曲线平缓
- React:JSX → 更接近JS,灵活度高
层次二:响应式原理
javascript
// Vue 3:Proxy 代理
let count = ref(0)
count.value++ // 自动追踪依赖
// React:不可变数据
const [count, setCount] = useState(0)
setCount(count + 1) // 显式触发
层次三:使用场景
| 场景 | 更适合 | 理由 |
|---|---|---|
| 传统后台系统 | Vue | 开发快、约定多、少踩坑 |
| 大型复杂应用 | React | 灵活、生态丰富、可控性强 |
| 跨端开发 | React Native | 一套代码多端 |
| 快速原型 | Vue | 开箱即用 |
Vue2 vs Vue3:深度对比
1. 响应式原理
javascript
// Vue 2: Object.defineProperty
- 无法检测对象属性新增/删除
- 无法直接监听数组变化(需要hack)
// Vue 3: Proxy
- 可以监听所有操作
- 懒响应式(性能更好)
2. Diff算法优化(面试官肯定你的点)
javascript
// Vue 2: 双端比较
- 同时比较新旧头尾四个节点
- 时间复杂度O(n)
// Vue 3: 最长递增子序列
- 静态标记(PatchFlag)
- 只比较动态节点
- 比Vue 2快1.3-2倍
3. 生命周期对比
| Vue2 | Vue3 | 说明 |
|---|---|---|
| beforeCreate | setup() | 组合式API取代 |
| created | setup() | 同上 |
| beforeMount | onBeforeMount | 需要导入 |
| mounted | onMounted | 需要导入 |
🔐 JWT双Token刷新机制(前后端联动)
问题复盘:
项目中用到了JWT双Token的刷新机制,说说怎么做的
中间被打断,面试官问了后端的实现原理
✅ 完整答案结构:
1. 前端实现(你答的)
javascript
// axios请求拦截器
axios.interceptors.request.use(config => {
const token = localStorage.getItem('accessToken');
if (token) {
config.headers.Authorization = `Bearer ${token}`;
}
return config;
});
// 响应拦截器(处理token过期)
axios.interceptors.response.use(
response => response,
async error => {
const originalRequest = error.config;
if (error.response?.status === 401 && !originalRequest._retry) {
originalRequest._retry = true;
// 调用刷新token接口
const { data } = await axios.post('/refresh', {
refreshToken: localStorage.getItem('refreshToken')
});
localStorage.setItem('accessToken', data.accessToken);
originalRequest.headers.Authorization = `Bearer ${data.accessToken}`;
return axios(originalRequest);
}
return Promise.reject(error);
}
);
2. 后端实现原理(面试官追问的)
javascript
// 核心逻辑
1. 用户登录 → 生成两个token
- accessToken(短时效,如15分钟)
- refreshToken(长时效,如7天,带唯一标识)
2. 刷新逻辑
- 客户端带refreshToken请求刷新接口
- 服务端验证refreshToken有效性(签名+过期+是否已撤销)
- 生成新的accessToken返回
- 可选:同时刷新refreshToken(轮换机制)
3. 安全设计
- refreshToken存储在数据库(或Redis)
- 每次刷新检查是否被滥用(同一个refreshToken多次刷新?)
- 登出时删除refreshToken
💡 面经关键点:
你用了AI生成后端代码没问题,但要理解每一行在做什么 。
面试官问"后端原理"不是要你写Java/Python,是要你描述清楚数据流转和设计意图。
🌊 流式输出:fetch vs SSE vs WebSocket
问题复盘:
流式对答项目用fetch获取结果,为什么不用其他的协议
✅ 技术选型对比:
| 协议 | 适用场景 | 优点 | 缺点 |
|---|---|---|---|
| fetch | 普通API请求 | 简单、通用 | 不支持流式(需要Reader) |
| SSE | 服务端推送(AI流式) | 原生支持流、自动重连 | 单向、连接数限制 |
| WebSocket | 双向实时通信 | 全双工、低延迟 | 复杂、需要心跳 |
| WebRTC | 音视频流 | P2P、低延迟 | 极复杂 |
AI流式场景的最佳实践:
javascript
// ✅ 推荐:fetch + ReadableStream
const response = await fetch('/api/chat', {
method: 'POST',
body: JSON.stringify({ prompt }),
headers: { 'Content-Type': 'application/json' }
});
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done) break;
const chunk = decoder.decode(value);
// 处理流式数据
updateUI(chunk);
}
// 或者用 SSE(更简洁)
const eventSource = new EventSource('/api/stream');
eventSource.onmessage = (e) => {
updateUI(e.data);
};
💡 面经关键点:
面试官笑是因为你承认"懒得改" → 诚实但不够专业。
更好的回答:"一开始用fetch是因为项目快速原型,如果重构我会用SSE,因为它..."
🔄 并发上传控制器(手撕代码)
题目: 实现并发上传控制器(用fetch)
✅ 标准答案:
javascript
class ConcurrentUploader {
constructor(concurrency = 3) {
this.concurrency = concurrency; // 并发数
this.queue = []; // 待上传队列
this.running = 0; // 正在上传数
this.results = []; // 上传结果
}
// 添加上传任务
add(file) {
return new Promise((resolve, reject) => {
this.queue.push({
file,
resolve,
reject
});
this._next();
});
}
// 执行下一个任务
_next() {
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
const { file, resolve, reject } = this.queue.shift();
this.running++;
this._upload(file)
.then(result => {
this.results.push(result);
resolve(result);
})
.catch(error => {
reject(error);
})
.finally(() => {
this.running--;
this._next(); // 继续下一个
});
}
// 上传文件(fetch实现)
async _upload(file) {
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Upload failed');
}
return response.json();
}
// 等待所有任务完成
async waitAll() {
return new Promise((resolve) => {
const check = () => {
if (this.queue.length === 0 && this.running === 0) {
resolve(this.results);
} else {
setTimeout(check, 100);
}
};
check();
});
}
}
// 使用示例
const uploader = new ConcurrentUploader(3);
const files = [...]; // 多个文件
Promise.all(files.map(file => uploader.add(file)))
.then(results => {
console.log('所有文件上传完成', results);
});
💡 面经关键点:
fetch的详细使用不熟悉 → 这是基本功,必须补。
并发控制的核心是队列 + 计数器 + 递归调用_next。
🧮 三数之和变形题
题目:
三数之和给了个要求sum === target,不是三数之和为0
✅ 解法:
javascript
function threeSum(nums, target) {
const result = [];
nums.sort((a, b) => a - b); // 排序
for (let i = 0; i < nums.length - 2; i++) {
// 去重
if (i > 0 && nums[i] === nums[i - 1]) continue;
let left = i + 1;
let right = nums.length - 1;
while (left < right) {
const sum = nums[i] + nums[left] + nums[right];
if (sum === target) {
result.push([nums[i], nums[left], nums[right]]);
// 去重
while (left < right && nums[left] === nums[left + 1]) left++;
while (left < right && nums[right] === nums[right - 1]) right--;
left++;
right--;
} else if (sum < target) {
left++;
} else {
right--;
}
}
}
return result;
}
注意点:
- 题目说"一开始题目都没看,直接秒" → 这是经验主义陷阱
- 三数之和为0是特例,target可变是通解
- 面试官给简单题不是真的简单,是看你会不会认真审题
❓ 反问环节深度解析
你的问题:
怎么深入了解vite和webpack的使用,怎么才能答出面试中的vite/webpack相关的题目的要点
面试官回答:
其实日常开发深入用的也比较少,除非专门写底层原理,也就考一些常见的八股和官方文档的一些配置啥的
💡 解读:
- 面试官在帮你降低焦虑 → 不用怕,大家都是表面功夫
- 潜台词:你能把常见配置说明白,能对比差异,就够了
- 但注意:她说的是"日常开发",不是"面试要求"
面试官反问:
为什么本科金融转前端?
这是压力面后的 放松题**,也是价值观题。
你的回答分析:
✅ 优点:
- 诚恳(社会资源要求、性格不适合)
- 有自驱力(深入学习后发现有意思)
- 有求生欲(沟通能力也重要)
❌ 可优化点:
- "前端竞争压力没后端大" → 这是刻板印象,现在前端也很卷
- 可以说:"前端能快速看到成果,正反馈强,适合我这种转行的"
📌 MiniMax二面·通关密码
1. 技术深度 > 技术广度
- Vue diff算法说得详细 → 被肯定
- Web Worker说不出优化效果 → 被追问
- ✅ 结论:把1-2个点挖深,比泛泛而谈10个点有用
2. 不知道就说"我猜",不要瞎扯
- AST抽象语法树 → 被笑(明显在跳)
- 不如说:"我不确定,但我猜Tailwind在打包时会扫描用到的class,只生成对应的CSS..."
3. 项目亮点要准备"第二层"
- 双Token刷新 → 准备前端实现
- 面试官问后端原理 → 你要能说出数据流转
- ✅ 准备方法:每个亮点问自己三个问题:
- 我为什么这么设计?
- 如果数据量翻10倍,哪里会崩?
- 换个方案会怎样?
4. 非科班要准备"故事线"
- 为什么转行?
- 为什么选前端?
- 学了什么?怎么学的?
- 遇到困难怎么解决的?
💡 这个故事不是编的,是你真实的成长轨迹。
🎁 附:MiniMax二面复习清单
| 知识点 | 掌握程度 |
|---|---|
| Web Worker + 大文件上传 | ⭐⭐⭐⭐ |
| TailwindCSS 打包原理 | ⭐⭐⭐ |
| Vue/React 深度对比 | ⭐⭐⭐⭐ |
| Vue2/3 diff算法 | ⭐⭐⭐⭐⭐ |
| JWT双Token(前后端) | ⭐⭐⭐⭐ |
| 流式协议选型 | ⭐⭐⭐ |
| 并发控制器手写 | ⭐⭐⭐⭐ |
| 三数之和(通用) | ⭐⭐⭐ |
📌 最后一句:
MiniMax的面试,是一场诚实度测试 + 学习能力测试 。
答错不可怕,可怕的是明明不懂却装懂 。
面试官跟你一起笑的那一刻,其实是在说:
"没关系,我们都是从不懂开始的。"