全面系统的前端面试指南,涵盖HTML、CSS、JavaScript、Vue/React、算法、项目经验等所有面试要点
前端面试完全指南:从准备到拿到 Offer
前端面试是每个前端开发者职业生涯中的重要环节。本文为你提供一套完整的前端面试指南,从基础知识到项目经验,从算法题到系统设计,帮助你全面准备,成功拿到心仪的 Offer。
第一章:面试前准备(第 1 周)
1.1 了解公司和岗位
公司调研
- 公司背景:了解公司规模、业务、文化
- 技术栈:查看公司使用的技术栈
- 团队情况:了解团队规模、工作方式
- 薪资范围:了解市场薪资水平
岗位要求分析
- 技能要求:仔细阅读 JD,了解技能要求
- 项目经验:了解需要的项目经验
- 软技能:沟通、协作能力要求
- 加分项:了解加分项,提前准备
1.2 简历准备
简历要点
- 项目经验:突出有亮点的项目
- 技术栈:列出掌握的技术
- 成果数据:用数据展示成果
- GitHub 链接:展示代码能力
简历优化
- 简洁明了:控制在 2 页以内
- 重点突出:重要信息放在前面
- 量化成果:用数据说话
- 定期更新:保持简历最新
1.3 知识体系梳理
前端知识体系
- HTML/CSS:基础语法、布局、响应式
- JavaScript:ES6+、异步、闭包、原型链
- 框架:Vue/React 原理、使用经验
- 工程化:Webpack、Vite、构建优化
- 性能优化:加载优化、渲染优化
- 浏览器原理:渲染机制、事件循环
第二章:HTML/CSS 面试题(第 1-2 周)
2.1 HTML 基础
常见问题
Q1: HTML5 新增了哪些语义化标签?
1
2
3
4
5
6
7
8
|
<!-- 语义化标签 -->
<header>页面头部</header>
<nav>导航</nav>
<main>主要内容</main>
<article>文章</article>
<section>区域</section>
<aside>侧边栏</aside>
<footer>页脚</footer>
|
Q2: 什么是 HTML5 的离线存储?
- localStorage:永久存储,除非手动清除
- sessionStorage:会话存储,关闭标签页清除
- IndexedDB:浏览器数据库,存储大量数据
Q3: HTML 和 XHTML 的区别?
- HTML:语法宽松,容错性强
- XHTML:语法严格,必须闭合标签,属性必须加引号
2.2 CSS 基础
常见问题
Q1: CSS 盒模型有哪些?有什么区别?
1
2
3
4
5
6
7
8
9
|
/* 标准盒模型 */
.box {
box-sizing: content-box; /* width = content */
}
/* IE 盒模型 */
.box {
box-sizing: border-box; /* width = content + padding + border */
}
|
Q2: 如何实现垂直居中?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
/* 方法1: Flexbox */
.container {
display: flex;
justify-content: center;
align-items: center;
}
/* 方法2: Grid */
.container {
display: grid;
place-items: center;
}
/* 方法3: 绝对定位 */
.container {
position: relative;
}
.child {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
|
Q3: BFC 是什么?如何触发?
- BFC 定义:块级格式化上下文,独立的渲染区域
- 触发条件:
overflow: hidden/auto/scroll
display: flex/grid
position: absolute/fixed
float: left/right
Q4: CSS 选择器优先级?
1
|
!important > 内联样式 > ID选择器 > 类选择器 > 标签选择器
|
2.3 布局相关
常见问题
Q1: Flexbox 和 Grid 的区别?
- Flexbox:一维布局(行或列)
- Grid:二维布局(行和列)
Q2: 如何实现响应式设计?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
/* 媒体查询 */
@media (max-width: 768px) {
.container {
flex-direction: column;
}
}
/* 响应式单位 */
.container {
width: 100%;
max-width: 1200px;
padding: 2rem;
font-size: clamp(14px, 2vw, 18px);
}
|
第三章:JavaScript 面试题(第 2-3 周)
3.1 JavaScript 基础
常见问题
Q1: var、let、const 的区别?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// var: 函数作用域,存在变量提升
var a = 1;
function test() {
console.log(a); // undefined(变量提升)
var a = 2;
}
// let: 块级作用域,不存在变量提升
let b = 1;
{
let b = 2; // 不同的作用域
}
// const: 块级作用域,常量,不能重新赋值
const c = 1;
// c = 2; // TypeError
|
Q2: 什么是闭包?有什么应用?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 闭包:函数可以访问外部作用域的变量
function outer() {
let count = 0;
return function inner() {
count++;
return count;
};
}
const counter = outer();
console.log(counter()); // 1
console.log(counter()); // 2
// 应用:模块化、数据私有化
|
Q3: 原型链是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 原型链:对象通过 __proto__ 查找属性
function Person(name) {
this.name = name;
}
Person.prototype.sayHello = function () {
console.log("Hello, " + this.name);
};
const person = new Person("Tom");
person.sayHello(); // 通过原型链找到 sayHello
// 原型链查找:person -> Person.prototype -> Object.prototype -> null
|
Q4: this 的指向?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
// 1. 普通函数:this 指向调用者
function test() {
console.log(this); // window(严格模式下 undefined)
}
// 2. 对象方法:this 指向对象
const obj = {
name: "Tom",
getName() {
return this.name; // obj
},
};
// 3. 箭头函数:this 继承外层
const obj2 = {
name: "Tom",
getName: () => {
return this.name; // window
},
};
// 4. call/apply/bind:改变 this 指向
function greet() {
console.log(this.name);
}
greet.call({ name: "Tom" }); // Tom
|
3.2 异步编程
常见问题
Q1: Promise 和 async/await 的区别?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// Promise
fetch("/api/data")
.then((response) => response.json())
.then((data) => console.log(data))
.catch((error) => console.error(error));
// async/await
async function fetchData() {
try {
const response = await fetch("/api/data");
const data = await response.json();
console.log(data);
} catch (error) {
console.error(error);
}
}
|
Q2: 事件循环(Event Loop)是什么?
1
2
3
4
5
6
7
8
9
10
11
12
13
|
// 执行顺序:同步代码 -> 微任务 -> 宏任务
console.log("1");
setTimeout(() => {
console.log("2");
}, 0);
Promise.resolve().then(() => {
console.log("3");
});
console.log("4");
// 输出:1, 4, 3, 2
|
Q3: 如何实现 Promise.all?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
Promise.myAll = function (promises) {
return new Promise((resolve, reject) => {
const results = [];
let count = 0;
promises.forEach((promise, index) => {
Promise.resolve(promise).then(
(value) => {
results[index] = value;
count++;
if (count === promises.length) {
resolve(results);
}
},
(error) => reject(error)
);
});
});
};
|
3.3 数组和对象
常见问题
Q1: 数组去重的方法?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
// 方法1: Set
const arr = [1, 2, 2, 3, 3, 4];
const unique = [...new Set(arr)];
// 方法2: filter + indexOf
const unique2 = arr.filter((item, index) => arr.indexOf(item) === index);
// 方法3: reduce
const unique3 = arr.reduce((acc, cur) => {
if (!acc.includes(cur)) {
acc.push(cur);
}
return acc;
}, []);
|
Q2: 深拷贝的实现?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
|
// 方法1: JSON(有局限性)
const deepCopy1 = JSON.parse(JSON.stringify(obj));
// 方法2: 递归
function deepCopy(obj) {
if (typeof obj !== "object" || obj === null) {
return obj;
}
const copy = Array.isArray(obj) ? [] : {};
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopy(obj[key]);
}
}
return copy;
}
// 方法3: 处理循环引用
function deepCopyWithCircular(obj, map = new WeakMap()) {
if (typeof obj !== "object" || obj === null) {
return obj;
}
if (map.has(obj)) {
return map.get(obj);
}
const copy = Array.isArray(obj) ? [] : {};
map.set(obj, copy);
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
copy[key] = deepCopyWithCircular(obj[key], map);
}
}
return copy;
}
|
第四章:Vue/React 框架面试题(第 3-4 周)
4.1 Vue.js 面试题
常见问题
Q1: Vue 2 和 Vue 3 的区别?
- 响应式系统:Vue 2 使用 Object.defineProperty,Vue 3 使用 Proxy
- Composition API:Vue 3 新增组合式 API
- 性能优化:Vue 3 性能更好,包体积更小
- TypeScript:Vue 3 更好的 TypeScript 支持
Q2: Vue 的响应式原理?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// Vue 2: Object.defineProperty
function defineReactive(obj, key, val) {
Object.defineProperty(obj, key, {
get() {
// 收集依赖
return val;
},
set(newVal) {
if (newVal !== val) {
val = newVal;
// 触发更新
}
},
});
}
// Vue 3: Proxy
function reactive(obj) {
return new Proxy(obj, {
get(target, key) {
// 收集依赖
return target[key];
},
set(target, key, value) {
target[key] = value;
// 触发更新
return true;
},
});
}
|
Q3: v-if 和 v-show 的区别?
- v-if:条件渲染,不满足条件不渲染 DOM
- v-show:条件显示,通过 CSS display 控制
Q4: Vue 的生命周期?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
|
export default {
beforeCreate() {
// 实例创建前
},
created() {
// 实例创建后,可以访问 data、methods
},
beforeMount() {
// 挂载前
},
mounted() {
// 挂载后,可以访问 DOM
},
beforeUpdate() {
// 更新前
},
updated() {
// 更新后
},
beforeUnmount() {
// 卸载前
},
unmounted() {
// 卸载后
},
};
|
Q5: Vue Router 的实现原理?
1
2
3
4
5
6
7
8
9
10
11
|
// Hash 模式
window.addEventListener("hashchange", () => {
const hash = window.location.hash.slice(1);
// 根据 hash 渲染对应组件
});
// History 模式
window.addEventListener("popstate", () => {
const path = window.location.pathname;
// 根据 path 渲染对应组件
});
|
4.2 React 面试题
常见问题
Q1: React Hooks 的使用?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
// useState
const [count, setCount] = useState(0);
// useEffect
useEffect(() => {
// 副作用
return () => {
// 清理函数
};
}, [dependencies]);
// useContext
const value = useContext(MyContext);
// useReducer
const [state, dispatch] = useReducer(reducer, initialState);
|
Q2: React 的虚拟 DOM?
- 虚拟 DOM:JavaScript 对象,描述真实 DOM
- Diff 算法:比较新旧虚拟 DOM,找出差异
- 批量更新:合并多个更新,提高性能
Q3: React 的性能优化?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
// 1. React.memo
const MyComponent = React.memo(({ prop }) => {
return <div>{prop}</div>;
});
// 2. useMemo
const memoizedValue = useMemo(() => computeExpensiveValue(a, b), [a, b]);
// 3. useCallback
const memoizedCallback = useCallback(() => {
doSomething(a, b);
}, [a, b]);
// 4. 代码分割
const LazyComponent = React.lazy(() => import("./LazyComponent"));
|
第五章:算法与数据结构(第 4-5 周)
5.1 常见算法题
数组相关
Q1: 两数之和
1
2
3
4
5
6
7
8
9
10
11
|
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
}
map.set(nums[i], i);
}
return [];
}
|
Q2: 合并两个有序数组
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
function merge(nums1, m, nums2, n) {
let i = m - 1,
j = n - 1,
k = m + n - 1;
while (i >= 0 && j >= 0) {
if (nums1[i] > nums2[j]) {
nums1[k--] = nums1[i--];
} else {
nums1[k--] = nums2[j--];
}
}
while (j >= 0) {
nums1[k--] = nums2[j--];
}
}
|
Q3: 最长公共前缀
1
2
3
4
5
6
7
8
9
10
11
12
|
function longestCommonPrefix(strs) {
if (strs.length === 0) return "";
let prefix = strs[0];
for (let i = 1; i < strs.length; i++) {
while (strs[i].indexOf(prefix) !== 0) {
prefix = prefix.slice(0, -1);
if (prefix === "") return "";
}
}
return prefix;
}
|
5.2 链表相关
Q1: 反转链表
1
2
3
4
5
6
7
8
9
10
11
12
13
|
function reverseList(head) {
let prev = null;
let curr = head;
while (curr !== null) {
const next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
|
Q2: 判断链表是否有环
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
function hasCycle(head) {
let slow = head;
let fast = head;
while (fast !== null && fast.next !== null) {
slow = slow.next;
fast = fast.next.next;
if (slow === fast) {
return true;
}
}
return false;
}
|
5.3 树相关
Q1: 二叉树的最大深度
1
2
3
4
5
6
7
8
|
function maxDepth(root) {
if (root === null) return 0;
const leftDepth = maxDepth(root.left);
const rightDepth = maxDepth(root.right);
return Math.max(leftDepth, rightDepth) + 1;
}
|
Q2: 二叉树的遍历
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
|
// 前序遍历
function preorderTraversal(root) {
const result = [];
function traverse(node) {
if (node === null) return;
result.push(node.val);
traverse(node.left);
traverse(node.right);
}
traverse(root);
return result;
}
// 中序遍历
function inorderTraversal(root) {
const result = [];
function traverse(node) {
if (node === null) return;
traverse(node.left);
result.push(node.val);
traverse(node.right);
}
traverse(root);
return result;
}
|
第六章:项目经验准备(第 5 周)
6.1 项目描述
STAR 法则
- Situation:项目背景
- Task:你的任务
- Action:采取的行动
- Result:取得的成果
项目描述示例
项目:电商网站前端开发
- 背景:公司需要开发新的电商平台
- 任务:负责前端开发,包括商品展示、购物车、订单等模块
- 行动:
- 使用 Vue 3 + TypeScript 开发
- 实现响应式设计,适配移动端
- 优化首屏加载时间,从 3s 降到 1s
- 使用 Pinia 管理状态
- 成果:
- 项目按时上线
- 首屏加载时间减少 66%
- 用户满意度提升 30%
6.2 技术难点
常见技术难点
- 性能优化:如何优化首屏加载时间
- 兼容性问题:如何处理浏览器兼容性
- 复杂交互:如何实现复杂的交互效果
- 状态管理:如何管理复杂的状态
6.3 项目亮点
如何突出亮点
- 技术创新:使用了新技术或新方案
- 性能提升:有具体的性能数据
- 用户体验:改善了用户体验
- 团队协作:展示了协作能力
第七章:系统设计题(第 5-6 周)
7.1 前端系统设计
常见题目
Q1: 如何设计一个图片懒加载组件?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
// 实现思路
class LazyImage {
constructor(selector) {
this.images = document.querySelectorAll(selector);
this.init();
}
init() {
const observer = new IntersectionObserver((entries) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
observer.unobserve(img);
}
});
});
this.images.forEach((img) => {
observer.observe(img);
});
}
}
|
Q2: 如何实现一个虚拟滚动列表?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
// 实现思路
class VirtualList {
constructor(container, itemHeight, items) {
this.container = container;
this.itemHeight = itemHeight;
this.items = items;
this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
this.startIndex = 0;
this.endIndex = this.visibleCount;
this.render();
}
render() {
const visibleItems = this.items.slice(this.startIndex, this.endIndex);
// 渲染可见项
}
onScroll() {
const scrollTop = this.container.scrollTop;
this.startIndex = Math.floor(scrollTop / this.itemHeight);
this.endIndex = this.startIndex + this.visibleCount;
this.render();
}
}
|
7.2 性能优化设计
Q1: 如何优化首屏加载时间?
- 代码分割:按路由分割代码
- 资源压缩:压缩 JS、CSS、图片
- CDN 加速:使用 CDN 加速静态资源
- 预加载:预加载关键资源
- 懒加载:非关键资源懒加载
Q2: 如何优化渲染性能?
- 减少重排重绘:使用 transform 代替 top/left
- 虚拟滚动:长列表使用虚拟滚动
- 防抖节流:优化事件处理
- Web Worker:将计算密集型任务移到 Worker
第八章:面试技巧(第 6 周)
8.1 面试流程
一般流程
- 电话/视频初筛:基础问题、项目介绍
- 技术面试:算法、基础知识、项目深入
- HR 面试:薪资、文化、期望
- 终面:技术总监/CTO 面试
8.2 回答技巧
STAR 法则应用
- 具体化:用具体例子说明
- 数据化:用数据展示成果
- 结构化:逻辑清晰,分点说明
- 重点突出:突出关键信息
常见问题回答
Q: 为什么离职?
- 正面回答:寻求更好的发展机会
- 避免负面:不要说前公司坏话
- 展示规划:说明职业规划
Q: 你的优缺点?
- 优点:结合岗位要求
- 缺点:说可以改进的,并说明改进措施
Q: 你有什么问题?
- 技术相关:团队技术栈、技术挑战
- 团队相关:团队规模、协作方式
- 发展相关:晋升路径、学习机会
8.3 薪资谈判
谈判技巧
- 了解市场:了解市场薪资水平
- 合理期望:根据能力设定期望
- 综合考量:薪资、福利、发展综合考虑
- 灵活应对:根据公司情况调整
第九章:面试后跟进(第 6 周)
9.1 面试复盘
复盘要点
- 记录问题:记录面试中的问题
- 总结不足:找出不足之处
- 持续改进:针对不足改进
9.2 感谢信
感谢信要点
- 及时发送:面试后 24 小时内
- 简洁明了:表达感谢和兴趣
- 个性化:针对具体面试内容
第十章:常见面试题汇总
10.1 HTML/CSS 高频题
- 盒模型、BFC、IFC
- Flexbox 和 Grid 布局
- 响应式设计实现
- CSS 选择器优先级
- 垂直居中方法
10.2 JavaScript 高频题
- 闭包、作用域、this
- 原型链、继承
- 事件循环、异步编程
- Promise、async/await
- 数组方法、对象方法
10.3 框架高频题
- Vue/React 原理
- 生命周期
- 组件通信
- 状态管理
- 性能优化
10.4 算法高频题
- 数组操作(去重、排序、查找)
- 字符串处理
- 链表操作
- 树遍历
- 动态规划基础
结语:准备充分,自信面试
前端面试是一个综合能力的考察,需要:
- 扎实基础:HTML、CSS、JavaScript 基础扎实
- 框架熟练:Vue/React 熟练使用
- 算法能力:能够解决常见算法题
- 项目经验:有实际项目经验
- 沟通能力:能够清晰表达
记住:
- 多练习:通过刷题提高算法能力
- 多总结:总结常见问题和答案
- 多实践:通过项目积累经验
- 保持自信:面试时保持自信和冷静
愿每一位前端开发者都能找到心仪的工作!