前端面试题库 - 浏览器与网络篇

一、浏览器渲染机制

1. 浏览器的渲染流程

完整渲染流程:

复制代码
1. 解析HTML → 构建DOM树
2. 解析CSS → 构建CSSOM树
3. DOM + CSSOM → 构建渲染树(Render Tree)
4. 布局(Layout/Reflow) → 计算元素位置和大小
5. 绘制(Paint) → 绘制像素
6. 合成(Composite) → 合成图层到屏幕

详细过程:

html 复制代码
<!DOCTYPE html>
<html>
<head>
  <style>
    .box { width: 100px; height: 100px; background: red; }
  </style>
</head>
<body>
  <div class="box"></div>
</body>
</html>

渲染步骤解析:

  1. HTML解析 → DOM树

    • 字节 → 字符 → 令牌 → 节点 → DOM树
    • 遇到<script>会阻塞解析(除非有async/defer)
    • 遇到<img>等资源,异步加载
  2. CSS解析 → CSSOM树

    • CSS会阻塞渲染,但不会阻塞DOM解析
    • CSS加载完成前不会构建渲染树
  3. 构建渲染树

    • 遍历DOM树,匹配CSSOM规则
    • 跳过不可见元素(display: none
    • visibility: hidden的元素会保留在渲染树中
  4. 布局(Layout)

    • 计算元素的几何信息(位置、大小)
    • 第一次布局称为"首次布局"
    • 后续布局称为"重排(Reflow)"
  5. 绘制(Paint)

    • 将渲染树转换为屏幕上的像素
    • 分多个图层绘制
  6. 合成(Composite)

    • 将多个图层合成最终图像

2. 回流(Reflow)与重绘(Repaint)

回流(Reflow): 元素的几何属性变化,需要重新计算布局

触发回流的操作:

javascript 复制代码
// 1. 添加/删除可见DOM元素
document.body.appendChild(newElement);

// 2. 元素位置、尺寸改变
element.style.width = '200px';
element.style.height = '200px';
element.style.margin = '10px';

// 3. 内容改变
element.textContent = '新内容';

// 4. 浏览器窗口尺寸改变
window.addEventListener('resize', handler);

// 5. 读取某些属性(强制同步布局)
const height = element.offsetHeight;
const width = element.clientWidth;
const rect = element.getBoundingClientRect();

重绘(Repaint): 元素样式改变但不影响布局

javascript 复制代码
// 只触发重绘的操作
element.style.color = 'red';
element.style.backgroundColor = 'blue';
element.style.visibility = 'hidden';
element.style.outline = '1px solid red';

性能影响: 回流 > 重绘 > 合成

优化策略:

javascript 复制代码
// ❌ 多次操作触发多次回流
element.style.width = '100px';
element.style.height = '100px';
element.style.margin = '10px';

// ✅ 使用class一次性修改
element.className = 'new-styles';

// ✅ 使用cssText
element.style.cssText = 'width: 100px; height: 100px; margin: 10px;';

// ❌ 读写分离问题
element.style.width = '100px';
const height = element.offsetHeight; // 强制回流
element.style.height = '100px';

// ✅ 批量读取,批量写入
const height = element.offsetHeight;
const width = element.offsetWidth;
element.style.width = '100px';
element.style.height = '100px';

// ✅ 使用文档片段
const fragment = document.createDocumentFragment();
for (let i = 0; i < 1000; i++) {
  const li = document.createElement('li');
  li.textContent = `Item ${i}`;
  fragment.appendChild(li);
}
ul.appendChild(fragment); // 只触发一次回流

// ✅ 使用transform代替left/top
// ❌ 触发回流
element.style.left = '100px';

// ✅ 只触发合成
element.style.transform = 'translateX(100px)';

// ✅ 使用requestAnimationFrame
function animate() {
  element.style.transform = `translateX(${pos}px)`;
  requestAnimationFrame(animate);
}

3. 浏览器的事件循环机制

事件循环组成:

复制代码
调用栈(Call Stack)
  ↓
Web APIs (setTimeout, DOM事件等)
  ↓
任务队列 {
  宏任务队列: setTimeout, setInterval, I/O, UI渲染
  微任务队列: Promise, MutationObserver, queueMicrotask
}

执行顺序:

javascript 复制代码
console.log('1. 同步代码');

setTimeout(() => {
  console.log('2. 宏任务 - setTimeout');
}, 0);

Promise.resolve().then(() => {
  console.log('3. 微任务 - Promise');
});

console.log('4. 同步代码');

// 输出顺序:
// 1. 同步代码
// 4. 同步代码
// 3. 微任务 - Promise
// 2. 宏任务 - setTimeout

详细执行流程:

javascript 复制代码
console.log('Start');

setTimeout(() => {
  console.log('setTimeout 1');
  Promise.resolve().then(() => {
    console.log('Promise in setTimeout');
  });
}, 0);

Promise.resolve()
  .then(() => {
    console.log('Promise 1');
    setTimeout(() => {
      console.log('setTimeout in Promise');
    }, 0);
  })
  .then(() => {
    console.log('Promise 2');
  });

console.log('End');

// 输出顺序:
// Start
// End
// Promise 1
// Promise 2
// setTimeout 1
// Promise in setTimeout
// setTimeout in Promise

执行规则:

  1. 执行同步代码
  2. 执行所有微任务
  3. 执行一个宏任务
  4. 执行所有微任务
  5. 重复3-4

常见宏任务与微任务:

宏任务 微任务
setTimeout Promise.then/catch/finally
setInterval MutationObserver
setImmediate (Node) queueMicrotask
I/O process.nextTick (Node)
UI渲染

4. 浏览器缓存机制

缓存类型:

  1. 强缓存
javascript 复制代码
// 响应头设置
Cache-Control: max-age=3600  // 1小时内使用缓存
Expires: Wed, 21 Oct 2026 07:28:00 GMT  // 过期时间(HTTP/1.0)

// 优先级:Cache-Control > Expires

Cache-Control常用指令:

  • max-age=<seconds>:缓存最大有效时间
  • no-cache:使用缓存前必须验证
  • no-store:完全不缓存
  • public:可被任何缓存存储
  • private:只能被浏览器缓存
  1. 协商缓存
javascript 复制代码
// 基于Last-Modified
// 1. 首次请求
Response Headers:
  Last-Modified: Wed, 21 Oct 2025 07:28:00 GMT

// 2. 再次请求
Request Headers:
  If-Modified-Since: Wed, 21 Oct 2025 07:28:00 GMT
  
Response:
  304 Not Modified (使用缓存)
  或
  200 OK (返回新资源)

// 基于ETag(更精确)
// 1. 首次请求
Response Headers:
  ETag: "33a64df551425fcc55e4d42a148795d9f25f89d4"

// 2. 再次请求
Request Headers:
  If-None-Match: "33a64df551425fcc55e4d42a148795d9f25f89d4"
  
Response:
  304 Not Modified 或 200 OK

缓存策略实践:

javascript 复制代码
// 1. HTML文件 - no-cache,每次验证
Cache-Control: no-cache

// 2. CSS/JS带hash - 长期缓存
// app.a1b2c3.js
Cache-Control: max-age=31536000, immutable

// 3. 图片资源 - 适中缓存
Cache-Control: max-age=86400

// 4. API数据 - 不缓存
Cache-Control: no-store

浏览器缓存位置(优先级从高到低):

  1. Service Worker Cache
  2. Memory Cache(内存缓存)
  3. Disk Cache(磁盘缓存)
  4. Push Cache(HTTP/2推送缓存)

二、HTTP与HTTPS

5. HTTP各版本的区别

HTTP/1.0:

  • 无连接:每次请求都要建立新连接
  • 无状态:服务器不保存客户端信息

HTTP/1.1:

javascript 复制代码
// 持久连接
Connection: keep-alive

// 管道化(发送多个请求,按顺序接收响应)
// 请求1 → 请求2 → 请求3 → 响应1 → 响应2 → 响应3

// 新增请求方法
PUT, DELETE, PATCH, OPTIONS

// Host头必需
Host: www.example.com

// 缓存控制增强
Cache-Control: max-age=3600

// 分块传输编码
Transfer-Encoding: chunked

HTTP/2:

javascript 复制代码
// 1. 二进制分帧
// 将消息分割为更小的帧,提高传输效率

// 2. 多路复用
// 单一连接上并行交错多个请求和响应
// 解决了HTTP/1.1的队头阻塞问题

// 3. 头部压缩(HPACK)
// 减少重复头部传输

// 4. 服务器推送
// 服务器主动推送资源
Link: </style.css>; rel=preload; as=style

// 5. 请求优先级
// 客户端可以指定资源优先级

HTTP/3 (基于QUIC):

  • 基于UDP而非TCP
  • 解决TCP队头阻塞
  • 更快的连接建立(0-RTT)
  • 连接迁移(网络切换不断连)

6. HTTPS工作原理

HTTPS = HTTP + SSL/TLS

加密过程:

复制代码
1. 客户端发起HTTPS请求
   ↓
2. 服务器返回证书(包含公钥)
   ↓
3. 客户端验证证书有效性
   ↓
4. 客户端生成随机密钥,用服务器公钥加密
   ↓
5. 服务器用私钥解密获得密钥
   ↓
6. 双方使用该密钥进行对称加密通信

证书验证流程:

javascript 复制代码
// 1. 检查证书是否过期
Valid From: 2025-01-01
Valid To: 2026-01-01

// 2. 检查域名是否匹配
Subject: CN=www.example.com

// 3. 检查证书颁发机构是否可信
Issuer: CN=DigiCert

// 4. 检查证书是否被吊销
// 通过CRL或OCSP检查

混合加密:

  • 非对称加密:交换密钥(RSA、ECC)
  • 对称加密:实际数据传输(AES)

TLS握手过程:

复制代码
客户端                                服务器
  |                                     |
  |-------- ClientHello --------------->|
  |  (支持的加密套件、随机数)              |
  |                                     |
  |<------- ServerHello ----------------|
  |  (选择的加密套件、随机数、证书)         |
  |                                     |
  |------ ClientKeyExchange ----------->|
  |  (预主密钥,用服务器公钥加密)          |
  |                                     |
  |------ ChangeCipherSpec ------------>|
  |------ Finished -------------------->|
  |                                     |
  |<----- ChangeCipherSpec -------------|
  |<----- Finished ---------------------|
  |                                     |
  |======== 加密通信开始 ================|

7. 跨域问题与解决方案

同源策略: 协议 + 域名 + 端口 完全相同

javascript 复制代码
// 同源示例
http://www.example.com/page1.html
http://www.example.com/page2.html  // ✅ 同源

// 不同源示例
http://www.example.com
https://www.example.com  // ❌ 协议不同
http://api.example.com   // ❌ 域名不同
http://www.example.com:8080  // ❌ 端口不同

解决方案:

1. CORS(跨域资源共享)

javascript 复制代码
// 服务器端设置
// 简单请求
Access-Control-Allow-Origin: *  // 或指定域名
Access-Control-Allow-Credentials: true

// 预检请求(非简单请求)
// OPTIONS请求
Access-Control-Allow-Methods: GET, POST, PUT, DELETE
Access-Control-Allow-Headers: Content-Type, Authorization
Access-Control-Max-Age: 86400  // 预检结果缓存时间

// Node.js示例
app.use((req, res, next) => {
  res.header('Access-Control-Allow-Origin', 'http://localhost:3000');
  res.header('Access-Control-Allow-Methods', 'GET,POST,PUT,DELETE');
  res.header('Access-Control-Allow-Headers', 'Content-Type');
  res.header('Access-Control-Allow-Credentials', 'true');
  
  if (req.method === 'OPTIONS') {
    return res.sendStatus(200);
  }
  next();
});

2. JSONP(仅支持GET)

javascript 复制代码
// 客户端
function handleResponse(data) {
  console.log(data);
}

const script = document.createElement('script');
script.src = 'http://api.example.com/data?callback=handleResponse';
document.body.appendChild(script);

// 服务器端返回
handleResponse({ name: 'Alice', age: 18 })

3. 代理服务器

javascript 复制代码
// 开发环境 - Webpack DevServer
module.exports = {
  devServer: {
    proxy: {
      '/api': {
        target: 'http://api.example.com',
        changeOrigin: true,
        pathRewrite: { '^/api': '' }
      }
    }
  }
};

// 生产环境 - Nginx
location /api {
    proxy_pass http://api.example.com;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
}

4. PostMessage(跨窗口通信)

javascript 复制代码
// 父窗口
const iframe = document.getElementById('myIframe');
iframe.contentWindow.postMessage('Hello', 'http://child.com');

window.addEventListener('message', (event) => {
  if (event.origin !== 'http://child.com') return;
  console.log(event.data);
});

// 子窗口
window.addEventListener('message', (event) => {
  if (event.origin !== 'http://parent.com') return;
  console.log(event.data);
  event.source.postMessage('Hi', event.origin);
});

5. WebSocket

javascript 复制代码
// WebSocket不受同源策略限制
const ws = new WebSocket('ws://api.example.com');

ws.onopen = () => {
  ws.send('Hello Server');
};

ws.onmessage = (event) => {
  console.log(event.data);
};

三、浏览器存储

8. Cookie、LocalStorage、SessionStorage对比

特性 Cookie LocalStorage SessionStorage
容量 4KB 5-10MB 5-10MB
生命周期 可设置过期时间 永久(除非手动清除) 页面关闭时清除
作用域 同源且路径匹配 同源 同源且同窗口
网络传输 每次HTTP请求都携带 不参与 不参与
操作API 复杂 简单 简单

Cookie操作:

javascript 复制代码
// 1. 设置Cookie
document.cookie = 'username=Alice; max-age=3600; path=/; secure; samesite=strict';

// Cookie属性
// expires: 过期时间(UTC格式)
// max-age: 有效期(秒)
// domain: 域名
// path: 路径
// secure: 仅HTTPS传输
// httpOnly: 禁止JavaScript访问(仅服务器端设置)
// samesite: 防止CSRF攻击
//   - strict: 完全禁止第三方Cookie
//   - lax: GET请求可以携带
//   - none: 无限制(需配合secure使用)

// 2. 读取Cookie
function getCookie(name) {
  const cookies = document.cookie.split('; ');
  for (const cookie of cookies) {
    const [key, value] = cookie.split('=');
    if (key === name) {
      return decodeURIComponent(value);
    }
  }
  return null;
}

// 3. 删除Cookie
document.cookie = 'username=; max-age=0';

// 4. 封装Cookie工具类
class CookieUtil {
  static set(name, value, options = {}) {
    let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
    
    if (options.maxAge) cookie += `; max-age=${options.maxAge}`;
    if (options.path) cookie += `; path=${options.path}`;
    if (options.domain) cookie += `; domain=${options.domain}`;
    if (options.secure) cookie += '; secure';
    if (options.sameSite) cookie += `; samesite=${options.sameSite}`;
    
    document.cookie = cookie;
  }
  
  static get(name) {
    const cookies = document.cookie.split('; ');
    for (const cookie of cookies) {
      const [key, value] = cookie.split('=');
      if (decodeURIComponent(key) === name) {
        return decodeURIComponent(value);
      }
    }
    return null;
  }
  
  static remove(name, options = {}) {
    this.set(name, '', { ...options, maxAge: 0 });
  }
}

LocalStorage操作:

javascript 复制代码
// 1. 存储
localStorage.setItem('user', JSON.stringify({ name: 'Alice', age: 18 }));

// 2. 读取
const user = JSON.parse(localStorage.getItem('user'));

// 3. 删除
localStorage.removeItem('user');

// 4. 清空
localStorage.clear();

// 5. 遍历
for (let i = 0; i < localStorage.length; i++) {
  const key = localStorage.key(i);
  console.log(key, localStorage.getItem(key));
}

// 6. 封装LocalStorage工具类
class StorageUtil {
  static set(key, value, expire = null) {
    const data = {
      value,
      expire: expire ? Date.now() + expire : null
    };
    localStorage.setItem(key, JSON.stringify(data));
  }
  
  static get(key) {
    const item = localStorage.getItem(key);
    if (!item) return null;
    
    const data = JSON.parse(item);
    
    // 检查是否过期
    if (data.expire && Date.now() > data.expire) {
      localStorage.removeItem(key);
      return null;
    }
    
    return data.value;
  }
  
  static remove(key) {
    localStorage.removeItem(key);
  }
  
  static clear() {
    localStorage.clear();
  }
}

// 使用示例
StorageUtil.set('token', 'abc123', 3600000); // 1小时后过期

SessionStorage操作:

javascript 复制代码
// API与LocalStorage相同
sessionStorage.setItem('temp', 'data');
sessionStorage.getItem('temp');
sessionStorage.removeItem('temp');
sessionStorage.clear();

Storage事件监听:

javascript 复制代码
// 监听其他标签页的storage变化
window.addEventListener('storage', (event) => {
  console.log('Key:', event.key);
  console.log('Old Value:', event.oldValue);
  console.log('New Value:', event.newValue);
  console.log('URL:', event.url);
});

9. IndexedDB

特点:

  • 大容量存储(通常大于250MB)
  • 支持事务
  • 异步操作
  • 可以存储复杂数据类型
javascript 复制代码
// 1. 打开数据库
const request = indexedDB.open('MyDatabase', 1);

request.onerror = () => console.error('数据库打开失败');
request.onsuccess = (event) => {
  const db = event.target.result;
  console.log('数据库打开成功');
};

// 2. 创建对象存储空间(表)
request.onupgradeneeded = (event) => {
  const db = event.target.result;
  
  if (!db.objectStoreNames.contains('users')) {
    const objectStore = db.createObjectStore('users', { keyPath: 'id', autoIncrement: true });
    objectStore.createIndex('name', 'name', { unique: false });
    objectStore.createIndex('email', 'email', { unique: true });
  }
};

// 3. 添加数据
function addUser(db, user) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  const request = objectStore.add(user);
  
  request.onsuccess = () => console.log('用户添加成功');
  request.onerror = () => console.error('用户添加失败');
}

