2026 年 6 月深圳前端工作行情
给正在找工作的朋友一些参考。
🔧技术栈
- React 机会 >> Vue 机会,学习 Vue 的必要性不是很大
- 不少前端岗位也要求 React Native 或 Flutter,有时间可以学习一下
- 全栈/后端纯 Node.js 的机会不多,学习 Go/Java/Python 其中之一是巨大的补足
- 英语口语,外企面试必备,重要程度不亚于编程技术
- AI 相关:
- Vibe Coding 工具:更偏好使用 Claude Code,Codex 这种先进工具的使用者,会问任务拆解、SKILLS等最佳实践
- LLM 开发框架:LangChain,Dify,n8n,OpenClaw(稍微了解即可)
- SSE 流式输出原理和实现,NDJSON 等流式传输数据格式
📋笔试要点
算法题类
传统企业考算法题的比较多(前端的算法这块儿本就是劣势,随缘就好,死抠算法的公司多半也不怎么样)
- 递归、排序、查找、结构翻转、手动实现 Promise 之类的算法,实现难度并不大
- 有一部分是机考,可以直接测试是否代码通过,但是即便代码跑用例通过了,面试官也可能人工检查代码
- 也有一部分是现场编程(用电脑或者纸笔),面试官现场问问题
- 时间复杂度 O(N) 是最常问的,也是最容易翻车的,除非你连刷一个月算法题,否则很难每次都找到最优算法。注意前端经常用的 Array.indexOf() 和 String.includes() 等遍历查找方法,性能极低,算法题的隐藏测试用例超长数据可能会超时,尽量不要用,尤其不要在循环、递归里面用。
最小不重复子串(Minimum Unique Substring)
- 二分查找长度:子串长度越长,唯一的概率越大(具有单调性)。我们可以二分枚举子串的长度 L(范围为 1 ~ N)。
- 滑动窗口计数:对于固定的长度 L,利用固定大小为 L 的滑动窗口遍历字符串,用哈希表(Map)统计每个长度为 L 的子串出现的次数。
- 判定:只要发现某个长度为 L 的子串出现频次恰好为 1,说明存在长度为 L 的不重复子串,尝试寻找更短的长度(缩小右边界);否则增大长度(增大左边界)。
/**
* 查找字符串中的最小不重复子串
* @param {string} str
* @returns {string} 长度最短且唯一的子串(若有多个同长度的,返回最先出现的)
*/
function findMinUniqueSubstring(str) {
const n = str.length;
if (n === 0) return '';
let left = 1, right = n;
let result = '';
// 辅助函数:校验是否存在长度为 len 的唯一子串
function check(len) {
const counts = new Map();
// 1. 滑动窗口统计所有长度为 len 的子串频次
for (let i = 0; i <= n - len; i++) {
const sub = str.substring(i, i + len);
counts.set(sub, (counts.get(sub) || 0) + 1);
}
// 2. 查找是否有频次恰好为 1 的子串
for (let [sub, count] of counts.entries()) {
if (count === 1) {
return sub; // 返回满足条件的最先出现的子串
}
}
return null;
}
// 二分查找最短长度
while (left <= right) {
const mid = Math.floor((left + right) / 2);
const uniqueSub = check(mid);
if (uniqueSub !== null) {
result = uniqueSub; // 记录答案
right = mid - 1; // 尝试寻找更短的
} else {
left = mid + 1; // 找不到,说明需要更长的子串
}
}
return result;
}
// --- 测试用例 ---
console.log(findMinUniqueSubstring("aaaaa")); // 输出: "aaaaa" (所有更短的 "a", "aa" 等都重复出现了)
console.log(findMinUniqueSubstring("banana")); // 输出: "b" ("b" 只出现过 1 次,长度为 1 最短)
console.log(findMinUniqueSubstring("abcabc")); // 输出: "abca" (1-3 长度的子串如 "a","ab","abc" 都重复了)
任务队列(Task Queue)
class TaskQueue {
/**
* @param {number} concurrency 最大并发数
*/
constructor(concurrency = 2) {
this.concurrency = concurrency; // 最大并发限制
this.running = 0; // 当前正在执行的任务数
this.queue = []; // 任务等待队列
}
/**
* 添加异步任务
* @param {Function} taskFn 返回 Promise 的任务函数
* @returns {Promise}
*/
add(taskFn) {
return new Promise((resolve, reject) => {
// 将任务与 Promise 的控制权一同推入队列
this.queue.push({
taskFn,
resolve,
reject
});
// 尝试触发下一个任务
this.runNext();
});
}
runNext() {
// 如果正在运行的任务达到并发上限,或者队列为空,则停止启动新任务
if (this.running >= this.concurrency || this.queue.length === 0) {
return;
}
// 按 FIFO(先进先出)出队一个任务
const { taskFn, resolve, reject } = this.queue.shift();
this.running++;
// 执行任务
Promise.resolve()
.then(() => taskFn())
.then(resolve)
.catch(reject)
.finally(() => {
this.running--;
// 任务完成后,递归尝试启动后续任务
this.runNext();
});
}
}
// --- 测试用例 ---
const queue = new TaskQueue(2); // 限制同时最多跑 2 个任务
// 模拟异步请求
const createTask = (id, time) => () => {
return new Promise((resolve) => {
console.log(`[开始] 任务 ${id}`);
setTimeout(() => {
console.log(`[完成] 任务 ${id}`);
resolve(`任务 ${id} 的结果`);
}, time);
});
};
// 连续添加 4 个任务
queue.add(createTask(1, 1000)).then(res => console.log('输出:', res));
queue.add(createTask(2, 500)).then(res => console.log('输出:', res));
queue.add(createTask(3, 300)).then(res => console.log('输出:', res));
queue.add(createTask(4, 400)).then(res => console.log('输出:', res));
/*
执行输出流程:
1. [开始] 任务 1 (预计 1000ms)
2. [开始] 任务 2 (预计 500ms)
(等待 500ms...)
3. [完成] 任务 2 -> 输出: 任务 2 的结果
4. [开始] 任务 3 (任务 2 释放空位,任务 3 启动)
(等待 300ms...)
5. [完成] 任务 3 -> 输出: 任务 3 的结果
6. [开始] 任务 4 (任务 3 释放空位,任务 4 启动)
(再等待 200ms...)
7. [完成] 任务 1 -> 输出: 任务 1 的结果
8. [完成] 任务 4 -> 输出: 任务 4 的结果
*/
手写 Promise
class MyPromise {
// 定义三种状态常量
static PENDING = 'pending';
static FULFILLED = 'fulfilled';
static REJECTED = 'rejected';
constructor(executor) {
this.status = MyPromise.PENDING; // 初始状态为 pending
this.value = undefined; // 成功后的值
this.reason = undefined; // 失败的原因
this.onResolvedCallbacks = []; // 存放 then 中成功的回调
this.onRejectedCallbacks = []; // 存放 then 中失败的回调
const resolve = (value) => {
// 如果 value 本身是个 Promise,递归解析
if (value instanceof MyPromise) {
return value.then(resolve, reject);
}
// 状态只能从 pending 变更一次
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED;
this.value = value;
// 依次触发订阅的成功回调
this.onResolvedCallbacks.forEach(fn => fn());
}
};
const reject = (reason) => {
if (this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED;
this.reason = reason;
// 依次触发订阅的失败回调
this.onRejectedCallbacks.forEach(fn => fn());
}
};
// 执行器报错时直接 reject
try {
executor(resolve, reject);
} catch (error) {
reject(error);
}
}
then(onFulfilled, onRejected) {
// 参数校验与穿透处理(如果传入的不是函数,给默认函数)
onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : value => value;
onRejected = typeof onRejected === 'function' ? onRejected : reason => { throw reason; };
// 每次调用 .then 都返回一个新的 Promise 以支持链式调用
const promise2 = new MyPromise((resolve, reject) => {
// 封装微任务执行逻辑
const handleFulfilled = () => {
queueMicrotask(() => {
try {
const x = onFulfilled(this.value);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
const handleRejected = () => {
queueMicrotask(() => {
try {
const x = onRejected(this.reason);
resolvePromise(promise2, x, resolve, reject);
} catch (error) {
reject(error);
}
});
};
// 根据当前状态做出响应
if (this.status === MyPromise.FULFILLED) {
handleFulfilled();
} else if (this.status === MyPromise.REJECTED) {
handleRejected();
} else if (this.status === MyPromise.PENDING) {
// 异步任务:暂存回调
this.onResolvedCallbacks.push(handleFulfilled);
this.onRejectedCallbacks.push(handleRejected);
}
});
return promise2;
}
}
/**
* 规范的核心:解析 Promise 逻辑
*/
function resolvePromise(promise2, x, resolve, reject) {
// 1. 循环引用判断(不能自己等待自己)
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise'));
}
// 2. 状态锁,防止反复调用 resolve / reject
let called = false;
// 3. 判断 x 是不是对象或函数
if (x !== null && (typeof x === 'object' || typeof x === 'function')) {
try {
const then = x.then;
if (typeof then === 'function') {
// 如果 then 是函数,说明 x 是一个 thenable(比如 Promise 实例)
then.call(
x,
(y) => {
if (called) return;
called = true;
// 递归解析 resolve 的值(因为 y 可能又是一个 Promise)
resolvePromise(promise2, y, resolve, reject);
},
(r) => {
if (called) return;
called = true;
reject(r);
}
);
} else {
// 普通对象/函数,直接 resolve
resolve(x);
}
} catch (error) {
if (called) return;
called = true;
reject(error);
}
} else {
// 普通原始值(number, string 等),直接 resolve
resolve(x);
}
}
手写 EventEmitter
type Listener = (...args: any[]) => void;
class EventEmitter {
// 维护事件名到监听函数数组的映射
private events: Map<string, Listener[]> = new Map();
/**
* 订阅事件
*/
on(event: string, listener: Listener): this {
if (!this.events.has(event)) {
this.events.set(event, []);
}
this.events.get(event)!.push(listener);
return this; // 支持链式调用
}
/**
* 触发事件
*/
emit(event: string, ...args: any[]): boolean {
const listeners = this.events.get(event);
if (!listeners || listeners.length === 0) {
return false;
}
// 防御性浅拷贝:防止在执行 listener 期间有人调用 off 导致数组索引错乱
const callbacks = [...listeners];
callbacks.forEach((fn) => fn(...args));
return true;
}
/**
* 取消订阅
*/
off(event: string, listener: Listener): this {
const listeners = this.events.get(event);
if (!listeners) return this;
// 过滤掉匹配的监听器(同时需兼容 once 包装后的原始函数引用)
const index = listeners.findIndex(
(fn) => fn === listener || (fn as any)._origin === listener
);
if (index !== -1) {
listeners.splice(index, 1);
}
// 若该事件下已无监听器,清理 key 避免内存泄漏
if (listeners.length === 0) {
this.events.delete(event);
}
return this;
}
/**
* 单次订阅:触发一次后自动销毁
*/
once(event: string, listener: Listener): this {
// 包装原始函数
const wrapper: Listener = (...args: any[]) => {
this.off(event, wrapper); // 执行前先解绑
listener(...args);
};
// 挂载原始引用,确保可以通过 off(event, originalListener) 正确卸载
(wrapper as any)._origin = listener;
this.on(event, wrapper);
return this;
}
/**
* 清除指定事件的所有监听,或清除所有事件
*/
removeAllListeners(event?: string): this {
if (event) {
this.events.delete(event);
} else {
this.events.clear();
}
return this;
}
}
项目实现类
外企考项目实现的比较多
- 你会接到一个具体的项目要求,做一个小型的全栈网站
- 有一周左右的时间完成代码编写,并部署到 AWS 之类的云上
- 要特别注意代码结构清晰,功能上打磨丝滑、交互考虑全面
- 避免套模板产生过多多余代码(尤其是 AI 生成的代码)
- 面试官会根据你提交的成果提问,包括系统设计,技术选型,实现细节,CI/CD
🤵♂️面试要点
- 自我介绍时,着重介绍和岗位匹配的部分,比如对方要全栈,就多讲一些 Node.js 数据库之类的
- 准备几个自己做的最有深度的技术项目案例,设计思路,技术难点,原创亮点
🔍招聘筛选咨询要点
- 岗位预算,预算不足的直接拒掉
- 工作时间和加班情况,尽量避免常态固定加班的,如果能接受也要提高期望薪资才不亏
- 办公地点,通勤是个大问题,可能还要搬家
- 技术团队规模,少于 10 人的公司慎选,公司研发能力和资本厚度都要打个问号,尽量选 50 人以上的公司
⚠️避坑的行业和公司
Web3 区块链
- 合规风险极高,尤其是用 USDT 发工资的,容易被抓被没收,最后一场空
- 加密货币低迷,比特币等价格大幅跳水,市场前景堪忧
- 蹭世界杯热度,世界杯结束后可能会裁员
AI 初创:具身智能 & 自动驾驶 & 大模型应用
- 加班强度极大,995 或 10105 是常态,甚至有些还大小周,这是身体上的累
- 内部管理混乱,很多都无法产品落地,经常调整方向,个人还要背锅,这是心理上的累
其他避坑关键点
- 有年龄限制的,比如某社交媒体公司限制 28 岁以下。如果一个公司/团队除了领导全是年轻人,说明人员流动性极大,工作强度极高,末位淘汰严重,老人活不下来
- 军事化或准军事化管理,推崇狼性文化,搞战役、军团、服从性测试
- 用期权给薪资注水的(现金只给一半),老板疯狂画饼的(让你当“储备”CTO)
互联网大厂
字节
- 目前招聘软件上公开岗位比较少,建议找朋友内推或去官网 https://jobs.bytedance.com/
我的面试经历
互联网大厂
- 腾讯:
- CodeBuddy/WorkBuddy AI 全栈:
- 一面(技术面):大致聊了了一下经历,没有考代码,面完直接告诉你寄了
- CodeBuddy/WorkBuddy AI 全栈:
- 字节
- 国际化广告前端:
- 简历寄了
- 国际电商:
- 内推无反应
- 飞书 AI Agent 前端(被调剂了)
- 一面(技术交叉面):面试官比较随和,项目经历聊的比较广,算法题也是比较简单的(数组操作、时间复杂度),可能是前端的缘故,后续还没消息
- 国际化广告前端:
- 京东
- 国际电商
- 非常混乱,投的前端,被调剂到了Java后端开发,直接放弃
- 国际电商
- 虾皮
- FOOD外卖
- 一面(技术面):面试官非常压抑,感觉就不是很想和你聊,算法题比较简单(最小不重复序列,数组扁平化之类的),没后续(传闻开启了大裁员)
- FOOD外卖
硬件公司
- 大疆
- 加班强度太高,直接放弃
- 安克
- 一面(技术面):感觉面试官问不到点子上,你讲的项目他也不感兴趣,没考算法题,面完直接寄了
- 有几个大厂前端朋友去面也是一面寄了,前端岗位招聘的真实性存疑
- 汝原
- 大疆系的创业团队,公司规模几百人,研发几十人,有成熟产品线,需要加班
- 一面(技术面):大致了解一下项目经历
- 二面(现场技术面):技术和算法题,以及对职业规划的期望
- 谈薪资预期:应该是超了对方预算,寄了
- 快造
- 3D打印小公司,公司规模几百人,研发几十人,实力肯定是比不了大疆系的拓竹,但是压力会小很多
- 线下一口气走完面试全流程,非常挑战体力,聊的内容比较轻松愉快
- 一面(技术面):了解一下项目经历,技术细节,开放话题,没考算法
- 二面(技术面):,即兴英语口语对话(应该不是必需,非留学生不会考)
金融证券
- 汇丰(新开深圳,也有广州、西安可选)
- 全栈开发(偏后端):
- 投完简历,等了一周通知岗位停招,目前传闻汇丰全球裁员一万人
- 全栈开发(偏后端):
- 中信证券
- 全栈开发(偏后端):
- 笔试 2 小时,手搓代码(没有运行测试环境),直接放弃
- 月薪比较低(低于 4 万),全靠奖金
- 上班时间比较早,办公地点在福田
- 全栈开发(偏后端):
- 平安保险
- 前端开发:
- 内推无反应
- 前端开发:
区块链 Web3
- 领创集团
- 公司情况:新加坡金融外企,规模还算比较大,原本在东南亚做类似蚂蚁小额贷芝麻信用类似的东西,新开了 Web3 期货交易业务(面向中东客户)
- 岗位情况:移动端开发(React Native + H5),不强制移动端开发经验
- 一面(技术面):聊的比较轻松,没有算法题,主要是项目经历,技术细节,体验设计思维
- 没有后续
- 某 AI + Web3 公司
- 公司情况:香港公司,深圳有不到十个开发,彻底本地化,996,避坑快跑
自动驾驶
聊了几个小公司都是要 995 或者 10105 的,薪资预算也偏低,只能给到 50万以内的年包。大厂出来的同学,建议直接 pass 这个行业的初创和小公司。
大型外企
- 沃尔玛
- 刚投递还没消息
- 西门子
- 投递简历没反应,近期传出全球裁员数万人
感谢,很有趣,很励志