FP、FCP、FMP、LCP都是什么P?

本文介绍了前端性能监控中的关键指标,包括FP(First Paint)、FCP(First Contentful Paint)、FMP(First Meaningful Paint)和LCP(Largest Contentful Paint)的含义、计算方法及其在用户体验中的作用。同时,文章讨论了加载性能、可交互时间和稳定性指标,强调了谷歌的Core Web Vitals中LCP、FID和CLS的重要性。

1. 简介

面试中经常问到前端性能监控相关指标,你知道FP、FCP、FMP、LCP代表什么事件吗?它们的先后顺序如何呢?如何计算?前端性能监控中常用哪些指标?

本文将介绍前端项目中常用到的性能指标和它们的计算方法以及实际应用场景。

最初,评价前端页面加载性能有两个指标:DOMContentLoaded和load事件,分别代表DOM构造完成和首屏资源加载完成。

对于之前的页面和现代的服务端渲染的页面,这两个指标都可以很好地衡量首屏内容展示时间。但对于现代复杂的单页应用,都是通过JS操作DOM向页面添加主要内容,对于这种场景,DOMContentLoaded和load事件就不能很好地衡量首屏显示时间了。

于是有FP、FCP、FMP被提出来,它们关注的不是“加载”,而是“渲染”,因此能更好地表现用户看到的情况。

FP、FCP这两个指标虽然表达了渲染的事件,但对“用户关注的内容”没有体现,比如首屏渲染出来一个背景,或者一个loading,可能对于用户来说和白屏区别不大。FMP虽然体现了“关键内容”的要素,但它是复杂的、模糊的,甚至是错误的,并不能准确识别页面主要内容的加载时机。

后来LCP指标被提出来,表示“用于度量视口中最大的内容元素何时可见”,它用来代替FMP,表征页面的关键元素何时可以被用户看到。

除了加载性能,还有可交互时间、稳定性指标、流畅性指标,在不同的业务场景都可以被监控用来作为提升用户体验的依据。

谷歌一直十分重视网站的用户体验,移动友好性,页面加载速度和HTTPS是Google已经使用的页面排名因素,而2020年,谷歌将Core Web Vitals新纳入的用户体验指标。其中核心的3个就是LCP、FID、CLS。后面会详细说明。

前端面试刷题网站灵题库,收集大厂面试真题,相关知识点详细解析。】

2. 加载性能指标

我们知道我们使用浏览器访问页面时候,浏览器将页面从网络下载到本地后,主要做几个事情:解析HTML,创建DOM,同时加载依赖的资源:CSS、图片等(加载资源的过程不会阻塞DOM解析),然后调用渲染进程渲染到界面上。

这里需要注意一点,在现在浏览器中,为了减缓渲染被阻塞的情况,现代的浏览器都使用了猜测预加载。当解析被阻塞的时候,浏览器会有一个轻量级的HTML(或CSS)扫描器(scanner)继续在文档中扫描,查找那些将来可能能够用到的资源文件的url,在渲染器使用它们之前将其下载下来。

在整个加载和渲染过程中会触发多个事件

关键事件

  • load(Onload Event),它代表页面中依赖的所有资源加载完的事件。
  • DCL(DOMContentLoaded),DOM解析完毕。
  • FP(First Paint),表示渲染出第一个像素点。FP一般在HTML解析完成或者解析一部分时候触发。
  • FCP(First Contentful Paint),表示渲染出第一个内容,这里的“内容”可以是文本、图片、canvas。
  • FMP(First Meaningful Paint),首次渲染有意义的内容的时间,“有意义”没有一个标准的定义,FMP的计算方法也很复杂。
  • LCP(largest contentful Paint),最大内容渲染时间。

下面对各个事件和特点详细说明

白屏和首屏

白屏时间 = 地址栏输入网址后回车 - 浏览器出现第一个元素

首屏时间 = 地址栏输入网址后回车 - 浏览器第一屏渲染完成

根据白屏和首屏的定义,我们可以用FP和FCP来计算白屏和首屏。

白屏结束时间 = FP事件触发时间

首屏结束时间 = FCP事件触发时间

\

