【性能提升300%】仿1688首页的Webpack优化全记录
一、项目背景与挑战
1.1 1688首页业务特点分析
1688作为阿里巴巴旗下的B2B批发平台,其首页承载着海量商家和商品的展示需求,具有以下典型特征:
• 海量商品信息:首页需要展示数千个SKU的商品卡片,每个卡片包含图片、标题、价格、起批量等信息
• 复杂筛选体系:支持按价格、产地、经营模式、实力商家等多维度筛选
• 实时供需匹配:供应商和采购商的实时对接,需要快速响应
• 多语言多币种:面向全球买家,支持多语言和货币切换
• 商家认证体系:诚信通、实力商家、超级工厂等认证标识展示
• 移动端适配:PC端和移动端体验差异巨大,需要响应式设计
1.2 仿1688首页的性能痛点
在开发仿1688首页过程中,我们遇到了典型的B2B电商平台性能挑战:
┌─────────────────────────────────────────────────────────────────────┐
│ 仿1688首页性能瓶颈分析 │
├─────────────────────────────────────────────────────────────────────┤
│ 📊 Bundle体积爆炸 │
│ • vendor.js: 2.8MB → 加载时间4.2s │
│ • main.js: 1.6MB → 解析时间1.8s │
│ • 总资源数: 156个 → HTTP请求过多 │
│ │
│ 🖼️ 商品图片资源臃肿 │
│ • 商品主图: 平均800KB/张 × 50张 = 40MB │
│ • 缩略图未优化: 200KB/张 × 200张 = 40MB │
│ • 图片格式不统一: JPG/PNG/WEBP混用 │
│ │
│ ⚡ 首屏渲染缓慢 │
│ • DOMContentLoaded: 3.2s │
│ • LCP (最大内容绘制): 4.8s │
│ • FID (首次输入延迟): 280ms │
│ • TTI (可交互时间): 5.1s │
│ │
│ 🔄 开发体验问题 │
│ • 热更新时间: 8-12s (HMR) │
│ • 生产构建时间: 3-4分钟 │
│ • SourceMap生成: 导致构建时间翻倍 │
│ │
│ 📱 移动端性能问题 │
│ • 3G网络下首屏: 12.5s │
│ • 内存占用过高: 移动端崩溃率3.2% │
│ • 滚动卡顿: 商品列表FPS < 30 │
└─────────────────────────────────────────────────────────────────────┘
1.3 性能目标设定
基于业务需求和用户体验,我们设定了明确的优化目标:
指标 优化前 目标值 提升幅度
首屏加载时间 4.8s 1.2s 75%
包体积 (gzip后) 4.4MB 1.1MB 75%
构建时间 240s 45s 81%
热更新时间 10s 1.5s 85%
Lighthouse评分 52分 90分 73%
移动端3G加载 12.5s 3.5s 72%
二、Webpack配置深度优化
2.1 基础配置架构重构
// webpack.config.js - 优化后的完整配置
const path = require('path');
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
const TerserPlugin = require('terser-webpack-plugin');
const OptimizeCSSAssetsPlugin = require('optimize-css-assets-webpack-plugin');
const HtmlWebpackPlugin = require('html-webpack-plugin');
const CompressionPlugin = require('compression-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
const WorkboxPlugin = require('workbox-webpack-plugin');
const MomentLocalesPlugin = require('moment-locales-webpack-plugin');
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const ThreadLoader = require('thread-loader');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
// 环境判断
const isProduction = process.env.NODE_ENV === 'production';
const isDevelopment = process.env.NODE_ENV === 'development';
// 路径配置
const PATHS = {
src: path.resolve(dirname, 'src'),
dist: path.resolve(dirname, 'dist'),
public: path.resolve(dirname, 'public'),
nodeModules: path.resolve(dirname, 'node_modules')
};
// 线程池配置(用于多线程编译)
const threadPool = {
workers: require('os').cpus().length - 1,
poolTimeout: isDevelopment ? 2000 : 500
};
module.exports = {
mode: isProduction ? 'production' : 'development',
// 入口配置优化
entry: {
main: isDevelopment
? ['react-hot-loader/patch', './src/index.tsx']
: './src/index.tsx',
// 将第三方库分离到单独chunk
vendor: {
import: [
'react',
'react-dom',
'redux',
'react-redux',
'react-router-dom',
'antd',
'axios'
],
filename: 'js/[name].[contenthash:8].js',
// 防止vendor chunk变化
enforce: true
}
},
output: {
path: PATHS.dist,
filename: isProduction
? 'js/[name].[contenthash:8].js'
: 'js/[name].js',
chunkFilename: isProduction
? 'js/[name].[contenthash:8].chunk.js'
: 'js/[name].chunk.js',
publicPath: isProduction ? 'https://cdn.1688clone.com/' : '/',
// 预加载提示
crossOriginLoading: 'anonymous',
// 清理旧文件
clean: true
},
resolve: {
extensions: ['.tsx', '.ts', '.js', '.jsx', '.json'],
alias: {
'@': PATHS.src,
'@components': path.resolve(PATHS.src, 'components'),
'@pages': path.resolve(PATHS.src, 'pages'),
'@utils': path.resolve(PATHS.src, 'utils'),
'@store': path.resolve(PATHS.src, 'store'),
'@services': path.resolve(PATHS.src, 'services'),
// React相关别名优化
'react': path.resolve(PATHS.nodeModules, 'react/cjs/react.production.min.js'),
'react-dom': path.resolve(PATHS.nodeModules, 'react-dom/cjs/react-dom.production.min.js'),
// 移除moment locales减少体积
'moment/locale': path.resolve(__dirname, 'empty-module.js')
},
// 模块解析优化
modules: [
PATHS.src,
'node_modules'
],
// 缓存已解析的模块
unsafeCache: true
},
optimization: {
minimize: isProduction,
minimizer: [
// JS压缩优化
new TerserPlugin({
terserOptions: {
parse: {
ecma: 2015
},
compress: {
ecma: 5,
warnings: false,
comparisons: false,
inline: 2,
drop_console: true,
drop_debugger: true,
pure_funcs: ['console.log', 'console.info']
},
mangle: {
safari10: true
},
output: {
ecma: 5,
comments: false,
ascii_only: true
}
},
parallel: threadPool.workers,
extractComments: false,
cache: true
}),
// CSS压缩
new OptimizeCSSAssetsPlugin({
cssProcessorOptions: {
parser: require('postcss-safe-parser'),
map: false
}
})
],
// 代码分割策略
splitChunks: {
chunks: 'all',
maxInitialRequests: 20,
maxAsyncRequests: 30,
minSize: 20000,
minRemainingSize: 0,
minChunks: 1,
maxSize: 244000,
cacheGroups: {
// 第三方库分离
vendors: {
test: /[\\/]node_modules[\\/]/,
priority: 30,
name: 'vendors',
reuseExistingChunk: true,
chunks: 'all'
},
// antd组件库单独打包
antd: {
test: /[\\/]node_modules[\\/]antd[\\/]/,
priority: 25,
name: 'antd',
reuseExistingChunk: true
},
// 工具函数库
utils: {
test: /[\\/]node_modules[\\/](lodash|moment|dayjs|axios)[\\/]/,
priority: 20,
name: 'utils',
reuseExistingChunk: true
},
// 公共组件
common: {
test: /[\\/]src[\\/]components[\\/]/,
priority: 15,
name: 'common',
minChunks: 2,
reuseExistingChunk: true
},
// 样式文件
styles: {
test: /\.(css|less|scss)$/,
priority: 10,
name: 'styles',
chunks: 'all',
enforce: true
}
}
},
// 运行时chunk提取
runtimeChunk: {
name: 'runtime'
},
// Tree Shaking优化
usedExports: true,
sideEffects: true,
// 模块合并优化
concatenateModules: true,
// 模块ID优化
moduleIds: isProduction ? 'deterministic' : 'named'
},
module: {
rules: [
// TypeScript/JavaScript处理
{
test: /.(tsx?|jsx?)$/,
exclude: PATHS.nodeModules,
use: [
// 多线程babel处理
{
loader: 'thread-loader',
options: threadPool
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
compact: isProduction,
presets: [
['@babel/preset-env', {
targets: {
browsers: ['> 1%', 'last 2 versions', 'not dead', 'not ie 11']
},
useBuiltIns: 'usage',
corejs: 3,
modules: false
}],
'@babel/preset-react',
'@babel/preset-typescript'
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-proposal-object-rest-spread',
'@babel/plugin-transform-runtime',
isDevelopment && 'react-hot-loader/babel',
// Tree shaking优化
['transform-remove-console', { exclude: ['error', 'warn'] }]
].filter(Boolean)
}
}
]
},
// CSS/Less处理
{
test: /\.(css|less)$/,
exclude: /\.module\.(css|less)$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
importLoaders: 2,
sourceMap: !isProduction
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: [
'autoprefixer',
'cssnano'
]
},
sourceMap: !isProduction
}
},
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true,
modifyVars: {
// 1688主题色配置
'@primary-color': '#ff6a00',
'@link-color': '#ff6a00',
'@success-color': '#52c41a',
'@warning-color': '#faad14',
'@error-color': '#f5222d',
'@font-size-base': '14px',
'@heading-color': 'rgba(0, 0, 0, 0.85)',
'@text-color': 'rgba(0, 0, 0, 0.65)',
'@text-color-secondary': 'rgba(0, 0, 0, 0.45)',
'@border-radius-base': '4px',
'@box-shadow-base': '0 2px 8px rgba(0, 0, 0, 0.15)'
}
}
}
}
]
},
// CSS Modules处理
{
test: /\.module\.(css|less)$/,
use: [
isProduction ? MiniCssExtractPlugin.loader : 'style-loader',
{
loader: 'css-loader',
options: {
modules: {
localIdentName: isProduction
? '[hash:base64:8]'
: '[name]__[local]--[hash:base64:5]'
},
importLoaders: 2
}
},
{
loader: 'postcss-loader',
options: {
postcssOptions: {
plugins: ['autoprefixer']
}
}
},
{
loader: 'less-loader',
options: {
lessOptions: {
javascriptEnabled: true
}
}
}
]
},
// 图片资源处理
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8KB以下转base64
}
},
generator: {
filename: 'images/[name].[contenthash:8][ext]',
publicPath: isProduction
? 'https://cdn.1688clone.com/images/'
: '../images/'
},
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 75
},
optipng: {
enabled: true
},
pngquant: {
quality: [0.65, 0.9],
speed: 4
},
gifsicle: {
interlaced: false
},
webp: {
quality: 75
}
}
}
]
},
// 字体文件处理
{
test: /\.(woff2?|eot|ttf|otf)$/,
type: 'asset/resource',
generator: {
filename: 'fonts/[name].[contenthash:8][ext]',
publicPath: isProduction
? 'https://cdn.1688clone.com/fonts/'
: '../fonts/'
}
},
// 视频文件处理
{
test: /\.(mp4|webm|ogg|mp3|wav|flac|aac)$/,
type: 'asset/resource',
generator: {
filename: 'media/[name].[contenthash:8][ext]'
}
}
]
},
plugins: [
// HTML模板处理
new HtmlWebpackPlugin({
template: path.resolve(PATHS.public, 'index.html'),
filename: 'index.html',
inject: true,
minify: isProduction ? {
removeComments: true,
collapseWhitespace: true,
removeRedundantAttributes: true,
useShortDoctype: true,
removeEmptyAttributes: true,
removeStyleLinkTypeAttributes: true,
keepClosingSlash: true,
minifyJS: true,
minifyCSS: true,
minifyURLs: true
} : false,
chunksSortMode: 'auto',
// 预加载关键资源
links: [
{ rel: 'preconnect', href: 'https://cdn.1688clone.com' },
{ rel: 'dns-prefetch', href: 'https://cdn.1688clone.com' }
]
}),
// CSS提取
new MiniCssExtractPlugin({
filename: isProduction
? 'css/[name].[contenthash:8].css'
: 'css/[name].css',
chunkFilename: isProduction
? 'css/[name].[contenthash:8].chunk.css'
: 'css/[name].chunk.css',
ignoreOrder: true
}),
// 生产环境插件
...(isProduction ? [
// Gzip压缩
new CompressionPlugin({
algorithm: 'gzip',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8,
deleteOriginalAssets: false
}),
// Brotli压缩
new CompressionPlugin({
algorithm: 'brotliCompress',
test: /\.(js|css|html|svg)$/,
threshold: 8192,
minRatio: 0.8,
compressionOptions: {
level: 11
}
}),
// moment多语言移除
new MomentLocalesPlugin({
localesToKeep: ['zh-cn', 'en']
}),
// PWA支持
new WorkboxPlugin.GenerateSW({
clientsClaim: true,
skipWaiting: true,
maximumFileSizeToCacheInBytes: 5 * 1024 * 1024,
runtimeCaching: [
{
urlPattern: /^https:\/\/api\.1688clone\.com\/.*/i,
handler: 'NetworkFirst',
options: {
cacheName: 'api-cache',
expiration: {
maxEntries: 50,
maxAgeSeconds: 5 * 60
}
}
},
{
urlPattern: /\.(?:png|jpg|jpeg|svg|gif|webp)$/i,
handler: 'CacheFirst',
options: {
cacheName: 'images-cache',
expiration: {
maxEntries: 100,
maxAgeSeconds: 30 * 24 * 60 * 60
}
}
},
{
urlPattern: /\.(?:js|css)$/i,
handler: 'StaleWhileRevalidate',
options: {
cacheName: 'static-resources'
}
}
]
}),
// 包体积分析
process.env.ANALYZE && new BundleAnalyzerPlugin({
analyzerMode: 'static',
openAnalyzer: true,
reportFilename: 'bundle-report.html',
defaultSizes: 'gzip'
})
] : []),
// 开发环境插件
...(isDevelopment ? [
// 热更新支持
new HardSourceWebpackPlugin({
environmentHash: {
root: process.cwd(),
directories: [],
files: ['package-lock.json', 'yarn.lock']
}
}),
// TypeScript类型检查(独立进程)
new ForkTsCheckerWebpackPlugin({
typescript: {
diagnosticOptions: {
semantic: true,
syntactic: true
}
},
async: true
})
] : [])
],
// 开发服务器配置
devServer: isDevelopment ? {
port: 3000,
hot: true,
liveReload: false,
compress: true,
historyApiFallback: true,
static: {
directory: PATHS.public
},
client: {
overlay: {
errors: true,
warnings: false
},
progress: true
},
devMiddleware: {
writeToDisk: false
},
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
secure: false
}
}
} : undefined,
// SourceMap配置
devtool: isProduction
? 'hidden-source-map'
: 'eval-cheap-module-source-map',
// 性能提示
performance: {
hints: isProduction ? 'warning' : false,
maxEntrypointSize: 512000,
maxAssetSize: 512000
},
// 统计信息
stats: isProduction ? 'minimal' : 'normal',
// 实验性功能
experiments: {
topLevelAwait: true,
backCompat: false
},
// 缓存配置
cache: {
type: 'filesystem',
buildDependencies: {
config: [filename]
},
cacheDirectory: path.resolve(dirname, '.webpack-cache')
}
};
2.2 关键优化配置详解
2.2.1 智能代码分割策略
// splitChunks配置深度优化
splitChunks: {
chunks: 'all',
// 防止过度分割
maxInitialRequests: 20,
maxAsyncRequests: 30,
// 最小chunk大小
minSize: 20000,
// 防止重复打包
minRemainingSize: 0,
minChunks: 1,
// 单个chunk最大体积
maxSize: 244000,
cacheGroups: {
// 核心第三方库 - 高频使用
reactVendor: {
test: /[\/]node_modules\/[\/]/,
name: 'react-vendor',
priority: 40,
chunks: 'all',
reuseExistingChunk: true,
enforce: true
},
// UI组件库 - 中等频率
uiLibrary: {
test: /[\\/]node_modules[\\/](antd|@ant-design|rc-)[\\/]/,
name: 'ui-library',
priority: 35,
chunks: 'all',
reuseExistingChunk: true
},
// 工具函数库 - 低频但必需
utilityVendor: {
test: /[\\/]node_modules[\\/](lodash|moment|dayjs|axios|qs)[\\/]/,
name: 'utility-vendor',
priority: 30,
chunks: 'all',
reuseExistingChunk: true
},
// 数据处理库
dataVendor: {
test: /[\\/]node_modules[\\/](immutable|redux|mobx|recoil)[\\/]/,
name: 'data-vendor',
priority: 25,
chunks: 'async',
reuseExistingChunk: true
},
// 图表库 - 按需加载
chartVendor: {
test: /[\\/]node_modules[\\/](echarts|recharts|d3)[\\/]/,
name: 'chart-vendor',
priority: 20,
chunks: 'async',
reuseExistingChunk: true
},
// 公共组件 - 项目内部
commonComponents: {
test: /[\\/]src[\\/]components[\\/](Common|Base|UI)[\\/]/,
name: 'common-components',
priority: 15,
minChunks: 2,
chunks: 'all',
reuseExistingChunk: true
},
// 业务组件
businessComponents: {
test: /[\\/]src[\\/]components[\\/](Business|Feature)[\\/]/,
name: 'business-components',
priority: 10,
minChunks: 3,
chunks: 'all',
reuseExistingChunk: true
},
// 样式文件
styles: {
test: /\.(css|less|scss)$/,
name: 'styles',
priority: 5,
chunks: 'all',
enforce: true,
reuseExistingChunk: true
},
// 默认分组
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true
}
}
}
2.2.2 图片资源优化流水线
// 图片处理完整配置
{
test: /.(png|jpe?g|gif|webp|svg|bmp|ico)$/i,
oneOf: [
// 小图标 -> Data URI
{
resourceQuery: /^\?inline$/,
type: 'asset/inline'
},
// SVG Sprite
{
test: /.svg$/,
resourceQuery: /^\?sprite$/,
loader: 'svg-sprite-loader',
options: {
symbolId: 'icon-[name]'
}
},
// 普通图片处理
{
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024 // 8KB阈值
}
},
generator: {
filename: 'images/[name].[contenthash:8][ext]',
publicPath: isProduction
? 'https://cdn.1688clone.com/images/'
: '../images/'
},
use: [
// 图片压缩处理
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 75,
// 针对商品图片优化
quantTable: 2
},
optipng: {
enabled: true,
optimizationLevel: 7
},
pngquant: {
quality: [0.65, 0.9],
speed: 4,
strip: true
},
gifsicle: {
interlaced: false,
optimizationLevel: 3
},
webp: {
quality: 80,
// 针对商品图片的webp转换
method: 6
},
// 雪碧图生成
svgo: {
plugins: [
{ name: 'removeTitle', active: true },
{ name: 'removeDesc', active: true },
{ name: 'removeViewBox', active: false },
{ name: 'removeEmptyAttrs', active: true }
]
}
}
}
]
}
]
}
2.2.3 多线程构建优化
// thread-loader配置优化
{
test: /.(tsx?|jsx?)$/,
exclude: /node_modules/,
use: [
{
loader: 'thread-loader',
options: {
workers: require('os').cpus().length - 1,
workerParallelJobs: 50,
poolTimeout: isDevelopment ? 2000 : 500,
poolParallelJobs: 50,
name: 'js-pool'
}
},
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
cacheCompression: false,
compact: isProduction,
presets: [
['@babel/preset-env', {
targets: {
browsers: ['> 1%', 'last 2 versions', 'not dead']
},
useBuiltIns: 'usage',
corejs: 3,
modules: false
}]
],
plugins: [
'@babel/plugin-proposal-class-properties',
'@babel/plugin-transform-runtime'
]
}
}
]
}
// ForkTsCheckerWebpackPlugin配置
new ForkTsCheckerWebpackPlugin({
typescript: {
diagnosticOptions: {
semantic: true,
syntactic: true,
declaration: false,
global: false
},
mode: 'write-references'
},
eslint: {
files: './src//*.{ts,tsx,js,jsx}'
},
formatter: 'codeframe',
logger: {
infrastructure: 'silent',
issues: 'console'
},
async: true,
issue: {
include: [
{ file: './src//' }
],
exclude: [
{ file: './src/**/.spec.ts' },
{ file: './src/*/.test.ts' }
]
}
})
三、依赖管理与Tree Shaking
3.1 精细化依赖分析
// webpack-analyzer.js - 依赖分析报告脚本
const webpack = require('webpack');
const { BundleAnalyzerPlugin } = require('webpack-bundle-analyzer');
const fs = require('fs');
const path = require('path');
class DependencyAnalyzer {
constructor(config) {
this.config = config;
this.dependencyGraph = new Map();
this.sizeReport = [];
}
async analyze() {
console.log('🔍 开始依赖分析...\n');
// 1. 生成构建报告
const stats = await this.buildWithStats();
// 2. 解析模块信息
this.parseModuleStats(stats);
// 3. 生成详细报告
this.generateReport();
// 4. 输出优化建议
this.generateOptimizationSuggestions();
}
async buildWithStats() {
return new Promise((resolve, reject) => {
const compiler = webpack({
...this.config,
plugins: [
...this.config.plugins,
new BundleAnalyzerPlugin({
analyzerMode: 'disabled',
generateStatsFile: true,
statsFilename: path.resolve(__dirname, 'stats.json')
})
]
});
compiler.run((err, stats) => {
if (err) {
reject(err);
return;
}
resolve(stats);
});
});
}
parseModuleStats(stats) {
const jsonStats = stats.toJson({
source: false,
reasons: true,
optimizationBailout: true,
chunkModules: true,
nestedModules: true
});
// 分析chunks
jsonStats.chunks.forEach(chunk => {
this.dependencyGraph.set(chunk.id, {
files: chunk.files,
modules: chunk.modules,
size: chunk.size
});
});
// 分析模块大小
jsonStats.modules.forEach(module => {
const moduleInfo = {
name: module.name,
size: module.size,
package: this.extractPackageName(module.name),
reasons: module.reasons?.map(r => r.moduleName) || [],
optimizationBailout: module.optimizationBailout || []
};
this.sizeReport.push(moduleInfo);
});
// 按包名分组
this.packageAnalysis = this.sizeReport.reduce((acc, module) => {
const pkg = module.package || 'unknown';
if (!acc[pkg]) {
acc[pkg] = { size: 0, modules: [], bailouts: [] };
}
acc[pkg].size += module.size;
acc[pkg].modules.push(module.name);
if (module.optimizationBailout.length > 0) {
acc[pkg].bailouts.push(...module.optimizationBailout);
}
return acc;
}, {});
// 排序
this.packageAnalysis = Object.entries(this.packageAnalysis)
.sort(([, a], [, b]) => b.size - a.size)
.reduce((acc, [key, value]) => {
acc[key] = value;
return acc;
}, {});
}
extractPackageName(moduleName) {
// 提取npm包名
const match = moduleName.match(/^[.\/~]*(node_modules\/)?((?:@[^/]+\/)?[^/]+)/);
return match ? match[2] : 'project';
}
generateReport() {
console.log('='.repeat(80));
console.log('📦 依赖分析报告');
console.log('='.repeat(80));
// 总体统计
const totalSize = Object.values(this.packageAnalysis)
.reduce((sum, pkg) => sum + pkg.size, 0);
console.log(`\n📊 总体统计:`);
console.log(` 总模块数: ${this.sizeReport.length}`);
console.log(` 总大小: ${(totalSize / 1024 / 1024).toFixed(2)} MB`);
console.log(` 包数量: ${Object.keys(this.packageAnalysis).length}`);
// Top 20大依赖
console.log('\n🔝 Top 20 最大依赖包:');
console.log('-'.repeat(80));
Object.entries(this.packageAnalysis)
.slice(0, 20)
.forEach(([pkg, info], index) => {
const sizeMB = (info.size / 1024 / 1024).toFixed(2);
const percentage = ((info.size / totalSize) * 100).toFixed(1);
const bar = '█'.repeat(Math.min(Math.floor(percentage / 2), 50));
console.log(
`${(index + 1).toString().padStart(2)}. ${pkg.padEnd(30)} ` +
`${sizeMB.padStart(8)} MB ` +
`${percentage.padStart(5)}% ` +
bar
);
});
// Tree Shaking问题检测
console.log('\n⚠️ Tree Shaking 问题分析:');
console.log('-'.repeat(80));
const bailoutPatterns = {
'CommonJS': /CommonJS/,
'side effects': /side effects/,
'multiple components': /more than one component/,
'minified': /minified/,
'harmony': /in harmony/
};
Object.entries(this.packageAnalysis).forEach(([pkg, info]) => {
if (info.bailouts.length > 0) {
console.log(`\n📦 ${pkg}:`);
info.bailouts.forEach(bailout => {
Object.entries(bailoutPatterns).forEach(([pattern, regex]) => {
if (regex.test(bailout)) {
console.log(` ⚠️ ${pattern}: ${bailout}`);
}
});
});
}
});
// 重复依赖检测
console.log('\n🔄 重复依赖检测:');
console.log('-'.repeat(80));
const duplicates = this.findDuplicateDependencies();
duplicates.forEach(({ pkg, paths }) => {
console.log(`\n📦 ${pkg}:`);
paths.slice(0, 5).forEach(path => {
console.log(` 📁 ${path}`);
});
if (paths.length > 5) {
console.log(` ... 还有 ${paths.length - 5} 个路径`);
}
});
}
findDuplicateDependencies() {
const pkgPaths = {};
this.sizeReport.forEach(module => {
const pkg = module.package;
if (pkg && pkg !== 'project') {
if (!pkgPaths[pkg]) {
pkgPaths[pkg] = [];
}
pkgPaths[pkg].push(module.name);
}
});
return Object.entries(pkgPaths)
.filter(([, paths]) => paths.length > 1)
.sort(([, a], [, b]) => b.length - a.length);
}
generateOptimizationSuggestions() {
console.log('\n💡 优化建议:');
console.log('='.repeat(80));
const suggestions = [];
// 大依赖优化建议
Object.entries(this.packageAnalysis).forEach(([pkg, info]) => {
const sizeMB = info.size / 1024 / 1024;
if (pkg === 'moment' && sizeMB > 200) {
suggestions.push({
priority: 'high',
category: '替换库',
suggestion: '用 dayjs 替换 moment.js',
impact: `~${sizeMB.toFixed(0)}MB 节省`,
implementation: `
// 安装
npm install dayjs
// 替换导入
- import moment from 'moment';
- import dayjs from 'dayjs';
// 兼容API
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
`
});
}
if (pkg === 'lodash' && sizeMB > 150) {
suggestions.push({
priority: 'high',
category: '按需引入',
suggestion: '使用 lodash-es + babel-plugin-lodash',
impact: `~${sizeMB.toFixed(0)}MB 节省`,
implementation: `
// 安装
npm install lodash-es babel-plugin-lodash
// babel.config.js
plugins: ['lodash']
// 代码中使用
- import _ from 'lodash';
import debounce from 'lodash-es/debounce';
`}); } if (pkg === 'antd' && sizeMB > 500) { suggestions.push({ priority: 'high', category: '按需加载', suggestion: '启用 antd 的 babel-plugin-import', impact: `~${(sizeMB * 0.7).toFixed(0)}MB 节省`, implementation: `// babel.config.js
plugins: [
['import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: 'css'
}]
]
// 代码中使用
- import { Button, Table } from 'antd';
- import Button from 'antd/es/button';
import 'antd/es/button/style';
`}); } if (pkg === 'echarts' && sizeMB > 400) { suggestions.push({ priority: 'medium', category: '按需加载', suggestion: '只引入需要的 echarts 模块', impact: `~${(sizeMB * 0.8).toFixed(0)}MB 节省`, implementation: `// 代替完整引入
- import * as echarts from 'echarts';
// 按需引入
import * as echarts from 'echarts/core';
import { BarChart, LineChart } from 'echarts/charts';
import {
TitleComponent,
TooltipComponent,
GridComponent
} from 'echarts/components';
import { CanvasRenderer } from 'echarts/renderers';
echarts.use([
BarChart,
LineChart,
TitleComponent,
TooltipComponent,
GridComponent,
CanvasRenderer
]);
`
});
}
});
// Tree Shaking优化建议
suggestions.push({
priority: 'medium',
category: 'Tree Shaking',
suggestion: '确保所有库都支持 ES Module',
impact: '减小 bundle 体积',
implementation: `
// package.json
{
"sideEffects": [
".css",
".less",
"**/tree-shaking-problem.js"
]
}
// webpack.config.js
optimization: {
usedExports: true,
sideEffects: true,
concatenateModules: true
}
`
});
// 代码分割建议
suggestions.push({
priority: 'medium',
category: '代码分割',
suggestion: '动态导入大型组件',
impact: '减少首屏加载时间',
implementation: `
// 代替静态导入
- import HeavyComponent from './HeavyComponent';
// 使用动态导入
const HeavyComponent = React.lazy(() => import('./HeavyComponent'));
// 配合 Suspense 使用
}>
`
});
// 输出建议
suggestions
.sort((a, b) =>
(a.priority === 'high' ? 0 : 1) - (b.priority === 'high' ? 0 : 1)
)
.forEach(({ priority, category, suggestion, impact, implementation }) => {
const priorityIcon = priority === 'high' ? '🔴' : priority === 'medium' ? '🟡' : '🟢';
console.log(`\n${priorityIcon} [${priority.toUpperCase()}] ${category}`);
console.log(` 💡 ${suggestion}`);
console.log(` 📈 预期收益: ${impact}`);
console.log(` 🛠️ 实现方式:`);
console.log(implementation);
});
}
}
// 运行分析
const config = require('./webpack.config.js');
const analyzer = new DependencyAnalyzer(config);
analyzer.analyze().catch(console.error);
3.2 精细化Tree Shaking配置
// babel.config.js - Tree Shaking优化配置
module.exports = function(api) {
api.cache(true);
const isProduction = process.env.NODE_ENV === 'production';
return {
presets: [
['@babel/preset-env', {
targets: {
browsers: ['> 1%', 'last 2 versions', 'not dead']
},
useBuiltIns: 'usage',
corejs: 3,
modules: false, // 保留ES Module格式供Tree Shaking
bugfixes: true
}],
'@babel/preset-react',
'@babel/preset-typescript'
],
plugins: [
// Class属性支持
'@babel/plugin-proposal-class-properties',
// 对象展开运算符
'@babel/plugin-proposal-object-rest-spread',
// 运行时转换
['@babel/plugin-transform-runtime', {
corejs: 3,
helpers: true,
regenerator: true,
useESModules: true // 使用ES Module版本的helpers
}],
// lodash按需加载
'lodash',
// antd按需加载
['import', {
libraryName: 'antd',
libraryDirectory: 'es',
style: true
}, 'antd'],
// 移除console(仅生产环境)
isProduction && ['transform-remove-console', {
exclude: ['error', 'warn', 'info']
}],
// Tree Shaking辅助
isProduction && 'babel-plugin-transform-remove-unused-imports',
// 可选链操作符转换
'@babel/plugin-proposal-optional-chaining',
// 空值合并操作符转换
'@babel/plugin-proposal-nullish-coalescing-operator'
].filter(Boolean),
// 忽略不必要的polyfill
ignore: [
'./src/**/*.test.ts',
'./src/**/*.spec.ts'
]
};
};
// package.json - sideEffects配置
{
"name": "1688-clone",
"sideEffects": [
".css",
".less",
"*.scss",
"src/utils/polyfills.ts",
"src/hooks/useTheme.ts",
"src/components/GlobalStyles/index.tsx"
]
}
四、图片资源专项优化
4.1 1688商品图片优化策略
// src/utils/imageOptimizer.ts - 图片优化工具类
interface ImageOptimizationConfig {
width?: number;
height?: number;
quality?: number;
format?: 'auto' | 'webp' | 'avif' | 'jpg' | 'png';
fit?: 'cover' | 'contain' | 'fill' | 'inside' | 'outside';
sharpen?: boolean;
watermark?: boolean;
}
class ImageOptimizer {
private static readonly CDN_BASE_URL = 'https://cdn.1688clone.com/image-process';
private static readonly DEFAULT_QUALITY = 85;
private static readonly MOBILE_QUALITY = 75;
private static readonly THUMBNAIL_QUALITY = 70;
/**
- 生成1688风格的商品图片URL
支持智能裁剪、水印、质量调节
*/
static generateProductImageUrl(
originalUrl: string,
config: ImageOptimizationConfig = {}
): string {
const {
width,
height,
quality = this.DEFAULT_QUALITY,
format = 'auto',
fit = 'cover',
sharpen = false,
watermark = false
} = config;const params = new URLSearchParams({
url: encodeURIComponent(originalUrl),
...(width && { w: width.toString() }),
...(height && { h: height.toString() }),
q: quality.toString(),
fmt: format,
fit,
sharpen: sharpen.toString(),
watermark: watermark.toString(),
// 1688特色参数
brand_mode: 'wholesale',
detail_enhance: 'enabled',
color_accuracy: 'professional'
});return
${this.CDN_BASE_URL}/process?${params.toString()};
}/**
- 生成商品列表缩略图URL
针对列表场景优化,平衡质量和加载速度
*/
static generateThumbnailUrl(originalUrl: string, size: 'small' | 'medium' | 'large' = 'medium'): string {
const sizeConfig = {
small: { width: 160, height: 160, quality: this.THUMBNAIL_QUALITY },
medium: { width: 280, height: 280, quality: this.DEFAULT_QUALITY },
large: { width: 400, height: 400, quality: this.DEFAULT_QUALITY }
};const config = sizeConfig[size];
return this.generateProductImageUrl(originalUrl, {
...config,
fit: 'cover',
sharpen: true
});
}/**
- 生成商品详情主图URL
保持商品细节清晰,适合放大查看
*/
static generateDetailImageUrl(originalUrl: string, resolution: 'hd' | 'uhd' = 'hd'): string {
const resolutionConfig = {
hd: { width: 800, height: 800, quality: this.DEFAULT_QUALITY },
uhd: { width: 1600, height: 1600, quality: 95 }
};return this.generateProductImageUrl(originalUrl, {
...resolutionConfig[resolution],
fit: 'inside',
sharpen: true,
watermark: true
});
}/**
- 生成响应式图片sourceset
为不同屏幕尺寸提供最优图片
*/
static generateResponsiveSources(
originalUrl: string,
options: {
sizes?: number[];
format?: 'auto' | 'webp' | 'jpg';
quality?: number;
} = {}
): {
srcSet: string;
type: string;
media?: string;
}[] {
const {
sizes = [320, 640, 960, 1280, 1920],
format = 'auto',
quality = this.DEFAULT_QUALITY
} = options;return sizes.map((size, index) => {
const nextSize = sizes[index + 1];
const media = nextSize
?(max-width: ${nextSize}px)
: undefined;return {
srcSet: this.generateProductImageUrl(originalUrl, {width: size, quality, format}),
type: format === 'webp' ? 'image/webp' : 'image/jpeg',
media
};
});
}/**
检测浏览器支持的图片格式
*/
static detectSupportedFormats(): Promise<{
webp: boolean;
avif: boolean;
jxl: boolean;
}> {
return new Promise((resolve) => {
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');if (!ctx) {
resolve({ webp: false, avif: false, jxl: false });
return;
}const checkFormat = async (format: string): Promise => {
return new Promise((res) => {const dataUrl = canvas.toDataURL(`image/${format}`); res(dataUrl.indexOf(`data:image/${format}`) === 0);});
};Promise.all([
checkFormat('webp'),
checkFormat('avif'),
checkFormat('jxl')
]).then(([webp, avif, jxl]) => {
resolve({ webp, avif, jxl });
});
});
}/**
- 预加载关键商品图片
针对首屏商品进行优先级加载
*/
static preloadCriticalImages(images: string[], priority: 'high' | 'low' = 'high'): void {
images.slice(0, 6).forEach((url, index) => {
const link = document.createElement('link');
link.rel = 'preload';
link.as = 'image';
link.href = this.generateThumbnailUrl(url, 'medium');
link.setAttribute('fetchpriority', priority);
link.setAttribute('imagesrcset', this.generateResponsiveSources(url)
.map(s =>${s.srcSet} ${s.media ? s.media.replace(/[^\d]/g, '') : '1200w'})
.join(', '));document.head.appendChild(link);
});
}/**
- 图片懒加载观察器
针对商品列表优化
*/
static createLazyLoadObserver(
callback: (element: HTMLImageElement, url: string) => void,
options: IntersectionObserverInit = {}
): IntersectionObserver {
const defaultOptions: IntersectionObserverInit = {
rootMargin: '100px 0px',
threshold: 0.01,
...options
};return new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {const img = entry.target as HTMLImageElement; const originalUrl = img.dataset.src; if (originalUrl) { callback(img, originalUrl); observer.unobserve(img); }}
});
}, defaultOptions);
}
}
export default ImageOptimizer;
4.2 Webpack图片处理完整配置
// webpack.image.config.js - 专门的图片处理配置
const ImageMinimizerPlugin = require('image-minimizer-webpack-plugin');
const CopyWebpackPlugin = require('copy-webpack-plugin');
const ImageminWebpWebpackPlugin = require('imagemin-webp-webpack-plugin');
module.exports = {
module: {
rules: [
// 商品图片处理
{
test: /.(png|jpe?g|gif|webp|svg)$/i,
include: /src\/assets\/products/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 4 * 1024 // 4KB以下内联
}
},
generator: {
filename: 'images/products/[name].[contenthash:8][ext]',
publicPath: 'https://cdn.1688clone.com/images/products/'
},
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 80,
// 商品图片专用优化
trellisQuant: true,
overshootDeringing: true,
optimizeScans: true
},
optipng: {
enabled: true,
optimizationLevel: 7,
bitDepthReduction: true,
colorTypeReduction: true
},
pngquant: {
quality: [0.7, 0.9],
speed: 3,
strip: true,
dithering: false
},
gifsicle: {
interlaced: false,
optimizationLevel: 3
},
webp: {
quality: 85,
method: 6,
// 保留透明通道
lossless: false,
nearLossless: 100
}
}
}
]
},
// 图标和UI图片
{
test: /\.(png|jpe?g|gif|svg|ico)$/i,
include: /src\/assets\/icons|src\/assets\/ui/,
type: 'asset',
parser: {
dataUrlCondition: {
maxSize: 8 * 1024
}
},
generator: {
filename: 'images/ui/[name].[contenthash:8][ext]'
},
use: [
{
loader: 'svgo-loader',
options: {
plugins: [
{ name: 'removeTitle', active: true },
{ name: 'removeDesc', active: true },
{ name: 'removeViewBox', active: false },
{ name: 'removeEmptyAttrs', active: true },
{ name: 'removeEmptyContainers', active: true },
{ name: 'removeEmptyText', active: true },
{ name: 'removeEmptyText', active: true },
{ name: 'convertColors', params: { currentColor: true } },
{ name: 'removeUnknownsAndDefaults', active: true },
{ name: 'removeNonInheritableGroupAttrs', active: true },
{ name: 'removeUselessStrokeAndFill', active: true },
{ name: 'cleanupEnableBackground', active: true },
{ name: 'minifyStyles', active: true },
{ name: 'convertPathData', active: true },
{ name: 'convertTransform', active: true },
{ name: 'mergePaths', active: true },
{ name: 'convertShapeToPath', active: true }
]
}
}
]
},
// 背景图片
{
test: /\.(png|jpe?g|webp|svg)$/i,
include: /src\/assets\/backgrounds/,
type: 'asset',
generator: {
filename: 'images/backgrounds/[name].[contenthash:8][ext]'
},
use: [
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 75
},
webp: {
quality: 80
}
}
}
]
}
]
},
optimization: {
minimizer: [
// JavaScript压缩
new TerserPlugin({
terserOptions: {
compress: {
drop_console: true,
drop_debugger: true
}
}
}),
// 图片压缩优化器
new ImageMinimizerPlugin({
minimizer: {
implementation: ImageMinimizerPlugin.imageminMinify,
options: {
plugins: [
['mozjpeg', { quality: 80, progressive: true }],
['optipng', { optimizationLevel: 7 }],
['gifsicle', { interlaced: false }],
['webp', { quality: 85 }]
]
}
},
generator: [
{
// 生成WebP版本
implementation: ImageMinimizerPlugin.imageminGenerate,
options: {
plugins: ['webp']
}
}
]
})
]
},
plugins: [
// 复制静态图片资源
new CopyWebpackPlugin({
patterns: [
{
from: 'src/assets/products//*',
to: 'images/products/[name][ext]',
globOptions: {
ignore: ['/.md', '**/.txt']
}
},
{
from: 'src/assets/icons/*/',
to: 'images/icons/[name][ext]'
}
]
}),
// WebP生成插件
new ImageminWebpWebpackPlugin({
config: [{
test: /\.(jpe?g|png)/,
options: {
quality: 85,
mount: 'images/webp/'
}
}],
overrideExtension: true,
detailedLogs: false,
silent: true
})
]
};
五、构建性能优化
5.1 构建缓存策略
// webpack.cache.config.js - 高级缓存配置
const HardSourceWebpackPlugin = require('hard-source-webpack-plugin');
const InvalidatePlugin = require('webpack-invalidate-plugin');
const ModuleFederationPlugin = require('webpack/lib/container/ModuleFederationPlugin');
module.exports = {
cache: {
type: 'filesystem',
version: require('./package.json').version,
buildDependencies: {
config: [filename, 'webpack.config.js', 'babel.config.js'],
tsconfig: [path.resolve(dirname, 'tsconfig.json')]
},
cacheDirectory: path.resolve(__dirname, '.webpack-cache'),
// 持久化缓存配置
name: cache-${process.env.NODE_ENV}-${require('os').cpus().length}-cores,
// 压缩缓存
compression: 'gzip',
// 最大缓存大小
maxAge: 1000 60 60 24 7, // 7天
// 缓存存储策略
store: 'pack',
// IDB缓存(可选)
idleTimeout: 10000,
idleTimeoutForInitialStore: 0,
read: (cacheName, cacheEntry) => {
// 自定义读取逻辑
return Promise.resolve(cacheEntry);
},
write: (cacheName, cacheEntry, assets) => {
// 自定义写入逻辑
return Promise.resolve();
}
},
plugins: [
// 硬缓存插件 - 加速二次构建
new HardSourceWebpackPlugin({
environmentHash: {
root: process.cwd(),
directories: ['src', 'webpack.config.js', 'babel.config.js'],
files: [
'package-lock.json',
'yarn.lock',
'.eslintrc.js',
'.browserslistrc'
]
},
// 缓存配置
cachePrune: {
maxAge: 1000 60 60 24 7, // 7天
sizeThreshold: 500 1024 1024 // 500MB
},
// 并行处理
parallel: {
cache: true,
pool: require('os').cpus().length - 1
}
}),
// 开发环境热更新缓存
isDevelopment && new HardSourceWebpackPlugin.ExcludeModulePlugin([
{
test: /mini-css-extract-plugin[\\/]dist[\\/]loader/
}
]),
// 生产环境缓存清理
isProduction && new InvalidatePlugin(
/node_modules/,
/dist/,
/build/,
/coverage/,
/logs/,
/tmp/,
/temp/,
/\.git/,
/node_modules\.cache/,
/package-lock\.json/,
/yarn\.lock/,
/tsconfig\.tsbuildinfo/,
/.*\.log$/,
/.*\.map$/,
/.*\.hot-update\..*$/,
/.*\.swp$/,
/.*~$/
)
].filter(Boolean)
};
5.2 增量构建与并行处理
// webpack.parallel.config.js - 并行构建配置
const os = require('os');
const HappyPack = require('happypack');
const ParallelUglifyPlugin = require('webpack-parallel-uglify-plugin');
module.exports = {
module: {
rules: [
// 使用HappyPack并行处理JS
{
test: /.(tsx?|jsx?)$/,
exclude: /node_modules/,
use: 'happypack/loader?id=ts'
},
// 并行处理CSS
{
test: /\.(css|less)$/,
exclude: /node_modules/,
use: 'happypack/loader?id=styles'
},
// 并行处理图片
{
test: /\.(png|jpe?g|gif|webp|svg)$/,
use: 'happypack/loader?id=images'
}
]
},
plugins: [
// HappyPack JS处理
new HappyPack({
id: 'ts',
threads: os.cpus().length - 1,
loaders: [
{
loader: 'babel-loader',
options: {
cacheDirectory: true,
presets: ['@babel/preset-env', '@babel/preset-react', '@babel/preset-typescript'],
plugins: ['@babel/plugin-proposal-class-properties']
}
}
],
verbose: false
}),
// HappyPack CSS处理
new HappyPack({
id: 'styles',
threads: Math.ceil(os.cpus().length / 2),
loaders: [
'style-loader',
'css-loader',
'postcss-loader',
'less-loader'
],
verbose: false
}),
// HappyPack图片处理
new HappyPack({
id: 'images',
threads: 2,
loaders: [
'file-loader',
{
loader: 'image-webpack-loader',
options: {
mozjpeg: { progressive: true, quality: 75 },
optipng: { enabled: true },
gifsicle: { interlaced: false }
}
}
],
verbose: false
}),
// 并行Uglify压缩
new ParallelUglifyPlugin({
cacheDir: '.uglify-cache',
uglifyJS: {
output: {
beautify: false,
comments: false
},
compress: {
warnings: false,
drop_console: true,
collapse_vars: true,
reduce_vars: true
}
},
parallel: {
cache: true,
workers: os.cpus().length - 1
}
})
],
// 并行处理配置
parallelism: os.cpus().length * 2,
// 快照优化
snapshot: {
managedPaths: [path.resolve(__dirname, 'node_modules')],
immutablePaths: [],
buildDependencies: {
hash: true,
timestamp: true
}
}
};
六、性能监控与分析
6.1 构建性能监控
// webpack.stats.config.js - 构建统计分析配置
const { StatsWriterPlugin } = require('webpack-stats-plugin');
const SpeedMeasurePlugin = require('speed-measure-webpack-plugin');
const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;
// 构建性能测量
const smp = new SpeedMeasurePlugin({
outputTarget: path.resolve(__dirname, 'build-stats.json'),
pluginNames: {
'hard-source-webpack-plugin': 'HardSource',
'webpack-bundle-analyzer': 'BundleAnalyzer'
}
});
module.exports = smp.wrap({
plugins: [
// 生成详细的构建统计
new StatsWriterPlugin({
filename: 'build-stats.json',
fields: ['assets', 'modules', 'chunks', 'errors', 'warnings', 'assetsByChunkName'],
stats: {
chunks: true,
chunkModules: true,
chunkRelations: true,
modules: true,
reasons: true,
cached: true,
cachedAssets: true,
children: true,
source: false,
errorDetails: true,
timings: true,
performance: true
}
}),
// Bundle体积分析
new BundleAnalyzerPlugin({
analyzerMode: process.env.ANALYZE === 'true' ? 'server' : 'disabled',
analyzerHost: '127.0.0.1',
analyzerPort: 8888,
openAnalyzer: process.env.ANALYZE === 'true',
reportFilename: 'bundle-report.html',
defaultSizes: 'gzip',
logLevel: 'info',
generateStatsFile: true,
statsFilename: 'bundle-stats.json',
statsOptions: {
source: false,
reasons: true,
optimizationBailout: true,
chunkModules: true,
chunkOrigins: true,
nestedModules: true,
modulesSpace: Infinity,
providedExports: true,
unusedExports: true,
assetsSpace: Infinity,
childrenSpace: Infinity,
chunkModulesSpace: Infinity,
nestedModulesSpace: Infinity
}
})
],
stats: {
preset: 'verbose',
assets: true,
assetsSort: '!size',
modules: true,
modulesSort: '!size',
chunks: true,
chunksSort: '!size',
chunkModules: true,
chunkRelations: true,
reasons: true,
cached: true,
cachedAssets: true,
children: true,
source: false,
errorDetails: true,
timings: true,
performance: true,
builtAt: true,
hash: true,
version: true,
logging: 'info'
}
});
6.2 运行时性能监控
// src/utils/performanceMonitor.ts - 运行时性能监控
interface PerformanceMetric {
name: string;
startTime: number;
endTime?: number;
duration?: number;
metadata?: Record;
}
class PerformanceMonitor {
private static instance: PerformanceMonitor;
private metrics: Map = new Map();
private observers: PerformanceObserver[] = [];
static getInstance(): PerformanceMonitor {
if (!PerformanceMonitor.instance) {
PerformanceMonitor.instance = new PerformanceMonitor();
}
return PerformanceMonitor.instance;
}
/**
开始计时
*/
startTimer(name: string, metadata?: Record): string {
const id =${name}_${Date.now()}_${Math.random().toString(36).substr(2, 9)};this.metrics.set(id, [{
name,
startTime: performance.now(),
metadata
}]);return id;
}/**
结束计时并记录
*/
endTimer(id: string): PerformanceMetric | null {
const metricArray = this.metrics.get(id);
if (!metricArray || metricArray.length === 0) {
return null;
}const metric = metricArray[metricArray.length - 1];
metric.endTime = performance.now();
metric.duration = metric.endTime - metric.startTime;// 上报性能指标
this.reportMetric(metric);return metric;
}/**
测量异步操作
*/
async measureAsync(
name: string,
operation: () => Promise,
metadata?: Record
): Promise {
const id = this.startTimer(name, metadata);try {
const result = await operation();
this.endTimer(id);
return result;
} catch (error) {
const metric = this.endTimer(id);
console.error(Performance metric ${name}:, metric, error);
throw error;
}
}/**
测量同步操作
*/
measureSync(
name: string,
operation: () => T,
metadata?: Record
): T {
const id = this.startTimer(name, metadata);try {
const result = operation();
this.endTimer(id);
return result;
} catch (error) {
this.endTimer(id);
throw error;
}
}/**
上报性能指标
*/
private reportMetric(metric: PerformanceMetric): void {
// 发送到监控系统
if (navigator.sendBeacon) {
const payload = {
metric_name: metric.name,
metric_value: metric.duration,
timestamp: Date.now(),
metadata: metric.metadata,
url: window.location.href,
user_agent: navigator.userAgent
};navigator.sendBeacon('/api/metrics/build-performance', JSON.stringify(payload));
}// 控制台输出(开发环境)
if (process.env.NODE_ENV === 'development') {
console.log(📊 ${metric.name}: ${metric.duration?.toFixed(2)}ms, metric.metadata || '');
}
}/**
初始化Web Vitals监控
*/
initWebVitals(): void {
// LCP (Largest Contentful Paint)
this.observeWebVital('largest-contentful-paint', (entries) => {
const lastEntry = entries[entries.length - 1];
this.reportWebVital('LCP', lastEntry.startTime, {
element: lastEntry.element?.tagName,
size: lastEntry.size
});
});// FID (First Input Delay)
this.observeWebVital('first-input', (entries) => {
const firstEntry = entries[0];
this.reportWebVital('FID', firstEntry.processingStart - firstEntry.startTime, {
eventType: firstEntry.name
});
});// CLS (Cumulative Layout Shift)
let clsValue = 0;
this.observeWebVital('layout-shift', (entries) => {
for (const entry of entries) {
if (!entry.hadRecentInput) {clsValue += entry.value;}
}
});// 页面隐藏时上报CLS
document.addEventListener('visibilitychange', () => {
if (document.visibilityState === 'hidden') {
this.reportWebVital('CLS', clsValue, {});
}
});
}private observeWebVital(type: string, callback: (entries: PerformanceEntry[]) => void): void {
try {
const observer = new PerformanceObserver((list) => {
callback(list.getEntries());
});
observer.observe({ type, buffered: true });
this.observers.push(observer);
} catch (e) {
console.warn(Failed to observe ${type}:, e);
}
}private reportWebVital(name: string, value: number, metadata: Record): void {
if (navigator.sendBeacon) {
const payload = {
web_vital: name,
value,
timestamp: Date.now(),
url: window.location.href,
metadata
};
navigator.sendBeacon('/api/metrics/web-vitals', JSON.stringify(payload));
}
}/**
获取性能报告
*/
getReport(): {
summary: Record;
recentMetrics: PerformanceMetric[];
} {
const summary: Record = {};
const allMetrics: PerformanceMetric[] = [];this.metrics.forEach((metricArray) => {
metricArray.forEach((metric) => {
if (metric.duration !== undefined) {allMetrics.push(metric); if (!summary[metric.name]) { summary[metric.name] = { count: 0, totalTime: 0, avgTime: 0, maxTime: 0 }; } const stat = summary[metric.name]; stat.count++; stat.totalTime += metric.duration; stat.avgTime = stat.totalTime / stat.count; stat.maxTime = Math.max(stat.maxTime, metric.duration);}
});
});return {
summary,
recentMetrics: allMetrics.slice(-100) // 最近100条
};
}/**
- 清理资源
*/
cleanup(): void {
this.observers.forEach(observer => observer.disconnect());
this.observers = [];
this.metrics.clear();
}
}
export default PerformanceMonitor;
七、优化效果验证
7.1 性能测试数据对比
// performance-test-runner.js - 性能测试脚本
const { execSync, spawn } = require('child_process');
const fs = require('fs');
const path = require('path');
class PerformanceTestRunner {
constructor() {
this.results = [];
this.baselineMetrics = null;
}
async runFullBenchmark() {
console.log('🚀 开始性能基准测试...\n');
// 1. 基线测试(优化前配置)
console.log('📋 运行基线测试...');
this.baselineMetrics = await this.runTestSuite('baseline');
// 2. 优化配置测试
console.log('\n📋 运行优化配置测试...');
const optimizedMetrics = await this.runTestSuite('optimized');
// 3. 生成对比报告
this.generateComparisonReport(this.baselineMetrics, optimizedMetrics);
// 4. 验证优化效果
this.verifyOptimizations(this.baselineMetrics, optimizedMetrics);
}
async runTestSuite(scenario: string): Promise {
const startTime = Date.now();
// 清理缓存
this.cleanBuildCache(scenario);
// 构建测试
const buildResult = await this.runBuild(scenario);
// 分析产物
const bundleAnalysis = await this.analyzeBundle(scenario);
// 运行Lighthouse测试
const lighthouseResult = await this.runLighthouseTest(scenario);
// 运行性能预算检查
const budgetCheck = await this.runBudgetCheck(scenario);
return {
scenario,
buildTime: buildResult.time,
buildSize: buildResult.size,
bundleAnalysis,
lighthouseResult,
budgetCheck,
totalTime: Date.now() - startTime
};
}
async runBuild(scenario: string): Promise<{ time: number; size: number }> {
const configFile = scenario === 'baseline'
? 'webpack.config.baseline.js'
: 'webpack.config.js';
const startTime = Date.now();
execSync(`npx webpack --config ${configFile} --mode production`, {
stdio: 'pipe',
encoding: 'utf-8'
});
const buildTime = Date.now() - startTime;
// 计算构建产物大小
const distPath = path.resolve(__dirname, 'dist');
const size = this.calculateDirectorySize(distPath);
return { time: buildTime, size };
}
calculateDirectorySize(dirPath: string): number {
let totalSize = 0;
const files = fs.readdirSync(dirPath, { withFileTypes: true });
for (const file of files) {
const filePath = path.join(dirPath, file.name);
if (file.isDirectory()) {
totalSize += this.calculateDirectorySize(filePath);
} else {
const stats = fs.statSync(filePath);
totalSize += stats.size;
}
}
return totalSize;
}
async analyzeBundle(scenario: string): Promise {
const statsPath = path.resolve(__dirname, 'dist', 'bundle-stats.json');
if (!fs.existsSync(statsPath)) {
return { chunks: [], totalChunks: 0, largestChunk: null };
}
const stats = JSON.parse(fs.readFileSync(statsPath, 'utf-8'));
const chunks = stats.chunks.map((chunk: any) => ({
name: chunk.names?.[0] || 'unnamed',
size: chunk.size,
files: chunk.files
})).sort((a: any, b: any) => b.size - a.size);
return {
chunks: chunks.slice(0, 20), // Top 20
totalChunks: chunks.length,
largestChunk: chunks[0]
};
}
async runLighthouseTest(scenario: string): Promise {
// 模拟Lighthouse测试结果
// 实际项目中应使用 lighthouse CLI 或 PageSpeed Insights API
return {
performance: scenario === 'baseline' ? 52 : 91,
accessibility: scenario === 'baseline' ? 78 : 94,
bestPractices: scenario === 'baseline' ? 71 : 93,
seo: scenario === 'baseline' ? 82 : 96,
firstContentfulPaint: scenario === 'baseline' ? 2800 : 890,
largestContentfulPaint: scenario === 'baseline' ? 4800 : 1200,
cumulativeLayoutShift: scenario === 'baseline' ? 0.32 : 0.08,
firstInputDelay: scenario === 'baseline' ? 280 : 45
};
}
async runBudgetCheck(scenario: string): Promise {
const budget = {
totalBundleSize: 1100 1024, // 1.1MB
jsSize: 700 1024, // 700KB
cssSize: 100 1024, // 100KB
imageSize: 300 1024, // 300KB
firstLoadTime: 1500 // 1.5s
};
const distPath = path.resolve(__dirname, 'dist');
const stats = fs.statSync(distPath);
// 简化的预算检查
return {
passed: stats.size <= budget.totalBundleSize,
budget,
actual: {
totalSize: stats.size,
exceededBy: Math.max(0, stats.size - budget.totalBundleSize)
}
};
}
generateComparisonReport(baseline: TestMetrics, optimized: TestMetrics) {
console.log('\n' + '='.repeat(80));
console.log('📊 性能优化对比报告');
console.log('='.repeat(80));
// 构建性能对比
console.log('\n🔨 构建性能对比:');
console.log('-'.repeat(80));
console.log(` 基线配置 优化配置 提升幅度`);
console.log('-'.repeat(80));
console.log(`构建时间: ${baseline.buildTime}s ${optimized.buildTime}s ${((baseline.buildTime - optimized.buildTime) / baseline.buildTime * 100).toFixed(1)}%`);
console.log(`产物大小: ${(baseline.buildSize / 1024 / 1024).toFixed(2)}MB ${(optimized.buildSize / 1024 / 1024).toFixed(2)}MB ${((baseline.buildSize - optimized.buildSize) / baseline.buildSize * 100).toFixed(1)}%`);
console.log(`构建产物数量: ${baseline.bundleAnalysis.totalChunks}个 ${optimized.bundleAnalysis.totalChunks}个 ${((baseline.bundleAnalysis.totalChunks - optimized.bundleAnalysis.totalChunks) / baseline.bundleAnalysis.totalChunks * 100).toFixed(1)}%`);
// 包体积分析
console.log('\n📦 包体积分析:');
console.log('-'.repeat(80));
console.log('Top 5 最大 Chunk 对比:');
console.log(` 基线配置 优化配置 节省`);
console.log('-'.repeat(80));
for (let i = 0; i < 5; i++) {
const baseChunk = baseline.bundleAnalysis.chunks[i];
const optChunk = optimized.bundleAnalysis.chunks[i];
if (baseChunk && optChunk) {
const baseSize = (baseChunk.size / 1024).toFixed(1);
const optSize = (optChunk.size / 1024).toFixed(1);
const saved = (baseChunk.size - optChunk.size) / 1024;
const savedPercent = ((baseChunk.size - optChunk.size) / baseChunk.size * 100).toFixed(1);
console.log(`${baseChunk.name.padEnd(20)} ${baseSize.padStart(8)}KB ${optSize.padStart(8)}KB -${saved.toFixed(1)}KB (${savedPercent}%)`);
}
}
// Lighthouse评分对比
console.log('\n🎯 Lighthouse 评分对比:');
console.log('-'.repeat(80));
console.log(` 基线配置 优化配置 提升`);
console.log('-'.repeat(80));
console.log(`总体性能: ${baseline.lighthouseResult.performance}分 ${optimized.lighthouseResult.performance}分 +${optimized.lighthouseResult.performance - baseline.lighthouseResult.performance}分`);
console.log(`可访问性: ${baseline.lighthouseResult.accessibility}分 ${optimized.lighthouseResult.accessibility}分 +${optimized.lighthouseResult.accessibility - baseline.lighthouseResult.accessibility}分`);
console.log(`最佳实践: ${baseline.lighthouseResult.bestPractices}分 ${optimized.lighthouseResult.bestPractices}分 +${optimized.lighthouseResult.bestPractices - baseline.lighthouseResult.bestPractices}分`);
console.log(`SEO: ${baseline.lighthouseResult.seo}分 ${optimized.lighthouseResult.seo}分 +${optimized.lighthouseResult.seo - baseline.lighthouseResult.seo}分`);
// 核心Web指标对比
console.log('\n⚡ 核心Web指标对比:');
console.log('-'.repeat(80));
console.log(` 基线配置 优化配置 提升幅度`);
console.log('-'.repeat(80));
console.log(`首次内容绘制(FCP): ${(baseline.lighthouseResult.firstContentfulPaint / 1000).toFixed(2)}s ${(optimized.lighthouseResult.firstContentfulPaint / 1000).toFixed(2)}s ${((baseline.lighthouseResult.firstContentfulPaint - optimized.lighthouseResult.firstContentfulPaint) / baseline.lighthouseResult.firstContentfulPaint * 100).toFixed(1)}%`);
console.log(`最大内容绘制(LCP): ${(baseline.lighthouseResult.largestContentfulPaint / 1000).toFixed(2)}s ${(optimized.lighthouseResult.largestContentfulPaint / 1000).toFixed(2)}s ${((baseline.lighthouseResult.largestContentfulPaint - optimized.lighthouseResult.largestContentfulPaint) / baseline.lighthouseResult.largestContentfulPaint * 100).toFixed(1)}%`);
console.log(`累积布局偏移(CLS): ${baseline.lighthouseResult.cumulativeLayoutShift.toFixed(3)} ${optimized.lighthouseResult.cumulativeLayoutShift.toFixed(3)} ${((baseline.lighthouseResult.cumulativeLayoutShift - optimized.lighthouseResult.cumulativeLayoutShift) / baseline.lighthouseResult.cumulativeLayoutShift * 100).toFixed(1)}%`);
console.log(`首次输入延迟(FID): ${baseline.lighthouseResult.firstInputDelay}ms ${optimized.lighthouseResult.firstInputDelay}ms ${((baseline.lighthouseResult.firstInputDelay - optimized.lighthouseResult.firstInputDelay) / baseline.lighthouseResult.firstInputDelay * 100).toFixed(1)}%`);
// 预算检查结果
console.log('\n💰 性能预算检查:');
console.log('-'.repeat(80));
console.log(`预算状态: ${optimized.budgetCheck.passed ? '✅ 通过' : '❌ 未通过'}`);
console.log(`预算限制: ${(optimized.budgetCheck.budget.totalBundleSize / 1024 / 1024).toFixed(2)}MB`);
console.log(`实际大小: ${(optimized.budgetCheck.actual.totalSize / 1024 / 1024).toFixed(2)}MB`);
if (!optimized.budgetCheck.passed) {
console.log(`超出预算: ${(optimized.budgetCheck.actual.exceededBy / 1024 / 1024).toFixed(2)}MB`);
}
console.log('\n' + '='.repeat(80));
console.log('📈 总体优化效果总结');
console.log('='.repeat(80));
const overallImprovement = (
(baseline.lighthouseResult.performance - optimized.lighthouseResult.performance) / baseline.lighthouseResult.performance +
(baseline.buildTime - optimized.buildTime) / baseline.buildTime +
(baseline.buildSize - optimized.buildSize) / baseline.buildSize
) / 3 * 100;
console.log(`🎉 综合性能提升: ${overallImprovement.toFixed(1)}%`);
console.log(`⚡ 构建速度提升: ${((baseline.buildTime - optimized.buildTime) / baseline.buildTime * 100).toFixed(1)}%`);
console.log(`📦 包体积减少: ${((baseline.buildSize - optimized.buildSize) / baseline.buildSize * 100).toFixed(1)}%`);
console.log(`🎯 用户体验提升: ${((optimized.lighthouseResult.performance - baseline.lighthouseResult.performance) / baseline.lighthouseResult.performance * 100).toFixed(1)}%`);
}
封装好API供应商demo url=https://console.open.onebound.cn/console/?i=Lex
verifyOptimizations(baseline: TestMetrics, optimized: TestMetrics) {
console.log('\n🔍 优化验证检查:');
console.log('-'.repeat(80));
const checks = [
{
name: '构建时间优化',
passed: optimized.buildTime < baseline.buildTime,
expected: '构建时间应该减少',
actual: `从 ${baseline.buildTime}s 减少到 ${optimized.buildTime}s`
},
{
name: '包体积优化',
passed: optimized.buildSize < baseline.buildSize,
expected: '包体积应该减少',
actual: `从 ${(baseline.buildSize / 1024 / 1024).toFixed(2)}MB 减少到 ${(optimized.buildSize / 1024 / 1024).toFixed(2)}MB`
},
{
name: 'Chunk数量优化',
passed: optimized.bundleAnalysis.totalChunks <= baseline.bundleAnalysis.totalChunks,
expected: 'Chunk数量应该减少或持平',
actual: `从 ${baseline.bundleAnalysis.totalChunks} 个减少到 ${optimized.bundleAnalysis.totalChunks} 个`
},
{
name: 'Lighthouse性能提升',
passed: optimized.lighthouseResult.performance > baseline.lighthouseResult.performance,
expected: 'Lighthouse性能评分应该提升',
actual: `从 ${baseline.lighthouseResult.performance}分 提升到 ${optimized.lighthouseResult.performance}分`
},
{
name: '首屏加载优化',
passed: optimized.lighthouseResult.firstContentfulPaint < baseline.lighthouseResult.firstContentfulPaint,
expected: '首次内容绘制应该更快',
actual: `从 ${(baseline.lighthouseResult.firstContentfulPaint / 1000).toFixed(2)}s 提升到 ${(optimized.lighthouseResult.firstContentfulPaint / 1000).toFixed(2)}s`
},
{
name: '布局稳定性优化',
passed: optimized.lighthouseResult.cumulativeLayoutShift < baseline.lighthouseResult.cumulativeLayoutShift,
expected: '累积布局偏移应该减少',
actual: `从 ${baseline.lighthouseResult.cumulativeLayoutShift.toFixed(3)} 减少到 ${optimized.lighthouseResult.cumulativeLayoutShift.toFixed(3)}`
}
];
checks.forEach(check => {
const status = check.passed ? '✅' : '❌';
console.log(`${status} ${check.name}`);
console.log(` 期望: ${check.expected}`);
console.log(` 实际: ${check.actual}`);
});
const passedChecks = checks.filter(c => c.passed).length;
const totalChecks = checks.length;
console.log(`\n📋 验证结果: ${passedChecks}/${totalChecks} 项检查通过`);
if (passedChecks === totalChecks) {
console.log('🎉 所有优化目标均已达成!');
} else {
console.log('⚠️ 部分优化目标未达成,需要进一步调整配置。');
}
}
cleanBuildCache(scenario: string): void {
const cachePaths = [
path.resolve(dirname, '.webpack-cache'),
path.resolve(dirname, 'dist'),
path.resolve(dirname, 'build'),
path.resolve(dirname, '.hardsource')
];
cachePaths.forEach(cachePath => {
if (fs.existsSync(cachePath)) {
fs.rmSync(cachePath, { recursive: true, force: true });
}
});
// 清理npm缓存(可选)
try {
execSync('npm cache clean --force', { stdio: 'pipe' });
} catch (e) {
// 忽略错误
}
}
}
// 类型定义
interface TestMetrics {
scenario: string;
buildTime: number;
buildSize: number;
bundleAnalysis: BundleAnalysis;
lighthouseResult: LighthouseResult;
budgetCheck: BudgetCheckResult;
totalTime: number;
}
interface BundleAnalysis {
chunks: Array<{ name: string; size: number; files: string[] }>;
totalChunks: number;
largestChunk: { name: string; size: number; files: string[] } | null;
}
interface LighthouseResult {
performance: number;
accessibility: number;
bestPractices: number;
seo: number;
firstContentfulPaint: number;
largestContentfulPaint: number;
cumulativeLayoutShift: number;
firstInputDelay: number;
}
interface BudgetCheckResult {
passed: boolean;
budget: {
totalBundleSize: number;
jsSize: number;
cssSize: number;
imageSize: number;
firstLoadTime: number;
};
actual: {
totalSize: number;
exceededBy: number;
};
}
// 运行测试
const runner = new PerformanceTestRunner();
runner.runFullBenchmark().catch(console.error);
7.2 最终优化成果
指标类别 具体指标 优化前 优化后 提升幅度 状态
构建性能 生产构建时间 240s 45s 81% ✅
热更新时间(HMR) 10s 1.5s 85% ✅
开发构建时间 45s 12s 73% ✅
包体积 总Bundle大小(gzip) 4.4MB 1.1MB 75% ✅
vendor.js 2.8MB 680KB 76% ✅
main.js 1.6MB 320KB 80% ✅
Chunk数量 156个 23个 85% ✅
运行时性能 Lighthouse评分 52分 91分 75% ✅
首屏加载时间 4.8s 1.2s 75% ✅
LCP 4.8s 1.2s 75% ✅
FID 280ms 45ms 84% ✅
CLS 0.32 0.08 75% ✅
移动端性能 3G网络首屏 12.5s 3.5s 72% ✅
内存占用峰值 280MB 145MB 48% ✅
滚动FPS 28 58 107% ✅
业务指标 页面跳出率 52% 28% 46% ✅
平均会话时长 2m15s 4m32s 102% ✅
转化率 1.8% 3.1% 72% ✅
八、核心优化经验总结
8.1 Webpack优化关键点
8.1.1 代码分割策略
✅ 正确做法:
├── 第三方库单独打包(vendor)
├── UI组件库单独打包(antd)
├── 工具函数库单独打包(utils)
├── 公共组件单独打包(common)
├── 样式文件单独打包(styles)
└── 动态导入大型组件
❌ 避免做法:
├── 将所有依赖打入一个vendor.js
├── 不进行代码分割
├── 忽略runtime chunk提取
└── 分割粒度不合理
8.1.2 Tree Shaking优化
✅ 正确做法:
├── 使用ES Module格式
├── 配置sideEffects字段
├── 启用usedExports优化
├── 使用concatenateModules
├── babel配置modules: false
└── 避免CommonJS模块
❌ 避免做法:
├── 使用require/module.exports
├── 忽略sideEffects配置
├── 打包时混入副作用代码
└── 禁用tree shaking
8.1.3 缓存策略
✅ 正确做法:
├── 文件系统缓存(filesystem cache)
├── hard-source-webpack-plugin
├── 持久化缓存目录
├── 构建依赖缓存
├── DLL缓存(可选)
└── 多线程缓存
❌ 避免做法:
├── 每次清理缓存重建
├── 忽略缓存配置
├── 缓存目录混乱
└── 不使用增量构建
8.2 图片优化最佳实践
8.2.1 格式选择策略
📊 图片格式选择指南:
JPEG:
├── 适用于: 照片、商品主图
├── 优点: 高压缩比、广泛支持
├── 缺点: 不支持透明
└── 推荐质量: 75-85%
PNG:
├── 适用于: 图标、透明图片
├── 优点: 支持透明、无损压缩
├── 缺点: 文件较大
└── 推荐: 必要时才使用
WebP:
├── 适用于: 所有静态图片
├── 优点: 更小体积、支持透明和动画
├── 缺点: 旧浏览器不支持
└── 推荐质量: 80-85%
AVIF:
├── 适用于: 现代浏览器图片
├── 优点: 极致压缩、HDR支持
├── 缺点: 兼容性有限、编码慢
└── 推荐: 渐进式采用
8.2.2 响应式图片策略
📱 响应式图片实施:
srcset + sizes:
<img src="image-400.jpg"srcset="image-400.jpg 400w, image-800.jpg 800w, image-1200.jpg 1200w" sizes="(max-width: 768px) 400px, (max-width: 1200px) 800px, 1200px">picture元素:

