跳到主要内容字节前端一面面经解析:网络原理、Vue 及手写题详解 | 极客日志JavaScript大前端算法
字节前端一面面经解析:网络原理、Vue 及手写题详解
字节前端一面面试真题,涵盖网络请求流程、事件循环机制、CSS 布局方案、Vue 响应式原理、设计模式应用、Git 冲突解决及常见手写算法题。通过深度解析面试官考察点与参考实现,帮助求职者掌握前端核心知识点与实战技巧。
ByteFlow35 浏览 面经原文内容
📍面试公司:字节
🕐面试时间:2 月 1 日
💻面试岗位:前端
❓面试问题:
- 1.自我介绍
- 2.平时如何学习前端
- 3.实验室的指导
- 4.用户在浏览器地址栏输入 URL 到页面展示发生了什么(http,浏览器渲染逻辑)
- 5.异步代码输出(set time out,async)
- 6.js 事件循环
- 7.css 垂直居中
- 8.position 垂直居中具体做法
- 9.vue 响应式原理(vue2.3)
- 10.工作中遇到的设计模式
- 11.选择器优先级
- 12.git 代码提交冲突怎么解决
- 13.项目:拖拽功能实现,图片懒加载(什么情况下加载什么情况下不加载)
- 14.项目中最有技术挑战的是什么
-
- (1)实现 new 操作符
(2)列表到数的数据变形
📝 字节前端一面·面经深度解析
🎯 面试整体画像
| 维度 | 特征 |
|---|
| 部门定位 | 字节跳动(未明确部门) |
| 面试风格 | 广度覆盖型 |
| 难度评级 | ⭐⭐(两星,覆盖面广都是常见八股文) |
| 考察重心 | 网络原理、事件循环、Vue 原理、设计模式、Git 协作、手写实现 |
🌐 从输入 URL 到页面展示·完整流程
问题:用户在浏览器地址栏输入 URL 到页面展示发生了什么
✅ 参考复习知识:
第一阶段:网络请求
第二阶段:浏览器渲染
第三阶段:特殊处理
这个问题不是让你背流程,而是看你对 Web 技术栈的整体理解
能讲出细节(如 TLS 握手、渲染树构建) → 加分
能讲出优化点 → 加分
🔄 异步代码输出·事件循环
问题:异步代码输出 + JS 事件循环
✅ 完整解析:
1. 典型考题
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
async1();
new Promise((resolve) => {
console.log('promise1');
resolve();
}).then(() => {
console.log('promise2');
});
console.log('script end');
2. 关键点解析
async function test() {
console.log('1');
await 1;
console.log('2');
}
function test() {
console.log('1');
return Promise.resolve(1).then(() => {
console.log('2');
});
}
3. 事件循环机制
🎨 CSS 垂直居中·全方案总结
问题:CSS 垂直居中 + position 垂直居中具体做法
✅ 完整方案:
1. Flexbox 方案(推荐)
.container {
display: flex;
align-items: center;
justify-content: center;
height: 300px;
}
2. Grid 方案
.container {
display: grid;
place-items: center;
height: 300px;
}
3. Position + Transform(最常考)
.container {
position: relative;
height: 300px;
}
.child {
position: absolute;
top: 50%;
transform: translateY(-50%);
left: 50%;
transform: translate(-50%, -50%);
}
4. Position + Margin(需知宽高)
.child {
position: absolute;
top: 50%;
left: 50%;
width: 200px;
height: 100px;
margin-top: -50px;
margin-left: -100px;
}
5. Line-height(单行文本)
.container {
height: 300px;
line-height: 300px;
text-align: center;
}
6. Table-cell
.container {
display: table-cell;
vertical-align: middle;
text-align: center;
height: 300px;
}
7. 伪元素法
.container::before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
}
.child {
display: inline-block;
vertical-align: middle;
}
position 垂直居中为什么用 translateY(-50%) 而不是 margin-top?
⚛️ Vue 响应式原理·Vue2 vs Vue3
问题:vue 响应式原理(vue2.3)
✅ 完整对比:
Vue2 响应式(Object.defineProperty)
function defineReactive(obj, key, val) {
observe(val);
const dep = new Dep();
Object.defineProperty(obj, key, {
enumerable: true,
configurable: true,
get() {
if (Dep.target) { dep.depend(); }
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
observe(newVal);
dep.notify();
}
});
}
const arrayMethods = Object.create(Array.prototype);
['push', 'pop', 'shift', 'unshift', 'splice', 'sort', 'reverse'].forEach(method => {
const original = arrayMethods[method];
arrayMethods[method] = function (...args) {
const result = original.apply(this, args);
const ob = this.__ob__;
ob.dep.notify();
return result;
};
});
Vue2 的局限性
Vue3 响应式(Proxy)
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key);
const value = Reflect.get(target, key, receiver);
if (typeof value === 'object' && value !== null) {
return reactive(value);
}
return value;
},
set(target, key, value, receiver) {
const oldValue = target[key];
const result = Reflect.set(target, key, value, receiver);
if (oldValue !== value) {
trigger(target, key);
}
return result;
},
deleteProperty(target, key) {
const hadKey = Reflect.has(target, key);
const result = Reflect.deleteProperty(target, key);
if (hadKey) {
trigger(target, key);
}
return result;
}
});
}
Vue3 的优势
const obj = reactive({});
obj.newProp = 'value';
const arr = reactive([1,2,3]);
arr[0] = 10;
const map = reactive(new Map());
map.set('key', 'value');
🏗️ 设计模式·前端常见模式
问题:工作中遇到的设计模式
✅ 前端常用设计模式:
1. 单例模式(Singleton)
class Store {
constructor() {
if (Store.instance) return Store.instance;
this.state = {};
Store.instance = this;
}
}
const store = createPinia();
2. 观察者模式(Observer)
class EventBus {
constructor() {
this.events = {};
}
on(event, callback) {
if (!this.events[event]) this.events[event] = [];
this.events[event].push(callback);
}
emit(event, data) {
if (this.events[event]) {
this.events[event].forEach(cb => cb(data));
}
}
}
3. 工厂模式(Factory)
class ButtonFactory {
createButton(type) {
switch (type) {
case 'primary': return new PrimaryButton();
case 'danger': return new DangerButton();
default: return new DefaultButton();
}
}
}
4. 策略模式(Strategy)
const validators = {
required: (value) => !!value,
email: (value) => /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(value),
minLength: (value, length) => value.length >= length
};
function validate(value, rules) {
for (const rule of rules) {
const [name, ...args] = rule.split(':');
if (!validators[name](value, ...args)) return false;
}
return true;
}
5. 装饰器模式(Decorator)
function withLogging(Component) {
return (props) => {
useEffect(() => {
console.log('组件渲染', Component.name);
});
return <Component {...props} />;
};
}
const LoggedButton = withLogging(Button);
6. 代理模式(Proxy)
const imageProxy = (src) => {
const img = new Image();
const proxy = new Proxy(img, {
set(target, key, value) {
if (key === 'src') {
target.src = 'placeholder.jpg';
setTimeout(() => {
target.src = value;
}, 100);
}
return true;
}
});
proxy.src = src;
return proxy;
};
🎯 CSS 选择器优先级
问题:选择器优先级
✅ 完整规则:
1. 优先级计算(权重)
!important > 内联样式 > ID 选择器 > 类选择器/属性/伪类 > 元素/伪元素 > 通配符
- 内联样式:1000
- ID 选择器:0100
- 类/属性/伪类:0010
- 元素/伪元素:0001
- 通配符 (*): 0000
- 继承:无权重
#nav .list li a:hover
2. 特殊情况
.color {
color: blue !important;
}
.title {
color: red;
}
.title {
color: blue;
}
.parent {
color: red;
}
.child {
}
3. 实用技巧
- 重复选择器:.title.title { color: red; }
- 添加父选择器:.container .title
- 使用 id:不要滥用
- !important:最后手段
🔧 Git 代码冲突解决
问题:git 代码提交冲突怎么解决
✅ 完整流程:
1. 冲突产生的原因
2. 解决步骤
git pull origin main
git status
<<<<<<< HEAD
# 当前分支的修改
=======
# 合并进来的修改
>>>>>>> feature-branch
# 5. 手动解决冲突
# 选择保留哪个,或者合并两者
# 删除冲突标记 <<< === >>>
# 6. 标记为已解决
git add src/main.js
# 7. 完成合并
git commit -m "解决冲突"
# 8. 推送
git push
3. 更优雅的解决方式
git mergetool
git pull --rebase
git add .
git rebase --continue
git merge --abort
git rebase --abort
4. 预防冲突的策略
🖱️ 拖拽功能实现
问题:项目中的拖拽功能实现
✅ 完整实现:
1. 原生拖拽 API
<div draggable="true" ondragstart="dragStart(event)">可拖拽元素</div>
function dragStart(e) {
e.dataTransfer.setData('text/plain', e.target.id);
e.dataTransfer.effectAllowed = 'move';
}
function drop(e) {
e.preventDefault();
const data = e.dataTransfer.getData('text/plain');
const element = document.getElementById(data);
e.target.appendChild(element);
}
2. 鼠标事件模拟(更常用)
class Draggable {
constructor(element) {
this.element = element;
this.isDragging = false;
this.startX = 0;
this.startY = 0;
this.initialLeft = 0;
this.initialTop = 0;
this.bindEvents();
}
bindEvents() {
this.element.addEventListener('mousedown', this.onMouseDown.bind(this));
document.addEventListener('mousemove', this.onMouseMove.bind(this));
document.addEventListener('mouseup', this.onMouseUp.bind(this));
}
onMouseDown(e) {
this.isDragging = true;
this.startX = e.clientX;
this.startY = e.clientY;
const rect = this.element.getBoundingClientRect();
this.initialLeft = rect.left;
this.initialTop = rect.top;
this.element.style.position = 'absolute';
this.element.style.zIndex = '1000';
this.element.style.cursor = 'grabbing';
e.preventDefault();
}
onMouseMove(e) {
if (!this.isDragging) return;
const dx = e.clientX - this.startX;
const dy = e.clientY - this.startY;
this.element.style.left = this.initialLeft + dx + 'px';
this.element.style.top = this.initialTop + dy + 'px';
this.applyBoundary();
}
onMouseUp() {
this.isDragging = false;
this.element.style.cursor = 'grab';
}
applyBoundary() {
const left = parseInt(this.element.style.left);
const top = parseInt(this.element.style.top);
const parent = this.element.parentElement;
if (parent) {
const maxLeft = parent.clientWidth - this.element.offsetWidth;
const maxTop = parent.clientHeight - this.element.offsetHeight;
this.element.style.left = Math.max(0, Math.min(left, maxLeft)) + 'px';
this.element.style.top = Math.max(0, Math.min(top, maxTop)) + 'px';
}
}
}
🖼️ 图片懒加载
问题:图片懒加载(什么情况下加载,什么情况下不加载)
✅ 参考实现:
1. Intersection Observer(推荐)
class LazyLoader {
constructor() {
this.images = document.querySelectorAll('img[data-src]');
this.observer = null;
this.init();
}
init() {
this.observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
this.loadImage(img);
this.observer.unobserve(img);
}
});
}, {
root: null,
rootMargin: '50px',
threshold: 0.01
});
this.images.forEach(img => {
this.observer.observe(img);
});
}
loadImage(img) {
const src = img.dataset.src;
if (src) {
img.src = src;
img.removeAttribute('data-src');
img.addEventListener('load', () => {
img.classList.add('loaded');
});
}
}
}
const lazyLoader = new LazyLoader();
2. 什么情况下加载?
3. 优化版本(考虑网络状态)
class SmartLazyLoader extends LazyLoader {
constructor() {
super();
this.connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
this.checkNetwork();
}
checkNetwork() {
if (this.connection) {
this.connection.addEventListener('change', this.onNetworkChange.bind(this));
if (this.shouldLoad()) {
this.startObserving();
} else {
this.pauseObserving();
}
}
}
shouldLoad() {
if (!this.connection) return true;
if (this.connection.effectiveType.includes('2g')) return false;
if (this.connection.saveData) return false;
return true;
}
onNetworkChange() {
if (this.shouldLoad()) {
this.startObserving();
} else {
this.pauseObserving();
}
}
}
🏗️ 实现 new 操作符
问题:实现 new 操作符
✅ 参考实现:
function myNew(constructor, ...args) {
const obj = Object.create(constructor.prototype);
const result = constructor.apply(obj, args);
return (typeof result === 'object' && result !== null) || typeof result === 'function' ? result : obj;
}
function Person(name, age) {
this.name = name;
this.age = age;
}
const p1 = myNew(Person, 'Tom', 18);
console.log(p1);
console.log(p1 instanceof Person);
function ReturnsObject(name) {
this.name = name;
return { custom: 'object' };
}
const p2 = myNew(ReturnsObject, 'Jerry');
console.log(p2);
关键点解析:
🌳 列表转树·数据变形
问题:列表到树的数据变形
✅ 参考实现:
1. 题目理解
const list = [
{ id: 1, name: '节点 1', parentId: null },
{ id: 2, name: '节点 2', parentId: 1 },
{ id: 3, name: '节点 3', parentId: 1 },
{ id: 4, name: '节点 4', parentId: 2 },
{ id: 5, name: '节点 5', parentId: 3 }
];
const tree = [
{ id: 1, name: '节点 1', children: [
{ id: 2, name: '节点 2', children: [
{ id: 4, name: '节点 4', children: [] }
]},
{ id: 3, name: '节点 3', children: [
{ id: 5, name: '节点 5', children: [] }
]}
]}
];
2. 一次遍历 + Map
function listToTree(list) {
const map = new Map();
const roots = [];
list.forEach(item => {
map.set(item.id, { ...item, children: [] });
});
list.forEach(item => {
const node = map.get(item.id);
if (item.parentId === null || item.parentId === undefined) {
roots.push(node);
} else {
const parent = map.get(item.parentId);
if (parent) {
parent.children.push(node);
} else {
roots.push(node);
}
}
});
return roots;
}
3. 递归版本(适用于小数据)
function listToTreeRecursive(list, parentId = null) {
return list
.filter(item => item.parentId === parentId)
.map(item => ({
...item,
children: listToTreeRecursive(list, item.id)
}));
}
4. 进阶要求:排序、过滤
function listToTreeAdvanced(list, options = {}) {
const {
idKey = 'id',
parentKey = 'parentId',
childrenKey = 'children',
sortKey = null,
sortOrder = 'asc',
filter = null
} = options;
const map = new Map();
const roots = [];
const filteredList = filter ? list.filter(filter) : list;
filteredList.forEach(item => {
map.set(item[idKey], { ...item, [childrenKey]: [] });
});
filteredList.forEach(item => {
const node = map.get(item[idKey]);
const parentId = item[parentKey];
if (parentId === null || parentId === undefined) {
roots.push(node);
} else {
const parent = map.get(parentId);
if (parent) {
parent[childrenKey].push(node);
}
}
});
if (sortKey) {
const sortFn = (a, b) => {
if (sortOrder === 'asc') {
return a[sortKey] > b[sortKey] ? 1 : -1;
} else {
return a[sortKey] < b[sortKey] ? 1 : -1;
}
};
const sortTree = (nodes) => {
nodes.sort(sortFn);
nodes.forEach(node => {
if (node[childrenKey].length) {
sortTree(node[childrenKey]);
}
});
};
sortTree(roots);
}
return roots;
}
5. 应用场景
🎁 附:字节一面复习清单
| 知识点 | 掌握程度 | 重点方向 |
|---|
| 输入 URL 全过程 | ⭐⭐⭐⭐⭐ | DNS、TCP、TLS、HTTP、渲染 |
| 事件循环 | ⭐⭐⭐⭐⭐ | 宏任务/微任务、async/await |
| CSS 垂直居中 | ⭐⭐⭐⭐ | 多种方案、优缺点 |
| Vue 响应式 | ⭐⭐⭐⭐ | Vue2/3 对比、原理实现 |
| 设计模式 | ⭐⭐⭐ | 常见模式、应用场景 |
| 选择器优先级 | ⭐⭐⭐ | 权重计算、特殊情况 |
| Git 冲突解决 | ⭐⭐⭐ | 解决流程、预防策略 |
| 拖拽实现 | ⭐⭐⭐ | 原生 API、鼠标模拟 |
| 图片懒加载 | ⭐⭐⭐ | Intersection Observer、网络判断 |
| new 实现 | ⭐⭐⭐⭐ | 原型链、返回值处理 |
| 列表转树 | ⭐⭐⭐⭐ | 一次遍历、递归、排序过滤 |
本文整理了字节前端一面面试真题,涵盖网络请求流程、事件循环机制、CSS 布局方案、Vue 响应式原理、设计模式应用、Git 冲突解决及常见手写算法题。通过深度解析面试官考察点与参考实现,帮助求职者掌握前端核心知识点与实战技巧。
相关免费在线工具
- 加密/解密文本
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
- Gemini 图片去水印
基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online
- Keycode 信息
查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
- Escape 与 Native 编解码
JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
- JavaScript / HTML 格式化
使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
- JavaScript 压缩与混淆
Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online