浏览器前端指南-2

🧠 第一章:事件机制与事件循环

1.1 事件流的三阶段

javascript 复制代码
// 事件流:捕获阶段 → 目标阶段 → 冒泡阶段

element.addEventListener('click', () => {
  console.log('冒泡阶段执行');
}, false); // 默认false,冒泡阶段

element.addEventListener('click', () => {
  console.log('捕获阶段执行');
}, true); // true,捕获阶段

执行顺序

css 复制代码
【捕获阶段】window → document → html → body → parent → target
【目标阶段】target上的监听器(按注册顺序)
【冒泡阶段】target → parent → body → html → document → window

1.2 事件代理(委托)

javascript 复制代码
// ❌ 不好:给每个li绑定事件
document.querySelectorAll('li').forEach(li => {
  li.addEventListener('click', () => console.log('点击'));
});

// ✅ 好:利用冒泡,一个事件搞定
document.querySelector('ul').addEventListener('click', (e) => {
  if (e.target.tagName === 'LI') {
    console.log('点击了', e.target.textContent);
  }
});

优势

  • 减少内存占用
  • 动态添加的元素也能响应

1.3 自定义事件

javascript 复制代码
// 创建自定义事件
const event = new CustomEvent('userLogin', {
  detail: { userId: 123, name: '张三' },
  bubbles: true,
  cancelable: true
});

// 触发事件
window.dispatchEvent(event);

// 监听
window.addEventListener('userLogin', (e) => {
  console.log('用户登录:', e.detail);
});

1.4 事件循环(Event Loop)的微观细节

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

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

Promise.resolve().then(() => {
  console.log('3'); // 微任务
  Promise.resolve().then(() => console.log('4')); // 微任务嵌套
});

queueMicrotask(() => console.log('5')); // 微任务

requestAnimationFrame(() => console.log('6')); // 渲染前执行

// 输出顺序:1, 3, 4, 5, 2, 6

任务优先级

javascript 复制代码
【微任务】Promise.then、MutationObserver、queueMicrotask
   ↓
【渲染】requestAnimationFrame、样式计算、布局、绘制
   ↓
【宏任务】setTimeout、setInterval、I/O、UI交互
   ↓
【微任务】...(下一轮循环)
  • 微任务在当前宏任务结束后、下一个宏任务前执行
  • requestAnimationFrame在渲染前执行
  • 事件循环的每一轮 = 1个宏任务 + 所有微任务 + 可能的渲染

🎨 第二章:现代Web API与性能

2.1 Intersection Observer(交叉观察器)

javascript 复制代码
// 监听元素是否进入视口
const observer = new IntersectionObserver((entries) => {
  entries.forEach(entry => {
    if (entry.isIntersecting) {
      console.log('元素进入视口', entry.target);
      
      // 懒加载图片
      const img = entry.target;
      img.src = img.dataset.src;
      
      // 停止观察(加载后就不需要了)
      observer.unobserve(img);
    }
  });
}, {
  threshold: 0.5, // 50%可见时触发
  rootMargin: '50px' // 提前50px触发
});

// 观察所有懒加载图片
document.querySelectorAll('img[data-src]').forEach(img => {
  observer.observe(img);
});

应用场景

  • 图片懒加载
  • 无限滚动
  • 曝光埋点
  • 广告可见性统计

2.2 Resize Observer(尺寸变化观察)

javascript 复制代码
// 监听元素尺寸变化
const observer = new ResizeObserver((entries) => {
  for (let entry of entries) {
    const { width, height } = entry.contentRect;
    console.log('尺寸变化:', width, height);
    
    // 自适应布局
    if (width < 600) {
      entry.target.classList.add('mobile');
    } else {
      entry.target.classList.remove('mobile');
    }
  }
});

observer.observe(document.querySelector('.container'));

2.3 Mutation Observer(DOM变化观察)

javascript 复制代码
// 监听DOM变化
const observer = new MutationObserver((mutations) => {
  mutations.forEach(mutation => {
    if (mutation.type === 'childList') {
      console.log('子节点变化:', mutation.addedNodes, mutation.removedNodes);
    }
    if (mutation.type === 'attributes') {
      console.log('属性变化:', mutation.attributeName);
    }
  });
});

observer.observe(document.body, {
  childList: true, // 子节点变化
  attributes: true, // 属性变化
  subtree: true, // 子树也观察
  characterData: true // 文本变化
});

