性能优化不应是事后的补救措施,而应贯穿于前端开发的整个生命周期。本文将从四个核心层面------代码、构建、渲染、网络------构建一套完整的性能优化体系。
一、核心性能指标(Core Web Vitals)
Largest Contentful Paint (LCP)
衡量页面主要内容加载完成的时间,理想值应小于2.5秒。优化方向包括压缩图片、延迟加载非关键资源、使用CDN加速静态资源。
First Input Delay (FID)
评估用户首次交互(如点击按钮)到浏览器响应的延迟时间,目标值需低于100毫秒。优化策略包括拆分长任务、减少主线程阻塞、使用Web Worker。
Cumulative Layout Shift (CLS)
量化页面布局稳定性,得分应低于0.1。避免动态插入内容导致偏移,为媒体元素预设尺寸,使用CSS transform动画替代影响布局的属性。
二、资源加载指标
Time to First Byte (TTFB)
服务器响应首字节的时间,建议控制在200ms内。可通过优化后端查询、启用缓存、升级服务器硬件实现。
Total Blocking Time (TBT)
主线程被长任务阻塞的总时间,影响交互响应。需拆分JavaScript任务、移除未使用的代码、优化事件监听器。
三、渲染效率指标
Speed Index
反映页面内容可视化完成速度,数值越低越好。优化关键渲染路径、内联关键CSS、预加载关键资源可改善此指标。
Time to Interactive (TTI)
从导航开始到页面完全可交互的时间。减少第三方脚本、按需加载Polyfill、使用PRPL模式能有效提升TTI。
四、性能检测


