前端面试完全指南:从准备到拿到Offer

全面系统的前端面试指南,涵盖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 面试流程

一般流程

  1. 电话/视频初筛:基础问题、项目介绍
  2. 技术面试:算法、基础知识、项目深入
  3. HR 面试:薪资、文化、期望
  4. 终面:技术总监/CTO 面试

8.2 回答技巧

STAR 法则应用

  • 具体化:用具体例子说明
  • 数据化:用数据展示成果
  • 结构化:逻辑清晰,分点说明
  • 重点突出:突出关键信息

常见问题回答

Q: 为什么离职?

  • 正面回答:寻求更好的发展机会
  • 避免负面:不要说前公司坏话
  • 展示规划:说明职业规划

Q: 你的优缺点?

  • 优点:结合岗位要求
  • 缺点:说可以改进的,并说明改进措施

Q: 你有什么问题?

  • 技术相关:团队技术栈、技术挑战
  • 团队相关:团队规模、协作方式
  • 发展相关:晋升路径、学习机会

8.3 薪资谈判

谈判技巧

  • 了解市场:了解市场薪资水平
  • 合理期望:根据能力设定期望
  • 综合考量:薪资、福利、发展综合考虑
  • 灵活应对:根据公司情况调整

第九章:面试后跟进(第 6 周)

9.1 面试复盘

复盘要点

  • 记录问题:记录面试中的问题
  • 总结不足:找出不足之处
  • 持续改进:针对不足改进

9.2 感谢信

感谢信要点

  • 及时发送:面试后 24 小时内
  • 简洁明了:表达感谢和兴趣
  • 个性化:针对具体面试内容

第十章:常见面试题汇总

10.1 HTML/CSS 高频题

  1. 盒模型、BFC、IFC
  2. Flexbox 和 Grid 布局
  3. 响应式设计实现
  4. CSS 选择器优先级
  5. 垂直居中方法

10.2 JavaScript 高频题

  1. 闭包、作用域、this
  2. 原型链、继承
  3. 事件循环、异步编程
  4. Promise、async/await
  5. 数组方法、对象方法

10.3 框架高频题

  1. Vue/React 原理
  2. 生命周期
  3. 组件通信
  4. 状态管理
  5. 性能优化

10.4 算法高频题

  1. 数组操作(去重、排序、查找)
  2. 字符串处理
  3. 链表操作
  4. 树遍历
  5. 动态规划基础

结语:准备充分,自信面试

前端面试是一个综合能力的考察,需要:

  1. 扎实基础:HTML、CSS、JavaScript 基础扎实
  2. 框架熟练:Vue/React 熟练使用
  3. 算法能力:能够解决常见算法题
  4. 项目经验:有实际项目经验
  5. 沟通能力:能够清晰表达

记住:

  • 多练习:通过刷题提高算法能力
  • 多总结:总结常见问题和答案
  • 多实践:通过项目积累经验
  • 保持自信:面试时保持自信和冷静

愿每一位前端开发者都能找到心仪的工作!

Licensed under CC BY-NC-SA 4.0
comments powered by Disqus
使用 Hugo 构建
主题 StackJimmy 设计