2.4 Performance API

javascript 复制代码
// 性能监控
const perfData = {
  // 导航计时
  navigation: performance.getEntriesByType('navigation')[0],
  
  // 资源加载
  resources: performance.getEntriesByType('resource'),
  
  // 关键时间点
  timing: performance.timing,
  
  // 内存信息(Chrome only)
  memory: performance.memory
};

// 计算关键指标
const paint = performance.getEntriesByType('paint');
const fcp = paint.find(entry => entry.name === 'first-contentful-paint');
console.log('FCP:', fcp.startTime);

// 自定义打点
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');
performance.measure('fetch-time', 'start-fetch', 'end-fetch');

// 获取测量结果
const measures = performance.getEntriesByType('measure');

2.5 长任务监控

javascript 复制代码
// 监控长任务(>50ms的任务)
const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('长任务:', {
      duration: entry.duration,
      startTime: entry.startTime,
      name: entry.name
    });
    
    // 上报监控系统
    reportLongTask(entry);
  }
});

observer.observe({ entryTypes: ['longtask'] });

🌐 第三章:网络高级特性

3.1 预加载系列

html 复制代码
<!-- 预加载:当前页面必须的资源 -->
<link rel="preload" href="style.css" as="style">
<link rel="preload" href="font.woff2" as="font" crossorigin>

<!-- 预连接:提前建立连接 -->
<link rel="preconnect" href="https://api.example.com">

<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">

<!-- 预获取:下一页可能用到的资源 -->
<link rel="prefetch" href="next-page.js">

<!-- 预渲染:提前渲染下一页 -->
<link rel="prerender" href="https://example.com/next-page">

优先级

lua 复制代码
preload > preconnect > dns-prefetch > prefetch

3.2 HTTP/2 和 HTTP/3

javascript 复制代码
// HTTP/2 特性
// 1. 多路复用(一个连接并发多个请求)
// 2. 服务器推送(主动推送资源)
// 3. 头部压缩
// 4. 二进制分帧

// HTTP/3 特性
// 1. 基于QUIC(UDP)
// 2. 0-RTT 连接建立
// 3. 更好的丢包处理
// 4. 无队头阻塞

3.3 WebSocket

javascript 复制代码
// 建立连接
const ws = new WebSocket('wss://example.com/chat');

// 连接打开
ws.addEventListener('open', () => {
  ws.send('Hello Server!');
});

// 接收消息
ws.addEventListener('message', (event) => {
  console.log('收到:', event.data);
});

// 心跳检测(防止连接断开)
setInterval(() => {
  if (ws.readyState === WebSocket.OPEN) {
    ws.send('ping');
  }
}, 30000);

// 断线重连
ws.addEventListener('close', () => {
  setTimeout(() => {
    reconnect();
  }, 3000);
});

3.4 Server-Sent Events

javascript 复制代码
// 服务器推送事件(单向)
const eventSource = new EventSource('/api/events');

// 监听默认消息
eventSource.onmessage = (event) => {
  console.log('收到:', event.data);
};

// 监听自定义事件
eventSource.addEventListener('notification', (event) => {
  console.log('通知:', event.data);
});

// 错误处理
eventSource.onerror = () => {
  console.log('连接断开');
};

🚀 第四章:渲染性能进阶

4.1 关键渲染路径

html 复制代码
<!-- 优化关键渲染路径 -->
<head>
  <!-- 关键CSS内联 -->
  <style>
    .header { background: red; }
    .hero { height: 500px; }
  </style>
  
  <!-- 非关键CSS异步加载 -->
  <link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
  
  <!-- 关键JS内联 -->
  <script>
    // 首屏需要的JS
  </script>
  
  <!-- 非关键JS延迟加载 -->
  <script src="app.js" defer></script>
</head>

4.2 图层管理高级技巧

css 复制代码
/* 主动创建图层 */
.will-animate {
  will-change: transform, opacity;
  transform: translateZ(0); /* 老方案,不推荐 */
}

/* 避免层爆炸 */
.too-many-layers * {
  will-change: transform; /* 危险!每个元素都创建层 */
}

/* 合理使用contain */
.independent-component {
  contain: layout style paint; /* 告诉浏览器内部变化不影响外部 */
}

4.3 渲染阻塞诊断

