阶段三:首屏加载时间优化

当前(阶段二优化未应用)项目的项目首屏加载时间,加载时间较长:
- FCP (首次内容绘制):13.76s(正常应 < 3s)
- DCL (DOMContentLoaded): 13.68s(正常应 < 4s)
- LCP (最大内容绘制): 13.76s(正常应 < 2.5s)
- L(资源完全加载): 18.21s(正常应 < 8s)
SVG动态导入优化
将 SvgIcon 组件从全量导入改为动态导入,不在模块加载时就导入所有SVG文件,影响首屏加载时间,实现按需加载SVG图标。
优化前:
const importAll = (requireContext) =>
requireContext.keys().forEach(requireContext)
try {
importAll(require.context('xxx/assets/svg', true, /\.svg$/))
} catch (error) {}
export const SvgIcon = ({ name, ...props }) => {
return (
<svg {...props}>
<use href={`#${name}`} />
</svg>
)
}- 模块加载时就导入所有SVG文件
- 即使不使用的SVG也会被加载
- 初始bundle包含所有SVG
- 无法按需加载
优化后:
const loadSvgIcon = (iconName: string): void => {
if (loadedIcons.has(iconName)) {
return // 已加载,直接返回
}
// 动态导入指定的SVG文件
svgContextCache(path)
loadedIcons.add(iconName)
}
export const SvgIcon = ({ name, preload = true, ...props }) => {
useEffect(() => {
// 按需加载图标
loadSvgIcon(name)
}, [name, preload])
return (
<svg {...props}>
<use href={`#${name}`} />
</svg>
)
}- 按需加载,只加载使用的SVG
- 避免重复导入
这个问题是导致LCP过高的主要原因,每次都会先拉190多张图片,无论当前页面是否使用,按需加载后LCP降至5.8s左右。
WebP转换(> 5KB)
我这边是增加 generator 来做webp的转换,参考包括阶段二上文的配置,我的图片处理规则是:
- < 2KB:内联为 Base64
- 2KB - 5KB:使用 Sharp 压缩优化
- > 5KB:转换为 WebP 格式
getImageMinimizerPlugin: (IS_PRO) => {
if (!IS_PRO) {
return null;
}
return new ImageMinimizerPlugin({
deleteOriginalAssets: false,
test: /\.(png|jpg|jpeg)$/i,
// 错误处理:遇到错误时只警告,不中断构建
severityError: 'warning',
minimizer: {
implementation: ImageMinimizerPlugin.sharpMinify,
filter: (source, filename) => {
try {
return source.length < 5 * 1024; // 5KB
} catch (error) {
console.error('[Minimizer Filter] 错误:', error.message, '文件:', filename);
return false;
}
},
options: {
encodeOptions: {
// PNG 压缩配置
png: {
quality: 80,
compressionLevel: 9,
palette: true,
},
// JPEG 压缩配置
jpeg: {
quality: 80,
mozjpeg: false,
},
},
},
},
generator: [
{
type: 'asset',
implementation: ImageMinimizerPlugin.sharpGenerate,
// 只转换大于5kb的图片为webp
filter: (source, filename) => {
try {
return source.length > 5 * 1024; // 5KB
} catch (error) {
console.error('[WebP Filter] 错误:', error.message, '文件:', filename);
return false;
}
},
options: {
encodeOptions: {
webp: {
quality: 80,
lossless: false,
},
},
},
filename: 'assets/images/[name][ext].webp',
}
],
});
},
/**
* 获取优化的图片loader配置
*/
getImageLoaders: () => {
return [
{
test: /\.(png|jpg|jpeg)$/,
type: 'asset/resource',
exclude: /node_modules/,
parser: {
dataUrlCondition: {
maxSize: 2 * 1024,
},
},
generator: {
filename: 'assets/images/[name].[contenthash:8][ext]',
},
},
{
test: /\.svg$/,
exclude: /node_modules/,
use: [
{
loader: 'svg-sprite-loader',
options: {
symbolId: '[name]',
extract: false,
},
},
{
loader: 'svgo-loader',
options: {
plugins: [
{ name: 'removeViewBox', active: false },
{ name: 'removeDimensions', active: true },
{ name: 'removeUselessStrokeAndFill', active: true },
'moveElemsAttrsToGroup',
'convertPathData',
],
},
},
],
},
];
},我这边保留了原类型文件做降级方案。如要生成不用尺寸的图片,或者想全部图片转webp,配置简单一点,可以使用 responsive-loader+sharp,自动为每个PNG/JPG图片生成对应的WebP版本:
{
test: /\.(png|jpg|jpeg)$/,
// include: /[\\/]assets[\\/]imges[\\/]/i,
exclude: /node_modules/,
use: [
{
loader: 'responsive-loader',
options: {
已在FreeBuf发表 0 篇文章
本文为 独立观点,未经授权禁止转载。
如需授权、对文章有疑问或需删除稿件,请联系 FreeBuf
客服小蜜蜂(微信:freebee1024)