当然FCP代表第一个内容被渲染出来,有些业务中希望用更关键的内容的渲染来表示首屏,这时候可以用FMP或者LCP来作为首屏的计算指标。

load事件

Onload Event代表页面中依赖的所有资源:DOM、图片、CSS、Flash等都加载完,window.onload注册的回调就会在load事件触发时候被调用。

有时候FCP比Onload Event先触发,因为渲染第一个内容时候可能不包括图片的展示,只有文本内容。

所有依赖的资源包括异步加载的资源,但不包括延时加载的资源。

<html>
<head>
    <title>demo</title>
</head>
<body>
    <script>
        // 该资源算在Onload Event加载的资源中
        const img = document.createElement('img');
        img.src = 'https://domain/path/image.png';
        document.appendChild(img);

        // 该资源算在Onload Event加载的资源中
        setTimeout(() => {
            const img = document.createElement('img');
            img.src = 'https://domain/path/image.png';
            document.appendChild(img);
        }, 0);

        // 该资源不会算在Onload Event加载的资源中
        setTimeout(() => {
            const img = document.createElement('img');
            img.src = 'https://domain/path/image.png';
            document.appendChild(img);
        }, 5000);
    </script>
</body>
</html>

\

DCL

DOMContentLoaded事件,当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载。

可以通过注册回调监听该事件

  document.addeventListener('DOMContentLoaded', function() {}, false);

document.onloaddocument.body.onload也是这个事件的回调。

\

DCL和load的先后顺序

如果需要渲染的内容不多,DCL在load之前,如果需要渲染的内容很多,那么DCL会在load之后。

FP和FCP的关系

浏览器渲染的界面可能是“内容”,例如文本,也可能不是“内容”,比如一个背景为红色的div标签。FCP事件指渲染出第一个内容的事件,而FP指渲染出第一个像素点,渲染出的东西可能是内容,也可能不是。

有节点不一定有渲染,如果没有任何样式,是没有界面的,也不需要渲染。下面代码就没有FP事件

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>no FP</title>
  </head>
  <body>
    <div></div>
  </body>
</html>

下面代码,会渲染界面,因此会触发FP事件,但是不会触发FCP,因为没有内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>has FP, no FCP</title>
    <style>
        div {
            width: 1px;
            height: 1px;
            background-color: red;
        }
    </style>
</head>
<body>
    <div></div>
</body>
</html>

注意:渲染的操作一定是发生在视口内的,对于视口外不可见的内容,不会触发“Paint”操作,比如下面代码,不会触发FP事件。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>no FP</title>
    <style>
        div {
            position: absolute;
            left: -99999px;
            width: 1px;
            height: 1px;
            background-color: red;
            float: left;
        }
    </style>
</head>
<body>
    <div></div>
</body>
</html>

通过上面对FP和FCP的介绍,可以知道,如果html本身有内容(文本、图片)或者js脚本很快能创建内容,那么FP和FCP会一起触发。否则FP比FCP提前触发。FP肯定不会在FCP后面出现,因为渲染出内容,一定也渲染出了像素。

FP和DCL的先后顺序

浏览器不一定等到所有的DOM都解析完再开始渲染,如果DOM节点少,浏览器会加载完再渲染,但是如果节点很多,浏览器解析一部分节点后就会开始渲染(这时候就会触发FP)。也就是说,当需要渲染的节点数少的时候,DCL会在FP前面;当需要渲染的节点数很多时候,DCL会在FP后面。

例如下面的代码,只有一个div,会先触发DCL然后再触发FP。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>FP after DCL</title>
    <style>
        div {
            width: 1px;
            height: 1px;
            background-color: red;
            float: left;
        }
    </style>
</head>
<body>
    <div></div>
</body>
</html>

而下面的代码,有10000个div,FP会在DCL前面。

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>FP before DCL</title>
    <style>
        div {
            width: 1px;
            height: 1px;
            background-color: red;
            float: left;
        }
    </style>
</head>
<body>
    <!-- 10000个div... -->
</body>
</html>

各指标的计算

FP、FCP和L时间都可以通过performance API计算

// load
// loadEventStart是load事件发送给文档,也即load回调函数开始执行的时间
// loadEventEnd是load回调函数执行完成的时间
const loadTime = performance.timing.loadEventStart - performance.timing.fetchStart