javascript 复制代码
// 检测长任务
const observer = new PerformanceObserver((list) => {
  list.getEntries().forEach(entry => {
    if (entry.duration > 50) {
      console.log('渲染阻塞任务:', entry);
      
      // 分析调用栈
      if (entry.attribution) {
        console.log('原因:', entry.attribution[0]?.containerType);
      }
    }
  });
});

observer.observe({ entryTypes: ['longtask', 'frame'] });

📦 第五章:Web Workers 与多线程

5.1 Dedicated Worker

javascript 复制代码
// main.js
const worker = new Worker('worker.js');

worker.postMessage({ type: 'compute', data: largeArray });

worker.onmessage = (e) => {
  console.log('计算结果:', e.data);
};

worker.onerror = (e) => {
  console.error('Worker错误:', e.message);
};

// worker.js
self.onmessage = (e) => {
  const { type, data } = e.data;
  
  if (type === 'compute') {
    // 耗时计算
    const result = heavyComputation(data);
    
    // 返回结果
    self.postMessage(result);
  }
};

// 关闭worker
worker.terminate();

5.2 Shared Worker

javascript 复制代码
// 多个页面共享同一个worker
// main.js
const worker = new SharedWorker('shared-worker.js');

worker.port.start();
worker.port.postMessage('hello');

worker.port.onmessage = (e) => {
  console.log('收到:', e.data);
};

// shared-worker.js
const connections = [];

self.onconnect = (e) => {
  const port = e.ports[0];
  connections.push(port);
  
  port.onmessage = (e) => {
    // 广播给所有连接
    connections.forEach(conn => {
      conn.postMessage(e.data);
    });
  };
};

5.3 Service Worker(离线能力)

javascript 复制代码
// sw.js
const CACHE_NAME = 'v1';
const urlsToCache = ['/', '/style.css', '/app.js'];

// 安装时缓存资源
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME).then((cache) => {
      return cache.addAll(urlsToCache);
    })
  );
});

// 拦截请求
self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.match(event.request).then((response) => {
      // 有缓存返回缓存,否则网络请求
      return response || fetch(event.request).then((networkResponse) => {
        // 可选:缓存新资源
        if (event.request.url.includes('/api/')) {
          return networkResponse;
        }
        
        const responseClone = networkResponse.clone();
        caches.open(CACHE_NAME).then((cache) => {
          cache.put(event.request, responseClone);
        });
        
        return networkResponse;
      });
    })
  );
});

// 更新缓存
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then((keys) => {
      return Promise.all(
        keys.filter(key => key !== CACHE_NAME)
          .map(key => caches.delete(key))
      );
    })
  );
});

🔧 第六章:调试与诊断高级技巧

6.1 Performance 面板深度使用

javascript 复制代码
// 添加自定义标记
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');

// 测量
performance.measure('fetch-time', 'start-fetch', 'end-fetch');

// 在Performance面板可以看到

关键指标解读

  • FPS:帧率,低于30表示卡顿
  • CPU:占用率,红色表示满负荷
  • NET:网络请求,长条表示慢请求
  • HEAP:内存使用,锯齿状是正常GC

6.2 Memory 面板深度使用

javascript 复制代码
// 排查内存泄漏
// 1. 拍快照
// 2. 执行操作
// 3. 再拍快照对比

// 查找分离的DOM节点
// 在快照中搜索"Detached"

6.3 Layers 面板

javascript 复制代码
// 查看图层信息
// 1. 打开DevTools
// 2. 三个点 → More tools → Layers
// 3. 查看每个图层的大小、内存占用
// 4. 找出不必要的图层

6.4 Coverage 面板

javascript 复制代码
// 查看代码覆盖率
// 1. 打开Coverage面板
// 2. 点击录制
// 3. 操作页面
// 4. 红色表示未执行的代码

🚀 第七章:浏览器性能优化

7.1 性能指标

javascript 复制代码
// Google定义的三大核心指标
const coreWebVitals = {
  LCP: 'Largest Contentful Paint', // 最大内容绘制 - 加载性能
  FID: 'First Input Delay',        // 首次输入延迟 - 交互性能
  CLS: 'Cumulative Layout Shift'   // 累计布局偏移 - 视觉稳定性
};

// 理想阈值
const thresholds = {
  LCP: { good: 2500, poor: 4000 },      // 2.5秒内良好,4秒以上差
  FID: { good: 100, poor: 300 },        // 100ms内良好,300ms以上差
  CLS: { good: 0.1, poor: 0.25 }        // 0.1以内良好,0.25以上差
};