五、代码层优化:从源头提升效率
5.1 算法与数据结构的选择
核心原则:选择适合场景的算法和数据结构。在JavaScript中,简单的算法选择差异可能导致性能的巨大差距。
代码示例对比:寻找数组中的重复项
javascript
// 不好的做法:嵌套循环 O(n²) 时间复杂度
// 当数组长度为1000时,需要执行近50万次比较
function findDuplicates(arr) {
const duplicates = [];
for (let i = 0; i < arr.length; i++) {
for (let j = i + 1; j < arr.length; j++) {
if (arr[i] === arr[j]) {
duplicates.push(arr[i]);
}
}
}
return duplicates;
}
// 好的做法:使用Set数据结构 O(n) 时间复杂度
// 利用Set的哈希表特性,只需遍历一次数组
function findDuplicatesOptimized(arr) {
const seen = new Set(); // 存储已见过的元素
const duplicates = new Set(); // 使用Set自动去重
for (const item of arr) {
if (seen.has(item)) {
duplicates.add(item); // 如果已经见过,就是重复项
} else {
seen.add(item); // 第一次见到,加入已见集合
}
}
return Array.from(duplicates); // 将Set转换回数组
}
5.2 避免不必要的计算和操作
防抖与节流:处理高频事件的利器
在用户交互中,scroll、resize、input等事件可能以极高的频率触发。如果不加限制,会导致大量不必要的函数调用和计算。
javascript
// 防抖 (Debounce):事件触发后等待一段时间再执行
// 适用于搜索框输入、窗口调整大小等场景
function debounce(func, wait) {
let timeout; // 存储定时器ID
return function executedFunction(...args) {
const later = () => {
clearTimeout(timeout); // 清除之前的定时器
func(...args); // 执行目标函数
};
clearTimeout(timeout); // 每次调用都清除之前的定时器
timeout = setTimeout(later, wait); // 设置新的定时器
};
}
// 节流 (Throttle):确保函数在一定时间内只执行一次
// 适用于滚动事件、鼠标移动等连续触发场景
function throttle(func, limit) {
let inThrottle = false; // 节流状态标志
return function(...args) {
if (!inThrottle) { // 如果不在节流状态
func.apply(this, args); // 执行函数
inThrottle = true; // 进入节流状态
// 一段时间后解除节流
setTimeout(() => inThrottle = false, limit);
}
};
}
// 实际应用示例
window.addEventListener('resize', debounce(handleResize, 250));
window.addEventListener('scroll', throttle(handleScroll, 100));
计算缓存(Memoization):避免重复计算
对于耗时的纯函数,可以通过缓存计算结果来提升性能。
javascript
// Memoization 高阶函数
const memoize = (fn) => {
const cache = new Map(); // 使用Map存储缓存结果
return (...args) => {
const key = JSON.stringify(args); // 将参数序列化为缓存键
if (cache.has(key)) {
console.log('从缓存读取结果');
return cache.get(key); // 缓存命中,直接返回
}
console.log('执行计算并缓存结果');
const result = fn(...args); // 执行实际计算
cache.set(key, result); // 缓存结果
return result;
};
};
// 示例:优化斐波那契数列计算
// 原始递归实现时间复杂度为 O(2^n),使用缓存后降为 O(n)
const fibonacci = memoize((n) => {
if (n <= 1) return n;
return fibonacci(n - 1) + fibonacci(n - 2);
});
console.log(fibonacci(10)); // 第一次计算,执行55次函数调用
console.log(fibonacci(10)); // 第二次直接从缓存读取
5.3 减少重绘与重排
关键理解:
- 重排(Reflow):改变元素的几何属性(尺寸、位置),需要重新计算布局
- 重绘(Repaint):改变元素的外观(颜色、背景),不需要重新布局,但仍需绘制
浏览器渲染流程:JavaScript → 样式计算 → 布局 → 绘制 → 合成
优化策略对比:
javascript
// ❌ 不好的做法:多次修改样式,触发多次重排
// 每次修改style都会立即触发重排,性能极差
element.style.width = '100px'; // 触发重排
element.style.height = '200px'; // 触发重排
element.style.margin = '10px'; // 触发重排
// ✅ 好的做法1:使用classList批量修改
// 所有样式变化在一次重排中完成
element.classList.add('new-style');
// ✅ 好的做法2:使用cssText或直接修改className
element.style.cssText = 'width: 100px; height: 200px; margin: 10px;';
// ✅ 好的做法3:脱离文档流进行大量DOM操作
// 先隐藏元素,操作完成后再显示
element.style.display = 'none'; // 触发一次重排
// ... 进行大量DOM操作(此时不会触发重排)
element.style.display = 'block'; // 触发一次重排
// ✅ 好的做法4:使用文档片段批量插入
// DocumentFragment在内存中操作,不会触发DOM更新
const fragment = document.createDocumentFragment();
for (let i = 0; i < 100; i++) {
const item = document.createElement('div');
item.textContent = `Item ${i}`;
fragment.appendChild(item); // 内存操作,无重排
}
container.appendChild(fragment); // 一次插入,触发一次重排
5.4 事件委托与懒加载
事件委托:减少事件监听器数量
javascript
// ❌ 传统做法:为每个列表项绑定点击事件
// 如果有1000个列表项,就会创建1000个事件监听器
const listItems = document.querySelectorAll('#list li');
listItems.forEach(item => {
item.addEventListener('click', () => {
console.log('Clicked:', item.textContent);
});
});
// ✅ 事件委托:利用事件冒泡机制
// 只在父元素上绑定一个事件监听器
document.getElementById('list').addEventListener('click', function(e) {
// 检查点击的是否是列表项
if (e.target.tagName === 'LI') {
console.log('List item clicked:', e.target.textContent);
}
// 如果列表项内有其他元素,可以使用closest
const listItem = e.target.closest('li');
if (listItem) {
console.log('Clicked item:', listItem.dataset.id);
}
});
图片懒加载:按需加载非可视区域图片
javascript
// 原生实现:Intersection Observer API
// 更高效,不依赖scroll事件,性能更好
const lazyImages = document.querySelectorAll('img[data-src]');
// 创建观察器实例
const imageObserver = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting) { // 元素进入视口
const img = entry.target;
// 替换src属性,开始加载图片
img.src = img.dataset.src;
// 加载完成后移除data-src属性
img.addEventListener('load', () => {
img.removeAttribute('data-src');
});
// 停止观察该图片
imageObserver.unobserve(img);
}
});
}, {
rootMargin: '50px', // 提前50px开始加载
threshold: 0.1 // 10%可见时触发
});
// 开始观察所有懒加载图片
lazyImages.forEach(img => imageObserver.observe(img));
// HTML中使用示例
// <img data-src="large-image.jpg" src="placeholder.jpg" alt="描述">
六、构建层优化:高效的打包与交付
6.1 现代构建工具的选择与配置
不同的构建工具有不同的适用场景:
javascript
// Vite 配置示例 (vite.config.js)
// Vite 使用ES模块原生支持,开发环境极速启动
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
rollupOptions: {
output: {
// 代码分割策略
manualChunks: {
vendor: ['react', 'react-dom'], // 第三方库
utils: ['lodash', 'moment'], // 工具库
}
}
}
}
});
// Webpack 配置示例 (webpack.config.js)
// Webpack 功能全面,适合复杂项目
const path = require('path');
module.exports = {
entry: './src/index.js',
output: {
filename: '[name].[contenthash:8].bundle.js',
path: path.resolve(__dirname, 'dist'),
clean: true, // 构建前清理dist目录
},
// ... 其他配置
};
6.2 代码分割与动态导入
代码分割是减少初始加载包大小的关键技术:
javascript
// React 路由懒加载 + 错误边界 + 加载状态
import React, { Suspense, lazy } from 'react';
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
// 错误边界组件
class ErrorBoundary extends React.Component {
state = { hasError: false };
static getDerivedStateFromError(error) {
return { hasError: true };
}
render() {
if (this.state.hasError) {
return <h1>页面加载失败</h1>;
}
return this.props.children;
}
}
// 使用React.lazy进行动态导入
const Home = lazy(() => import('./pages/Home'));
const About = lazy(() => import('./pages/About'));
const UserProfile = lazy(() => import('./pages/UserProfile'));
// 预加载策略:鼠标悬停时预加载
const preloadComponent = (importFunc) => {
const component = lazy(() => importFunc());
component.preload = importFunc; // 添加预加载方法
return component;
};
function App() {
return (
<Router>
<ErrorBoundary>
<Suspense
fallback={
<div className="loading-spinner">
页面加载中...
</div>
}
>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
<Route path="/profile" component={UserProfile} />
</Switch>
</Suspense>
</ErrorBoundary>
</Router>
);
}
6.3 Tree Shaking 与依赖优化
确保你的项目正确配置以支持Tree Shaking:
javascript
// package.json 配置示例
{
"name": "my-project",
"sideEffects": [
"*.css", // 标记CSS文件有副作用
"*.scss",
"*.less",
"polyfill.js" // 标记polyfill文件
],
"module": "dist/esm/index.js", // ES模块入口
"main": "dist/cjs/index.js", // CommonJS入口
"exports": {
".": {
"import": "./dist/esm/index.js", // ES模块
"require": "./dist/cjs/index.js" // CommonJS
}
}
}
// 组件库按需引入对比
// ❌ 全部引入:打包所有组件,体积大
import * as Antd from 'antd';
import 'antd/dist/antd.css';
// ✅ 按需引入:只打包使用的组件
import { Button, Input, Form } from 'antd';
import Button from 'antd/es/button'; // 直接导入ES模块
import 'antd/es/button/style/css'; // 按需引入样式
// 使用babel-plugin-import自动转换(.babelrc)
{
"plugins": [
["import", {
"libraryName": "antd",
"libraryDirectory": "es",
"style": "css" // 自动引入样式
}]
]
}
6.4 资源压缩与优化配置
完整的Webpack优化配置示例:
javascript
// webpack.prod.config.js
const path = require('path');
const CssMinimizerPlugin = require('css-minimizer-webpack-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
module.exports = {
mode: 'production',
// 优化配置
optimization: {
minimize: true, // 开启压缩
minimizer: [
// JavaScript压缩
new TerserPlugin({
parallel: true, // 开启多进程并行压缩
terserOptions: {
compress: {
drop_console: process.env.NODE_ENV === 'production', // 生产环境移除console
drop_debugger: true, // 移除debugger
pure_funcs: ['console.log'], // 移除console.log
},
mangle: true, // 混淆变量名
output: {
comments: false, // 移除注释
},
},
extractComments: false, // 不提取注释到单独文件
}),
// CSS压缩
new CssMinimizerPlugin({
minimizerOptions: {
preset: [
'default',
{
discardComments: { removeAll: true }, // 移除所有注释
},
],
},
}),
],
// 代码分割配置
splitChunks: {
chunks: 'all', // 优化所有类型的chunks
minSize: 20000, // 最小大小20KB
maxSize: 244000, // 最大大小244KB,尝试分割更大的chunk
minChunks: 1, // 至少被引用一次
maxAsyncRequests: 30, // 最大异步请求数
maxInitialRequests: 30, // 最大初始请求数
automaticNameDelimiter: '~', // 名称分隔符
cacheGroups: {
// 第三方库
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10, // 优先级
name: 'vendors',
reuseExistingChunk: true,
},
// 公共模块
commons: {
name: 'commons',
minChunks: 2, // 至少被2个chunk引用
priority: -20,
reuseExistingChunk: true,
},
// 按需加载的公共组件
asyncCommons: {
name: 'async-commons',
minChunks: 2,
chunks: 'async', // 只处理异步chunk
priority: -30,
reuseExistingChunk: true,
},
},
},
// 运行时chunk
runtimeChunk: {
name: 'runtime',
},
},
// 输出配置
output: {
filename: '[name].[contenthash:8].js', // 使用内容哈希
chunkFilename: '[name].[contenthash:8].chunk.js',
path: path.resolve(__dirname, 'dist'),
publicPath: '/', // CDN路径可以在这里配置
},
// 插件配置
plugins: [
// 提取CSS到单独文件
new MiniCssExtractPlugin({
filename: '[name].[contenthash:8].css',
chunkFilename: '[name].[contenthash:8].chunk.css',
}),
// Gzip压缩
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 10240, // 只处理大于10KB的文件
minRatio: 0.8, // 压缩比小于0.8才处理
}),
// Brotli压缩(更高效的压缩算法)
new CompressionPlugin({
filename: '[path][base].br',
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
compressionOptions: { level: 11 }, // 压缩级别1-11
threshold: 10240,
minRatio: 0.8,
}),
// 打包分析(开发时使用)
process.env.ANALYZE && new BundleAnalyzerPlugin({
analyzerMode: 'static',
reportFilename: 'bundle-report.html',
openAnalyzer: false,
}),
].filter(Boolean), // 过滤掉false的插件
// 模块配置
module: {
rules: [
{
test: /\.(png|jpe?g|gif|webp|avif)$/i,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024, // 8KB以下的图片转base64
},
},
generator: {
filename: 'images/[name].[hash:8][ext]',
},
},
],
},
// 解析配置
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'), // 路径别名
},
extensions: ['.js', '.jsx', '.ts', '.tsx'], // 自动解析的扩展名
},
};
图片优化策略实践
html
<!-- 使用现代图片格式和响应式图片 -->
<!-- picture元素提供多种格式支持,浏览器选择最优格式 -->
<picture>
<!-- 优先使用AVIF格式(压缩率最高) -->
<source
srcset="image.avif"
type="image/avif"
media="(min-width: 1200px)"
>
<!-- 回退到WebP格式(兼容性更好) -->
<source
srcset="image.webp"
type="image/webp"
media="(min-width: 768px)"
>
<!-- 最终回退到JPEG -->
<img
src="image.jpg"
alt="描述性文字"
loading="lazy" <!-- 原生懒加载 -->
decoding="async" <!-- 异步解码 -->
width="800" <!-- 指定尺寸帮助布局计算 -->
height="600"
>
</picture>
<!-- 响应式图片:根据屏幕尺寸加载不同大小的图片 -->
<img
src="image-small.jpg"
srcset="
image-small.jpg 480w,
image-medium.jpg 768w,
image-large.jpg 1200w
"
sizes="
(max-width: 480px) 100vw,
(max-width: 768px) 80vw,
1200px
"
alt="响应式图片示例"
>
七、渲染层优化:流畅的视觉体验
7.1 关键渲染路径优化实践
关键渲染路径(Critical Rendering Path)是从接收HTML、CSS、JavaScript到渲染像素的过程。优化CRP可以显著提升首屏加载速度。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 1. 关键CSS内联或优先加载 -->
<!-- 首屏关键样式内联,避免阻塞渲染 -->
<style>
/* 首屏必要的最小样式 */
.header, .hero, .navigation { display: block; }
body { margin: 0; font-family: sans-serif; }
</style>
<!-- 2. 预加载关键资源 -->
<!-- 字体预加载 -->
<link
rel="preload"
href="font.woff2"
as="font"
type="font/woff2"
crossorigin
>
<!-- 首屏关键图片预加载 -->
<link
rel="preload"
href="hero-image.webp"
as="image"
imagesrcset="hero-image-small.webp 480w, hero-image-large.webp 1200w"
imagesizes="100vw"
>
<!-- 3. 异步加载非关键CSS -->
<link
rel="stylesheet"
href="non-critical.css"
media="print"
onload="this.media='all'"
>
<!-- 4. DNS预连接 -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://cdn.example.com">
<title>性能优化示例</title>
</head>
<body>
<!-- 首屏内容 -->
<header class="header">...</header>
<main class="main-content">
<!-- 首屏可见内容优先 -->
<section class="hero">...</section>
</main>
<!-- 非首屏内容延迟加载 -->
<section class="secondary-content" data-lazy>
<!-- 内容会在进入视口时加载 -->
</section>
<!-- 5. 脚本优化 -->
<!-- 关键脚本内联或使用preload -->
<script>
// 关键功能脚本内联
function initCriticalFeatures() {
// 初始化首屏必要功能
}
document.addEventListener('DOMContentLoaded', initCriticalFeatures);
</script>
<!-- 非关键脚本使用defer -->
<script src="analytics.js" defer></script>
<!-- 第三方脚本使用async -->
<script
src="https://third-party.com/widget.js"
async
crossorigin="anonymous"
></script>
<!-- 延迟加载的脚本 -->
<script>
// 使用requestIdleCallback延迟非关键任务
if ('requestIdleCallback' in window) {
requestIdleCallback(() => {
const script = document.createElement('script');
script.src = 'non-critical.js';
document.body.appendChild(script);
});
}
</script>
</body>
</html>
7.2 CSS性能优化详解
CSS选择器的性能虽然在现代浏览器中影响较小,但在复杂页面中仍值得优化。
css
/* ❌ 避免的选择器模式 */
/* 1. 过于具体的嵌套选择器 */
.container .wrapper .content .list .item .title span {
color: red;
/* 浏览器从右向左匹配:先找到所有span,再向上匹配 */
}
/* 2. 通配符过度使用 */
div * {
margin: 0;
/* 匹配所有div下的所有元素 */
}
/* 3. 属性选择器模糊匹配 */
a[href*="example"] {
color: blue;
/* 需要检查所有a标签的href属性 */
}
/* ✅ 推荐的优化实践 */
/* 1. 使用类选择器,减少嵌套 */
.item-title {
color: red;
font-size: 1.2em;
}
/* 2. 使用BEM命名规范,避免嵌套 */
.block__element--modifier {
/* 独立样式,无需依赖父元素 */
}
/* 3. 减少不必要的层叠 */
.btn-primary {
/* 直接定义,不依赖.btn */
background: #007bff;
color: white;
}
/* ✅ GPU加速动画优化 */
.animate-element {
/* 触发GPU硬件加速的属性 */
transform: translateZ(0);
will-change: transform; /* 提前告知浏览器可能的变化 */
/* 使用transform和opacity做动画(不触发重排) */
transition:
transform 0.3s cubic-bezier(0.4, 0, 0.2, 1),
opacity 0.3s ease;
}
.animate-element:hover {
transform: scale(1.05) translateY(-2px);
opacity: 0.9;
}
/* 避免使用left/top做动画(触发重排) */
@keyframes slide {
from { left: 0; } /* ❌ 触发重排 */
to { left: 100px; }
}
@keyframes slide-optimized {
from { transform: translateX(0); } /* ✅ GPU加速 */
to { transform: translateX(100px); }
}
/* ✅ 使用CSS Containment API优化 */
.widget {
/* 告诉浏览器这个元素的布局是独立的 */
contain: layout style paint;
/* 或更精细的控制 */
contain-intrinsic-size: 300px 200px; /* 预估尺寸 */
content-visibility: auto; /* 自动跳过不可见内容渲染 */
}
/* ✅ 减少重绘区域 */
.button {
position: relative;
}
.button::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.1);
opacity: 0;
transition: opacity 0.3s;
}
.button:hover::before {
opacity: 1;
/* 只重绘伪元素,不影响按钮文本 */
}
/* ✅ 字体加载优化 */
@font-face {
font-family: 'CustomFont';
src: url('font.woff2') format('woff2'),
url('font.woff') format('woff');
font-display: swap; /* 显示回退字体,字体加载后替换 */
font-weight: 400;
font-style: normal;
}
/* ✅ 使用CSS变量减少重复计算 */
:root {
--primary-color: #007bff;
--spacing-unit: 8px;
--border-radius: 4px;
}
.component {
color: var(--primary-color);
margin: calc(var(--spacing-unit) * 2);
border-radius: var(--border-radius);
}
7.3 JavaScript执行与动画优化
requestAnimationFrame优化动画
javascript
// ❌ 使用setTimeout/setInterval的动画可能卡顿
function animateWithTimeout() {
const element = document.getElementById('animated');
let position = 0;
function step() {
position += 5;
element.style.left = position + 'px'; // 触发重排
if (position < 500) {
setTimeout(step, 16); // 约60fps
}
}
step();
}
// ✅ 使用requestAnimationFrame优化动画
// 与浏览器刷新率同步,避免卡顿和过度绘制
class OptimizedAnimation {
constructor(element) {
this.element = element;
this.position = 0;
this.speed = 5;
this.running = false;
this.animationId = null;
}
start() {
if (this.running) return;
this.running = true;
this.animate();
}
stop() {
this.running = false;
if (this.animationId) {
cancelAnimationFrame(this.animationId);
this.animationId = null;
}
}
animate() {
// 检查是否应该继续动画
if (!this.running || this.position >= 500) {
this.stop();
return;
}
// 更新位置 - 使用transform避免重排
this.position += this.speed;
this.element.style.transform = `translateX(${this.position}px)`;
// 请求下一帧
this.animationId = requestAnimationFrame(() => this.animate());
}
// 获取当前FPS(性能监控)
getFPS() {
let lastTime = performance.now();
let frameCount = 0;
const checkFPS = (currentTime) => {
frameCount++;
if (currentTime - lastTime >= 1000) {
const fps = Math.round((frameCount * 1000) / (currentTime - lastTime));
console.log(`当前FPS: ${fps}`);
frameCount = 0;
lastTime = currentTime;
}
requestAnimationFrame(checkFPS);
};
requestAnimationFrame(checkFPS);
}
}
// 使用示例
const animatedElement = document.getElementById('animated');
const animation = new OptimizedAnimation(animatedElement);
animation.start();
// 监听页面可见性,页面隐藏时暂停动画
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
animation.stop();
} else {
animation.start();
}
});
Web Workers处理耗时任务
javascript
// main.js - 主线程
class TaskManager {
constructor() {
// 创建Web Worker
this.worker = new Worker('worker.js');
this.taskQueue = new Map(); // 任务队列
this.taskId = 0;
this.setupWorkerListeners();
}
setupWorkerListeners() {
// 处理Worker返回的结果
this.worker.onmessage = (event) => {
const { taskId, result, error } = event.data;
const task = this.taskQueue.get(taskId);
if (task) {
if (error) {
task.reject(new Error(error));
} else {
task.resolve(result);
}
this.taskQueue.delete(taskId);
}
};
// 处理Worker错误
this.worker.onerror = (error) => {
console.error('Worker error:', error);
// 清理所有待处理任务
for (const [taskId, task] of this.taskQueue) {
task.reject(error);
this.taskQueue.delete(taskId);
}
};
}
// 执行耗时任务
executeTask(data, transferable = []) {
return new Promise((resolve, reject) => {
const taskId = ++this.taskId;
// 存储任务引用
this.taskQueue.set(taskId, { resolve, reject });
// 发送任务到Worker
this.worker.postMessage(
{ taskId, data },
transferable.length > 0 ? transferable : undefined
);
});
}
// 批量处理大量数据
async processLargeDataset(dataset) {
const chunkSize = 1000;
const chunks = [];
// 分割数据
for (let i = 0; i < dataset.length; i += chunkSize) {
chunks.push(dataset.slice(i, i + chunkSize));
}
// 并行处理所有块
const results = await Promise.all(
chunks.map(chunk => this.executeTask(chunk))
);
// 合并结果
return results.flat();
}
// 清理Worker
terminate() {
this.worker.terminate();
this.taskQueue.clear();
}
}
// 使用示例
const taskManager = new TaskManager();
// 处理图像数据
async function processImage(imageData) {
try {
// 转换ImageData为Transferable
const { data } = imageData;
const result = await taskManager.executeTask(
{ type: 'image', data: data.buffer },
[data.buffer] // 转移所有权,避免复制
);
return new ImageData(
new Uint8ClampedArray(result.data),
result.width,
result.height
);
} catch (error) {
console.error('图像处理失败:', error);
throw error;
}
}
// worker.js - Worker线程
// 处理耗时计算,不阻塞主线程
self.onmessage = async function(event) {
const { taskId, data } = event.data;
try {
let result;
// 根据任务类型处理
if (data.type === 'image') {
result = await processImageData(data);
} else if (data.type === 'calculation') {
result = heavyCalculation(data);
} else {
throw new Error(`未知任务类型: ${data.type}`);
}
// 返回结果给主线程
self.postMessage({ taskId, result });
} catch (error) {
// 捕获错误并返回
self.postMessage({
taskId,
error: error.message
});
}
};
// Worker中的图像处理函数
async function processImageData(data) {
// 这里可以执行各种耗时操作
// 例如:图像滤镜、人脸识别、数据转换等
const buffer = data.data;
const array = new Uint8ClampedArray(buffer);
// 示例:简单的灰度处理
for (let i = 0; i < array.length; i += 4) {
const avg = (array[i] + array[i + 1] + array[i + 2]) / 3;
array[i] = avg; // R
array[i + 1] = avg; // G
array[i + 2] = avg; // B
// Alpha通道保持不变
}
return {
data: array.buffer,
width: data.width || 0,
height: data.height || 0
};
}
function heavyCalculation(data) {
// 模拟复杂计算
let result = 0;
for (let i = 0; i < 1000000; i++) {
result += Math.sin(i) * Math.cos(i);
}
return { result };
}
7.4 虚拟列表优化长列表渲染
虚拟列表是处理大量数据展示的关键技术,它只渲染可视区域内的元素。
javascript
/**
* 高性能虚拟列表组件
* 支持动态高度、滚动缓冲、异步加载
*/
class VirtualList {
constructor(container, config = {}) {
// 容器元素
this.container = container;
// 配置项
this.config = {
itemHeight: 50, // 预估项目高度
bufferCount: 5, // 缓冲区项目数
overscanCount: 3, // 前后预加载项目数
throttleDelay: 16, // 节流延迟
...config
};
// 状态
this.items = []; // 所有数据项
this.visibleItems = []; // 当前可见项目
this.startIndex = 0; // 起始索引
this.endIndex = 0; // 结束索引
this.totalHeight = 0; // 列表总高度
this.scrollTop = 0; // 当前滚动位置
this.isScrolling = false; // 是否正在滚动
// 渲染相关
this.contentEl = null; // 内容容器
this.placeholderEl = null; // 占位元素
// 性能监控
this.renderTimes = [];
this.lastRenderTime = 0;
this.init();
}
init() {
this.setupDOM();
this.setupEvents();
this.calculateMetrics();
}
setupDOM() {
// 创建内部结构
this.container.style.position = 'relative';
this.container.style.overflow = 'auto';
// 占位元素 - 撑开滚动区域
this.placeholderEl = document.createElement('div');
this.placeholderEl.style.position = 'absolute';
this.placeholderEl.style.top = '0';
this.placeholderEl.style.left = '0';
this.placeholderEl.style.width = '100%';
this.container.appendChild(this.placeholderEl);
// 内容容器 - 实际渲染内容
this.contentEl = document.createElement('div');
this.contentEl.style.position = 'absolute';
this.contentEl.style.top = '0';
this.contentEl.style.left = '0';
this.contentEl.style.width = '100%';
this.container.appendChild(this.contentEl);
}
setupEvents() {
// 使用节流处理滚动事件
this.handleScroll = this.throttle(() => {
this.onScroll();
}, this.config.throttleDelay);
this.container.addEventListener('scroll', this.handleScroll);
// 监听窗口大小变化
this.handleResize = this.throttle(() => {
this.calculateMetrics();
this.renderVisibleItems();
}, 100);
window.addEventListener('resize', this.handleResize);
// 使用Intersection Observer监听可见性
if ('IntersectionObserver' in window) {
this.visibilityObserver = new IntersectionObserver(
(entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
this.onContainerVisible();
} else {
this.onContainerHidden();
}
});
},
{ threshold: 0.1 }
);
this.visibilityObserver.observe(this.container);
}
}
// 设置数据
setItems(items) {
this.items = items;
this.calculateTotalHeight();
this.calculateVisibleRange();
this.renderVisibleItems();
}
// 计算总高度
calculateTotalHeight() {
if (Array.isArray(this.config.itemHeight)) {
// 动态高度
this.totalHeight = this.items.reduce((sum, item, index) => {
return sum + (this.config.itemHeight[index] || this.config.itemHeight.default || 50);
}, 0);
} else {
// 固定高度
this.totalHeight = this.items.length * this.config.itemHeight;
}
this.placeholderEl.style.height = `${this.totalHeight}px`;
}
// 计算可见范围
calculateVisibleRange() {
const { itemHeight, bufferCount } = this.config;
const containerHeight = this.container.clientHeight;
// 计算理论上应该渲染的项目数
const visibleCount = Math.ceil(containerHeight / itemHeight) + bufferCount * 2;
// 计算起始索引
let newStartIndex = Math.floor(this.scrollTop / itemHeight) - bufferCount;
newStartIndex = Math.max(0, newStartIndex);
// 计算结束索引
let newEndIndex = newStartIndex + visibleCount;
newEndIndex = Math.min(this.items.length - 1, newEndIndex);
// 添加前后预加载
newStartIndex = Math.max(0, newStartIndex - this.config.overscanCount);
newEndIndex = Math.min(this.items.length - 1, newEndIndex + this.config.overscanCount);
this.startIndex = newStartIndex;
this.endIndex = newEndIndex;
}
// 渲染可见项目
renderVisibleItems() {
const startTime = performance.now();
// 计算需要渲染的项目
const itemsToRender = this.items.slice(this.startIndex, this.endIndex + 1);
// 批量DOM操作
const fragment = document.createDocumentFragment();
itemsToRender.forEach((item, relativeIndex) => {
const absoluteIndex = this.startIndex + relativeIndex;
const itemElement = this.renderItem(item, absoluteIndex);
// 设置位置
let top = 0;
if (Array.isArray(this.config.itemHeight)) {
// 动态高度计算
for (let i = 0; i < absoluteIndex; i++) {
top += this.config.itemHeight[i] || this.config.itemHeight.default || 50;
}
} else {
// 固定高度计算
top = absoluteIndex * this.config.itemHeight;
}
itemElement.style.position = 'absolute';
itemElement.style.top = `${top}px`;
itemElement.style.width = '100%';
fragment.appendChild(itemElement);
});
// 清空并添加新元素
this.contentEl.innerHTML = '';
this.contentEl.appendChild(fragment);
// 更新可见项目列表
this.visibleItems = itemsToRender;
// 性能记录
const renderTime = performance.now() - startTime;
this.renderTimes.push(renderTime);
if (this.renderTimes.length > 60) {
this.renderTimes.shift();
}
// 记录最后渲染时间
this.lastRenderTime = Date.now();
// 如果渲染时间过长,发出警告
if (renderTime > 16) { // 超过一帧时间
console.warn(`虚拟列表渲染耗时: ${renderTime.toFixed(2)}ms`);
}
}
// 渲染单个项目(可被子类覆盖)
renderItem(item, index) {
const element = document.createElement('div');
element.className = 'virtual-list-item';
element.textContent = `Item ${index}: ${item}`;
element.dataset.index = index;
// 添加数据属性便于调试
element.dataset.originalIndex = index;
element.dataset.item = JSON.stringify(item);
return element;
}
// 滚动事件处理
onScroll() {
this.scrollTop = this.container.scrollTop;
this.isScrolling = true;
// 清除之前的定时器
if (this.scrollEndTimer) {
clearTimeout(this.scrollEndTimer);
}
// 计算新的可见范围
const oldStartIndex = this.startIndex;
const oldEndIndex = this.endIndex;
this.calculateVisibleRange();
// 如果可见范围变化超过阈值,重新渲染
const threshold = this.config.bufferCount;
if (
Math.abs(this.startIndex - oldStartIndex) > threshold ||
Math.abs(this.endIndex - oldEndIndex) > threshold
) {
this.renderVisibleItems();
}
// 设置滚动结束检测
this.scrollEndTimer = setTimeout(() => {
this.isScrolling = false;
this.onScrollEnd();
}, 150);
}
// 滚动结束
onScrollEnd() {
// 滚动结束后进行一次精确渲染
this.calculateVisibleRange();
this.renderVisibleItems();
// 触发滚动结束事件
this.triggerEvent('scrollend');
}
// 容器可见时
onContainerVisible() {
// 恢复渲染
this.isContainerVisible = true;
this.renderVisibleItems();
}
// 容器隐藏时
onContainerHidden() {
// 暂停渲染以节省性能
this.isContainerVisible = false;
this.contentEl.innerHTML = '';
}
// 节流函数
throttle(func, delay) {
let lastCall = 0;
let timeout = null;
return (...args) => {
const now = Date.now();
const remaining = delay - (now - lastCall);
if (remaining <= 0) {
lastCall = now;
func.apply(this, args);
} else if (!timeout) {
timeout = setTimeout(() => {
lastCall = Date.now();
timeout = null;
func.apply(this, args);
}, remaining);
}
};
}
// 事件触发器
triggerEvent(eventName, detail = {}) {
const event = new CustomEvent(eventName, {
detail: {
...detail,
instance: this
}
});
this.container.dispatchEvent(event);
}
// 获取性能指标
getPerformanceMetrics() {
if (this.renderTimes.length === 0) return null;
const sum = this.renderTimes.reduce((a, b) => a + b, 0);
const avg = sum / this.renderTimes.length;
return {
averageRenderTime: avg,
maxRenderTime: Math.max(...this.renderTimes),
minRenderTime: Math.min(...this.renderTimes),
renderCount: this.renderTimes.length,
lastRenderTime: this.lastRenderTime
};
}
// 清理资源
destroy() {
this.container.removeEventListener('scroll', this.handleScroll);
window.removeEventListener('resize', this.handleResize);
if (this.visibilityObserver) {
this.visibilityObserver.disconnect();
}
if (this.scrollEndTimer) {
clearTimeout(this.scrollEndTimer);
}
this.contentEl.innerHTML = '';
this.placeholderEl.remove();
this.contentEl.remove();
this.items = [];
this.visibleItems = [];
}
}
// 使用示例
class AdvancedVirtualList extends VirtualList {
constructor(container, config) {
super(container, config);
// 添加项高度缓存
this.itemHeightCache = new Map();
// 添加项渲染缓存
this.itemRenderCache = new Map();
// 添加图片懒加载
this.imageObserver = null;
this.setupImageLazyLoad();
}
setupImageLazyLoad() {
if ('IntersectionObserver' in window) {
this.imageObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const img = entry.target;
img.src = img.dataset.src;
img.classList.add('loaded');
this.imageObserver.unobserve(img);
}
});
}, {
root: this.container,
rootMargin: '50px',
threshold: 0.1
});
}
}
renderItem(item, index) {
// 检查缓存
if (this.itemRenderCache.has(index)) {
return this.itemRenderCache.get(index).cloneNode(true);
}
const element = document.createElement('div');
element.className = 'virtual-list-item';
element.dataset.index = index;
// 复杂项目渲染
element.innerHTML = `
<div class="item-content">
<div class="item-header">
<h3>${item.title}</h3>
<span class="item-date">${item.date}</span>
</div>
<div class="item-body">
<p>${item.description}</p>
${item.image ? `
<img
class="lazy-image"
data-src="${item.image}"
alt="${item.title}"
width="100"
height="100"
>
` : ''}
</div>
<div class="item-footer">
<span class="item-tags">${item.tags?.join(', ') || ''}</span>
</div>
</div>
`;
// 缓存渲染结果
this.itemRenderCache.set(index, element.cloneNode(true));
// 设置懒加载图片
const lazyImage = element.querySelector('.lazy-image');
if (lazyImage && this.imageObserver) {
this.imageObserver.observe(lazyImage);
}
return element;
}
// 测量并缓存项目高度
measureItemHeight(element, index) {
if (this.itemHeightCache.has(index)) {
return this.itemHeightCache.get(index);
}
// 临时添加到文档测量
const tempContainer = document.createElement('div');
tempContainer.style.position = 'absolute';
tempContainer.style.visibility = 'hidden';
tempContainer.appendChild(element.cloneNode(true));
document.body.appendChild(tempContainer);
const height = element.offsetHeight;
this.itemHeightCache.set(index, height);
document.body.removeChild(tempContainer);
return height;
}
destroy() {
super.destroy();
if (this.imageObserver) {
this.imageObserver.disconnect();
}
this.itemHeightCache.clear();
this.itemRenderCache.clear();
}
}
// 初始化虚拟列表
document.addEventListener('DOMContentLoaded', () => {
const container = document.getElementById('list-container');
// 生成测试数据
const mockData = Array.from({ length: 10000 }, (_, i) => ({
id: i,
title: `项目 ${i + 1}`,
description: `这是第 ${i + 1} 个项目的详细描述内容...`,
date: new Date().toLocaleDateString(),
tags: ['标签1', '标签2', '标签3'].slice(0, Math.random() * 3 + 1),
image: i % 5 === 0 ? `https://picsum.photos/100/100?random=${i}` : null
}));
// 创建虚拟列表实例
const virtualList = new AdvancedVirtualList(container, {
itemHeight: 100, // 预估高度
bufferCount: 10,
overscanCount: 5
});
// 设置数据
virtualList.setItems(mockData);
// 监听事件
container.addEventListener('scrollend', () => {
console.log('滚动结束');
});
// 获取性能数据
setInterval(() => {
const metrics = virtualList.getPerformanceMetrics();
if (metrics) {
console.log('渲染性能:', metrics);
}
}, 5000);
// 动态添加数据示例
setTimeout(() => {
const newData = Array.from({ length: 1000 }, (_, i) => ({
id: 10000 + i,
title: `新项目 ${i + 1}`,
description: '新添加的数据...',
date: new Date().toLocaleDateString(),
tags: ['新标签'],
image: null
}));
virtualList.setItems([...mockData, ...newData]);
}, 10000);
});
八、网络层优化:极速的内容传输
8.1 HTTP缓存策略详解与配置
缓存是提升网页加载速度最有效的手段之一。正确的缓存策略可以减少服务器负载,提升用户体验。
Nginx缓存配置示例
nginx
# nginx.conf - 完整的性能优化配置
http {
# 基础优化
sendfile on; # 使用sendfile系统调用
tcp_nopush on; # 在sendfile开启时优化数据包
tcp_nodelay on; # 禁用Nagle算法
keepalive_timeout 65; # 保持连接时间
types_hash_max_size 2048;
# MIME类型
include /etc/nginx/mime.types;
default_type application/octet-stream;
# 日志格式
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
# Gzip压缩配置
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 6; # 压缩级别1-9,6是平衡点
gzip_types
text/plain
text/css
text/xml
text/javascript
application/javascript
application/x-javascript
application/json
application/xml
application/xml+rss
image/svg+xml;
# Brotli压缩(需要安装ngx_brotli模块)
# brotli on;
# brotli_comp_level 6;
# brotli_types text/plain text/css application/javascript application/json image/svg+xml;
# 缓存路径配置
proxy_cache_path /var/cache/nginx levels=1:2 keys_zone=my_cache:10m max_size=10g
inactive=60m use_temp_path=off;
server {
listen 80;
server_name example.com;
# 静态资源 - 强缓存策略
location ~* \.(jpg|jpeg|png|gif|ico|webp|avif|svg|svgz)$ {
# 图片资源缓存1年
expires 1y;
add_header Cache-Control "public, immutable";
# 开启图片优化
image_filter resize 800 600; # 按需调整大小
image_filter_jpeg_quality 85; # JPEG质量
# 响应头优化
add_header X-Content-Type-Options "nosniff";
add_header X-Frame-Options "SAMEORIGIN";
# 访问日志(可选关闭以提升性能)
access_log off;
}
location ~* \.(css|js)$ {
# CSS和JS资源缓存1年,使用内容哈希
expires 1y;
add_header Cache-Control "public, immutable";
# 安全头
add_header X-Content-Type-Options "nosniff";
# 预压缩版本
gzip_static on; # 优先使用预压缩的.gz文件
}
location ~* \.(woff|woff2|ttf|eot)$ {
# 字体文件缓存1年
expires 1y;
add_header Cache-Control "public, immutable";
add_header Access-Control-Allow-Origin "*"; # 跨域字体
}
# HTML文件 - 协商缓存
location ~* \.html$ {
expires 1h; # 短期缓存
add_header Cache-Control "public, must-revalidate";
etag on; # 开启ETag
}
# API接口 - 精细缓存控制
location /api/ {
proxy_pass http://backend;
proxy_cache my_cache;
proxy_cache_key "$scheme$request_method$host$request_uri";
proxy_cache_valid 200 5m; # 成功响应缓存5分钟
proxy_cache_valid 404 1m; # 404响应缓存1分钟
proxy_cache_use_stale error timeout http_500 http_502 http_503 http_504;
# 缓存控制头
add_header X-Cache-Status $upstream_cache_status;
# 连接优化
proxy_connect_timeout 5s;
proxy_send_timeout 10s;
proxy_read_timeout 30s;
}
# 安全头配置
add_header Strict-Transport-Security "max-age=31536000; includeSubDomains" always;
add_header X-XSS-Protection "1; mode=block" always;
add_header X-Content-Type-Options "nosniff" always;
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
# 根目录配置
location / {
root /usr/share/nginx/html;
index index.html index.htm;
# SPA应用路由支持
try_files $uri $uri/ /index.html;
# 安全头
add_header Content-Security-Policy "default-src 'self'; script-src 'self' 'unsafe-inline' https://cdn.example.com; style-src 'self' 'unsafe-inline'; img-src 'self' data: https:; font-src 'self' https:; connect-src 'self' https://api.example.com;";
}
# 健康检查
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
}
CDN缓存策略配置
javascript
// Cloudflare Workers - 边缘计算缓存示例
addEventListener('fetch', event => {
event.respondWith(handleRequest(event.request));
});
async function handleRequest(request) {
const url = new URL(request.url);
// 只缓存GET请求
if (request.method !== 'GET') {
return fetch(request);
}
// 定义缓存策略
const cacheRules = {
'/api/data': { ttl: 300 }, // 5分钟
'/api/user': { ttl: 60 }, // 1分钟
'/static/': { ttl: 31536000 }, // 1年
'/images/': { ttl: 86400 }, // 1天
default: { ttl: 600 } // 10分钟
};
// 匹配缓存规则
let cacheTTL = cacheRules.default.ttl;
for (const [path, rule] of Object.entries(cacheRules)) {
if (url.pathname.startsWith(path)) {
cacheTTL = rule.ttl;
break;
}
}
// 构建缓存键
const cacheKey = new Request(url.toString(), request);
const cache = caches.default;
// 尝试从缓存读取
let response = await cache.match(cacheKey);
if (!response) {
// 缓存未命中,从源站获取
response = await fetch(request);
// 只缓存成功的响应
if (response.ok) {
// 克隆响应以便缓存
const responseToCache = response.clone();
// 设置缓存头
const headers = new Headers(responseToCache.headers);
headers.set('Cache-Control', `public, max-age=${cacheTTL}`);
headers.set('CDN-Cache-Control', `max-age=${cacheTTL}`);
// 添加自定义头
headers.set('X-Cache-Status', 'MISS');
headers.set('X-Edge-Location', 'Cloudflare');
// 存储到缓存
event.waitUntil(
cache.put(cacheKey, new Response(responseToCache.body, {
status: responseToCache.status,
statusText: responseToCache.statusText,
headers: headers
}))
);
}
} else {
// 缓存命中,更新缓存状态头
const headers = new Headers(response.headers);
headers.set('X-Cache-Status', 'HIT');
response = new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers: headers
});
}
// 添加Timing-Allow-Origin头支持性能监测
response.headers.set('Timing-Allow-Origin', '*');
return response;
}
// 缓存预热脚本
async function warmCache(urls) {
const cache = caches.default;
for (const url of urls) {
try {
const request = new Request(url);
const response = await fetch(request);
if (response.ok) {
await cache.put(request, response.clone());
console.log(`缓存预热成功: ${url}`);
}
} catch (error) {
console.error(`缓存预热失败: ${url}`, error);
}
}
}
// 要预热的URL列表
const criticalURLs = [
'https://example.com/',
'https://example.com/static/main.css',
'https://example.com/static/main.js',
'https://example.com/api/initial-data'
];
// 定期执行缓存预热
setInterval(() => warmCache(criticalURLs), 5 * 60 * 1000); // 每5分钟
8.2 资源预加载与预连接策略
现代浏览器提供了多种资源提示(Resource Hints)来优化资源加载。
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 1. DNS预解析 -->
<!-- 提前解析第三方域名的DNS -->
<link rel="dns-prefetch" href="https://fonts.googleapis.com">
<link rel="dns-prefetch" href="https://cdn.example.com">
<link rel="dns-prefetch" href="https://analytics.example.com">
<!-- 2. 预连接 -->
<!-- 提前建立TCP连接和TLS握手 -->
<link
rel="preconnect"
href="https://fonts.googleapis.com"
crossorigin
>
<link
rel="preconnect"
href="https://fonts.gstatic.com"
crossorigin
>
<link
rel="preconnect"
href="https://api.example.com"
>
<!-- 3. 预加载关键资源 -->
<!-- 首屏渲染必须的资源 -->
<link
rel="preload"
href="/static/fonts/primary.woff2"
as="font"
type="font/woff2"
crossorigin
>
<link
rel="preload"
href="/static/css/critical.css"
as="style"
>
<link
rel="preload"
href="/static/js/main.js"
as="script"
>
<!-- 关键图片预加载 -->
<link
rel="preload"
href="/static/images/hero.webp"
as="image"
imagesrcset="
/static/images/hero-480.webp 480w,
/static/images/hero-768.webp 768w,
/static/images/hero-1200.webp 1200w
"
imagesizes="100vw"
>
<!-- 4. 预获取 -->
<!-- 提前加载下一页可能需要的资源 -->
<link
rel="prefetch"
href="/about"
as="document"
>
<link
rel="prefetch"
href="/static/js/about-page.js"
as="script"
>
<link
rel="prefetch"
href="/static/css/about.css"
as="style"
>
<!-- 5. 预渲染 -->
<!-- 在后台预先渲染整个页面(谨慎使用) -->
<link
rel="prerender"
href="/next-page"
>
<!-- 6. 模块预加载 -->
<!-- ES模块的预加载 -->
<link
rel="modulepreload"
href="/static/js/vendor.js"
>
<link
rel="modulepreload"
href="/static/js/app.js"
>
<!-- 7. 内联关键CSS -->
<style>
/* 首屏关键样式 */
:root {
--primary-color: #007bff;
--font-primary: 'PrimaryFont', sans-serif;
}
/* 字体加载期间的回退样式 */
@font-face {
font-family: 'PrimaryFont';
src: local('Arial');
font-display: swap;
}
/* 首屏布局 */
.critical-content {
opacity: 1;
transition: opacity 0.3s;
}
/* 避免布局抖动 */
.hero-image-container {
position: relative;
padding-bottom: 56.25%; /* 16:9宽高比 */
overflow: hidden;
}
.hero-image {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
<!-- 8. 异步加载非关键CSS -->
<link
rel="stylesheet"
href="/static/css/non-critical.css"
media="print"
onload="this.media='all'; this.onload=null;"
>
<noscript>
<link rel="stylesheet" href="/static/css/non-critical.css">
</noscript>
<title>资源预加载优化示例</title>
</head>
<body>
<!-- 使用JavaScript动态控制资源加载 -->
<script>
// 资源加载管理器
class ResourceLoader {
constructor() {
this.loaded = new Set();
this.pending = new Map();
this.priorityQueue = [];
}
// 根据网络状况调整加载策略
adjustLoadingStrategy() {
const connection = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
if (connection) {
const { effectiveType, saveData } = connection;
// 根据网络类型调整
switch (effectiveType) {
case 'slow-2g':
case '2g':
this.setLowBandwidthMode();
break;
case '3g':
this.setMediumBandwidthMode();
break;
case '4g':
this.setHighBandwidthMode();
break;
}
// 省流模式
if (saveData) {
this.enableDataSaverMode();
}
}
}
setLowBandwidthMode() {
// 低带宽模式:只加载关键资源
console.log('低带宽模式:优化资源加载');
// 延迟非关键资源
this.deferNonCriticalResources();
// 降低图片质量
this.reduceImageQuality();
}
// 动态加载脚本
loadScript(url, options = {}) {
return new Promise((resolve, reject) => {
if (this.loaded.has(url)) {
resolve();
return;
}
const script = document.createElement('script');
script.src = url;
// 设置属性
if (options.async) script.async = true;
if (options.defer) script.defer = true;
if (options.crossOrigin) script.crossOrigin = options.crossOrigin;
script.onload = () => {
this.loaded.add(url);
resolve();
};
script.onerror = reject;
// 根据优先级插入
if (options.priority === 'high') {
document.head.prepend(script);
} else {
document.head.appendChild(script);
}
});
}
// 动态预加载资源
prefetchResource(url, as = 'script') {
if (this.loaded.has(url) || this.pending.has(url)) {
return;
}
const link = document.createElement('link');
link.rel = 'prefetch';
link.href = url;
link.as = as;
this.pending.set(url, link);
document.head.appendChild(link);
// 监听加载完成
link.onload = () => {
this.loaded.add(url);
this.pending.delete(url);
};
}
// 预连接
preconnectToOrigin(origin) {
const link = document.createElement('link');
link.rel = 'preconnect';
link.href = origin;
link.crossOrigin = true;
document.head.appendChild(link);
}
}
// 初始化资源加载器
const resourceLoader = new ResourceLoader();
// 页面加载完成后预加载下一页资源
window.addEventListener('load', () => {
// 预加载用户可能访问的下一页
setTimeout(() => {
resourceLoader.prefetchResource('/products', 'document');
resourceLoader.prefetchResource('/static/js/products.js', 'script');
resourceLoader.prefetchResource('/static/css/products.css', 'style');
}, 1000);
// 预连接API域名
resourceLoader.preconnectToOrigin('https://api.example.com');
});
// 根据用户行为预测加载
document.addEventListener('mouseover', (e) => {
const link = e.target.closest('a');
if (link && link.href) {
const url = new URL(link.href);
// 如果是站内链接,预加载
if (url.origin === window.location.origin) {
resourceLoader.prefetchResource(link.href, 'document');
}
}
});
</script>
<!-- 主要内容 -->
<div class="critical-content">
<!-- 首屏内容 -->
</div>
<!-- 延迟加载的脚本 -->
<script defer src="/static/js/non-critical.js"></script>
</body>
</html>
JavaScript动态资源管理
javascript
// 高级资源管理器
class AdvancedResourceManager {
constructor() {
this.resources = new Map();
this.observers = new Map();
this.performanceEntries = [];
this.initPerformanceMonitoring();
this.initNetworkAwareLoading();
}
// 初始化性能监控
initPerformanceMonitoring() {
// 监听资源加载性能
if ('PerformanceObserver' in window) {
// 资源加载性能
const resourceObserver = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
this.performanceEntries.push(entry);
this.analyzeResourcePerformance(entry);
});
});
resourceObserver.observe({ entryTypes: ['resource'] });
// 长任务监控
const longTaskObserver = new PerformanceObserver((list) => {
list.getEntries().forEach(entry => {
if (entry.duration > 50) {
console.warn('长任务影响交互:', entry);
}
});
});
longTaskObserver.observe({ entryTypes: ['longtask'] });
}
}
// 网络感知加载
initNetworkAwareLoading() {
if ('connection' in navigator) {
const connection = navigator.connection;
connection.addEventListener('change', () => {
this.adjustLoadingStrategy();
});
this.adjustLoadingStrategy();
}
}
// 调整加载策略
adjustLoadingStrategy() {
const connection = navigator.connection;
let strategy = 'normal';
if (connection) {
const { effectiveType, downlink, rtt, saveData } = connection;
if (saveData) {
strategy = 'save-data';
} else if (effectiveType === 'slow-2g' || effectiveType === '2g') {
strategy = 'low-bandwidth';
} else if (effectiveType === '3g') {
strategy = 'medium-bandwidth';
} else {
strategy = 'high-bandwidth';
}
console.log(`网络状况: ${effectiveType}, 下载速度: ${downlink}Mbps, 策略: ${strategy}`);
}
this.applyLoadingStrategy(strategy);
}
applyLoadingStrategy(strategy) {
switch (strategy) {
case 'save-data':
this.enableDataSaverMode();
break;
case 'low-bandwidth':
this.enableLowBandwidthMode();
break;
case 'medium-bandwidth':
this.enableMediumBandwidthMode();
break;
case 'high-bandwidth':
this.enableHighBandwidthMode();
break;
}
}
enableDataSaverMode() {
// 省流模式:最小化资源加载
console.log('启用省流模式');
// 停止所有非关键资源加载
this.stopNonCriticalLoading();
// 降低图片质量
this.reduceImageQuality(0.3);
// 禁用动画
this.disableAnimations();
// 使用更小的字体
this.useCompactLayout();
}
enableLowBandwidthMode() {
// 低带宽优化
console.log('启用低带宽模式');
// 延迟非关键资源
this.deferNonCriticalResources();
// 降低图片质量
this.reduceImageQuality(0.5);
// 简化UI
this.simplifyUI();
}
// 智能图片加载
loadImage(src, options = {}) {
return new Promise((resolve, reject) => {
const img = new Image();
// 设置加载属性
img.loading = options.lazy ? 'lazy' : 'eager';
img.decoding = options.async ? 'async' : 'sync';
// 响应式图片支持
if (options.srcset) {
img.srcset = options.srcset;
}
if (options.sizes) {
img.sizes = options.sizes;
}
// 设置基础src
img.src = src;
// 加载完成
img.onload = () => {
// 添加到资源管理
this.resources.set(src, {
type: 'image',
loaded: true,
timestamp: Date.now()
});
resolve(img);
};
img.onerror = reject;
// 开始加载
document.body.appendChild(img);
img.style.display = 'none';
});
}
// 分析资源性能
analyzeResourcePerformance(entry) {
const { name, duration, transferSize, encodedBodySize } = entry;
// 识别性能问题
if (duration > 1000) {
console.warn(`资源加载过慢: ${name}`, entry);
// 记录到分析服务
this.reportPerformanceIssue({
type: 'slow_resource',
resource: name,
duration,
size: transferSize || encodedBodySize
});
}
// 识别过大资源
const size = transferSize || encodedBodySize;
if (size > 1024 * 500) { // 大于500KB
console.warn(`资源过大: ${name} (${(size / 1024).toFixed(2)}KB)`);
}
}
// 报告性能问题
reportPerformanceIssue(issue) {
// 发送到监控服务
if (navigator.sendBeacon) {
const data = JSON.stringify({
...issue,
url: window.location.href,
timestamp: Date.now(),
userAgent: navigator.userAgent
});
navigator.sendBeacon('/api/performance-log', data);
}
}
// 清理未使用的资源
cleanupUnusedResources() {
const now = Date.now();
const maxAge = 30 * 60 * 1000; // 30分钟
for (const [url, resource] of this.resources) {
if (now - resource.timestamp > maxAge) {
// 清理图片资源
if (resource.type === 'image') {
const img = document.querySelector(`img[src="${url}"]`);
if (img && !this.isElementInViewport(img)) {
img.remove();
this.resources.delete(url);
}
}
}
}
}
// 检查元素是否在视口中
isElementInViewport(el) {
const rect = el.getBoundingClientRect();
return (
rect.top >= 0 &&
rect.left >= 0 &&
rect.bottom <= (window.innerHeight || document.documentElement.clientHeight) &&
rect.right <= (window.innerWidth || document.documentElement.clientWidth)
);
}
// 销毁清理
destroy() {
// 清理所有观察器
for (const observer of this.observers.values()) {
observer.disconnect();
}
this.observers.clear();
this.resources.clear();
}
}
// 使用示例
const resourceManager = new AdvancedResourceManager();
// 页面卸载前保存状态
window.addEventListener('beforeunload', () => {
// 使用sendBeacon发送性能数据
const performanceData = resourceManager.performanceEntries;
const data = JSON.stringify(performanceData);
navigator.sendBeacon('/api/performance-metrics', data);
});
// 页面隐藏时清理资源
document.addEventListener('visibilitychange', () => {
if (document.hidden) {
// 页面隐藏,清理非关键资源
resourceManager.cleanupUnusedResources();
}
});
8.3 HTTP/2与HTTP/3的优化实践
HTTP/2和HTTP/3提供了更高效的传输协议,但需要正确配置才能发挥最大效果。
javascript
// HTTP/2服务器推送配置示例
// Node.js + Express
const express = require('express');
const fs = require('fs');
const http2 = require('http2');
const path = require('path');
const app = express();
// 静态资源中间件,支持HTTP/2推送
const staticWithPush = (req, res, next) => {
const filePath = path.join(__dirname, 'public', req.path);
// 检查是否是静态资源
if (fs.existsSync(filePath)) {
const ext = path.extname(filePath);
// 定义需要推送的资源
const pushResources = {
'/index.html': [
'/static/css/main.css',
'/static/js/main.js',
'/static/images/logo.png'
],
'/about.html': [
'/static/css/about.css',
'/static/js/about.js'
]
};
// 如果是HTML文件,推送相关资源
if (ext === '.html' && res.push) {
const resources = pushResources[req.path] || [];
resources.forEach(resourcePath => {
const push = res.push(resourcePath);
const fullPath = path.join(__dirname, 'public', resourcePath);
if (fs.existsSync(fullPath)) {
push.writeHead(200);
fs.createReadStream(fullPath).pipe(push);
} else {
push.end();
}
});
}
}
next();
};
app.use(staticWithPush);
app.use(express.static('public'));
// 创建HTTP/2服务器
const serverOptions = {
key: fs.readFileSync('server.key'),
cert: fs.readFileSync('server.crt'),
allowHTTP1: true // 允许HTTP/1.1回退
};
const server = http2.createSecureServer(serverOptions, app);
server.listen(443, () => {
console.log('HTTP/2服务器运行在 https://localhost');
});
// HTTP/2客户端优化
class HTTP2Client {
constructor() {
this.session = null;
this.streams = new Map();
this.pendingRequests = new Map();
}
// 连接到HTTP/2服务器
async connect(url) {
return new Promise((resolve, reject) => {
const session = http2.connect(url);
session.on('connect', () => {
console.log('HTTP/2连接已建立');
this.session = session;
resolve(session);
});
session.on('error', (err) => {
console.error('HTTP/2连接错误:', err);
reject(err);
});
session.on('goaway', () => {
console.log('收到GOAWAY帧,连接将关闭');
this.cleanup();
});
// 监听设置帧,了解服务器能力
session.on('remoteSettings', (settings) => {
console.log('服务器设置:', settings);
});
});
}
// 发送请求
async request(path, options = {}) {
if (!this.session) {
throw new Error('未连接到服务器');
}
return new Promise((resolve, reject) => {
const headers = {
':path': path,
':method': options.method || 'GET',
':scheme': 'https',
':authority': new URL(options.url || window.location.origin).hostname,
...options.headers
};
// 创建流
const stream = this.session.request(headers);
const streamId = stream.id;
// 存储流引用
this.streams.set(streamId, stream);
// 设置超时
const timeout = options.timeout || 30000;
const timeoutId = setTimeout(() => {
stream.close(http2.constants.NGHTTP2_CANCEL);
this.streams.delete(streamId);
reject(new Error(`请求超时: ${path}`));
}, timeout);
// 收集响应数据
let responseData = [];
let responseHeaders = {};
stream.on('response', (headers) => {
responseHeaders = headers;
// 检查服务器推送
if (headers[':status'] === '200' && headers['content-type']?.includes('text/html')) {
this.setupPushReceiver(stream);
}
});
stream.on('data', (chunk) => {
responseData.push(chunk);
});
stream.on('end', () => {
clearTimeout(timeoutId);
const response = {
headers: responseHeaders,
data: Buffer.concat(responseData),
streamId
};
this.streams.delete(streamId);
resolve(response);
});
stream.on('error', (err) => {
clearTimeout(timeoutId);
this.streams.delete(streamId);
reject(err);
});
// 发送请求体(如果有)
if (options.body) {
stream.write(options.body);
}
stream.end();
});
}
// 设置推送接收
setupPushReceiver(stream) {
stream.on('push', (pushHeaders, flags, rawHeaders) => {
const pushedPath = pushHeaders[':path'];
console.log(`收到服务器推送: ${pushedPath}`);
const pushStream = this.session.request({
...pushHeaders,
':method': 'GET'
});
let pushData = [];
pushStream.on('data', (chunk) => {
pushData.push(chunk);
});
pushStream.on('end', () => {
const data = Buffer.concat(pushData);
// 缓存推送的资源
this.cachePushedResource(pushedPath, data);
console.log(`推送资源接收完成: ${pushedPath} (${data.length} bytes)`);
});
});
}
// 缓存推送的资源
cachePushedResource(path, data) {
// 实现资源缓存逻辑
const cacheKey = `push-cache:${path}`;
try {
// 使用IndexedDB或Cache API存储
if ('caches' in window) {
caches.open('http2-push-cache').then(cache => {
const response = new Response(data, {
headers: { 'Content-Type': this.getContentType(path) }
});
cache.put(path, response);
});
}
console.log(`资源已缓存: ${path}`);
} catch (error) {
console.error('缓存推送资源失败:', error);
}
}
getContentType(path) {
const ext = path.split('.').pop().toLowerCase();
const types = {
'css': 'text/css',
'js': 'application/javascript',
'png': 'image/png',
'jpg': 'image/jpeg',
'webp': 'image/webp',
'html': 'text/html'
};
return types[ext] || 'application/octet-stream';
}
// 多路复用优化:批量请求
async batchRequests(requests) {
const promises = requests.map(req =>
this.request(req.path, req.options)
);
// 使用Promise.all同时发送多个请求
return Promise.all(promises);
}
// 优先级设置
async requestWithPriority(path, priority, options = {}) {
const weight = this.calculatePriorityWeight(priority);
const stream = this.session.request({
':path': path,
':method': options.method || 'GET',
':scheme': 'https',
':authority': new URL(options.url || window.location.origin).hostname
}, {
weight,
exclusive: priority === 'high',
parent: 0
});
// ... 处理响应
}
calculatePriorityWeight(priority) {
const weights = {
'highest': 256,
'high': 180,
'medium': 120,
'low': 60,
'lowest': 1
};
return weights[priority] || 120;
}
// 连接管理
async manageConnection() {
// 监控连接状态
setInterval(() => {
if (this.session) {
const { destroyed, closed } = this.session;
if (destroyed || closed) {
console.log('HTTP/2连接已关闭,尝试重连...');
this.reconnect();
}
}
}, 5000);
// 流量控制监控
this.session.on('flowControl', (stream) => {
const windowSize = stream.state.localWindowSize;
if (windowSize < 1024) {
console.warn('流量控制窗口较小,可能影响性能');
}
});
}
// 清理资源
cleanup() {
// 关闭所有流
for (const stream of this.streams.values()) {
stream.close();
}
this.streams.clear();
// 关闭会话
if (this.session) {
this.session.destroy();
this.session = null;
}
}
// 重连逻辑
async reconnect() {
this.cleanup();
try {
await this.connect(this.originalUrl);
console.log('HTTP/2连接重连成功');
} catch (error) {
console.error('重连失败:', error);
// 指数退避重试
setTimeout(() => this.reconnect(), 5000);
}
}
}
// HTTP/3检测与回退
class HTTP3Detector {
constructor() {
this.supportsHTTP3 = false;
this.checkHTTP3Support();
}
async checkHTTP3Support() {
try {
// 尝试使用HTTP/3连接
const testUrl = 'https://http3.is/';
const response = await fetch(testUrl, {
method: 'HEAD',
mode: 'no-cors'
});
// 检查Alt-Svc头部
const altSvc = response.headers.get('Alt-Svc');
if (altSvc && altSvc.includes('h3=')) {
this.supportsHTTP3 = true;
console.log('检测到HTTP/3支持');
} else {
console.log('未检测到HTTP/3支持,使用HTTP/2或HTTP/1.1');
}
} catch (error) {
console.log('HTTP/3检测失败:', error);
}
}
// 获取最佳协议
getBestProtocol() {
if (this.supportsHTTP3) {
return 'h3';
} else if (window.performance) {
// 检查是否已在使用HTTP/2
const entries = performance.getEntriesByType('navigation');
if (entries.length > 0) {
const protocol = entries[0].nextHopProtocol;
if (protocol && protocol.startsWith('h2')) {
return 'h2';
}
}
}
return 'http/1.1';
}
// 应用协议优化
applyProtocolOptimizations() {
const protocol = this.getBestProtocol();
switch (protocol) {
case 'h3':
this.applyHTTP3Optimizations();
break;
case 'h2':
this.applyHTTP2Optimizations();
break;
default:
this.applyHTTP11Optimizations();
}
}
applyHTTP3Optimizations() {
console.log('应用HTTP/3优化策略');
// HTTP/3特有优化
// 1. 更多的并发请求(无队头阻塞)
// 2. 0-RTT连接恢复
// 3. 改进的拥塞控制
// 调整资源加载策略
this.adjustResourceLoadingForHTTP3();
}
applyHTTP2Optimizations() {
console.log('应用HTTP/2优化策略');
// HTTP/2优化
// 1. 域名收缩(减少DNS查询)
// 2. 利用多路复用
// 3. 服务器推送
// 合并小文件
this.mergeSmallResources();
}
applyHTTP11Optimizations() {
console.log('应用HTTP/1.1优化策略');
// HTTP/1.1传统优化
// 1. 域名分片(突破浏览器并发限制)
// 2. 资源合并
// 3. 减少请求数
this.enableHTTP11Optimizations();
}
adjustResourceLoadingForHTTP3() {
// HTTP/3下可以更激进地并行加载
const scripts = document.querySelectorAll('script[defer]');
scripts.forEach(script => {
// 移除defer,直接加载
script.removeAttribute('defer');
});
}
mergeSmallResources() {
// 合并小的CSS和JS文件
// 实际项目中应在构建时完成
console.log('建议在构建时合并小文件以优化HTTP/2');
}
enableHTTP11Optimizations() {
// 传统优化:域名分片
const domains = [
'static1.example.com',
'static2.example.com',
'static3.example.com'
];
// 动态调整资源域名
const resources = document.querySelectorAll('img, script, link[rel="stylesheet"]');
resources.forEach((resource, index) => {
const domain = domains[index % domains.length];
const src = resource.src || resource.href;
if (src) {
const url = new URL(src, window.location.origin);
url.hostname = domain;
if (resource.src) {
resource.src = url.toString();
} else {
resource.href = url.toString();
}
}
});
}
}
// 使用示例
document.addEventListener('DOMContentLoaded', async () => {
// 检测HTTP/3支持
const http3Detector = new HTTP3Detector();
// 延迟执行,等待检测完成
setTimeout(() => {
http3Detector.applyProtocolOptimizations();
}, 1000);
// 如果支持HTTP/2,使用HTTP/2客户端
if (http3Detector.getBestProtocol() === 'h2') {
const http2Client = new HTTP2Client();
try {
await http2Client.connect('https://api.example.com');
// 批量请求示例
const responses = await http2Client.batchRequests([
{ path: '/api/user', options: { method: 'GET' } },
{ path: '/api/products', options: { method: 'GET' } },
{ path: '/api/settings', options: { method: 'GET' } }
]);
console.log('批量请求完成:', responses);
// 管理连接
http2Client.manageConnection();
} catch (error) {
console.error('HTTP/2连接失败:', error);
}
}
});
九、性能监控与持续优化
9.1 全面的性能度量指标
现代浏览器提供了丰富的性能API,我们可以利用这些API构建完整的性能监控体系。
javascript
// 综合性能监控器
class PerformanceMonitor {
constructor() {
this.metrics = new Map();
this.observers = [];
this.thresholds = {
fcp: 2000, // 首次内容绘制
lcp: 4000, // 最大内容绘制
fid: 100, // 首次输入延迟
cls: 0.1, // 累积布局偏移
ttfb: 600, // 首字节时间
tti: 5000 // 可交互时间
};
this.init();
}
init() {
// 监听所有性能指标
this.setupPerformanceObservers();
// 监听用户交互
this.setupUserInteractionTracking();
// 监控内存使用
this.setupMemoryMonitoring();
// 网络状态监控
this.setupNetworkMonitoring();
}
setupPerformanceObservers() {
// 1. 首次绘制相关
this.setupPaintObserver();
// 2. 最大内容绘制
this.setupLCPObserver();
// 3. 首次输入延迟
this.setupFIDObserver();
// 4. 累积布局偏移
this.setupCLSObserver();
// 5. 长任务监控
this.setupLongTaskObserver();
// 6. 布局偏移
this.setupLayoutShiftObserver();
}
setupPaintObserver() {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
if (entry.name === 'first-paint') {
this.recordMetric('fp', entry.startTime);
this.checkThreshold('fp', entry.startTime);
}
if (entry.name === 'first-contentful-paint') {
this.recordMetric('fcp', entry.startTime);
this.checkThreshold('fcp', entry.startTime);
}
}
});
observer.observe({ entryTypes: ['paint'] });
this.observers.push(observer);
}
}
setupLCPObserver() {
if ('PerformanceObserver' in window) {
let lcpValue = 0;
const observer = new PerformanceObserver((list) => {
const entries = list.getEntries();
const lastEntry = entries[entries.length - 1];
if (lastEntry) {
lcpValue = lastEntry.startTime;
this.recordMetric('lcp', lcpValue);
this.checkThreshold('lcp', lcpValue);
}
});
observer.observe({ entryTypes: ['largest-contentful-paint'] });
this.observers.push(observer);
// 页面隐藏时记录最终LCP
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.recordMetric('final-lcp', lcpValue);
}
});
}
}
setupFIDObserver() {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
const fid = entry.processingStart - entry.startTime;
this.recordMetric('fid', fid);
this.checkThreshold('fid', fid);
// 记录交互详情
this.recordInteractionDetail(entry);
}
});
observer.observe({ entryTypes: ['first-input'] });
this.observers.push(observer);
}
}
setupCLSObserver() {
if ('PerformanceObserver' in window) {
let clsValue = 0;
let sessionValue = 0;
let sessionEntries = [];
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
// 忽略最近500ms内的用户输入
if (!entry.hadRecentInput) {
const firstSessionEntry = sessionEntries[0];
const lastSessionEntry = sessionEntries[sessionEntries.length - 1];
// 如果条目间隔超过1秒或位置差距超过1秒,开始新的会话
if (sessionEntries.length === 0 ||
entry.startTime - lastSessionEntry.startTime < 1000 &&
entry.startTime - firstSessionEntry.startTime < 5000) {
sessionValue += entry.value;
sessionEntries.push(entry);
} else {
// 新会话
sessionValue = entry.value;
sessionEntries = [entry];
}
// 更新最大会话值
if (sessionValue > clsValue) {
clsValue = sessionValue;
this.recordMetric('cls', clsValue);
this.checkThreshold('cls', clsValue);
}
}
}
});
observer.observe({ entryTypes: ['layout-shift'] });
this.observers.push(observer);
}
}
setupLongTaskObserver() {
if ('PerformanceObserver' in window) {
const observer = new PerformanceObserver((list) => {
for (const entry of list.getEntries()) {
this.recordMetric('long-task', entry.duration);
// 记录长任务详情
this.recordLongTaskDetail(entry);
if (entry.duration > 50) {
console.warn(`检测到长任务: ${entry.duration.toFixed(2)}ms`);
this.reportPerformanceIssue('long_task', {
duration: entry.duration,
name: entry.name
});
}
}
});
observer.observe({ entryTypes: ['longtask'] });
this.observers.push(observer);
}
}
setupUserInteractionTracking() {
// 跟踪用户关键交互
const interactions = ['click', 'keydown', 'tap'];
interactions.forEach(eventType => {
document.addEventListener(eventType, (event) => {
const interactionTime = performance.now();
// 记录交互
this.recordInteraction({
type: eventType,
target: event.target?.tagName,
time: interactionTime,
path: this.getElementPath(event.target)
});
}, { passive: true });
});
}
setupMemoryMonitoring() {
// 监控内存使用
if ('memory' in performance) {
setInterval(() => {
const memory = performance.memory;
this.recordMetric('memory-used', memory.usedJSHeapSize);
this.recordMetric('memory-total', memory.totalJSHeapSize);
this.recordMetric('memory-limit', memory.jsHeapSizeLimit);
// 检查内存泄漏
this.checkMemoryLeak();
}, 10000);
}
}
setupNetworkMonitoring() {
// 网络信息API
if ('connection' in navigator) {
const connection = navigator.connection;
connection.addEventListener('change', () => {
this.recordNetworkInfo();
});
this.recordNetworkInfo();
}
// 监听离线/在线状态
window.addEventListener('online', () => {
this.recordMetric('network-status', 'online');
});
window.addEventListener('offline', () => {
this.recordMetric('network-status', 'offline');
});
}
recordNetworkInfo() {
if ('connection' in navigator) {
const connection = navigator.connection;
this.recordMetric('effective-type', connection.effectiveType);
this.recordMetric('downlink', connection.downlink);
this.recordMetric('rtt', connection.rtt);
this.recordMetric('save-data', connection.saveData);
}
}
// 记录指标
recordMetric(name, value, tags = {}) {
const timestamp = Date.now();
const metric = {
name,
value,
timestamp,
tags: {
url: window.location.href,
userAgent: navigator.userAgent,
...tags
}
};
this.metrics.set(`${name}-${timestamp}`, metric);
// 发送到监控服务
this.sendMetric(metric);
// 本地存储(用于离线分析)
this.storeMetricLocally(metric);
}
// 检查阈值
checkThreshold(name, value) {
const threshold = this.thresholds[name];
if (threshold && value > threshold) {
console.warn(`性能阈值警告: ${name}=${value}ms, 阈值=${threshold}ms`);
this.reportPerformanceIssue('threshold_exceeded', {
metric: name,
value,
threshold
});
}
}
// 报告性能问题
reportPerformanceIssue(type, data) {
const issue = {
type,
data,
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent
};
// 使用sendBeacon发送,即使页面卸载也能发送
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(issue)], {
type: 'application/json'
});
navigator.sendBeacon('/api/performance-issues', blob);
}
// 触发自定义事件
window.dispatchEvent(new CustomEvent('performance-issue', {
detail: issue
}));
}
// 发送指标到监控服务
sendMetric(metric) {
// 批量发送以减少请求
if (!this.sendQueue) {
this.sendQueue = [];
this.sendTimer = null;
}
this.sendQueue.push(metric);
if (!this.sendTimer) {
this.sendTimer = setTimeout(() => {
this.flushMetrics();
}, 1000);
}
}
// 批量发送指标
flushMetrics() {
if (this.sendQueue.length > 0) {
const metrics = [...this.sendQueue];
this.sendQueue = [];
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(metrics)], {
type: 'application/json'
});
navigator.sendBeacon('/api/performance-metrics', blob);
}
}
this.sendTimer = null;
}
// 本地存储指标
storeMetricLocally(metric) {
try {
// 使用IndexedDB存储
if ('indexedDB' in window) {
this.storeInIndexedDB(metric);
} else {
// 回退到localStorage
this.storeInLocalStorage(metric);
}
} catch (error) {
console.error('存储指标失败:', error);
}
}
// 获取性能报告
getPerformanceReport() {
const report = {
timestamp: Date.now(),
url: window.location.href,
metrics: {},
issues: [],
recommendations: []
};
// 收集所有指标
for (const metric of this.metrics.values()) {
if (!report.metrics[metric.name]) {
report.metrics[metric.name] = [];
}
report.metrics[metric.name].push(metric.value);
}
// 计算关键指标
this.calculateCoreWebVitals(report);
// 生成建议
this.generateRecommendations(report);
return report;
}
calculateCoreWebVitals(report) {
// 计算核心Web指标
const coreVitals = {};
// LCP
const lcpValues = report.metrics.lcp || [];
if (lcpValues.length > 0) {
coreVitals.lcp = Math.max(...lcpValues);
}
// FID
const fidValues = report.metrics.fid || [];
if (fidValues.length > 0) {
coreVitals.fid = Math.max(...fidValues);
}
// CLS
const clsValues = report.metrics.cls || [];
if (clsValues.length > 0) {
coreVitals.cls = Math.max(...clsValues);
}
report.coreWebVitals = coreVitals;
}
generateRecommendations(report) {
const recommendations = [];
// 基于指标生成优化建议
if (report.coreWebVitals?.lcp > 4000) {
recommendations.push({
type: 'lcp_optimization',
severity: 'high',
message: '最大内容绘制时间过长,建议优化首屏图片加载和服务器响应时间',
actions: [
'使用图片懒加载',
'优化服务器配置',
'使用CDN加速',
'预加载关键资源'
]
});
}
if (report.coreWebVitals?.fid > 100) {
recommendations.push({
type: 'fid_optimization',
severity: 'medium',
message: '首次输入延迟较高,建议优化JavaScript执行',
actions: [
'代码分割和懒加载',
'优化长任务',
'使用Web Workers处理复杂计算'
]
});
}
if (report.coreWebVitals?.cls > 0.1) {
recommendations.push({
type: 'cls_optimization',
severity: 'high',
message: '累积布局偏移严重,影响用户体验',
actions: [
'为图片和视频设置尺寸',
'避免动态插入内容',
'使用transform代替top/left动画'
]
});
}
report.recommendations = recommendations;
}
// 清理资源
destroy() {
// 清理所有观察器
this.observers.forEach(observer => {
try {
observer.disconnect();
} catch (e) {
// 忽略错误
}
});
this.observers = [];
// 发送剩余指标
this.flushMetrics();
// 清理定时器
if (this.sendTimer) {
clearTimeout(this.sendTimer);
}
}
}
// 使用示例
const performanceMonitor = new PerformanceMonitor();
// 页面卸载前获取报告
window.addEventListener('beforeunload', () => {
const report = performanceMonitor.getPerformanceReport();
// 发送最终报告
if (navigator.sendBeacon) {
const blob = new Blob([JSON.stringify(report)], {
type: 'application/json'
});
navigator.sendBeacon('/api/performance-report', blob);
}
// 清理
performanceMonitor.destroy();
});
// 定期获取报告(用于开发调试)
setInterval(() => {
const report = performanceMonitor.getPerformanceReport();
console.log('性能报告:', report);
}, 30000);
9.2 自动化性能测试与CI/CD集成
自动化性能测试可以确保代码变更不会引入性能回归。
javascript
// Lighthouse CI配置示例
// .lighthouserc.js
module.exports = {
ci: {
collect: {
// 收集配置
numberOfRuns: 3, // 每次测试运行3次取平均值
startServerCommand: 'npm run start', // 启动开发服务器
url: [
'http://localhost:3000',
'http://localhost:3000/about',
'http://localhost:3000/products'
],
settings: {
// 设备模拟
emulatedFormFactor: 'mobile', // 移动设备
screenEmulation: {
mobile: true,
width: 375,
height: 667,
deviceScaleFactor: 2,
disabled: false
},
// 网络限制
throttling: {
rttMs: 150, // 往返延迟
throughputKbps: 1638.4, // 吞吐量
requestLatencyMs: 150,
downloadThroughputKbps: 1474.56,
uploadThroughputKbps: 675,
cpuSlowdownMultiplier: 4 // CPU减速
},
// 其他设置
onlyCategories: ['performance', 'accessibility', 'best-practices', 'seo'],
skipAudits: ['uses-http2'] // 跳过特定审计
}
},
assert: {
// 断言配置
assertions: {
// 核心Web指标
'first-contentful-paint': ['error', { maxNumericValue: 2000 }],
'largest-contentful-paint': ['error', { maxNumericValue: 4000 }],
'first-meaningful-paint': ['error', { maxNumericValue: 3000 }],
'speed-index': ['error', { maxNumericValue: 4300 }],
'interactive': ['error', { maxNumericValue: 5000 }],
'total-blocking-time': ['error', { maxNumericValue: 300 }],
'cumulative-layout-shift': ['error', { maxNumericValue: 0.1 }],
// 最佳实践
'uses-responsive-images': ['warn', { minScore: 0.8 }],
'offscreen-images': ['warn', { minScore: 0.8 }],
'render-blocking-resources': ['warn', { minScore: 0.8 }],
// SEO
'document-title': ['error', { minScore: 1 }],
'meta-description': ['warn', { minScore: 0.9 }]
}
},
upload: {
// 上传配置
target: 'temporary-public-storage', // 临时存储
// 或上传到Lighthouse服务器
// target: 'lhci',
// serverBaseUrl: 'https://your-lighthouse-server.com',
// token: 'your-token'
}
}
};
// GitHub Actions工作流配置
// .github/workflows/lighthouse.yml
name: Lighthouse CI
on:
push:
branches: [ main, develop ]
pull_request:
branches: [ main ]
jobs:
lighthouse:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js
uses: actions/setup-node@v3
with:
node-version: '16'
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Run Lighthouse CI
uses: treosh/lighthouse-ci-action@v9
with:
configPath: './.lighthouserc.js'
uploadArtifacts: true
temporaryPublicStorage: true
runs: 3
- name: Performance Budget Check
run: |
# 自定义性能预算检查
npm run check-performance-budget
- name: Generate Report
if: always()
run: |
# 生成可读报告
npm run generate-performance-report
- name: Upload Lighthouse Results
if: always()
uses: actions/upload-artifact@v3
with:
name: lighthouse-results
path: |
.lighthouseci/
reports/
retention-days: 30
// 性能预算检查脚本
// scripts/check-performance-budget.js
const fs = require('fs');
const path = require('path');
// 性能预算定义
const PERFORMANCE_BUDGET = {
'first-contentful-paint': 2000,
'largest-contentful-paint': 4000,
'first-meaningful-paint': 3000,
'speed-index': 4300,
'interactive': 5000,
'total-blocking-time': 300,
'cumulative-layout-shift': 0.1,
// 资源大小预算
'resource-sizes': {
'total': 5 * 1024 * 1024, // 5MB
'javascript': 300 * 1024, // 300KB
'css': 100 * 1024, // 100KB
'images': 4 * 1024 * 1024 // 4MB
}
};
// 检查Lighthouse结果
function checkLighthouseResults() {
const resultsPath = path.join(__dirname, '..', '.lighthouseci');
const files = fs.readdirSync(resultsPath);
let hasBudgetViolation = false;
const violations = [];
files.forEach(file => {
if (file.endsWith('.json')) {
const filePath = path.join(resultsPath, file);
const data = JSON.parse(fs.readFileSync(filePath, 'utf8'));
// 检查各项指标
const audits = data.audits || {};
Object.keys(PERFORMANCE_BUDGET).forEach(key => {
if (key === 'resource-sizes') return;
const audit = audits[key];
if (audit && audit.numericValue) {
const budget = PERFORMANCE_BUDGET[key];
const value = audit.numericValue;
if (value > budget) {
hasBudgetViolation = true;
violations.push({
url: data.finalUrl,
metric: key,
value: Math.round(value),
budget: budget,
difference: Math.round(value - budget)
});
}
}
});
// 检查资源大小
const resourceAudit = audits['resource-summary'];
if (resourceAudit && resourceAudit.details) {
const resources = resourceAudit.details.items || [];
resources.forEach(resource => {
const type = resource.resourceType;
const size = resource.size;
if (PERFORMANCE_BUDGET['resource-sizes'][type] &&
size > PERFORMANCE_BUDGET['resource-sizes'][type]) {
hasBudgetViolation = true;
violations.push({
url: data.finalUrl,
metric: `${type}-size`,
value: Math.round(size / 1024),
budget: Math.round(PERFORMANCE_BUDGET['resource-sizes'][type] / 1024),
difference: Math.round((size - PERFORMANCE_BUDGET['resource-sizes'][type]) / 1024),
resource: resource.url
});
}
});
}
}
});
// 生成报告
if (violations.length > 0) {
console.error('🚨 性能预算违规检测到!');
console.table(violations);
// 生成详细的HTML报告
generateHTMLReport(violations);
// 在CI中标记为失败
process.exit(1);
} else {
console.log('✅ 所有性能指标都在预算内');
}
}
// 生成HTML报告
function generateHTMLReport(violations) {
const report = `
<!DOCTYPE html>
<html>
<head>
<title>性能预算违规报告</title>
<style>
body { font-family: Arial, sans-serif; margin: 20px; }
.violation {
border: 2px solid #ff6b6b;
padding: 15px;
margin: 10px 0;
border-radius: 5px;
}
.metric { font-weight: bold; color: #ff6b6b; }
.url { color: #666; font-size: 0.9em; }
.budget { color: #4ecdc4; }
.exceeded { color: #ff6b6b; }
table {
width: 100%;
border-collapse: collapse;
margin: 20px 0;
}
th, td {
padding: 10px;
text-align: left;
border-bottom: 1px solid #ddd;
}
th { background-color: #f2f2f2; }
</style>
</head>
<body>
<h1>性能预算违规报告</h1>
<p>生成时间: ${new Date().toLocaleString()}</p>
<table>
<thead>
<tr>
<th>URL</th>
<th>指标</th>
<th>实际值</th>
<th>预算值</th>
<th>超出</th>
</tr>
</thead>
<tbody>
${violations.map(v => `
<tr>
<td class="url">${v.url}</td>
<td class="metric">${v.metric}</td>
<td>${v.value}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
<td class="budget">${v.budget}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
<td class="exceeded">+${v.difference}${v.metric.includes('size') ? 'KB' : 'ms'}</td>
</tr>
`).join('')}
</tbody>
</table>
<h2>优化建议</h2>
<div class="recommendations">
${generateRecommendations(violations)}
</div>
</body>
</html>
`;
const reportPath = path.join(__dirname, '..', 'reports', 'performance-violations.html');
fs.writeFileSync(reportPath, report);
console.log(`📊 报告已生成: ${reportPath}`);
}
// 生成优化建议
function generateRecommendations(violations) {
const recommendations = [];
violations.forEach(violation => {
const { metric, value, budget } = violation;
if (metric.includes('fcp') || metric.includes('lcp')) {
recommendations.push(`
<div class="violation">
<h3>${metric.toUpperCase()} 优化建议</h3>
<p>当前值: ${value}ms | 预算: ${budget}ms</p>
<ul>
<li>优化服务器响应时间</li>
<li>使用CDN加速静态资源</li>
<li>预加载关键资源</li>
<li>优化首屏图片(使用WebP/AVIF格式)</li>
<li>移除渲染阻塞资源</li>
</ul>
</div>
`);
} else if (metric.includes('fid') || metric.includes('interactive')) {
recommendations.push(`
<div class="violation">
<h3>${metric.toUpperCase()} 优化建议</h3>
<p>当前值: ${value}ms | 预算: ${budget}ms</p>
<ul>
<li>代码分割和懒加载</li>
<li>优化JavaScript执行时间</li>
<li>使用Web Workers处理复杂计算</li>
<li>避免长任务(超过50ms的任务)</li>
<li>优化第三方脚本加载</li>
</ul>
</div>
`);
} else if (metric.includes('size')) {
recommendations.push(`
<div class="violation">
<h3>资源大小优化建议</h3>
<p>当前值: ${value}KB | 预算: ${budget}KB</p>
<ul>
<li>压缩图片资源</li>
<li>使用现代图片格式(WebP/AVIF)</li>
<li>Tree Shaking移除未使用代码</li>
<li>代码分割按需加载</li>
<li>Gzip/Brotli压缩</li>
</ul>
</div>
`);
}
});
return recommendations.join('');
}
// 运行检查
checkLighthouseResults();
十、实战优化流程与性能预算
10.1 四步优化法实践
javascript
// 性能优化工作流管理器
class PerformanceOptimizationWorkflow {
constructor() {
this.currentStep = 'measure';
this.optimizationHistory = [];
this.performanceBaseline = null;
this.workflowSteps = {
measure: this.measurePerformance.bind(this),
analyze: this.analyzePerformance.bind(this),
implement: this.implementOptimizations.bind(this),
monitor: this.monitorPerformance.bind(this)
};
}
// 第一步:度量 - 全面评估当前性能
async measurePerformance() {
console.log('🚀 第一步:性能度量');
const metrics = {
// 加载性能
loading: await this.measureLoadingPerformance(),
// 运行时性能
runtime: await this.measureRuntimePerformance(),
// 资源使用
resources: await this.measureResourceUsage(),
// 用户体验指标
userExperience: await this.measureUserExperience()
};
// 建立性能基线
this.performanceBaseline = metrics;
// 生成初始报告
this.generateInitialReport(metrics);
// 进入下一步
this.currentStep = 'analyze';
await this.workflowSteps[this.currentStep]();
return metrics;
}
async measureLoadingPerformance() {
// 使用Performance API收集加载指标
const navigation = performance.getEntriesByType('navigation')[0];
const resources = performance.getEntriesByType('resource');
return {
ttfb: navigation?.responseStart - navigation?.requestStart || 0,
fcp: this.getMetricValue('first-contentful-paint'),
lcp: this.getMetricValue('largest-contentful-paint'),
loadTime: navigation?.loadEventEnd - navigation?.startTime || 0,
resourceCount: resources.length,
totalResourceSize: resources.reduce((sum, r) => sum + (r.transferSize || 0), 0)
};
}
async measureRuntimePerformance() {
// 收集运行时性能数据
return {
fps: await this.calculateFPS(),
memoryUsage: this.getMemoryUsage(),
longTasks: this.getLongTasks(),
eventListeners: this.countEventListeners()
};
}
async measureResourceUsage() {
// 分析资源使用情况
const resources = performance.getEntriesByType('resource');
const resourceByType = resources.reduce((acc, resource) => {
const type = resource.initiatorType;
if (!acc[type]) acc[type] = { count: 0, size: 0 };
acc[type].count++;
acc[type].size += resource.transferSize || 0;
return acc;
}, {});
return {
byType: resourceByType,
unusedResources: await this.findUnusedResources()
};
}
async measureUserExperience() {
// 用户体验指标
return {
fid: this.getMetricValue('first-input-delay'),
cls: this.getMetricValue('cumulative-layout-shift'),
inp: await this.calculateINP(), // 交互到下次绘制
scrollJank: await this.measureScrollJank()
};
}
// 第二步:分析 - 定位性能瓶颈
async analyzePerformance() {
console.log('🔍 第二步:性能分析');
const bottlenecks = {
network: await this.analyzeNetworkBottlenecks(),
rendering: await this.analyzeRenderingBottlenecks(),
javascript: await this.analyzeJavaScriptBottlenecks(),
memory: await this.analyzeMemoryBottlenecks()
};
// 生成瓶颈报告
this.generateBottleneckReport(bottlenecks);
// 确定优化优先级
const optimizationPriority = this.prioritizeOptimizations(bottlenecks);
// 进入下一步
this.currentStep = 'implement';
this.optimizationPriority = optimizationPriority;
await this.workflowSteps[this.currentStep]();
return { bottlenecks, optimizationPriority };
}
async analyzeNetworkBottlenecks() {
const bottlenecks = [];
const resources = performance.getEntriesByType('resource');
// 分析慢资源
resources.forEach(resource => {
if (resource.duration > 1000) { // 超过1秒
bottlenecks.push({
type: 'slow_resource',
url: resource.name,
duration: resource.duration,
size: resource.transferSize || 0,
suggestion: '考虑压缩、缓存或CDN加速'
});
}
});
// 检查缓存效率
const cacheEfficiency = await this.calculateCacheEfficiency();
if (cacheEfficiency < 0.7) {
bottlenecks.push({
type: 'cache_inefficient',
efficiency: cacheEfficiency,
suggestion: '优化缓存策略,增加缓存命中率'
});
}
return bottlenecks;
}
async analyzeRenderingBottlenecks() {
const bottlenecks = [];
// 检查强制同步布局
const forcedReflows = await this.detectForcedReflows();
if (forcedReflows.length > 0) {
bottlenecks.push({
type: 'forced_reflows',
count: forcedReflows.length,
details: forcedReflows,
suggestion: '避免在循环中读取布局属性,批量DOM操作'
});
}
// 检查大面积的绘制
const largePaints = await this.detectLargePaints();
if (largePaints.length > 0) {
bottlenecks.push({
type: 'large_paints',
count: largePaints.length,
suggestion: '减少重绘区域,使用CSS contain属性'
});
}
return bottlenecks;
}
// 第三步:实施 - 执行优化措施
async implementOptimizations() {
console.log('⚡ 第三步:实施优化');
if (!this.optimizationPriority) {
console.warn('未找到优化优先级,请先运行分析步骤');
return;
}
const results = {
implemented: [],
skipped: [],
failed: []
};
// 按优先级实施优化
for (const optimization of this.optimizationPriority) {
try {
console.log(`实施优化: ${optimization.name}`);
const success = await this.applyOptimization(optimization);
if (success) {
results.implemented.push(optimization);
this.recordOptimization(optimization, 'success');
} else {
results.failed.push(optimization);
this.recordOptimization(optimization, 'failed');
}
// 每次优化后重新测量
await this.measureAfterOptimization(optimization);
} catch (error) {
console.error(`优化实施失败: ${optimization.name}`, error);
results.failed.push({ ...optimization, error: error.message });
}
}
// 生成优化报告
this.generateOptimizationReport(results);
// 进入下一步
this.currentStep = 'monitor';
await this.workflowSteps[this.currentStep]();
return results;
}
async applyOptimization(optimization) {
// 根据优化类型应用不同的策略
switch (optimization.type) {
case 'network':
return await this.applyNetworkOptimization(optimization);
case 'rendering':
return await this.applyRenderingOptimization(optimization);
case 'javascript':
return await this.applyJavaScriptOptimization(optimization);
case 'resource':
return await this.applyResourceOptimization(optimization);
default:
return false;
}
}
async applyNetworkOptimization(optimization) {
switch (optimization.subtype) {
case 'cache_optimization':
// 优化缓存策略
return this.optimizeCacheStrategy();
case 'resource_compression':
// 启用资源压缩
return this.enableResourceCompression();
case 'cdn_deployment':
// 部署到CDN
return this.deployToCDN();
default:
return false;
}
}
async applyRenderingOptimization(optimization) {
switch (optimization.subtype) {
case 'reduce_reflow':
// 减少重排
return this.reduceReflows();
case 'optimize_animations':
// 优化动画性能
return this.optimizeAnimations();
case 'virtualize_lists':
// 虚拟化长列表
return this.virtualizeLongLists();
default:
return false;
}
}
// 第四步:监控 - 持续性能监控
async monitorPerformance() {
console.log('📊 第四步:持续监控');
// 建立持续监控
const monitoring = {
realtime: this.setupRealtimeMonitoring(),
alerts: this.setupPerformanceAlerts(),
reporting: this.setupAutomatedReporting(),
regression: this.setupRegressionDetection()
};
// 生成监控仪表板
await this.generateMonitoringDashboard();
// 记录工作流完成
this.recordWorkflowCompletion();
return monitoring;
}
setupRealtimeMonitoring() {
// 实时性能监控
const monitor = new PerformanceMonitor();
// 关键指标监控
setInterval(() => {
const metrics = monitor.getCurrentMetrics();
this.updateRealtimeDashboard(metrics);
// 检查异常
this.checkForAnomalies(metrics);
}, 5000);
return monitor;
}
setupPerformanceAlerts() {
// 性能告警系统
const thresholds = {
lcp: 4000,
fid: 100,
cls: 0.1,
memory: 0.8, // 80%内存使用
errors: 10 // 每分钟错误数
};
// 设置监控
this.setupThresholdAlerts(thresholds);
return thresholds;
}
setupAutomatedReporting() {
// 自动化报告
const reportSchedule = {
daily: this.generateDailyReport.bind(this),
weekly: this.generateWeeklyReport.bind(this),
monthly: this.generateMonthlyReport.bind(this)
};
// 设置定时任务
this.scheduleReports(reportSchedule);
return reportSchedule;
}
setupRegressionDetection() {
// 性能回归检测
const regressionConfig = {
sensitivity: 0.1, // 10%变化视为回归
historySize: 100, // 保留100个历史记录
checkInterval: 300000 // 每5分钟检查一次
};
this.detector = new RegressionDetector(regressionConfig);
// 开始检测
this.detector.start();
return regressionConfig;
}
// 辅助方法
getMetricValue(metricName) {
const entries = performance.getEntriesByName(metricName);
return entries.length > 0 ? entries[0].startTime : null;
}
async calculateFPS() {
return new Promise(resolve => {
let frameCount = 0;
let startTime = performance.now();
const countFrames = () => {
frameCount++;
const currentTime = performance.now();
if (currentTime - startTime >= 1000) {
resolve(frameCount);
} else {
requestAnimationFrame(countFrames);
}
};
requestAnimationFrame(countFrames);
});
}
getMemoryUsage() {
if ('memory' in performance) {
const memory = performance.memory;
return {
used: memory.usedJSHeapSize,
total: memory.totalJSHeapSize,
limit: memory.jsHeapSizeLimit,
percentage: memory.usedJSHeapSize / memory.jsHeapSizeLimit
};
}
return null;
}
getLongTasks() {
const entries = performance.getEntriesByType('longtask');
return entries.filter(entry => entry.duration > 50);
}
countEventListeners() {
// 估算事件监听器数量
let count = 0;
const allElements = document.querySelectorAll('*');
allElements.forEach(element => {
// 获取元素的事件监听器(近似值)
const listeners = getEventListeners(element);
if (listeners) {
count += Object.values(listeners).flat().length;
}
});
return count;
}
// 记录优化历史
recordOptimization(optimization, status) {
this.optimizationHistory.push({
...optimization,
status,
timestamp: Date.now(),
baseline: this.performanceBaseline
});
}
recordWorkflowCompletion() {
console.log('✅ 性能优化工作流完成');
console.log(`优化历史: ${this.optimizationHistory.length} 条记录`);
// 生成最终报告
this.generateFinalReport();
}
}
// 性能回归检测器
class RegressionDetector {
constructor(config = {}) {
this.config = {
sensitivity: 0.1,
historySize: 100,
checkInterval: 300000,
...config
};
this.history = [];
this.alerts = [];
this.isRunning = false;
}
start() {
this.isRunning = true;
this.monitoringInterval = setInterval(
() => this.checkForRegression(),
this.config.checkInterval
);
}
stop() {
this.isRunning = false;
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
}
}
async checkForRegression() {
// 收集当前性能数据
const currentMetrics = await this.collectMetrics();
if (this.history.length > 0) {
// 与历史数据比较
const regression = this.detectRegression(currentMetrics);
if (regression) {
// 触发告警
this.triggerAlert(regression);
// 记录回归
this.alerts.push({
...regression,
timestamp: Date.now()
});
}
}
// 添加到历史记录
this.history.push({
timestamp: Date.now(),
metrics: currentMetrics
});
// 限制历史记录大小
if (this.history.length > this.config.historySize) {
this.history.shift();
}
}
async collectMetrics() {
const navigation = performance.getEntriesByType('navigation')[0];
return {
lcp: this.getMetricValue('largest-contentful-paint'),
fid: this.getMetricValue('first-input-delay'),
cls: this.getMetricValue('cumulative-layout-shift'),
ttfb: navigation?.responseStart - navigation?.requestStart || 0,
loadTime: navigation?.loadEventEnd - navigation?.startTime || 0,
memory: this.getMemoryUsage()?.percentage || 0
};
}
detectRegression(currentMetrics) {
// 计算历史平均值
const historicalAverage = this.calculateHistoricalAverage();
// 检查每个指标
for (const [metric, value] of Object.entries(currentMetrics)) {
const avg = historicalAverage[metric];
if (avg && value) {
const change = (value - avg) / avg;
// 如果变化超过敏感度阈值且是负向变化(性能下降)
if (Math.abs(change) > this.config.sensitivity && value > avg) {
return {
metric,
currentValue: value,
historicalAverage: avg,
changePercentage: change * 100,
severity: this.calculateSeverity(change)
};
}
}
}
return null;
}
calculateHistoricalAverage() {
if (this.history.length === 0) return {};
const sums = {};
const counts = {};
this.history.forEach(record => {
Object.entries(record.metrics).forEach(([metric, value]) => {
if (value != null) {
sums[metric] = (sums[metric] || 0) + value;
counts[metric] = (counts[metric] || 0) + 1;
}
});
});
const averages = {};
Object.keys(sums).forEach(metric => {
averages[metric] = sums[metric] / counts[metric];
});
return averages;
}
calculateSeverity(change) {
const absChange = Math.abs(change);
if (absChange > 0.5) return 'critical'; // 50%以上
if (absChange > 0.3) return 'high'; // 30%以上
if (absChange > 0.2) return 'medium'; // 20%以上
if (absChange > 0.1) return 'low'; // 10%以上
return 'info';
}
triggerAlert(regression) {
const alertMessage = `
🚨 性能回归检测到!
指标: ${regression.metric}
当前值: ${regression.currentValue.toFixed(2)}
历史平均值: ${regression.historicalAverage.toFixed(2)}
变化: ${regression.changePercentage.toFixed(2)}%
严重程度: ${regression.severity}
时间: ${new Date().toLocaleString()}
`;
console.error(alertMessage);
// 发送到监控系统
this.sendAlertToMonitoring(regression);
// 触发自定义事件
window.dispatchEvent(new CustomEvent('performance-regression', {
detail: regression
}));
}
sendAlertToMonitoring(regression) {
// 使用sendBeacon发送告警
if (navigator.sendBeacon) {
const data = JSON.stringify({
type: 'performance_regression',
data: regression,
url: window.location.href,
timestamp: Date.now()
});
navigator.sendBeacon('/api/performance-alerts', data);
}
}
getMetricValue(metricName) {
const entries = performance.getEntriesByName(metricName);
return entries.length > 0 ? entries[0].startTime : null;
}
getMemoryUsage() {
if ('memory' in performance) {
return performance.memory;
}
return null;
}
}
// 使用工作流
document.addEventListener('DOMContentLoaded', async () => {
const workflow = new PerformanceOptimizationWorkflow();
// 自动开始优化工作流
setTimeout(async () => {
try {
await workflow.measurePerformance();
} catch (error) {
console.error('性能优化工作流失败:', error);
}
}, 3000); // 等待3秒让页面稳定
// 手动触发按钮(用于演示)
document.getElementById('start-optimization')?.addEventListener('click', async () => {
await workflow.measurePerformance();
});
});
10.2 性能预算管理与执行
javascript
// 性能预算管理器
class PerformanceBudgetManager {
constructor(config = {}) {
this.budgets = this.loadBudgets(config);
this.violations = new Map();
this.enforcement = config.enforcement || 'warning'; // warning, error, or strict
this.monitoringInterval = null;
this.init();
}
init() {
// 加载预算配置
this.loadConfiguration();
// 设置监控
this.setupMonitoring();
// 绑定事件
this.bindEvents();
}
loadBudgets(config) {
// 默认性能预算
const defaultBudgets = {
// Core Web Vitals
lcp: { max: 4000, weight: 1.0 }, // Largest Contentful Paint
fid: { max: 100, weight: 0.8 }, // First Input Delay
cls: { max: 0.1, weight: 1.0 }, // Cumulative Layout Shift
// Loading performance
fcp: { max: 2000, weight: 0.7 }, // First Contentful Paint
ttfb: { max: 600, weight: 0.6 }, // Time to First Byte
load: { max: 5000, weight: 0.5 }, // Full Load Time
// Resource budgets
resources: {
total: { max: 5 * 1024 * 1024, weight: 1.0 }, // 5MB
js: { max: 300 * 1024, weight: 0.9 }, // 300KB
css: { max: 100 * 1024, weight: 0.7 }, // 100KB
images: { max: 4 * 1024 * 1024, weight: 0.8 }, // 4MB
fonts: { max: 200 * 1024, weight: 0.6 }, // 200KB
},
// Runtime performance
fps: { min: 50, weight: 0.7 }, // Frames Per Second
memory: { max: 0.8, weight: 0.9 }, // 80% memory usage
longTasks: { max: 0, weight: 1.0 }, // No long tasks
// Request counts
requests: {
total: { max: 50, weight: 0.5 },
thirdParty: { max: 10, weight: 0.8 },
}
};
return { ...defaultBudgets, ...config.budgets };
}
setupMonitoring() {
// 设置定期监控
this.monitoringInterval = setInterval(() => {
this.checkBudgets();
}, 30000); // 每30秒检查一次
// 页面加载完成后立即检查
window.addEventListener('load', () => {
setTimeout(() => this.checkBudgets(), 1000);
});
// 用户交互时检查
document.addEventListener('click', () => {
setTimeout(() => this.checkRuntimeBudgets(), 500);
}, { passive: true });
}
async checkBudgets() {
console.log('🔍 检查性能预算...');
const violations = {
loading: await this.checkLoadingBudgets(),
resources: await this.checkResourceBudgets(),
runtime: await this.checkRuntimeBudgets(),
cumulative: await this.checkCumulativeBudgets()
};
// 处理违规
this.handleViolations(violations);
// 更新仪表板
this.updateDashboard(violations);
return violations;
}
async checkLoadingBudgets() {
const violations = [];
// 检查核心Web指标
const lcp = this.getMetricValue('largest-contentful-paint');
if (lcp && lcp > this.budgets.lcp.max) {
violations.push(this.createViolation('lcp', lcp, this.budgets.lcp));
}
const fid = this.getMetricValue('first-input-delay');
if (fid && fid > this.budgets.fid.max) {
violations.push(this.createViolation('fid', fid, this.budgets.fid));
}
const cls = this.getMetricValue('cumulative-layout-shift');
if (cls && cls > this.budgets.cls.max) {
violations.push(this.createViolation('cls', cls, this.budgets.cls));
}
return violations;
}
async checkResourceBudgets() {
const violations = [];
const resources = performance.getEntriesByType('resource');
// 按类型汇总资源
const resourceSummary = resources.reduce((acc, resource) => {
const type = resource.initiatorType;
if (!acc[type]) {
acc[type] = { count: 0, size: 0 };
}
acc[type].count++;
acc[type].size += resource.transferSize || 0;
return acc;
}, {});
// 检查总资源大小
const totalSize = resources.reduce((sum, r) => sum + (r.transferSize || 0), 0);
if (totalSize > this.budgets.resources.total.max) {
violations.push(this.createViolation(
'resources.total',
totalSize,
this.budgets.resources.total
));
}
// 检查各类型资源
Object.entries(resourceSummary).forEach(([type, data]) => {
const budgetKey = type.toLowerCase();
const budget = this.budgets.resources[budgetKey];
if (budget && data.size > budget.max) {
violations.push(this.createViolation(
`resources.${budgetKey}`,
data.size,
budget
));
}
});
// 检查请求数量
const totalRequests = resources.length;
if (totalRequests > this.budgets.requests.total.max) {
violations.push(this.createViolation(
'requests.total',
totalRequests,
this.budgets.requests.total
));
}
// 检查第三方请求
const thirdPartyRequests = resources.filter(r => {
const url = new URL(r.name);
return url.hostname !== window.location.hostname;
}).length;
if (thirdPartyRequests > this.budgets.requests.thirdParty.max) {
violations.push(this.createViolation(
'requests.thirdParty',
thirdPartyRequests,
this.budgets.requests.thirdParty
));
}
return violations;
}
async checkRuntimeBudgets() {
const violations = [];
// 检查FPS
const fps = await this.calculateFPS();
if (fps < this.budgets.fps.min) {
violations.push(this.createViolation('fps', fps, this.budgets.fps));
}
// 检查内存使用
const memory = this.getMemoryUsage();
if (memory && memory.percentage > this.budgets.memory.max) {
violations.push(this.createViolation('memory', memory.percentage, this.budgets.memory));
}
// 检查长任务
const longTasks = performance.getEntriesByType('longtask');
const longTaskCount = longTasks.filter(lt => lt.duration > 50).length;
if (longTaskCount > this.budgets.longTasks.max) {
violations.push(this.createViolation(
'longTasks',
longTaskCount,
this.budgets.longTasks
));
}
return violations;
}
async checkCumulativeBudgets() {
// 检查累积预算(如CLS需要在整个页面生命周期中检查)
const violations = [];
// 监听布局偏移
if ('PerformanceObserver' in window) {
const clsObserver = new PerformanceObserver((list) => {
const entries = list.getEntries();
const totalCLS = entries.reduce((sum, entry) => {
if (!entry.hadRecentInput) {
return sum + entry.value;
}
return sum;
}, 0);
if (totalCLS > this.budgets.cls.max) {
violations.push(this.createViolation('cls_cumulative', totalCLS, this.budgets.cls));
}
});
clsObserver.observe({ entryTypes: ['layout-shift'] });
}
return violations;
}
createViolation(metric, value, budget) {
const violation = {
metric,
value,
budget: budget.max || budget.min,
exceededBy: budget.max ?
((value - budget.max) / budget.max * 100).toFixed(1) :
((budget.min - value) / budget.min * 100).toFixed(1),
weight: budget.weight || 1.0,
timestamp: Date.now(),
url: window.location.href
};
// 计算严重程度分数
violation.severityScore = this.calculateSeverityScore(violation);
return violation;
}
calculateSeverityScore(violation) {
const percentage = parseFloat(violation.exceededBy);
const weight = violation.weight;
// 基础分数 = 超出百分比 * 权重
let score = Math.abs(percentage) * weight;
// 关键指标额外权重
const criticalMetrics = ['lcp', 'cls', 'fid'];
if (criticalMetrics.includes(violation.metric)) {
score *= 1.5;
}
return Math.min(score, 100); // 上限100分
}
handleViolations(violations) {
const allViolations = Object.values(violations).flat();
if (allViolations.length === 0) {
console.log('✅ 所有性能指标都在预算内');
return;
}
// 按严重程度排序
allViolations.sort((a, b) => b.severityScore - a.severityScore);
// 记录违规
allViolations.forEach(violation => {
const key = `${violation.metric}_${violation.timestamp}`;
this.violations.set(key, violation);
this.logViolation(violation);
});
// 根据执行策略处理
switch (this.enforcement) {
case 'warning':
this.handleAsWarning(allViolations);
break;
case 'error':
this.handleAsError(allViolations);
break;
case 'strict':
this.handleAsStrict(allViolations);
break;
}
}
handleAsWarning(violations) {
// 警告模式:仅记录和通知
console.warn(`⚠️ 检测到 ${violations.length} 个性能预算违规`);
// 发送到监控系统
this.reportToMonitoring(violations);
// 显示用户通知(可选)
if (violations.some(v => v.severityScore > 50)) {
this.showUserWarning(violations);
}
}
handleAsError(violations) {
// 错误模式:开发环境报错
console.error(`❌ 检测到 ${violations.length} 个性能预算违规`);
// 发送到错误追踪
this.reportToErrorTracking(violations);
// 在开发环境抛出错误
if (process.env.NODE_ENV === 'development') {
const criticalViolations = violations.filter(v => v.severityScore > 70);
if (criticalViolations.length > 0) {
throw new Error(`关键性能预算违规: ${criticalViolations.map(v => v.metric).join(', ')}`);
}
}
}
handleAsStrict(violations) {
// 严格模式:阻止功能或降级体验
const criticalViolations = violations.filter(v => v.severityScore > 80);
if (criticalViolations.length > 0) {
console.error('🚨 检测到关键性能违规,启用降级模式');
// 启用降级体验
this.enableDegradedExperience();
// 发送紧急警报
this.sendCriticalAlert(criticalViolations);
}
}
enableDegradedExperience() {
// 降级体验策略
console.log('启用性能降级模式');
// 1. 禁用非关键功能
document.querySelectorAll('[data-non-critical]').forEach(el => {
el.style.display = 'none';
});
// 2. 降低图片质量
document.querySelectorAll('img').forEach(img => {
if (!img.hasAttribute('data-critical')) {
img.loading = 'lazy';
img.decoding = 'async';
}
});
// 3. 禁用复杂动画
document.body.classList.add('reduced-motion');
// 4. 延迟加载第三方脚本
this.deferThirdPartyScripts();
}
// 报告和监控
reportToMonitoring(violations) {
if (navigator.sendBeacon) {
const data = JSON.stringify({
type: 'performance_budget_violations',
violations,
timestamp: Date.now(),
url: window.location.href,
userAgent: navigator.userAgent
});
navigator.sendBeacon('/api/performance-violations', data);
}
}
reportToErrorTracking(violations) {
// 发送到错误追踪服务(如Sentry)
if (window.Sentry) {
violations.forEach(violation => {
Sentry.captureMessage(`性能预算违规: ${violation.metric}`, {
level: 'warning',
extra: violation
});
});
}
}
sendCriticalAlert(violations) {
// 发送关键警报
const alertData = {
title: '关键性能预算违规',
body: `检测到 ${violations.length} 个关键违规`,
violations: violations.map(v => ({
metric: v.metric,
exceededBy: v.exceededBy,
score: v.severityScore
})),
timestamp: Date.now()
};
// 发送到监控系统
fetch('/api/critical-alerts', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(alertData),
keepalive: true // 确保在页面卸载时也能发送
});
}
// 辅助方法
getMetricValue(metricName) {
const entries = performance.getEntriesByName(metricName);
return entries.length > 0 ? entries[0].startTime : null;
}
async calculateFPS() {
return new Promise(resolve => {
let frames = 0;
let start = performance.now();
const count = () => {
frames++;
const now = performance.now();
if (now - start >= 1000) {
resolve(frames);
} else {
requestAnimationFrame(count);
}
};
requestAnimationFrame(count);
});
}
getMemoryUsage() {
if ('memory' in performance) {
const mem = performance.memory;
return {
used: mem.usedJSHeapSize,
total: mem.totalJSHeapSize,
limit: mem.jsHeapSizeLimit,
percentage: mem.usedJSHeapSize / mem.jsHeapSizeLimit
};
}
return null;
}
// 仪表板更新
updateDashboard(violations) {
// 更新实时仪表板
const dashboard = document.getElementById('performance-dashboard');
if (dashboard) {
this.renderDashboard(dashboard, violations);
}
}
renderDashboard(container, violations) {
const allViolations = Object.values(violations).flat();
const totalScore = allViolations.reduce((sum, v) => sum + v.severityScore, 0);
container.innerHTML = `
<div class="performance-dashboard">
<h3>性能预算监控</h3>
<div class="score ${totalScore > 50 ? 'warning' : totalScore > 20 ? 'caution' : 'good'}">
风险评分: ${totalScore.toFixed(1)}
</div>
${allViolations.length > 0 ? `
<div class="violations">
<h4>违规项 (${allViolations.length})</h4>
<ul>
${allViolations.map(v => ` <li class="severity-${Math.floor(v.severityScore / 20)}"> ${v.metric}: ${v.value.toFixed(1)} (预算: ${v.budget}, 超出: ${v.exceededBy}%) </li> `).join('')}
</ul>
</div>
` : '<div class="all-good">✅ 所有指标都在预算内</div>'}
</div>
`;
}
// 配置管理
loadConfiguration() {
try {
const saved = localStorage.getItem('performance-budget-config');
if (saved) {
const config = JSON.parse(saved);
this.budgets = { ...this.budgets, ...config.budgets };
this.enforcement = config.enforcement || this.enforcement;
}
} catch (error) {
console.warn('加载性能预算配置失败:', error);
}
}
saveConfiguration() {
try {
const config = {
budgets: this.budgets,
enforcement: this.enforcement,
timestamp: Date.now()
};
localStorage.setItem('performance-budget-config', JSON.stringify(config));
} catch (error) {
console.warn('保存性能预算配置失败:', error);
}
}
// 清理
destroy() {
if (this.monitoringInterval) {
clearInterval(this.monitoringInterval);
}
// 保存最终状态
this.saveConfiguration();
}
}
// 使用性能预算管理器
document.addEventListener('DOMContentLoaded', () => {
// 初始化性能预算管理器
const budgetManager = new PerformanceBudgetManager({
enforcement: process.env.NODE_ENV === 'production' ? 'warning' : 'error',
budgets: {
// 可以根据项目需求覆盖默认预算
lcp: { max: 2500, weight: 1.0 }, // 更严格的LCP预算
resources: {
js: { max: 200 * 1024, weight: 0.9 }, // 200KB JS预算
images: { max: 2 * 1024 * 1024, weight: 0.8 } // 2MB图片预算
}
}
});
// 页面卸载前保存数据
window.addEventListener('beforeunload', () => {
budgetManager.destroy();
});
// 开发工具:手动检查预算
if (process.env.NODE_ENV === 'development') {
window.checkPerformanceBudget = () => budgetManager.checkBudgets();
// 添加性能预算面板到页面
const panel = document.createElement('div');
panel.id = 'performance-dashboard';
panel.style.cssText = `
position: fixed;
top: 10px;
right: 10px;
background: white;
border: 1px solid #ddd;
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 10000;
max-width: 300px;
font-family: monospace;
font-size: 12px;
`;
document.body.appendChild(panel);
}
});
实施建议
-
高回报优化:
- ✅ 启用Gzip/Brotli压缩
- ✅ 优化图片(转换为WebP/AVIF格式)
- ✅ 配置合理的缓存策略
- ✅ 使用CDN加速静态资源
-
中期优化:
- 🔄 实施代码分割和懒加载
- 🔄 优化关键渲染路径
- 🔄 减少JavaScript包体积
- 🔄 实现虚拟列表处理大数据
-
长期维护:
- 📊 建立性能监控体系
- 📊 设置性能预算和CI检查
- 📊 定期进行性能审计
- 📊 建立性能文化(团队重视性能)