// DCL
const dcl = performance.timing.domContentLoadedEventEnd - performance.timing.domContentLoadedEventStart

// FP
const fp = performance.getEntries('paint').filter(entry => entry.name == 'first-paint')[0].startTime;

// FCP
const fcp = performance.getEntries('paint').filter(entry => entry.name == 'first-contentful-paint')[0].startTime;

// Onload Event
const l = performance.timing.loadEventEnd - performance.timing.navigationStart;

// LCP
new PerformanceObserver((entryList) => {
  for (const entry of entryList.getEntries()) {
    console.log('LCP candidate:', entry.startTime, entry);
  }
}).observe({type: 'largest-contentful-paint', buffered: true});

// LCP也可以通过web-vitals计算
import {getLCP, getFID, getCLS} from 'web-vitals';

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

FMP计算比较复杂,lighthouse的计算的大体思路是,将页面中最大布局变化后的第一次渲染事件作为FMP事件,并且计算中考虑到了可视区的因素。

FMP计算过于复杂,没有现成的performance API,如果希望在监控中上报这个指标,可以自己使用MutationObserver计算。

3. 可交互时间

首次可交互时间

文档首次可交互时间可以用来衡量页面可交互的性能。

首次可交互,即DOM加载并解析完成后,界面上的元素可以交互(如输入框可以输入、按钮可以点击、超长元素可以滚动)。其时间用performance.timing.domInteractive计算。

performance.timing.domInteractive:当前网页DOM结构结束解析、开始加载内嵌资源的时间,document.readyState 变成interactive,并将抛出"readyStateChange"事件(注意只是DOM树解析完成,这时候并没有开始加载网页内的资源)\

const timeToInteractive = performance.timing.domInteractive - performance.timing.fetchStart, //首次可交互时间

TTI

用于标记应用已进行视觉渲染并能可靠响应用户输入的时间点

TTI的计算方法参考文章:深入浅出前端监控

这里定义一下什么是完全可交互状态的页面:

  1. 页面已经显示有用内容。
  2. 页面上的可见元素关联的事件响应函数已经完成注册。
  3. 事件响应函数可以在事件发生后的 50ms 内开始执行(主线程无 Long Task)。

计算方法描述如下:

  1. 从 FCP 时间开始,向前搜索一个不小于 5s 的静默窗口期。(静默窗口期定义:窗口所对应的时间内没有 Long Task,且进行中的网络请求数不超过 2 个)
  2. 找到静默窗口期后,从静默窗口期向后搜索到最近的一个 Long Task,Long Task 的结束时间即为 TTI。
  3. 如果一直找到 FCP 时刻仍然没有找到 Long Task,以 FCP 时间作为 TTI。

其实现需要支持 Long Tasks API 和 Resource Timing API。

longTask

Long Task:阻塞主线程达 50 毫秒或以上的任务,可以通过PerformanceObserver获取。

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('longtask candidate: ', entry.startTime);
  }
});

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

\

FID

FID(First Input Delay) 用于度量用户第一次与页面交互的延迟时间,是用户第一次与页面交互到浏览器真正能够开始处理事件处理程序以响应该交互的时间。

相对于TTI,FID表示实际的用户操作的延时,更能从用户角度反映网页的交互性能。

其计算使用简洁的 PerformanceEventTiming API 即可,回调的触发时机是用户首次与页面发生交互并得到浏览器响应(点击链接、输入文字等)。

const observer = new PerformanceObserver((list) => {
  for (const entry of list.getEntries()) {
    console.log('fid', entry.processingStart - entry.startTime);
  }
});

observer.observe({type: 'first-input', buffer: true});

4. 稳定性指标 CLS

CLS(Cumulative Layout Shift)是对在页面的整个生命周期中发生的每一次意外布局变化的最大布局变化得分的度量,布局变化得分越小证明你的页面越稳定。

听起来有点复杂,这里做一个简单的解释:

  • 不稳定元素:一个非用户操作但发生较大偏移的可见元素称为不稳定元素。
  • 布局变化得分:元素从原始位置偏移到当前位置影响的页面比例 * 元素偏移距离比例。