其他重要指标

javascript 复制代码
const otherMetrics = {
  // 传统指标
  FP: 'First Paint',                    // 首次绘制
  FCP: 'First Contentful Paint',        // 首次内容绘制
  TTI: 'Time to Interactive',           // 可交互时间
  TBT: 'Total Blocking Time',           // 总阻塞时间
  
  // 业务指标
  TTFB: 'Time to First Byte',            // 首字节时间
  DOMContentLoaded: 'DOMContentLoaded',  // DOM解析完成
  Load: 'Load',                          // 所有资源加载完成
  FPS: 'Frames Per Second'               // 帧率
};

7.2 性能优化模型

  1. RAIL 模型
javascript 复制代码
// Google提出的性能模型
const RAIL = {
  Response: '响应',      // 100ms内响应
  Animation: '动画',     // 10ms内生成一帧 (60fps)
  Idle: '空闲',          // 利用空闲时间处理任务
  Load: '加载'           // 1s内加载完成
};

// RAIL 目标
const railTargets = {
  response: 100,      // 用户操作100ms内要有反馈
  animation: 10,      // 每帧10ms内完成工作 (剩下6ms给浏览器)
  idle: 50,           // 空闲任务不超过50ms
  load: 1000          // 首屏1秒内加载
};
  1. 性能优化的三个层次
javascript 复制代码
const performanceLayers = {
  // 第一层:网络优化
  network: {
    goal: '减少请求次数,减小资源体积',
    techniques: ['缓存', '压缩', 'CDN', '预加载']
  },
  
  // 第二层:渲染优化
  rendering: {
    goal: '减少重排重绘,提高渲染效率',
    techniques: ['图层优化', '动画优化', '读写分离']
  },
  
  // 第三层:计算优化
  computing: {
    goal: '减少JS执行时间,避免阻塞主线程',
    techniques: ['Web Worker', '任务拆分', '懒计算']
  }
};

7.3 网络层优化

  1. 减少请求次数
javascript 复制代码
// ❌ 不好的做法:多个小文件
import Button from './Button';
import Input from './Input';
import Modal from './Modal';

// ✅ 好的做法:打包合并
import { Button, Input, Modal } from './components';

// webpack 配置
module.exports = {
  optimization: {
    splitChunks: {
      chunks: 'all',
      minSize: 20000,
      maxSize: 244000,
      cacheGroups: {
        vendors: {
          test: /[\\/]node_modules[\\/]/,
          priority: -10
        }
      }
    }
  }
};
  1. 减小资源体积
javascript 复制代码
// 1. 代码压缩
// webpack 自动压缩
module.exports = {
  mode: 'production',
  optimization: {
    minimize: true,
    minimizer: ['...', new TerserPlugin()]
  }
};

// 2. Tree Shaking
// 只引入使用的代码
import { debounce } from 'lodash-es'; // 用es版本支持tree shaking

// 3. 图片优化
// 使用 WebP 格式
<picture>
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" loading="lazy">
</picture>

// 4. Gzip/Brotli 压缩
// Nginx 配置
gzip on;
gzip_types text/plain text/css application/javascript;
gzip_comp_level 6;
brotli on;
brotli_types text/plain text/css application/javascript;
  1. 缓存策略
javascript 复制代码
// 1. 强缓存 (长期)
Cache-Control: public, max-age=31536000, immutable

// 2. 协商缓存 (验证)
Cache-Control: no-cache
ETag: "33a64df551"

// 3. 文件名哈希 (非覆盖式发布)
// 构建后: app.8d3f9e.js
// 下次发布: app.a1b2c3.js
  1. 预加载系列
html 复制代码
<!-- 预加载当前页关键资源 -->
<link rel="preload" href="critical.css" as="style">
<link rel="preload" href="font.woff2" as="font" crossorigin>

<!-- 预连接第三方域 -->
<link rel="preconnect" href="https://api.example.com">

<!-- DNS预解析 -->
<link rel="dns-prefetch" href="https://cdn.example.com">

<!-- 预获取下一页资源 -->
<link rel="prefetch" href="next-page.js">

<!-- 预渲染整个页面 -->
<link rel="prerender" href="https://example.com/next">
  1. 资源优先级
html 复制代码
<!-- 关键资源:高优先级 -->
<link rel="preload" href="style.css" as="style">

<!-- 非关键资源:低优先级 -->
<link rel="prefetch" href="analytics.js">