懒加载:
<img loading="lazy"data-src="image.jpg" class="lazyload">
8.3 性能监控体系
8.3.1 构建时监控
// 关键监控指标
const buildMetrics = {
// 构建时间
buildTime: '总构建时间',
moduleCount: '模块数量',
chunkCount: 'Chunk数量',
// 包体积
totalSize: '总体积',
gzipSize: 'Gzip后体积',
largestChunk: '最大Chunk',
// 缓存效率
cacheHitRate: '缓存命中率',
incrementalBuildTime: '增量构建时间'
};
8.3.2 运行时监控
// 核心Web指标
const webVitals = {
LCP: '最大内容绘制 < 2.5s',
FID: '首次输入延迟 < 100ms',
CLS: '累积布局偏移 < 0.1',
FCP: '首次内容绘制 < 1.8s',
TTFB: '首字节时间 < 800ms'
};
8.4 持续优化建议
8.4.1 短期优化
定期运行依赖分析,识别新增大包
监控构建时间,及时清理无效缓存
持续关注新图片格式和压缩算法
完善性能预算和自动化检查
8.4.2 长期规划
考虑模块联邦(Module Federation)微前端架构
探索Rust-based构建工具(如Turbopack)
实施更细粒度的代码分割策略
建设完整的性能监控和告警体系
总结: 通过系统性的Webpack配置优化、精细化的依赖管理、全面的图片优化策略和科学的性能监控,我们成功将仿1688首页的构建性能提升了300%以上,同时显著改善了用户体验和业务指标。这套优化方案不仅适用于B2B电商平台,其核心思想也可以推广到其他复杂的前端项目中。
以上是我在电商中台领域的一些实践,目前我正在这个方向进行更深入的探索/提供相关咨询与解决方案。如果你的团队有类似的技术挑战或合作需求,欢迎通过[我的GitHub/个人网站/邮箱]与我联系。
需要我进一步解释代码分割的具体配置技巧,或者如何设计更完善的性能监控体系吗?