网站应努力使 CLS 分数小于 0.1 。

可以通过web-vitals获取CLS。

import {getCLS} from 'web-vitals';

getCLS(console.log);

减少CLS的方法,参考文章:页面视觉稳定性之CLS

5. 流畅性指标

FPS

Chrome DevTool 中有一栏 Rendering 中包含 FPS 指标,但目前浏览器标准中暂时没有提供相应 API ,只能手动实现。这里需要借助 requestAnimationFrame 方法模拟实现,浏览器会在下一次重绘之前执行 rAF 的回调,因此可以通过计算每秒内 rAF 的执行次数来计算当前页面的 FPS。

FPS过低会让用户感觉卡顿,因此这个计算可以用来监控页面卡顿情况。

longTask

长任务监听,PerformanceObserver 监听,参考上面TTI一节中长任务的监听。

6. Core Web Vitals

Core Web Vitals是谷歌提出的,衡量用户体验的新指标,指标将被纳入谷歌搜索引擎的网页排名。

Core Web Vitals是用户体验和SEO的重要指标。

关键的指标包括

  • LCP,用来衡量页面加载性能,为了提供良好的用户体验,LCP应该在页面首次开始加载后的2.5秒内发生
  • FID,衡量交互性能,为了提供良好的用户体验,页面的FID应该小于100毫秒。
  • CLS,测量视觉稳定性。为了提供良好的用户体验,页面应该保持CLS小于0.1。\

Chrome提供了web-vitals库获取3个关键指标的数值。

import {getLCP, getFID, getCLS} from 'web-vitals';

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

更多前端面试真题可以查看前端大厂面试题库-灵题库