// 4. 读取数据
function getUser(db, id) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  const request = objectStore.get(id);
  
  request.onsuccess = (event) => {
    console.log('User:', event.target.result);
  };
}

// 5. 更新数据
function updateUser(db, user) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  const request = objectStore.put(user);
  
  request.onsuccess = () => console.log('用户更新成功');
}

// 6. 删除数据
function deleteUser(db, id) {
  const transaction = db.transaction(['users'], 'readwrite');
  const objectStore = transaction.objectStore('users');
  const request = objectStore.delete(id);
  
  request.onsuccess = () => console.log('用户删除成功');
}

// 7. 使用索引查询
function getUserByEmail(db, email) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  const index = objectStore.index('email');
  const request = index.get(email);
  
  request.onsuccess = (event) => {
    console.log('User:', event.target.result);
  };
}

// 8. 游标遍历
function getAllUsers(db) {
  const transaction = db.transaction(['users'], 'readonly');
  const objectStore = transaction.objectStore('users');
  const request = objectStore.openCursor();
  
  request.onsuccess = (event) => {
    const cursor = event.target.result;
    if (cursor) {
      console.log('User:', cursor.value);
      cursor.continue();
    }
  };
}
相关推荐
Csvn7 小时前
小程序开发:微信小程序与 uni-app 实战指南
前端
摸鱼小李上线了8 小时前
vue项目页面添加水印实现方法
前端·javascript·vue.js
pengyi8710158 小时前
共享 IP 防封维护策略,降低被封率、延长 IP 寿命
网络·网络协议·tcp/ip
砍材农夫8 小时前
物联网 基于netty构建mqtt协议规范(主题通配符订阅)
java·前端·javascript·物联网·netty
彩票管理中心秘书长8 小时前
智能体状态指示:何时思考、何时调用工具、何时出错
前端·后端·程序员
彩票管理中心秘书长8 小时前
React + TypeScript拆解一整套“AI 变现代码流程”
前端·后端·程序员
广州华水科技8 小时前
单北斗GNSS变形监测在基础设施安全中的应用与维护
前端
一叶遮惊鸿8 小时前
从零构建 AI 驱动的日志监控自愈系统
面试
码途漫谈8 小时前
把前端组件做成一座小岛:Animal-Island-UI 的自然风 React 组件库拆解
前端·开源