<!-- 图片懒加载 -->
<img src="image.jpg" loading="lazy" />

<!-- iframe懒加载 -->
<iframe src="video.html" loading="lazy"></iframe>

7.4 渲染层优化

  1. 关键渲染路径优化
html 复制代码
<!DOCTYPE html>
<html>
<head>
  <!-- 1. 关键CSS内联 -->
  <style>
    /* 首屏需要的样式 */
    .header { background: #333; }
    .hero { height: 500px; }
  </style>
  
  <!-- 2. 非关键CSS异步加载 -->
  <link rel="preload" href="non-critical.css" as="style" onload="this.rel='stylesheet'">
  
  <!-- 3. 关键JS内联 -->
  <script>
    // 首屏需要的JS
    window.initialData = { user: 'guest' };
  </script>
  
  <!-- 4. 非关键JS延迟加载 -->
  <script src="app.js" defer></script>
</head>
<body>
  <!-- 内容 -->
</body>
</html>
  1. 减少重排重绘
javascript 复制代码
// ❌ 不好的写法:多次触发重排
const el = document.getElementById('box');
el.style.width = '100px';      // 重排
el.style.height = '200px';     // 重排
el.style.margin = '10px';      // 重排
console.log(el.offsetWidth);   // 强制重排!

// ✅ 好的写法:批量操作
el.style.cssText = 'width:100px; height:200px; margin:10px;';

// 或者使用class
el.classList.add('box-style');

// ✅ 读写分离
const width = el.offsetWidth;  // 读
const height = el.offsetHeight; // 读

el.style.width = width + 100 + 'px'; // 批量写
el.style.height = height + 100 + 'px';

// ✅ 使用transform代替位置属性
// ❌ 不好
el.style.top = '100px';

// ✅ 好
el.style.transform = 'translateY(100px)';
  1. 图层优化
css 复制代码
/* 创建新图层的情况 */
.animate {
  transform: translateZ(0);  /* 老方法,不推荐 */
  will-change: transform;    /* 现代方法 */
}

/* 避免层爆炸 */
/* ❌ 危险 */
.too-many-layers * {
  will-change: transform;
}

/* ✅ 合理使用 */
.carousel {
  will-change: transform;  /* 轮播图需要动画 */
}

/* 使用contain隔离 */
.widget {
  contain: layout style paint;  /* 内部变化不影响外部 */
}
  1. 动画优化
css 复制代码
/* ✅ 合成器动画(推荐) */
@keyframes move {
  from { transform: translateX(0); }
  to { transform: translateX(100px); }
}

/* ❌ 触发布局的动画 */
@keyframes move-bad {
  from { left: 0; }
  to { left: 100px; }
}

/* 使用requestAnimationFrame */
function animate() {
  element.style.transform = `translateX(${pos}px)`;
  pos += 1;
  
  if (pos < 100) {
    requestAnimationFrame(animate);
  }
}

requestAnimationFrame(animate);

7.5 JavaScript 执行优化

  1. 长任务拆分
javascript 复制代码
// ❌ 不好的写法:长任务阻塞主线程
function processLargeArray(array) {
  array.forEach(item => {
    heavyComputation(item);  // 同步执行,可能卡顿
  });
}

// ✅ 好的写法:任务拆分
async function processLargeArray(array) {
  for (let i = 0; i < array.length; i++) {
    // 每次处理一小部分
    heavyComputation(array[i]);
    
    // 每10次让出主线程
    if (i % 10 === 0) {
      await new Promise(resolve => setTimeout(resolve, 0));
    }
  }
}

// ✅ 使用requestIdleCallback
function processWithIdle(array) {
  let i = 0;
  
  function processChunk(deadline) {
    while (i < array.length && deadline.timeRemaining() > 0) {
      heavyComputation(array[i]);
      i++;
    }
    
    if (i < array.length) {
      requestIdleCallback(processChunk);
    }
  }
  
  requestIdleCallback(processChunk);
}
  1. Web Worker 多线程
javascript 复制代码
// main.js
const worker = new Worker('worker.js');

worker.postMessage({ type: 'process', data: largeArray });

worker.onmessage = (e) => {
  console.log('处理结果:', e.data);
};

// worker.js
self.onmessage = (e) => {
  if (e.data.type === 'process') {
    // 耗时计算,不阻塞主线程
    const result = heavyComputation(e.data.data);
    self.postMessage(result);
  }
};

// 线程池实现
class WorkerPool {
  constructor(workerScript, size = navigator.hardwareConcurrency) {
    this.workers = [];
    this.queue = [];
    this.active = new Map();
    
    for (let i = 0; i < size; i++) {
      const worker = new Worker(workerScript);
      worker.onmessage = (e) => this.handleResult(worker, e);
      this.workers.push(worker);
    }
  }
  
  runTask(data) {
    return new Promise((resolve, reject) => {
      const task = { data, resolve, reject };
      
      const idleWorker = this.workers.find(w => !this.active.has(w));
      
      if (idleWorker) {
        this.executeTask(idleWorker, task);
      } else {
        this.queue.push(task);
      }
    });
  }
  
  executeTask(worker, task) {
    this.active.set(worker, task);
    worker.postMessage(task.data);
  }
  
  handleResult(worker, e) {
    const task = this.active.get(worker);
    this.active.delete(worker);
    
    task.resolve(e.data);
    
    if (this.queue.length > 0) {
      this.executeTask(worker, this.queue.shift());
    }
  }
}
  1. 虚拟列表
javascript 复制代码
// 只渲染可视区域的列表项
class VirtualList {
  constructor(container, items, itemHeight) {
    this.container = container;
    this.items = items;
    this.itemHeight = itemHeight;
    this.visibleCount = Math.ceil(container.clientHeight / itemHeight);
    this.startIndex = 0;
    
    container.style.overflow = 'auto';
    container.style.position = 'relative';
    
    container.addEventListener('scroll', () => {
      this.startIndex = Math.floor(container.scrollTop / itemHeight);
      this.render();
    });
    
    this.render();
  }
  
  render() {
    const fragment = document.createDocumentFragment();
    const endIndex = Math.min(this.startIndex + this.visibleCount + 2, this.items.length);
    
    for (let i = this.startIndex; i < endIndex; i++) {
      const item = this.items[i];
      const div = document.createElement('div');
      div.style.position = 'absolute';
      div.style.top = i * this.itemHeight + 'px';
      div.style.height = this.itemHeight + 'px';
      div.textContent = item;
      fragment.appendChild(div);
    }
    
    this.container.innerHTML = '';
    this.container.appendChild(fragment);
  }
}

7.6 资源加载优化

  1. 代码分割
javascript 复制代码
// React 懒加载
const LazyComponent = React.lazy(() => import('./HeavyComponent'));

function App() {
  return (
    <Suspense fallback={<Loading />}>
      <LazyComponent />
    </Suspense>
  );
}

// Vue 异步组件
const AsyncComponent = () => ({
  component: import('./HeavyComponent.vue'),
  loading: LoadingComponent,
  error: ErrorComponent,
  delay: 200,
  timeout: 3000
});

// 路由懒加载
const routes = [
  {
    path: '/dashboard',
    component: () => import('./pages/Dashboard')
  }
];
  1. 图片优化
html 复制代码
<!-- 响应式图片 -->
<img 
  srcset="
    small.jpg 300w,
    medium.jpg 600w,
    large.jpg 900w
  "
  sizes="(max-width: 320px) 280px,
         (max-width: 640px) 560px,
         800px"
  src="fallback.jpg"
  loading="lazy"
  alt="optimized"
>

<!-- 现代格式 -->
<picture>
  <source srcset="image.avif" type="image/avif">
  <source srcset="image.webp" type="image/webp">
  <img src="image.jpg" loading="lazy">
</picture>

<!-- 渐进式图片 -->
<img src="thumb.jpg" data-src="full.jpg" class="lazy">
  1. 字体优化
css 复制代码
/* 字体预加载 */
<link rel="preload" href="font.woff2" as="font" type="font/woff2" crossorigin>

/* font-display 控制 */
@font-face {
  font-family: 'CustomFont';
  src: url('font.woff2') format('woff2');
  font-display: swap; /* 先用系统字体,加载完再换 */
}

/* 字体子集化 */
@font-face {
  font-family: 'CustomFont';
  src: url('font-latin.woff2') format('woff2');
  unicode-range: U+00-FF; /* 只加载拉丁字符 */
}

7.7 性能监控与诊断

  1. 性能监控 SDK
javascript 复制代码
class PerformanceMonitor {
  constructor(options = {}) {
    this.metrics = {};
    this.initObservers();
  }
  
  initObservers() {
    // LCP
    new PerformanceObserver((list) => {
      const entries = list.getEntries();
      this.metrics.lcp = entries[entries.length - 1].startTime;
    }).observe({ entryTypes: ['largest-contentful-paint'] });
    
    // FID
    new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        this.metrics.fid = entry.processingStart - entry.startTime;
      });
    }).observe({ entryTypes: ['first-input'] });
    
    // CLS
    let cls = 0;
    new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (!entry.hadRecentInput) {
          cls += entry.value;
          this.metrics.cls = cls;
        }
      });
    }).observe({ entryTypes: ['layout-shift'] });
    
    // 资源
    new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.duration > 300) {
          this.reportSlowResource(entry);
        }
      });
    }).observe({ entryTypes: ['resource'] });
    
    // 长任务
    new PerformanceObserver((list) => {
      list.getEntries().forEach(entry => {
        if (entry.duration > 50) {
          this.reportLongTask(entry);
        }
      });
    }).observe({ entryTypes: ['longtask'] });
  }
  
  reportToAnalytics(data) {
    if (navigator.sendBeacon) {
      navigator.sendBeacon('/api/perf', JSON.stringify(data));
    } else {
      fetch('/api/perf', {
        method: 'POST',
        body: JSON.stringify(data),
        keepalive: true
      });
    }
  }
}

