前端工程师面试知识大全(深度扩展版)
目标:从基础到进阶,全面覆盖前端面试高频考点,适合系统复习和查漏补缺
一、JavaScript 基础(核心地基)
1️⃣ 语言特性
- 解释型语言(JIT 即时编译)
- 单线程(通过 Event Loop 实现并发)
- 动态类型语言(运行时确定类型)
- 弱类型语言(类型可以隐式转换)
- 基于原型的面向对象
2️⃣ 数据类型(必熟)
基本类型(Primitive)
- number(NaN、Infinity、-0、+0)
- string(不可变性、模板字符串)
- boolean(true/false)
- undefined(声明未赋值、void 0)
- null(空对象指针、typeof null === 'object')
- symbol(唯一标识、Symbol.for())
- bigint(大整数、n 后缀)
引用类型
- Object(普通对象、原型链)
- Array(类数组对象、稀疏数组)
- Function(一等公民、函数对象)
- Date(时间戳、时区)
- RegExp(正则表达式、修饰符)
- Map / Set(键值对、去重)
- Error(错误对象、堆栈信息)
类型判断(面试重点)
// typeof - 基础类型判断
typeof 42 // "number"
typeof "hello" // "string"
typeof true // "boolean"
typeof undefined // "undefined"
typeof null // "object" (历史遗留问题)
typeof {} // "object"
typeof [] // "object"
typeof function(){} // "function"
// instanceof - 原型链判断
[] instanceof Array // true
[] instanceof Object // true
// Object.prototype.toString - 精确判断
Object.prototype.toString.call([]) // "[object Array]"
Object.prototype.toString.call(null) // "[object Null]"
Object.prototype.toString.call(undefined) // "[object Undefined]"
// 自定义类型判断函数
function getType(obj) {
return Object.prototype.toString.call(obj).slice(8, -1).toLowerCase();
}
类型转换(高频考点)
// 隐式转换
"" + 1 // "1"
"" - 1 // -1
[] + {} // "[object Object]"
{} + [] // 0 (在某些环境下)
// 显式转换
Number("123") // 123
String(123) // "123"
Boolean(0) // false
// 特殊情况
Number("") // 0
Number(" ") // 0
Number("123a") // NaN
parseInt("123a") // 123
3️⃣ 变量 & 作用域(必考)
声明方式对比
| 特性 | var | let | const | |------|-----|-----|-------| | 作用域 | 函数作用域 | 块级作用域 | 块级作用域 | | 变量提升 | 是 | 是(但有TDZ) | 是(但有TDZ) | | 重复声明 | 允许 | 不允许 | 不允许 | | 重新赋值 | 允许 | 允许 | 不允许 |
暂时性死区(TDZ)
console.log(a); // undefined (var 提升)
console.log(b); // ReferenceError (TDZ)
var a = 1;
let b = 2;
作用域链
var x = 1;
function outer() {
var x = 2;
function inner() {
console.log(x); // 2 (词法作用域)
}
return inner;
}
4️⃣ this 指向(必考重点)
绑定规则优先级
- new 绑定(最高优先级)
- 显式绑定(call/apply/bind)
- 隐式绑定(对象方法调用)
- 默认绑定(全局对象或 undefined)
// 默认绑定
function foo() {
console.log(this); // window (非严格模式) 或 undefined (严格模式)
}
// 隐式绑定
const obj = {
name: 'obj',
foo() {
console.log(this.name); // 'obj'
}
};
// 显式绑定
foo.call(obj); // this 指向 obj
foo.apply(obj); // this 指向 obj
foo.bind(obj)(); // this 指向 obj
// new 绑定
function Person(name) {
this.name = name;
}
const p = new Person('Alice'); // this 指向新创建的对象
// 箭头函数
const arrow = () => {
console.log(this); // 继承外层作用域的 this
};
常见陷阱
// 隐式丢失
const obj = {
name: 'obj',
foo() {
console.log(this.name);
}
};
const fn = obj.foo;
fn(); // undefined (this 指向全局)
// 回调函数中的 this
setTimeout(obj.foo, 1000); // undefined
// 解决方案
setTimeout(() => obj.foo(), 1000); // 'obj'
setTimeout(obj.foo.bind(obj), 1000); // 'obj'
5️⃣ 闭包(核心概念)
定义与形成条件
// 闭包:函数 + 函数能够访问的自由变量
function outer(x) {
// 外部函数的变量
return function inner(y) {
return x + y; // 访问外部变量 x
};
}
const add5 = outer(5);
console.log(add5(3)); // 8
常见应用场景
// 1. 私有变量
function createCounter() {
let count = 0;
return {
increment() { return ++count; },
decrement() { return --count; },
getCount() { return count; }
};
}
// 2. 函数柯里化
function curry(fn) {
return function curried(...args) {
if (args.length >= fn.length) {
return fn.apply(this, args);
}
return function(...nextArgs) {
return curried.apply(this, args.concat(nextArgs));
};
};
}
// 3. 防抖节流
function debounce(fn, delay) {
let timer;
return function(...args) {
clearTimeout(timer);
timer = setTimeout(() => fn.apply(this, args), delay);
};
}
function throttle(fn, delay) {
let lastTime = 0;
return function(...args) {
const now = Date.now();
if (now - lastTime >= delay) {
lastTime = now;
fn.apply(this, args);
}
};
}
内存泄漏风险
// 潜在内存泄漏
function attachListeners() {
const largeData = new Array(1000000).fill('data');
document.getElementById('button').addEventListener('click', function() {
// 闭包持有 largeData 的引用
console.log('clicked');
});
}
// 解决方案:及时清理
function attachListeners() {
const button = document.getElementById('button');
const handler = function() {
console.log('clicked');
};
button.addEventListener('click', handler);
// 清理
return function cleanup() {
button.removeEventListener('click', handler);
};
}
6️⃣ 原型 & 原型链(重点)
核心概念
// 构造函数
function Person(name) {
this.name = name;
}
// 原型对象
Person.prototype.sayHello = function() {
console.log(`Hello, I'm ${this.name}`);
};
// 实例
const alice = new Person('Alice');
// 原型链关系
alice.__proto__ === Person.prototype; // true
Person.prototype.__proto__ === Object.prototype; // true
Object.prototype.__proto__ === null; // true
// instanceof 原理
function myInstanceof(obj, Constructor) {
let proto = obj.__proto__;
while (proto) {
if (proto === Constructor.prototype) {
return true;
}
proto = proto.__proto__;
}
return false;
}
原型链查找规则
const obj = {
a: 1
};
// 查找顺序:
// 1. obj.a -> 找到,返回 1
// 2. obj.b -> obj.__proto__.b -> Object.prototype.b -> undefined
console.log(obj.a); // 1
console.log(obj.toString); // [Function: toString] (来自 Object.prototype)
继承实现
// 1. 原型链继承
function Parent() {
this.name = 'parent';
}
function Child() {}
Child.prototype = new Parent();
// 2. 构造函数继承
function Child() {
Parent.call(this);
}
// 3. 组合继承
function Child() {
Parent.call(this);
}
Child.prototype = new Parent();
Child.prototype.constructor = Child;
// 4. ES6 类继承
class Parent {
constructor(name) {
this.name = name;
}
}
class Child extends Parent {
constructor(name, age) {
super(name);
this.age = age;
}
}
7️⃣ 异步编程(重点难点)
Promise 详解
// Promise 三种状态
// pending -> fulfilled
// pending -> rejected
// 基本用法
const promise = new Promise((resolve, reject) => {
// 异步操作
setTimeout(() => {
resolve('success');
// reject(new Error('failed'));
}, 1000);
});
promise
.then(result => console.log(result))
.catch(error => console.error(error))
.finally(() => console.log('cleanup'));
// Promise 静态方法
Promise.all([p1, p2, p3]) // 全部成功才成功
Promise.allSettled([p1, p2]) // 等待全部完成
Promise.race([p1, p2]) // 第一个完成的结果
Promise.any([p1, p2]) // 第一个成功的结果
async/await
// async 函数返回 Promise
async function fetchData() {
try {
const response = await fetch('/api/data');
const data = await response.json();
return data;
} catch (error) {
console.error('Error:', error);
throw error;
}
}
// 错误处理
async function handleErrors() {
try {
await fetchData();
} catch (error) {
// 处理错误
}
}
// 并发执行
async function concurrent() {
const [result1, result2] = await Promise.all([
fetchData1(),
fetchData2()
]);
}
Event Loop(面试高频)
// 宏任务 vs 微任务
console.log('1');
setTimeout(() => console.log('2'), 0); // 宏任务
Promise.resolve().then(() => console.log('3')); // 微任务
console.log('4');
// 输出顺序:1, 4, 3, 2
// 执行流程:
// 1. 执行同步代码:1, 4
// 2. 执行微任务队列:3
// 3. 执行宏任务队列:2
常见异步模式
// 1. 回调地狱
getData(function(a) {
getMoreData(a, function(b) {
getMoreData(b, function(c) {
// 嵌套太深
});
});
});
// 2. Promise 链
getData()
.then(a => getMoreData(a))
.then(b => getMoreData(b))
.then(c => {
// 处理结果
});
// 3. async/await
async function processData() {
const a = await getData();
const b = await getMoreData(a);
const c = await getMoreData(b);
return c;
}
8️⃣ 常用内置对象(实用技巧)
Array 高级方法
const arr = [1, 2, 3, 4, 5];
// 转换方法
arr.map(x => x * 2); // [2, 4, 6, 8, 10]
arr.filter(x => x > 2); // [3, 4, 5]
arr.reduce((sum, x) => sum + x, 0); // 15
// 查找方法
arr.find(x => x > 2); // 3
arr.findIndex(x => x > 2); // 2
arr.includes(3); // true
arr.indexOf(3); // 2
// 修改方法
arr.slice(1, 3); // [2, 3] (不改变原数组)
arr.splice(1, 2, 'a', 'b'); // 删除并插入 (改变原数组)
// 迭代方法
arr.forEach((item, index) => {});
arr.some(x => x > 3); // true
arr.every(x => x > 0); // true
// ES6+ 新方法
arr.flat(); // 扁平化
arr.flatMap(x => [x, x * 2]); // 映射后扁平化
Object 操作
const obj = { a: 1, b: 2, c: 3 };
// 获取键值
Object.keys(obj); // ['a', 'b', 'c']
Object.values(obj); // [1, 2, 3]
Object.entries(obj); // [['a', 1], ['b', 2], ['c', 3]]
// 属性检查
obj.hasOwnProperty('a'); // true
'a' in obj; // true
// 对象合并
Object.assign({}, obj, { d: 4 }); // { a: 1, b: 2, c: 3, d: 4 }
{ ...obj, d: 4 }; // 同上 (ES6)
// 属性描述符
Object.defineProperty(obj, 'x', {
value: 42,
writable: false,
enumerable: true,
configurable: true
});
// 冻结对象
Object.freeze(obj); // 不可修改
Object.seal(obj); // 不可添加/删除属性
Object.preventExtensions(obj); // 不可添加属性
Map / Set 详解
// Map vs Object
const map = new Map();
map.set('key', 'value');
map.set(1, 'number key');
map.set(obj, 'object key');
map.get('key'); // 'value'
map.has('key'); // true
map.delete('key'); // true
map.size; // 当前大小
// Set 去重
const set = new Set([1, 2, 2, 3]);
console.log([...set]); // [1, 2, 3]
set.add(4);
set.has(2); // true
set.delete(2);
// WeakMap / WeakSet (弱引用)
const weakMap = new WeakMap();
const key = {};
weakMap.set(key, 'value');
// 当 key 被垃圾回收时,WeakMap 中的条目也会被自动清理
9️⃣ ES6+ 新特性(现代 JS)
解构赋值
// 数组解构
const [a, b, ...rest] = [1, 2, 3, 4, 5];
const [x, , z] = [1, 2, 3]; // 跳过元素
// 对象解构
const { name, age, ...others } = person;
const { name: userName } = person; // 重命名
// 默认值
const { x = 10 } = {};
const [y = 20] = [];
// 嵌套解构
const { address: { city } } = person;
模板字符串
const name = 'World';
const message = `Hello, ${name}!`;
// 多行字符串
const html = `
<div>
<h1>${title}</h1>
<p>${content}</p>
</div>
`;
// 标签模板
function highlight(strings, ...values) {
return strings.reduce((result, string, i) => {
return result + string + (values[i] ? `<mark>${values[i]}</mark>` : '');
}, '');
}
const result = highlight`Hello ${name}, you have ${count} messages`;
箭头函数深入
// 基本语法
const add = (a, b) => a + b;
const square = x => x * x;
const greet = () => 'Hello';
// 返回对象字面量
const createObj = () => ({ name: 'test' });
// 箭头函数特点
// 1. 没有自己的 this
// 2. 没有 arguments 对象
// 3. 不能用作构造函数
// 4. 没有 prototype 属性
// 使用场景
const numbers = [1, 2, 3];
numbers.map(n => n * 2);
numbers.filter(n => n > 1);
类与继承
class Animal {
constructor(name) {
this.name = name;
}
speak() {
console.log(`${this.name} makes a sound`);
}
// 静态方法
static getSpecies() {
return 'Animal';
}
// 私有字段 (ES2022)
#privateField = 'secret';
// 私有方法
#privateMethod() {
return this.#privateField;
}
}
class Dog extends Animal {
constructor(name, breed) {
super(name);
this.breed = breed;
}
speak() {
console.log(`${this.name} barks`);
}
}
const dog = new Dog('Buddy', 'Golden Retriever');
模块系统
// 导出
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default class Calculator {}
// 导入
import Calculator, { PI, add } from './math.js';
import * as math from './math.js';
import { add as sum } from './math.js';
// 动态导入
async function loadModule() {
const module = await import('./math.js');
return module.default;
}
🔟 错误处理与调试
错误类型
// 基本错误类型
try {
throw new Error('Generic error');
throw new TypeError('Type error');
throw new ReferenceError('Reference error');
throw new SyntaxError('Syntax error');
} catch (error) {
console.log(error.name); // 错误类型
console.log(error.message); // 错误信息
console.log(error.stack); // 堆栈信息
}
// 自定义错误
class CustomError extends Error {
constructor(message, code) {
super(message);
this.name = 'CustomError';
this.code = code;
}
}
// 异步错误处理
async function handleAsyncError() {
try {
await riskyOperation();
} catch (error) {
if (error instanceof CustomError) {
// 处理自定义错误
} else {
// 处理其他错误
}
}
}
// 全局错误处理
window.addEventListener('error', (event) => {
console.error('Global error:', event.error);
});
window.addEventListener('unhandledrejection', (event) => {
console.error('Unhandled promise rejection:', event.reason);
});
调试技巧
// console 方法
console.log('基本输出');
console.error('错误信息');
console.warn('警告信息');
console.info('信息');
console.debug('调试信息');
console.table([{name: 'Alice', age: 25}, {name: 'Bob', age: 30}]);
console.group('分组开始');
console.log('分组内容');
console.groupEnd();
console.time('timer');
// 一些操作
console.timeEnd('timer');
console.trace('追踪调用栈');
// 断点调试
debugger; // 在浏览器中会暂停执行
// 性能监控
performance.mark('start');
// 一些操作
performance.mark('end');
performance.measure('operation', 'start', 'end');
二、浏览器基础(前端核心)
1️⃣ DOM 操作详解
DOM 树结构
// DOM 节点类型
Node.ELEMENT_NODE; // 1 - 元素节点
Node.TEXT_NODE; // 3 - 文本节点
Node.COMMENT_NODE; // 8 - 注释节点
Node.DOCUMENT_NODE; // 9 - 文档节点
// 节点关系
element.parentNode; // 父节点
element.childNodes; // 所有子节点
element.children; // 元素子节点
element.firstChild; // 第一个子节点
element.lastChild; // 最后一个子节点
element.nextSibling; // 下一个兄弟节点
element.previousSibling; // 上一个兄弟节点
元素获取与操作
// 获取元素
document.getElementById('id');
document.getElementsByClassName('class');
document.getElementsByTagName('tag');
document.querySelector('.class');
document.querySelectorAll('.class');
// 创建和插入元素
const div = document.createElement('div');
div.textContent = 'Hello World';
div.innerHTML = '<span>HTML content</span>';
parent.appendChild(div);
parent.insertBefore(div, referenceNode);
parent.removeChild(div);
parent.replaceChild(newNode, oldNode);
// 属性操作
element.getAttribute('attr');
element.setAttribute('attr', 'value');
element.removeAttribute('attr');
element.hasAttribute('attr');
// 样式操作
element.style.color = 'red';
element.style.cssText = 'color: red; font-size: 16px;';
element.className = 'new-class';
element.classList.add('class');
element.classList.remove('class');
element.classList.toggle('class');
element.classList.contains('class');
现代 DOM API
// 更现代的插入方法
element.append(node1, node2, 'text');
element.prepend(node1, node2);
element.before(node);
element.after(node);
element.replaceWith(newNode);
element.remove();
// 查找方法
element.closest('.parent-class'); // 向上查找
element.matches('.selector'); // 是否匹配选择器
// 位置和尺寸
element.getBoundingClientRect(); // 元素位置信息
element.offsetWidth; // 包含边框的宽度
element.clientWidth; // 不包含边框的宽度
element.scrollWidth; // 滚动宽度
2️⃣ 事件机制(重点)
事件流程
// 事件流:捕获 -> 目标 -> 冒泡
document.addEventListener('click', handler, true); // 捕获阶段
document.addEventListener('click', handler, false); // 冒泡阶段
// 事件对象
function handleClick(event) {
event.type; // 事件类型
event.target; // 触发事件的元素
event.currentTarget; // 绑定事件的元素
event.preventDefault(); // 阻止默认行为
event.stopPropagation(); // 阻止事件传播
event.stopImmediatePropagation(); // 阻止同元素其他监听器
}
事件委托(性能优化)
// 传统方式 - 为每个按钮绑定事件
document.querySelectorAll('.button').forEach(btn => {
btn.addEventListener('click', handleClick);
});
// 事件委托 - 只绑定一个事件
document.addEventListener('click', function(event) {
if (event.target.matches('.button')) {
handleClick(event);
}
});
// 实用的事件委托函数
function delegate(parent, selector, event, handler) {
parent.addEventListener(event, function(e) {
if (e.target.matches(selector)) {
handler.call(e.target, e);
}
});
}
delegate(document, '.button', 'click', function(e) {
console.log('Button clicked:', this.textContent);
});
常见事件类型
// 鼠标事件
element.addEventListener('click', handler);
element.addEventListener('dblclick', handler);
element.addEventListener('mousedown', handler);
element.addEventListener('mouseup', handler);
element.addEventListener('mouseover', handler);
element.addEventListener('mouseout', handler);
element.addEventListener('mouseenter', handler); // 不冒泡
element.addEventListener('mouseleave', handler); // 不冒泡
// 键盘事件
element.addEventListener('keydown', handler);
element.addEventListener('keyup', handler);
element.addEventListener('keypress', handler); // 已废弃
// 表单事件
element.addEventListener('submit', handler);
element.addEventListener('change', handler);
element.addEventListener('input', handler);
element.addEventListener('focus', handler);
element.addEventListener('blur', handler);
// 窗口事件
window.addEventListener('load', handler);
window.addEventListener('DOMContentLoaded', handler);
window.addEventListener('resize', handler);
window.addEventListener('scroll', handler);
自定义事件
// 创建自定义事件
const customEvent = new CustomEvent('myEvent', {
detail: { message: 'Hello World' },
bubbles: true,
cancelable: true
});
// 触发事件
element.dispatchEvent(customEvent);
// 监听自定义事件
element.addEventListener('myEvent', function(event) {
console.log(event.detail.message);
});
// 事件总线模式
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(callback => callback(data));
}
}
off(event, callback) {
if (this.events[event]) {
this.events[event] = this.events[event].filter(cb => cb !== callback);
}
}
}
3️⃣ 浏览器渲染机制(面试重点)
渲染流程
1. HTML 解析 → DOM Tree
2. CSS 解析 → CSSOM Tree
3. DOM + CSSOM → Render Tree
4. Layout (Reflow) → 计算位置尺寸
5. Paint (Repaint) → 绘制像素
6. Composite → 合成层
关键渲染路径优化
<!-- 1. 减少关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="dns-prefetch" href="//example.com">
<!-- 2. 减少关键字节 -->
<style>
/* 内联关键 CSS */
.above-fold { display: block; }
</style>
<!-- 3. 减少关键路径长度 -->
<script async src="non-critical.js"></script>
<script defer src="dom-dependent.js"></script>
Reflow 和 Repaint
// 引起 Reflow 的操作(昂贵)
element.style.width = '100px';
element.style.height = '100px';
element.offsetWidth; // 读取布局属性
element.scrollTop;
element.getBoundingClientRect();
// 只引起 Repaint 的操作(相对便宜)
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.visibility = 'hidden';
// 优化策略
// 1. 批量修改样式
element.style.cssText = 'width: 100px; height: 100px; color: red;';
// 2. 使用 class 替代直接样式修改
element.className = 'new-style';
// 3. 离线操作 DOM
const fragment = document.createDocumentFragment();
// 在 fragment 中操作
document.body.appendChild(fragment);
// 4. 使用 transform 和 opacity(不触发 reflow)
element.style.transform = 'translateX(100px)';
element.style.opacity = '0.5';
浏览器缓存机制
// 强缓存
// Cache-Control: max-age=3600
// Expires: Wed, 21 Oct 2025 07:28:00 GMT
// 协商缓存
// Last-Modified / If-Modified-Since
// ETag / If-None-Match
// Service Worker 缓存
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request).then(response => {
return response || fetch(event.request);
})
);
});
4️⃣ Web API 详解
Fetch API
// 基本用法
fetch('/api/data')
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.then(data => console.log(data))
.catch(error => console.error('Error:', error));
// 高级配置
fetch('/api/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
body: JSON.stringify({ key: 'value' }),
credentials: 'include', // 包含 cookies
cache: 'no-cache',
redirect: 'follow'
})
// 取消请求
const controller = new AbortController();
fetch('/api/data', { signal: controller.signal });
controller.abort(); // 取消请求
// 超时处理
function fetchWithTimeout(url, options = {}, timeout = 5000) {
return Promise.race([
fetch(url, options),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Timeout')), timeout)
)
]);
}
Web Storage
// localStorage (持久化)
localStorage.setItem('key', 'value');
localStorage.getItem('key');
localStorage.removeItem('key');
localStorage.clear();
// sessionStorage (会话级)
sessionStorage.setItem('key', 'value');
// 存储对象
const user = { name: 'Alice', age: 25 };
localStorage.setItem('user', JSON.stringify(user));
const savedUser = JSON.parse(localStorage.getItem('user'));
// 监听存储变化
window.addEventListener('storage', function(e) {
console.log('Storage changed:', e.key, e.oldValue, e.newValue);
});
// IndexedDB (大量数据)
const request = indexedDB.open('MyDB', 1);
request.onsuccess = function(event) {
const db = event.target.result;
// 数据库操作
};
Intersection Observer
// 懒加载图片
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.remove('lazy');
observer.unobserve(img);
}
});
});
document.querySelectorAll('img[data-src]').forEach(img => {
imageObserver.observe(img);
});
// 无限滚动
const loadMoreObserver = new IntersectionObserver(entries => {
if (entries[0].isIntersecting) {
loadMoreContent();
}
});
loadMoreObserver.observe(document.querySelector('.load-more-trigger'));
三、React 深度解析(现代前端核心)
1️⃣ React 核心思想深入
组件化思维
// 函数组件 vs 类组件
// 函数组件(推荐)
function Welcome({ name, children }) {
return (
<div>
<h1>Hello, {name}!</h1>
{children}
</div>
);
}
// 类组件(了解即可)
class Welcome extends React.Component {
render() {
return <h1>Hello, {this.props.name}!</h1>;
}
}
// 组件组合 vs 继承
function Dialog({ title, children }) {
return (
<div className="dialog">
<h1>{title}</h1>
<div className="dialog-content">
{children}
</div>
</div>
);
}
function WelcomeDialog() {
return (
<Dialog title="Welcome">
<p>Thank you for visiting!</p>
</Dialog>
);
}
单向数据流
// 数据向下流动,事件向上传递
function App() {
const [count, setCount] = useState(0);
return (
<div>
<Counter
value={count}
onIncrement={() => setCount(count + 1)}
onDecrement={() => setCount(count - 1)}
/>
</div>
);
}
function Counter({ value, onIncrement, onDecrement }) {
return (
<div>
<button onClick={onDecrement}>-</button>
<span>{value}</span>
<button onClick={onIncrement}>+</button>
</div>
);
}
2️⃣ JSX 深入理解
JSX 本质
// JSX 语法
const element = <h1 className="greeting">Hello, world!</h1>;
// 编译后的 JavaScript
const element = React.createElement(
'h1',
{ className: 'greeting' },
'Hello, world!'
);
// 复杂 JSX
const element = (
<div>
<h1>{user.name}</h1>
{user.isLoggedIn && <p>Welcome back!</p>}
<ul>
{items.map(item => (
<li key={item.id}>{item.name}</li>
))}
</ul>
</div>
);
JSX 最佳实践
// 1. 条件渲染
function UserGreeting({ user }) {
if (!user) {
return <div>Please log in</div>;
}
return (
<div>
<h1>Welcome, {user.name}!</h1>
{user.isAdmin && <AdminPanel />}
{user.notifications.length > 0 ? (
<NotificationList notifications={user.notifications} />
) : (
<p>No new notifications</p>
)}
</div>
);
}
// 2. 列表渲染
function TodoList({ todos }) {
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id} // 重要:稳定的 key
todo={todo}
onToggle={() => handleToggle(todo.id)}
onDelete={() => handleDelete(todo.id)}
/>
))}
</ul>
);
}
// 3. 事件处理
function Button({ onClick, children, disabled = false }) {
const handleClick = useCallback((event) => {
event.preventDefault();
if (!disabled && onClick) {
onClick(event);
}
}, [disabled, onClick]);
return (
<button
onClick={handleClick}
disabled={disabled}
className={`btn ${disabled ? 'btn-disabled' : 'btn-active'}`}
>
{children}
</button>
);
}
3️⃣ Hooks 深度解析(重点)
useState 进阶
// 基本用法
const [count, setCount] = useState(0);
// 函数式更新
const [count, setCount] = useState(0);
setCount(prevCount => prevCount + 1);
// 惰性初始化
const [state, setState] = useState(() => {
const initialState = someExpensiveComputation();
return initialState;
});
// 对象状态更新
const [user, setUser] = useState({ name: '', email: '' });
setUser(prevUser => ({ ...prevUser, name: 'Alice' }));
// 自定义 Hook 封装状态逻辑
function useCounter(initialValue = 0) {
const [count, setCount] = useState(initialValue);
const increment = useCallback(() => setCount(c => c + 1), []);
const decrement = useCallback(() => setCount(c => c - 1), []);
const reset = useCallback(() => setCount(initialValue), [initialValue]);
return { count, increment, decrement, reset };
}
useEffect 深度理解
// 基本用法
useEffect(() => {
// 副作用逻辑
document.title = `Count: ${count}`;
}, [count]); // 依赖数组
// 清理副作用
useEffect(() => {
const timer = setInterval(() => {
setCount(c => c + 1);
}, 1000);
return () => clearInterval(timer); // 清理函数
}, []);
// 条件执行
useEffect(() => {
if (user.id) {
fetchUserData(user.id);
}
}, [user.id]);
// 自定义 Hook 封装副作用
function useDocumentTitle(title) {
useEffect(() => {
const prevTitle = document.title;
document.title = title;
return () => {
document.title = prevTitle;
};
}, [title]);
}
function useLocalStorage(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? JSON.parse(item) : initialValue;
} catch (error) {
return initialValue;
}
});
useEffect(() => {
try {
window.localStorage.setItem(key, JSON.stringify(value));
} catch (error) {
console.error('Error saving to localStorage:', error);
}
}, [key, value]);
return [value, setValue];
}
性能优化 Hooks
// useMemo - 缓存计算结果
function ExpensiveComponent({ items, filter }) {
const filteredItems = useMemo(() => {
return items.filter(item => item.category === filter);
}, [items, filter]);
const expensiveValue = useMemo(() => {
return items.reduce((sum, item) => sum + item.price, 0);
}, [items]);
return (
<div>
<p>Total: ${expensiveValue}</p>
<ItemList items={filteredItems} />
</div>
);
}
// useCallback - 缓存函数
function TodoList({ todos, onToggle, onDelete }) {
const handleToggle = useCallback((id) => {
onToggle(id);
}, [onToggle]);
const handleDelete = useCallback((id) => {
onDelete(id);
}, [onDelete]);
return (
<ul>
{todos.map(todo => (
<TodoItem
key={todo.id}
todo={todo}
onToggle={handleToggle}
onDelete={handleDelete}
/>
))}
</ul>
);
}
// React.memo - 组件记忆化
const TodoItem = React.memo(({ todo, onToggle, onDelete }) => {
return (
<li>
<input
type="checkbox"
checked={todo.completed}
onChange={() => onToggle(todo.id)}
/>
<span>{todo.text}</span>
<button onClick={() => onDelete(todo.id)}>Delete</button>
</li>
);
});
其他重要 Hooks
// useRef - 引用 DOM 或保存可变值
function TextInput() {
const inputRef = useRef(null);
const countRef = useRef(0);
useEffect(() => {
inputRef.current.focus();
}, []);
const handleClick = () => {
countRef.current += 1;
console.log('Clicked', countRef.current, 'times');
};
return (
<div>
<input ref={inputRef} type="text" />
<button onClick={handleClick}>Click me</button>
</div>
);
}
// useContext - 跨组件状态共享
const ThemeContext = createContext();
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Header />
<Main />
</ThemeContext.Provider>
);
}
function Header() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<header className={`header-${theme}`}>
<button onClick={() => setTheme(theme === 'light' ? 'dark' : 'light')}>
Toggle Theme
</button>
</header>
);
}
// useReducer - 复杂状态管理
function todoReducer(state, action) {
switch (action.type) {
case 'ADD_TODO':
return [...state, { id: Date.now(), text: action.text, completed: false }];
case 'TOGGLE_TODO':
return state.map(todo =>
todo.id === action.id ? { ...todo, completed: !todo.completed } : todo
);
case 'DELETE_TODO':
return state.filter(todo => todo.id !== action.id);
default:
return state;
}
}
function TodoApp() {
const [todos, dispatch] = useReducer(todoReducer, []);
const addTodo = (text) => {
dispatch({ type: 'ADD_TODO', text });
};
const toggleTodo = (id) => {
dispatch({ type: 'TOGGLE_TODO', id });
};
return (
<div>
<AddTodo onAdd={addTodo} />
<TodoList todos={todos} onToggle={toggleTodo} />
</div>
);
}
4️⃣ React 生命周期与错误边界
类组件生命周期(了解)
class LifecycleDemo extends React.Component {
constructor(props) {
super(props);
this.state = { count: 0 };
console.log('1. Constructor');
}
static getDerivedStateFromProps(props, state) {
console.log('2. getDerivedStateFromProps');
return null;
}
componentDidMount() {
console.log('3. componentDidMount');
// 适合:数据获取、订阅、DOM 操作
}
shouldComponentUpdate(nextProps, nextState) {
console.log('4. shouldComponentUpdate');
return true; // 返回 false 可以阻止更新
}
getSnapshotBeforeUpdate(prevProps, prevState) {
console.log('5. getSnapshotBeforeUpdate');
return null;
}
componentDidUpdate(prevProps, prevState, snapshot) {
console.log('6. componentDidUpdate');
// 适合:DOM 更新后的操作
}
componentWillUnmount() {
console.log('7. componentWillUnmount');
// 适合:清理定时器、取消订阅
}
render() {
console.log('Render');
return <div>Count: {this.state.count}</div>;
}
}
错误边界
class ErrorBoundary extends React.Component {
constructor(props) {
super(props);
this.state = { hasError: false, error: null };
}
static getDerivedStateFromError(error) {
return { hasError: true, error };
}
componentDidCatch(error, errorInfo) {
console.error('Error caught by boundary:', error, errorInfo);
// 可以发送错误报告到服务器
}
render() {
if (this.state.hasError) {
return (
<div>
<h2>Something went wrong.</h2>
<details>
{this.state.error && this.state.error.toString()}
</details>
</div>
);
}
return this.props.children;
}
}
// 使用错误边界
function App() {
return (
<ErrorBoundary>
<Header />
<Main />
</ErrorBoundary>
);
}
// React 18 中的错误边界 Hook(实验性)
function useErrorBoundary() {
const [error, setError] = useState(null);
if (error) {
throw error;
}
return setError;
}
5️⃣ React 性能优化
渲染优化策略
// 1. 避免在 render 中创建对象/函数
// ❌ 不好的做法
function BadComponent({ items }) {
return (
<div>
{items.map(item => (
<Item
key={item.id}
item={item}
onClick={() => handleClick(item.id)} // 每次都创建新函数
style={{ color: 'red' }} // 每次都创建新对象
/>
))}
</div>
);
}
// ✅ 好的做法
function GoodComponent({ items }) {
const handleClick = useCallback((id) => {
// 处理点击
}, []);
const itemStyle = useMemo(() => ({ color: 'red' }), []);
return (
<div>
{items.map(item => (
<Item
key={item.id}
item={item}
onClick={handleClick}
style={itemStyle}
/>
))}
</div>
);
}
// 2. 组件拆分
// ❌ 大组件
function BigComponent({ user, posts, comments }) {
const [filter, setFilter] = useState('');
return (
<div>
<UserProfile user={user} />
<PostList posts={posts} filter={filter} />
<CommentList comments={comments} />
<FilterInput value={filter} onChange={setFilter} />
</div>
);
}
// ✅ 拆分后的组件
function App({ user, posts, comments }) {
return (
<div>
<UserProfile user={user} />
<PostSection posts={posts} />
<CommentSection comments={comments} />
</div>
);
}
function PostSection({ posts }) {
const [filter, setFilter] = useState('');
return (
<div>
<FilterInput value={filter} onChange={setFilter} />
<PostList posts={posts} filter={filter} />
</div>
);
}
虚拟化长列表
// 使用 react-window 进行虚拟化
import { FixedSizeList as List } from 'react-window';
function VirtualizedList({ items }) {
const Row = ({ index, style }) => (
<div style={style}>
<Item item={items[index]} />
</div>
);
return (
<List
height={600}
itemCount={items.length}
itemSize={50}
width="100%"
>
{Row}
</List>
);
}
// 自定义虚拟化 Hook
function useVirtualization({ items, containerHeight, itemHeight }) {
const [scrollTop, setScrollTop] = useState(0);
const visibleStart = Math.floor(scrollTop / itemHeight);
const visibleEnd = Math.min(
visibleStart + Math.ceil(containerHeight / itemHeight),
items.length
);
const visibleItems = items.slice(visibleStart, visibleEnd);
return {
visibleItems,
totalHeight: items.length * itemHeight,
offsetY: visibleStart * itemHeight,
onScroll: (e) => setScrollTop(e.target.scrollTop)
};
}
四、Vue 深度解析(渐进式框架)
1️⃣ Vue 响应式原理
Vue 2 响应式
// Object.defineProperty 实现
function defineReactive(obj, key, val) {
const dep = new Dep();
Object.defineProperty(obj, key, {
get() {
if (Dep.target) {
dep.depend();
}
return val;
},
set(newVal) {
if (newVal === val) return;
val = newVal;
dep.notify();
}
});
}
// 依赖收集
class Dep {
constructor() {
this.subs = [];
}
depend() {
if (Dep.target) {
this.subs.push(Dep.target);
}
}
notify() {
this.subs.forEach(sub => sub.update());
}
}
// 观察者
class Watcher {
constructor(vm, exp, cb) {
this.vm = vm;
this.exp = exp;
this.cb = cb;
this.value = this.get();
}
get() {
Dep.target = this;
const value = this.vm[this.exp];
Dep.target = null;
return value;
}
update() {
const newValue = this.get();
if (newValue !== this.value) {
this.value = newValue;
this.cb.call(this.vm, newValue);
}
}
}
Vue 3 响应式
// Proxy 实现
function reactive(target) {
return new Proxy(target, {
get(target, key, receiver) {
track(target, key);
return Reflect.get(target, key, receiver);
},
set(target, key, value, receiver) {
const result = Reflect.set(target, key, value, receiver);
trigger(target, key);
return result;
}
});
}
// 依赖追踪
const targetMap = new WeakMap();
function track(target, key) {
if (!activeEffect) return;
let depsMap = targetMap.get(target);
if (!depsMap) {
targetMap.set(target, (depsMap = new Map()));
}
let dep = depsMap.get(key);
if (!dep) {
depsMap.set(key, (dep = new Set()));
}
dep.add(activeEffect);
}
function trigger(target, key) {
const depsMap = targetMap.get(target);
if (!depsMap) return;
const dep = depsMap.get(key);
if (dep) {
dep.forEach(effect => effect());
}
}
// 副作用函数
let activeEffect = null;
function effect(fn) {
activeEffect = fn;
fn();
activeEffect = null;
}
2️⃣ Vue 3 Composition API
基础 API
<template>
<div>
<h1>{{ title }}</h1>
<p>Count: {{ count }}</p>
<button @click="increment">+</button>
<button @click="decrement">-</button>
<input v-model="searchTerm" placeholder="Search...">
<ul>
<li v-for="item in filteredItems" :key="item.id">
{{ item.name }}
</li>
</ul>
</div>
</template>
<script>
import { ref, reactive, computed, watch, onMounted, onUnmounted } from 'vue';
export default {
setup() {
// 响应式数据
const title = ref('Vue 3 Demo');
const count = ref(0);
const searchTerm = ref('');
const state = reactive({
items: [],
loading: false
});
// 计算属性
const filteredItems = computed(() => {
return state.items.filter(item =>
item.name.toLowerCase().includes(searchTerm.value.toLowerCase())
);
});
// 方法
const increment = () => count.value++;
const decrement = () => count.value--;
const fetchItems = async () => {
state.loading = true;
try {
const response = await fetch('/api/items');
state.items = await response.json();
} catch (error) {
console.error('Failed to fetch items:', error);
} finally {
state.loading = false;
}
};
// 监听器
watch(count, (newCount, oldCount) => {
console.log(`Count changed from ${oldCount} to ${newCount}`);
});
watch(searchTerm, (newTerm) => {
console.log('Search term:', newTerm);
}, { immediate: true });
// 生命周期
onMounted(() => {
fetchItems();
});
let timer;
onMounted(() => {
timer = setInterval(() => {
console.log('Timer tick');
}, 1000);
});
onUnmounted(() => {
if (timer) {
clearInterval(timer);
}
});
// 返回模板需要的数据和方法
return {
title,
count,
searchTerm,
filteredItems,
increment,
decrement,
loading: computed(() => state.loading)
};
}
};
</script>
自定义组合函数
// useCounter.js
import { ref, computed } from 'vue';
export function useCounter(initialValue = 0) {
const count = ref(initialValue);
const increment = () => count.value++;
const decrement = () => count.value--;
const reset = () => count.value = initialValue;
const isEven = computed(() => count.value % 2 === 0);
return {
count,
increment,
decrement,
reset,
isEven
};
}
// useFetch.js
import { ref, reactive } from 'vue';
export function useFetch(url) {
const data = ref(null);
const error = ref(null);
const loading = ref(false);
const execute = async () => {
loading.value = true;
error.value = null;
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
data.value = await response.json();
} catch (err) {
error.value = err.message;
} finally {
loading.value = false;
}
};
return {
data,
error,
loading,
execute
};
}
// 使用自定义组合函数
export default {
setup() {
const { count, increment, decrement, isEven } = useCounter(10);
const { data: users, loading, execute: fetchUsers } = useFetch('/api/users');
onMounted(() => {
fetchUsers();
});
return {
count,
increment,
decrement,
isEven,
users,
loading
};
}
};
3️⃣ Vue 指令与组件通信
自定义指令
// 全局指令
app.directive('focus', {
mounted(el) {
el.focus();
}
});
app.directive('color', {
beforeMount(el, binding) {
el.style.color = binding.value;
},
updated(el, binding) {
el.style.color = binding.value;
}
});
// 局部指令
export default {
directives: {
focus: {
mounted(el) {
el.focus();
}
},
clickOutside: {
beforeMount(el, binding) {
el.clickOutsideEvent = function(event) {
if (!(el === event.target || el.contains(event.target))) {
binding.value(event);
}
};
document.addEventListener('click', el.clickOutsideEvent);
},
unmounted(el) {
document.removeEventListener('click', el.clickOutsideEvent);
}
}
}
};
// 使用指令
// <input v-focus>
// <div v-color="'red'">Red text</div>
// <div v-click-outside="handleClickOutside">Click outside to close</div>
组件通信
<!-- 父子组件通信 -->
<!-- Parent.vue -->
<template>
<div>
<Child
:message="parentMessage"
@update="handleUpdate"
/>
</div>
</template>
<script>
import Child from './Child.vue';
export default {
components: { Child },
data() {
return {
parentMessage: 'Hello from parent'
};
},
methods: {
handleUpdate(data) {
console.log('Received from child:', data);
}
}
};
</script>
<!-- Child.vue -->
<template>
<div>
<p>{{ message }}</p>
<button @click="sendToParent">Send to Parent</button>
</div>
</template>
<script>
export default {
props: {
message: {
type: String,
required: true,
default: 'Default message'
}
},
emits: ['update'],
methods: {
sendToParent() {
this.$emit('update', 'Data from child');
}
}
};
</script>
<!-- 跨组件通信 - Provide/Inject -->
<!-- Ancestor.vue -->
<script>
export default {
provide() {
return {
theme: 'dark',
user: this.user
};
},
data() {
return {
user: { name: 'Alice', role: 'admin' }
};
}
};
</script>
<!-- Descendant.vue -->
<script>
export default {
inject: ['theme', 'user'],
mounted() {
console.log(this.theme); // 'dark'
console.log(this.user); // { name: 'Alice', role: 'admin' }
}
};
</script>
<!-- 事件总线 -->
<script>
// eventBus.js
import { createApp } from 'vue';
export const eventBus = createApp({}).config.globalProperties;
// 发送事件
eventBus.$emit('custom-event', data);
// 监听事件
eventBus.$on('custom-event', (data) => {
console.log('Received:', data);
});
// 移除监听
eventBus.$off('custom-event');
</script>
五、现代前端框架与工具
1️⃣ Next.js 深度解析
渲染模式详解
// 1. 静态生成 (SSG)
export async function getStaticProps() {
const posts = await fetchPosts();
return {
props: { posts },
revalidate: 60 // ISR: 60秒后重新生成
};
}
export async function getStaticPaths() {
const posts = await fetchPosts();
const paths = posts.map(post => ({
params: { id: post.id.toString() }
}));
return {
paths,
fallback: 'blocking' // 或 true, false
};
}
// 2. 服务端渲染 (SSR)
export async function getServerSideProps(context) {
const { req, res, params, query } = context;
const user = await fetchUser(req);
if (!user) {
return {
redirect: {
destination: '/login',
permanent: false
}
};
}
return {
props: { user }
};
}
// 3. 客户端渲染 (CSR)
function Profile() {
const [user, setUser] = useState(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
fetchUser().then(userData => {
setUser(userData);
setLoading(false);
});
}, []);
if (loading) return <div>Loading...</div>;
if (!user) return <div>No user found</div>;
return <div>Welcome, {user.name}!</div>;
}
App Router (Next.js 13+)
// app/layout.js - 根布局
export default function RootLayout({ children }) {
return (
<html lang="en">
<body>
<header>
<nav>Navigation</nav>
</header>
<main>{children}</main>
<footer>Footer</footer>
</body>
</html>
);
}
// app/page.js - 首页
export default function HomePage() {
return <h1>Welcome to Next.js 13!</h1>;
}
// app/blog/page.js - 博客列表页
export default async function BlogPage() {
const posts = await fetchPosts();
return (
<div>
<h1>Blog Posts</h1>
{posts.map(post => (
<article key={post.id}>
<h2>{post.title}</h2>
<p>{post.excerpt}</p>
</article>
))}
</div>
);
}
// app/blog/[slug]/page.js - 博客详情页
export default async function BlogPost({ params }) {
const post = await fetchPost(params.slug);
return (
<article>
<h1>{post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: post.content }} />
</article>
);
}
// 生成静态参数
export async function generateStaticParams() {
const posts = await fetchPosts();
return posts.map(post => ({
slug: post.slug
}));
}
API Routes
// pages/api/users.js (Pages Router)
export default function handler(req, res) {
if (req.method === 'GET') {
// 获取用户列表
const users = await getUsers();
res.status(200).json(users);
} else if (req.method === 'POST') {
// 创建用户
const newUser = await createUser(req.body);
res.status(201).json(newUser);
} else {
res.setHeader('Allow', ['GET', 'POST']);
res.status(405).end(`Method ${req.method} Not Allowed`);
}
}
// app/api/users/route.js (App Router)
export async function GET() {
const users = await getUsers();
return Response.json(users);
}
export async function POST(request) {
const body = await request.json();
const newUser = await createUser(body);
return Response.json(newUser, { status: 201 });
}
// 中间件
// middleware.js
import { NextResponse } from 'next/server';
export function middleware(request) {
// 检查认证
const token = request.cookies.get('token');
if (!token && request.nextUrl.pathname.startsWith('/dashboard')) {
return NextResponse.redirect(new URL('/login', request.url));
}
// 添加自定义头
const response = NextResponse.next();
response.headers.set('X-Custom-Header', 'value');
return response;
}
export const config = {
matcher: ['/dashboard/:path*', '/api/:path*']
};
2️⃣ Vite 深度解析
配置详解
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';
export default defineConfig({
plugins: [react()],
// 开发服务器配置
server: {
port: 3000,
open: true,
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
},
// 构建配置
build: {
outDir: 'dist',
sourcemap: true,
rollupOptions: {
input: {
main: resolve(__dirname, 'index.html'),
admin: resolve(__dirname, 'admin.html')
},
output: {
manualChunks: {
vendor: ['react', 'react-dom'],
utils: ['lodash', 'axios']
}
}
}
},
// 路径别名
resolve: {
alias: {
'@': resolve(__dirname, 'src'),
'@components': resolve(__dirname, 'src/components'),
'@utils': resolve(__dirname, 'src/utils')
}
},
// 环境变量
define: {
__APP_VERSION__: JSON.stringify(process.env.npm_package_version)
},
// CSS 配置
css: {
preprocessorOptions: {
scss: {
additionalData: `@import "@/styles/variables.scss";`
}
},
modules: {
localsConvention: 'camelCase'
}
}
});
插件开发
// 自定义插件
function myPlugin() {
return {
name: 'my-plugin',
configResolved(config) {
// 配置解析完成后调用
console.log('Config resolved:', config);
},
buildStart() {
// 构建开始时调用
console.log('Build started');
},
transform(code, id) {
// 转换代码
if (id.endsWith('.special')) {
return `export default ${JSON.stringify(code)}`;
}
},
generateBundle(options, bundle) {
// 生成 bundle 时调用
console.log('Bundle generated');
}
};
}
// 使用插件
export default defineConfig({
plugins: [
react(),
myPlugin()
]
});
HMR (热模块替换)
// 接受自身更新
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// 更新逻辑
console.log('Module updated:', newModule);
});
}
// 接受依赖更新
if (import.meta.hot) {
import.meta.hot.accept('./dependency.js', (newDep) => {
// 依赖更新时的处理
updateDependency(newDep);
});
}
// 处理状态保持
if (import.meta.hot) {
import.meta.hot.dispose((data) => {
// 保存状态
data.state = getCurrentState();
});
import.meta.hot.accept((newModule) => {
// 恢复状态
if (import.meta.hot.data.state) {
restoreState(import.meta.hot.data.state);
}
});
}
六、前端工程化与性能优化
1️⃣ 模块化系统
ES Modules vs CommonJS
// ES Modules (现代标准)
// 导出
export const PI = 3.14159;
export function add(a, b) { return a + b; }
export default class Calculator {}
// 导入
import Calculator, { PI, add } from './math.js';
import * as math from './math.js';
import { add as sum } from './math.js';
// 动态导入
async function loadModule() {
const { default: Calculator } = await import('./math.js');
return new Calculator();
}
// CommonJS (Node.js)
// 导出
module.exports = {
PI: 3.14159,
add: (a, b) => a + b
};
// 或者
exports.PI = 3.14159;
exports.add = (a, b) => a + b;
// 导入
const { PI, add } = require('./math');
const math = require('./math');
// 条件导入
if (condition) {
const module = require('./conditional-module');
}
模块打包原理
// 简化的打包器实现
class SimpleBundler {
constructor(entry) {
this.entry = entry;
this.modules = new Map();
}
// 解析模块依赖
parseModule(filename) {
const content = fs.readFileSync(filename, 'utf-8');
const ast = parse(content); // 解析 AST
const dependencies = [];
traverse(ast, {
ImportDeclaration(path) {
dependencies.push(path.node.source.value);
}
});
const code = transformFromAst(ast).code;
return {
filename,
dependencies,
code
};
}
// 构建依赖图
buildDependencyGraph() {
const queue = [this.entry];
const visited = new Set();
while (queue.length > 0) {
const filename = queue.shift();
if (visited.has(filename)) continue;
visited.add(filename);
const module = this.parseModule(filename);
this.modules.set(filename, module);
module.dependencies.forEach(dep => {
queue.push(resolve(dirname(filename), dep));
});
}
}
// 生成 bundle
generateBundle() {
let bundle = '(function(modules) {\n';
bundle += ' const require = (id) => {\n';
bundle += ' const module = { exports: {} };\n';
bundle += ' modules[id](module, module.exports, require);\n';
bundle += ' return module.exports;\n';
bundle += ' };\n';
bundle += ' require("' + this.entry + '");\n';
bundle += '})({';
this.modules.forEach((module, filename) => {
bundle += `"${filename}": function(module, exports, require) {\n`;
bundle += module.code;
bundle += '\n},';
});
bundle += '});';
return bundle;
}
}
2️⃣ 跨域解决方案
CORS 详解
// 服务端设置 CORS
app.use((req, res, next) => {
// 允许的源
const allowedOrigins = ['http://localhost:3000', 'https://example.com'];
const origin = req.headers.origin;
if (allowedOrigins.includes(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
}
// 允许的方法
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
// 允许的头部
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
// 允许携带凭证
res.setHeader('Access-Control-Allow-Credentials', 'true');
// 预检请求缓存时间
res.setHeader('Access-Control-Max-Age', '86400');
// 处理预检请求
if (req.method === 'OPTIONS') {
res.status(200).end();
return;
}
next();
});
// 客户端发送请求
fetch('https://api.example.com/data', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer token'
},
credentials: 'include', // 携带 cookies
body: JSON.stringify({ key: 'value' })
});
JSONP 实现
// JSONP 客户端实现
function jsonp(url, callback, callbackName = 'callback') {
return new Promise((resolve, reject) => {
const script = document.createElement('script');
const callbackId = `jsonp_${Date.now()}_${Math.random().toString(36).substr(2)}`;
// 全局回调函数
window[callbackId] = function(data) {
resolve(data);
document.head.removeChild(script);
delete window[callbackId];
};
// 错误处理
script.onerror = function() {
reject(new Error('JSONP request failed'));
document.head.removeChild(script);
delete window[callbackId];
};
// 构建 URL
const separator = url.includes('?') ? '&' : '?';
script.src = `${url}${separator}${callbackName}=${callbackId}`;
document.head.appendChild(script);
});
}
// 使用 JSONP
jsonp('https://api.example.com/data', 'callback')
.then(data => console.log(data))
.catch(error => console.error(error));
// 服务端支持 JSONP
app.get('/api/data', (req, res) => {
const data = { message: 'Hello JSONP' };
const callback = req.query.callback;
if (callback) {
// JSONP 响应
res.type('application/javascript');
res.send(`${callback}(${JSON.stringify(data)})`);
} else {
// 普通 JSON 响应
res.json(data);
}
});
代理服务器
// Webpack Dev Server 代理
module.exports = {
devServer: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}
}
}
};
// Vite 代理
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, '')
}
}
}
});
// Express 代理中间件
const { createProxyMiddleware } = require('http-proxy-middleware');
app.use('/api', createProxyMiddleware({
target: 'http://localhost:8080',
changeOrigin: true,
pathRewrite: {
'^/api': ''
}
}));
3️⃣ 前端缓存策略
HTTP 缓存
// 强缓存
// Cache-Control: max-age=31536000 (1年)
// Expires: Wed, 21 Oct 2025 07:28:00 GMT
// 协商缓存
// Last-Modified / If-Modified-Since
// ETag / If-None-Match
// 缓存策略设置
app.use('/static', express.static('public', {
maxAge: '1y', // 静态资源缓存1年
etag: true, // 启用 ETag
lastModified: true
}));
// 动态内容缓存控制
app.get('/api/data', (req, res) => {
res.set({
'Cache-Control': 'private, max-age=300', // 5分钟
'ETag': generateETag(data)
});
res.json(data);
});
// 客户端缓存检查
async function fetchWithCache(url, options = {}) {
const cacheKey = `cache_${url}`;
const cached = localStorage.getItem(cacheKey);
if (cached) {
const { data, timestamp, maxAge } = JSON.parse(cached);
if (Date.now() - timestamp < maxAge) {
return data;
}
}
const response = await fetch(url, options);
const data = await response.json();
// 缓存数据
localStorage.setItem(cacheKey, JSON.stringify({
data,
timestamp: Date.now(),
maxAge: 5 * 60 * 1000 // 5分钟
}));
return data;
}
Service Worker 缓存
// sw.js
const CACHE_NAME = 'my-app-v1';
const urlsToCache = [
'/',
'/static/css/main.css',
'/static/js/main.js'
];
// 安装事件
self.addEventListener('install', event => {
event.waitUntil(
caches.open(CACHE_NAME)
.then(cache => cache.addAll(urlsToCache))
);
});
// 拦截请求
self.addEventListener('fetch', event => {
event.respondWith(
caches.match(event.request)
.then(response => {
// 缓存命中,返回缓存
if (response) {
return response;
}
// 网络请求
return fetch(event.request).then(response => {
// 检查响应是否有效
if (!response || response.status !== 200 || response.type !== 'basic') {
return response;
}
// 克隆响应
const responseToCache = response.clone();
caches.open(CACHE_NAME)
.then(cache => {
cache.put(event.request, responseToCache);
});
return response;
});
})
);
});
// 注册 Service Worker
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js')
.then(registration => {
console.log('SW registered:', registration);
})
.catch(error => {
console.log('SW registration failed:', error);
});
}
内存缓存实现
// LRU 缓存实现
class LRUCache {
constructor(capacity) {
this.capacity = capacity;
this.cache = new Map();
}
get(key) {
if (this.cache.has(key)) {
// 移动到最前面
const value = this.cache.get(key);
this.cache.delete(key);
this.cache.set(key, value);
return value;
}
return null;
}
set(key, value) {
if (this.cache.has(key)) {
this.cache.delete(key);
} else if (this.cache.size >= this.capacity) {
// 删除最久未使用的
const firstKey = this.cache.keys().next().value;
this.cache.delete(firstKey);
}
this.cache.set(key, value);
}
clear() {
this.cache.clear();
}
}
// 使用缓存
const cache = new LRUCache(100);
async function cachedFetch(url) {
const cached = cache.get(url);
if (cached) {
return cached;
}
const response = await fetch(url);
const data = await response.json();
cache.set(url, data);
return data;
}
4️⃣ 性能优化策略
代码分割
// React 代码分割
import { lazy, Suspense } from 'react';
// 懒加载组件
const LazyComponent = lazy(() => import('./LazyComponent'));
function App() {
return (
<div>
<Suspense fallback={<div>Loading...</div>}>
<LazyComponent />
</Suspense>
</div>
);
}
// 路由级别的代码分割
import { BrowserRouter, Routes, Route } from 'react-router-dom';
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const Contact = lazy(() => import('./pages/Contact'));
function App() {
return (
<BrowserRouter>
<Suspense fallback={<div>Loading page...</div>}>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
<Route path="/contact" element={<Contact />} />
</Routes>
</Suspense>
</BrowserRouter>
);
}
// Webpack 代码分割
// 动态导入
async function loadModule() {
const module = await import(/* webpackChunkName: "my-chunk" */ './module');
return module.default;
}
// 分离第三方库
module.exports = {
optimization: {
splitChunks: {
chunks: 'all',
cacheGroups: {
vendor: {
test: /[\\/]node_modules[\\/]/,
name: 'vendors',
chunks: 'all'
}
}
}
}
};
图片优化
// 图片懒加载
class LazyImageLoader {
constructor() {
this.imageObserver = new IntersectionObserver(this.handleIntersection.bind(this));
this.init();
}
init() {
const lazyImages = document.querySelectorAll('img[data-src]');
lazyImages.forEach(img => this.imageObserver.observe(img));
}
handleIntersection(entries) {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
this.loadImage(img);
this.imageObserver.unobserve(img);
}
});
}
loadImage(img) {
const src = img.dataset.src;
const image = new Image();
image.onload = () => {
img.src = src;
img.classList.add('loaded');
};
image.onerror = () => {
img.src = '/placeholder.jpg';
img.classList.add('error');
};
image.src = src;
}
}
// 响应式图片
function ResponsiveImage({ src, alt, sizes }) {
const generateSrcSet = (src) => {
const sizes = [320, 640, 960, 1280];
return sizes.map(size =>
`${src}?w=${size} ${size}w`
).join(', ');
};
return (
<img
src={src}
srcSet={generateSrcSet(src)}
sizes={sizes || "(max-width: 768px) 100vw, 50vw"}
alt={alt}
loading="lazy"
/>
);
}
// WebP 支持检测
function supportsWebP() {
return new Promise(resolve => {
const webP = new Image();
webP.onload = webP.onerror = () => {
resolve(webP.height === 2);
};
webP.src = 'data:image/webp;base64,UklGRjoAAABXRUJQVlA4IC4AAACyAgCdASoCAAIALmk0mk0iIiIiIgBoSygABc6WWgAA/veff/0PP8bA//LwYAAA';
});
}
// 图片格式选择
async function getOptimalImageSrc(baseSrc) {
const isWebPSupported = await supportsWebP();
if (isWebPSupported) {
return baseSrc.replace(/\.(jpg|jpeg|png)$/, '.webp');
}
return baseSrc;
}
性能监控
// 性能指标收集
class PerformanceMonitor {
constructor() {
this.metrics = {};
this.init();
}
init() {
// 页面加载性能
window.addEventListener('load', () => {
this.collectLoadMetrics();
});
// 用户交互性能
this.observeUserInteractions();
// 资源加载性能
this.observeResourceLoading();
}
collectLoadMetrics() {
const navigation = performance.getEntriesByType('navigation')[0];
this.metrics.loadTime = {
dns: navigation.domainLookupEnd - navigation.domainLookupStart,
tcp: navigation.connectEnd - navigation.connectStart,
request: navigation.responseStart - navigation.requestStart,
response: navigation.responseEnd - navigation.responseStart,
dom: navigation.domContentLoadedEventEnd - navigation.navigationStart,
load: navigation.loadEventEnd - navigation.navigationStart
};
// Core Web Vitals
this.collectCoreWebVitals();
}
collectCoreWebVitals() {
// LCP (Largest Contentful Paint)
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
const lastEntry = entries[entries.length - 1];
this.metrics.lcp = lastEntry.startTime;
}).observe({ entryTypes: ['largest-contentful-paint'] });
// FID (First Input Delay)
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
this.metrics.fid = entry.processingStart - entry.startTime;
});
}).observe({ entryTypes: ['first-input'] });
// CLS (Cumulative Layout Shift)
let clsValue = 0;
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
if (!entry.hadRecentInput) {
clsValue += entry.value;
}
});
this.metrics.cls = clsValue;
}).observe({ entryTypes: ['layout-shift'] });
}
observeUserInteractions() {
['click', 'keydown', 'scroll'].forEach(eventType => {
document.addEventListener(eventType, (event) => {
const startTime = performance.now();
requestIdleCallback(() => {
const duration = performance.now() - startTime;
this.recordInteraction(eventType, duration);
});
});
});
}
observeResourceLoading() {
new PerformanceObserver((entryList) => {
const entries = entryList.getEntries();
entries.forEach(entry => {
if (entry.duration > 1000) { // 超过1秒的资源
console.warn('Slow resource:', entry.name, entry.duration);
}
});
}).observe({ entryTypes: ['resource'] });
}
recordInteraction(type, duration) {
if (!this.metrics.interactions) {
this.metrics.interactions = {};
}
if (!this.metrics.interactions[type]) {
this.metrics.interactions[type] = [];
}
this.metrics.interactions[type].push(duration);
}
getMetrics() {
return this.metrics;
}
sendMetrics() {
// 发送到分析服务
fetch('/api/analytics', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(this.metrics)
});
}
}
// 使用性能监控
const monitor = new PerformanceMonitor();
// 页面卸载时发送数据
window.addEventListener('beforeunload', () => {
monitor.sendMetrics();
});
七、面试高频算法与数据结构
1️⃣ 常见算法实现
排序算法
// 快速排序
function quickSort(arr) {
if (arr.length <= 1) return arr;
const pivot = arr[Math.floor(arr.length / 2)];
const left = arr.filter(x => x < pivot);
const middle = arr.filter(x => x === pivot);
const right = arr.filter(x => x > pivot);
return [...quickSort(left), ...middle, ...quickSort(right)];
}
// 归并排序
function mergeSort(arr) {
if (arr.length <= 1) return arr;
const mid = Math.floor(arr.length / 2);
const left = mergeSort(arr.slice(0, mid));
const right = mergeSort(arr.slice(mid));
return merge(left, right);
}
function merge(left, right) {
const result = [];
let i = 0, j = 0;
while (i < left.length && j < right.length) {
if (left[i] <= right[j]) {
result.push(left[i++]);
} else {
result.push(right[j++]);
}
}
return result.concat(left.slice(i)).concat(right.slice(j));
}
// 堆排序
function heapSort(arr) {
buildMaxHeap(arr);
for (let i = arr.length - 1; i > 0; i--) {
[arr[0], arr[i]] = [arr[i], arr[0]];
heapify(arr, 0, i);
}
return arr;
}
function buildMaxHeap(arr) {
const start = Math.floor(arr.length / 2) - 1;
for (let i = start; i >= 0; i--) {
heapify(arr, i, arr.length);
}
}
# frontend-extended