<think> 我们正在回答用户关于Webpack的两个核心问题: 1. Webpack的Loader和Plugin有哪些(最新版Webpack 5)? 2. 如何减小Webpack打包体积、提高打包速度、优化LCPFCP体验? 首先,我们整理Loader和Plugin的常见分类和代表,然后针对性能优化(体积、速度、LCP/FCP)给出具体方案。 ### 一、Webpack Loader 和 Plugin 概述(最新版Webpack 5) #### 1. Loader(模块转换器) Loader用于对模块的源代码进行转换,将非JavaScript文件转换为Webpack能够处理的模块。以下为常用Loader分类及代表: | 分类 | 常用Loader | 作用描述 | |------------|--------------------------------------|--------------------------------------------------------------------------| | JavaScript | `babel-loader` | 转换ES6+/TypeScript等为浏览器兼容的JS(需配合Babel核心) | | CSS | `css-loader` | 解析CSS中的`@import`和`url()`,处理模块依赖 | | | `style-loader` | 将CSS注入DOM(通过`<style>`标签) | | | `postcss-loader` | 使用PostCSS处理CSS(如自动添加前缀,需配合`postcss-preset-env`) | | 样式预处理器 | `sass-loader`/`less-loader`/`stylus-loader` | 将Sass/Less/Stylus编译为CSS | | 文件资源 | `file-loader` | 将文件(如图片、字体)输出到输出目录并返回URL | | | `url-loader` | 类似`file-loader`,但可设置文件大小阈值转Base64(Webpack 5已内置资源模块)| | | `image-webpack-loader` | 压缩图片(需配合`file-loader`或资源模块) | | HTML | `html-loader` | 处理HTML中的资源引用(如`<img src="..."`) | 注:Webpack 5 内置了[资源模块(Asset Modules)](https://webpack.js.org/guides/asset-modules/),可替代`file-loader`和`url-loader`。 #### 2. Plugin(插件) Plugin用于扩展Webpack功能,在构建流程中注入钩子实现更复杂的任务。以下为常用Plugin: | 分类 | 常用Plugin | 作用描述 | |----------------|------------------------------------------------|--------------------------------------------------------------------------| | 核心优化 | `TerserPlugin` | 压缩JavaScript(Webpack 5内置,需在`optimization.minimizer`中配置) | | | `CssMinimizerPlugin` | 压缩CSS(需配合`mini-css-extract-plugin`提取的CSS) | | HTML生成 | `HtmlWebpackPlugin` | 自动生成HTML文件,并注入打包后的资源链接(支持模板、压缩等) | | CSS提取 | `MiniCssExtractPlugin` | 将CSS提取为独立文件(替代`style-loader`,支持按需加载) | | 环境变量 | `DefinePlugin` | 定义全局常量(如`process.env.NODE_ENV`) | | 清理构建 | `CleanWebpackPlugin` | 在构建前清理输出目录 | | 打包分析 | `BundleAnalyzerPlugin` | 可视化分析打包体积(来自`webpack-bundle-analyzer`) | | 进度显示 | `ProgressPlugin` | 显示构建进度(Webpack内置) | | 模块热替换 | `HotModuleReplacementPlugin` | 启用模块热替换(HMR)(Webpack内置,开发模式下默认开启) | > 提示:Webpack 5 内置了部分功能(如资源模块、HMR),无需额外安装插件。 --- ### 二、优化策略:减小体积、提高速度、优化LCP/FCP #### 1. 减小打包体积(Bundle Size) - **策略1:代码压缩** - JavaScript压缩:使用`TerserPlugin`(Webpack 5默认集成)[^1]。 ```javascript // webpack.config.js const TerserPlugin = require('terser-webpack-plugin'); module.exports = { optimization: { minimize: true, minimizer: [new TerserPlugin()], }, }; ``` - CSS压缩:使用`CssMinimizerPlugin`(需配合`MiniCssExtractPlugin`)。 ```javascript const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [new MiniCssExtractPlugin()], optimization: { minimizer: [new CssMinimizerPlugin()], }, }; ``` - **策略2:Tree Shaking** - 移除未使用代码(Dead Code),需满足: - 使用ES6模块语法(`import/export`)。 - 在`package.json`中设置`"sideEffects": false`(或指定有副作用的文件)。 - 生产模式(`mode: 'production'`)默认开启。 - **策略3:资源压缩** - 图片压缩:使用`image-webpack-loader`(或构建流程外使用工具如Squoosh)。 ```javascript module: { rules: [{ test: /\.(png|jpg|gif)$/, use: [ 'file-loader', // 或 type: 'asset/resource' { loader: 'image-webpack-loader', options: { mozjpeg: { quality: 50 } } // 调整质量 } ] }] } ``` - Gzip/Brotli压缩:通过服务器(如Nginx)或Webpack插件(如`compression-webpack-plugin`)预压缩。 #### 2. 提高打包速度(Build Speed) - **策略1:限制Loader作用范围** - 使用`include/exclude`减少Loader处理文件数(如`node_modules`无需Babel处理)。 ```javascript module: { rules: [{ test: /\.js$/, exclude: /node_modules/, // 排除node_modules use: ['babel-loader'] }] } ``` - **策略2:缓存** - Webpack 5 内置持久化缓存(显著提升二次构建速度)。 ```javascript module.exports = { cache: { type: 'filesystem', // 使用文件系统缓存 }, }; ``` - Loader缓存(如`babel-loader`可配置`cacheDirectory: true`)。 - **策略3:多进程并行** - 使用`thread-loader`(将Loader置于线程池中运行)。 ```javascript rules: [{ test: /\.js$/, use: [ { loader: 'thread-loader', options: { workers: 4 } }, 'babel-loader' ] }] ``` - `TerserPlugin`开启多进程(Webpack 5默认并行,无需配置)。 - **策略4:减少查找范围** - 配置`resolve.modules`:指定模块搜索目录(如`path.resolve(__dirname, 'src')`)。 - 配置`resolve.extensions`:减少文件扩展名尝试(如`['.js', '.json']`)。 #### 3. 优化LCP(Largest Contentful Paint)和FCP(First Contentful Paint) > LCP:最大内容元素渲染时间;FCP:首次内容渲染时间。两者是Web Vitals核心指标[^1]。 - **策略1:代码分割(Code Splitting)** - 入口分割:多入口配置(适合多页应用)。 ```javascript entry: { page1: './src/page1.js', page2: './src/page2.js' } ``` - 动态导入(Dynamic Import):使用`import()`语法分割代码(异步加载)。 ```javascript // 在代码中 import('./module').then(module => { ... }); ``` - 提取公共代码:通过`splitChunks`配置共享模块。 ```javascript optimization: { splitChunks: { chunks: 'all', // 包括异步和非异步块 minSize: 20000, // 最小分割大小(20KB) }, }; ``` - **策略2:预加载关键资源(Critical Resources)** - 使用`<link rel="preload">`提前加载关键CSS/字体(通过`HtmlWebpackPlugin`注入)。 ```javascript // 在HtmlWebpackPlugin配置中 new HtmlWebpackPlugin({ inject: true, preload: ['**/*.css'], // 预加载所有CSS }) ``` - 使用`prefetch/preload` Webpack魔法注释(Magic Comments)[^1]。 ```javascript import(/* webpackPreload: true */ 'critical-module'); ``` - **策略3:内联关键CSS(Critical CSS)** - 提取首屏关键CSS内联到HTML(减少FCP时间),剩余CSS异步加载(如`critters-webpack-plugin`)。 ```javascript const Critters = require('critters-webpack-plugin'); module.exports = { plugins: [ new Critters({ preload: 'swap' }) // 内联关键CSS,异步加载剩余 ] }; ``` - **策略4:优化资源加载顺序** - 使用`HtmlWebpackPlugin`的`chunksSortMode`控制脚本顺序(如`auto`按依赖排序)。 - 延迟非关键脚本(如通过`async/defer`属性)。 --- ### 总结 通过合理配置Loader/Plugin、压缩代码、Tree Shaking、缓存、并行处理、代码分割和资源预加载,可显著优化Webpack构建体积、速度和LCP/FCP指标。Webpack 5的内置特性(如持久化缓存、资源模块)进一步简化了优化流程。建议结合[Webpack官方文档](https://webpack.js.org/)和构建分析工具(如`webpack-bundle-analyzer`)持续调优[^1][^2]。 §§相关问题§§ 1. Webpack 5的持久化缓存是如何工作的?如何配置? 2. 如何通过Webpack优化图片加载性能? 3. Web Vitals中的LCPFCP具体受哪些因素影响?如何量化测量? </think> ### Webpack Loader 和 Plugin 详解及性能优化指南 #### 一、最新版 Webpack 5 核心 Loader 和 Plugin 以下是 Webpack 5 的常用 Loader 和 Plugin 分类及代表工具: | 类型 | 名称 | 作用描述 | 安装命令 | |------------|----------------------|--------------------------------------------------------------------------|----------------------------------| | **Loader** | `babel-loader` | 转换 ES6+/TypeScript 代码为浏览器兼容 JS | `npm i babel-loader @babel/core` | | | `css-loader` | 解析 CSS 中的 `@import` 和 `url()` 依赖 | `npm i css-loader` | | | `style-loader` | 将 CSS 注入 DOM(通过 `<style>` 标签) | `npm i style-loader` | | | `sass-loader` | 编译 SCSS/Sass 为 CSS | `npm i sass-loader sass` | | | `postcss-loader` | 使用 PostCSS 处理 CSS(自动添加前缀等) | `npm i postcss-loader postcss` | | | `file-loader` | 处理图片/字体等资源文件(Webpack 5 可用资源模块替代) | `npm i file-loader` | | **Plugin** | `HtmlWebpackPlugin` | 自动生成 HTML 并注入打包后的资源链接 | `npm i html-webpack-plugin` | | | `MiniCssExtractPlugin` | 将 CSS 提取为独立文件(替代 style-loader) | `npm i mini-css-extract-plugin` | | | `CleanWebpackPlugin` | 构建前清理输出目录 | `npm i clean-webpack-plugin` | | | `CompressionPlugin` | 生成 Gzip/Brotli 压缩文件 | `npm i compression-webpack-plugin` | | | `BundleAnalyzerPlugin` | 可视化分析包体积 | `npm i webpack-bundle-analyzer` | > 💡 **Loader vs Plugin 核心区别**: > - **Loader**:文件加载时转换器(如转译 JSX、编译 SCSS) > - **Plugin**:扩展 Webpack 功能的钩子(如优化资源、生成 HTML) --- ### 二、Webpack 性能优化方案 #### 1. 减小打包体积策略 ```javascript // webpack.config.js const TerserPlugin = require('terser-webpack-plugin'); const CssMinimizerPlugin = require('css-minimizer-webpack-plugin'); module.exports = { optimization: { minimizer: [ new TerserPlugin(), // 压缩 JS(Webpack 5 默认集成) new CssMinimizerPlugin(), // 压缩 CSS ], splitChunks: { // 代码分割 chunks: 'all', minSize: 20000, // 超过 20KB 才拆分 } }, module: { rules: [{ test: /\.(png|jpe?g)$/, type: 'asset', // Webpack 5 资源模块 parser: { dataUrlCondition: { maxSize: 8 * 1024 } // <8KB 转 Base64 } }] } }; ``` **关键优化**: - **Tree Shaking**:生产模式自动启用(需 ES6 模块语法) - **资源压缩**:图片使用 `image-webpack-loader` 进一步压缩 - **按需加载**:使用 `import()` 动态导入路由组件 #### 2. 提高打包速度方案 ```javascript // 配置示例 module.exports = { cache: { type: 'filesystem' // Webpack 5 持久化缓存 }, module: { rules: [{ test: /\.js$/, exclude: /node_modules/, // 排除 node_modules use: [{ loader: 'babel-loader', options: { cacheDirectory: true } // 启用 Babel 缓存 }] }] }, resolve: { extensions: ['.js', '.jsx'], // 减少文件查找扩展名 alias: { '@': path.resolve(__dirname, 'src') // 设置路径别名 } } }; ``` **提速技巧**: - **限制 Loader 范围**:通过 `include/exclude` 减少处理文件 - **多进程构建**:使用 `thread-loader`(CPU 密集型操作) - **DLL 预构建**:对稳定库(如 React)提前打包 #### 3. 优化 LCP/FCP 性能 > **LCP(最大内容渲染)**:首屏主要内容加载时间 > **FCP(首次内容渲染)**:首次任意内容渲染时间 ```javascript // 优化配置 const HtmlWebpackPlugin = require('html-webpack-plugin'); const MiniCssExtractPlugin = require('mini-css-extract-plugin'); module.exports = { plugins: [ new HtmlWebpackPlugin({ minify: true, // 压缩 HTML preload: '**/*.css' // 预加载关键 CSS }), new MiniCssExtractPlugin({ filename: '[name].[contenthash].css' }) ], module: { rules: [{ test: /\.css$/, use: [ MiniCssExtractPlugin.loader, // 提取 CSS 文件 'css-loader' ] }] } }; ``` **LCP/FCP 专项优化**: 1. **关键 CSS 内联**:使用 `critters-webpack-plugin` 首屏 CSS 内联 ```bash npm i critters-webpack-plugin ``` 2. **资源预加载**:对关键字体/图片添加 `<link rel="preload">` 3. **延迟非关键 JS**:通过 `async/defer` 延迟第三方脚本 4. **CDN 加速**:将静态资源上传至 CDN 5. **优化图片格式**:使用 WebP 格式(通过 `image-minimizer-webpack-plugin`) --- ### 三、Web Vitals 优化实践 1. **LCP 优化**: - 使用 `loading="eager"` 优先加载 LCP 元素图片 - 服务端渲染(SSR)或静态站点生成(SSG) - 移除渲染阻塞资源 2. **FCP 优化**: - 内联关键 CSS(减少网络请求) - 使用骨架屏(Skeleton Screen) - 优化 Web 字体加载(`font-display: swap`) > ✅ **验证工具**: > - Lighthouse(Chrome DevTools) > - Webpack 性能分析:`webpack --profile --json > stats.json` + [Webpack Analyse](https://webpack.github.io/analyse/) 通过以上优化,典型项目可达到: - 打包体积减少 40%~60% - 构建速度提升 50%~70% - LCP 时间 < 2.5s(良好标准) --- ### 总结 优化 Webpack 需结合: 1. **现代工具**:Webpack 5 内置资源模块/持久化缓存 2. **按需处理**:Loader 范围限制/代码分割 3. **性能指标驱动**:专注 LCP/FCP 等核心 Web Vitals > 引用官方文档获取最新配置:[Webpack Performance Guidelines](https://webpack.js.org/guides/performance/)[^1]
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值