// 使用
const monitor = new PerformanceMonitor();
  1. 性能问题诊断
javascript 复制代码
// 1. 使用 Performance 面板
// 记录 → 操作 → 分析

// 2. 使用 Lighthouse
// Chrome DevTools → Lighthouse → 生成报告

// 3. 使用 Web Vitals 库
import { getLCP, getFID, getCLS } from 'web-vitals';

getLCP(console.log);
getFID(console.log);
getCLS(console.log);

// 4. 自定义性能标记
performance.mark('start-fetch');
await fetch('/api/data');
performance.mark('end-fetch');
performance.measure('fetch-time', 'start-fetch', 'end-fetch');
javascript 复制代码
// 优化前的问题
const problems = [
  '首屏图片太多',
  'JS打包过大',
  '交互卡顿',
  'API请求慢'
];

// 优化方案
const solutions = {
  // 1. 图片优化
  images: () => {
    // 使用WebP
    // 懒加载
    // 尺寸适配
  },
  
  // 2. 代码分割
  codeSplit: () => {
    // 路由懒加载
    // 组件动态导入
    // 第三方包拆包
  },
  
  // 3. 渲染优化
  rendering: () => {
    // 虚拟列表
    // 防抖节流
    // Web Worker
  },
  
  // 4. 缓存策略
  cache: () => {
    // Service Worker
    // API缓存
    // 预加载下一页
  }
};

// 优化效果
const result = {
  LCP: '2.8s → 1.2s',
  FID: '120ms → 45ms',
  CLS: '0.35 → 0.08',
  TTI: '4.5s → 2.1s'
};

7.8 性能优化的原则

优化的四个维度

javascript 复制代码
const optimizationPrinciples = {
  // 1. 减少
  reduce: {
    network: '减少请求数和体积',
    render: '减少重排重绘',
    compute: '减少JS执行时间'
  },
  
  // 2. 复用
  reuse: {
    cache: '充分利用缓存',
    connection: '复用TCP连接',
    data: '复用计算结果'
  },
  
  // 3. 延迟
  defer: {
    load: '非关键资源延迟加载',
    compute: '计算延迟到空闲时',
    render: '虚拟列表延迟渲染'
  },
  
  // 4. 提前
  preload: {
    dns: '预解析DNS',
    connect: '预连接',
    fetch: '预获取资源',
    render: '预渲染'
  }
};

优化的优先级

javascript 复制代码
const priority = {
  // 第一优先级:核心体验
  critical: [
    'LCP优化',  // 用户看到内容
    'FID优化',  // 用户可以交互
    'CLS优化'   // 不晃动
  ],
  
  // 第二优先级:流畅度
  smoothness: [
    '滚动流畅',
    '动画60fps',
    '无卡顿'
  ],
  
  // 第三优先级:加载速度
  speed: [
    '页面完全加载',
    '资源缓存',
    '预加载'
  ],
  
  // 第四优先级:资源消耗
  resource: [
    '内存占用',
    'CPU使用',
    '电量消耗'
  ]
};

性能优化的黄金法则

javascript 复制代码
const goldenRules = {
  // 1. 用户为中心
  rule1: '优先优化用户感知的指标 (LCP/FID/CLS)',
  
  // 2. 测量先行
  rule2: '不测量就无法优化',
  
  // 3. 基于原理
  rule3: '理解浏览器原理才能精准优化',
  
  // 4. 持续改进
  rule4: '性能优化是持续的过程,不是一次性任务',
  
  // 5. 平衡取舍
  rule5: '在加载速度、流畅度、资源消耗间找到平衡'
};

🎯 第八章:重点解析

Q1:requestAnimationFrame 和 requestIdleCallback 的区别?

javascript 复制代码
// requestAnimationFrame:渲染前执行
requestAnimationFrame(() => {
  console.log('下一帧渲染前执行');
});

// requestIdleCallback:浏览器空闲时执行
requestIdleCallback((deadline) => {
  while (deadline.timeRemaining() > 0) {
    // 执行任务
  }
}, { timeout: 2000 });

区别

  • rAF:必须执行,优先级高
  • rIC:有空才执行,优先级低

Q2:什么是 Layout Thrashing(布局抖动)?

javascript 复制代码
// 坏代码:反复触发强制布局
for (let i = 0; i < 1000; i++) {
  box.style.left = i + 'px'; // 写
  console.log(box.offsetLeft); // 读 → 强制布局!
}

// 好代码:读写分离
for (let i = 0; i < 1000; i++) {
  box.style.left = i + 'px'; // 批量写
}
const positions = []; // 统一读
for (let i = 0; i < 1000; i++) {
  positions.push(box.offsetLeft);
}

Q3:什么是 FOUC(无样式内容闪烁)?

html 复制代码
<!-- 原因:CSS加载延迟 -->
<head>
  <link rel="stylesheet" href="slow.css">
</head>
<body>
  <h1>先显示默认样式,CSS加载后突然变化</h1>
</body>

解决方案

  • 内联关键CSS
  • 使用<link rel="preload">
  • CSS放头部,JS放底部

Q4:什么是 FP、FCP、LCP、FID、CLS?

javascript 复制代码
// 核心Web指标
const metrics = {
  FP: 'First Paint', // 第一个像素绘制
  FCP: 'First Contentful Paint', // 第一个内容绘制
  LCP: 'Largest Contentful Paint', // 最大内容绘制(加载性能)
  FID: 'First Input Delay', // 首次输入延迟(交互性能)
  CLS: 'Cumulative Layout Shift' // 累计布局偏移(视觉稳定性)
};

// 获取指标
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log(entry.name, entry.startTime);
  }
}).observe({ entryTypes: ['paint', 'layout-shift', 'first-input'] });

Q5:浏览器是如何处理多个标签页的?

javascript 复制代码
// 进程模型
// - 每个标签页通常独立进程(Chrome)
// - 但同源页面可能共享进程(优化)
// - 浏览器的"站点隔离"策略

// 标签页间通信方式
// 1. localStorage事件
window.addEventListener('storage', (e) => {});

// 2. BroadcastChannel
const channel = new BroadcastChannel('tab-channel');
channel.postMessage('hello');

// 3. SharedWorker
// 4. postMessage + iframe
// 5. IndexedDB
相关推荐
wuhen_n1 小时前
AST转换:静态提升与补丁标志
前端·javascript·vue.js
cxxcode1 小时前
从 V8 引擎视角理解微任务与宏任务
前端
destinying2 小时前
性能优化之实战指南:让你的 Vue 应⽤跑得飞起
前端·javascript·vue.js
徐小夕3 小时前
JitWord Office预览引擎:如何用Vue3+Node.js打造丝滑的PDF/Excel/PPT嵌入方案
前端·vue.js·github
晴殇i3 小时前
揭秘JavaScript中那些“不冒泡”的DOM事件
前端·javascript·面试
孟陬3 小时前
国外技术周刊 #1:Paul Graham 重新分享最受欢迎的文章《创作者的品味》、本周被划线最多 YouTube《如何在 19 分钟内学会 AI》、为何我不
java·前端·后端
BER_c3 小时前
前端权限校验最佳实践:一个健壮的柯里化工具函数
前端·javascript
兆子龙4 小时前
别再用 useState / data 管 Tabs 的 activeKey 了:和 URL 绑定才香
前端·架构
sudo_jin4 小时前
前端包管理器演进史:为什么 npm 之后,Yarn 和 pnpm 成了新宠?
前端·npm