目录
浏览器进程:多进程
主进程:显示、交互,增删进程
UI进程:控制地址栏、书签、前进后退
渲染进程:每个标签页或窗口都有一个独立的渲染进程,解析HTML,执行js
插件进程
GPU进程:图像渲染
网络进程:请求
LightHouse v8/v9性能指标
FCP首次内容绘制(First Contentful Paint)⭐
优化手段:缩短字体加载时间
LCP最大内容绘制(Largest Contentful Paint)⭐
原因
缓慢的服务器响应速度
阻塞渲染的 JavaScript 和 CSS
缓慢的资源加载速度
客户端渲染
首次像素绘制(FP)白屏⭐
CLS(Cumulative Layout Shift):累积布局偏移,(衡量网页元件视觉稳定性)⭐
CLS 较差的最常见原因为:无尺寸、动态注入
优化手段
1. 除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容
2. 倾向于选择transform动画
TTI(Time to Interactive):变成完全交互式的时间
TBT(Total Blocking Time):阻止响应时间
长时间任务(Long Tasks): 长时间任务是指在浏览器主线程上执行的任务,其持续时间超过50毫秒(通常)
后果:这可能会阻塞其他任务的执行,包括用户输入响应,JavaScript脚本的执行、DOM操作、CSS计算等。
SI(Speed Index):帧之间的视觉进展
优化手段:减少主线程工作 、JavaScript的执行时间
首字节时间:网络后端的整体响应耗时
window.performance API
性能统计
Level 1 的规范(window.performance.timing)
确定统计起始点
fetchStart:准备好使用 HTTP 请求获取文档
浏览器重定向、卸载页面的耗时对页面加载分析并无太大作用;通常使用 fetchStart 作为统计起始点。
性能数据计算公式
使用 performance.timing 简单计算出网页性能数据
FCP首屏:domContentLoadedEventEnd/Start - fetchStart
FP白屏:domInteractive - fetchStart
使用performance.getEntries()获取所有资源请求的时间数据
使用performance.getEntriesByName(name)获取特定名称的时间数据
FP:item.startTime
LCP:创建性能观察器PerformanceObserver
使用performance.now()精确计算程序执行时间
使用performance.mark以及performance.measure手动测量性能
Google performance 面板
SPA
数据上报方式
使用的img标签get请求
性能优化
解决服务器响应慢
尽早连接:rel="preconnect"
尽快DNS查找:rel="dns-prefetc"(不支持preconnect时)
缓存
本地
静态HTML缓存到本地磁盘
Service Worker
代理
配置反向代理(Varnish、nginx)来提供缓存内容
将用户路由到附近的CDN(内容分发网络)
阻塞渲染的 JavaScript 和 CSS
减少 CSS 阻塞时间:减少非必要字符css-minimizer-webpack-plugin
延迟加载非关键 CSS:rel="preload"主线程启动之前
内联关键 CSS:
减少 JavaScript 阻塞时间
缩小和压缩 JavaScript 文件:
缩小是删除空格和不需要的代码
压缩是使用压缩算法修改数据的过程
静态压缩:WebpackPlugin
延迟加载未使用的 JavaScript
代码拆分
缓慢的资源加载速度
优化和压缩图像
压缩图像(例如使用 Imagemin)
将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
使用响应式图像
考虑使用图像 CDN
预加载重要资源:rel="preload"
浏览器进程:多进程
主进程:显示、交互,增删进程
Browser Process
主进程是浏览器的核心,负责管理用户界面、显示内容、用户交互等。
它还负责创建和销毁其他进程,如渲染进程、插件进程等。
UI进程:控制地址栏、书签、前进后退
存储进程:cookie,webstorage,indexDB
渲染进程:每个标签页或窗口都有一个独立的渲染进程,解析HTML,执行js
Renderer Process
每个标签页或窗口都有一个独立的渲染进程。
渲染进程负责解析HTML、执行JavaScript、渲染页面内容,同时拥有自己的渲染引擎和JavaScript引擎。
这种隔离性可以防止一个页面的崩溃影响其他页面。
插件进程
Plugin Process
如果页面使用了插件(如Flash、PDF阅读器等),浏览器会为插件创建独立的进程,以增加安全性和稳定性。
GPU进程:图像渲染
一些浏览器使用独立的GPU进程来处理图形渲染,以提高性能并减少主渲染进程的工作量。
网络进程:请求
一些浏览器还将网络请求和资源下载放在单独的进程中,以便更好地管理网络连接。
LightHouse v8/v9性能指标
Lighthouse是由Google开发的一款开源工具,用于评估Web应用程序的性能、可访问性、最佳实践和SEO(搜索引擎优化)等方面的质量。Lighthouse可以作为一个独立的CLI(命令行界面)工具,也可以通过Chrome浏览器的开发者工具来运行。
FCP
首次内容绘制(First Contentful Paint
)⭐
FCP
衡量的是,在用户导航到页面后,浏览器呈现第一块DOM内容所需的时间。
页面上的图片、非白色<canvas>
元素和svg
都被认为是DOM
内容;
iframe
内的任何内容都不包括在内
<iframe>
(内联框架)是HTML中的一个标签,用于在当前网页中嵌入另一个网页或资源。这可以用于在页面中显示其他网站的内容,或者将不同的Web内容集成到一个页面中
<!DOCTYPE html>
<html>
<head><title>嵌入Google搜索</title>
</head>
<body><h1>在页面中嵌入Google搜索</h1><iframe src="https://www.google.com" width="800" height="600" frameborder="0"></iframe><p>这是一个嵌入了Google搜索的示例。</p>
</body>
</html>
优化手段:缩短字体加载时间
LCP
最大内容绘制(Largest Contentful Paint
)⭐
LCP
测量视口中最大的内容元素何时呈现到屏幕上。这接近于用户可以看到页面的主要内容
LCP 的数据会通过 PerformanceEntry 对象记录, 每次出现更大的内容渲染, 则会产生一个新的 PerformanceEntry 对象
原因
缓慢的服务器响应速度
阻塞渲染的 JavaScript
和 CSS
缓慢的资源加载速度
客户端渲染
首次像素绘制(FP)白屏⭐
从页面开始加载到浏览器中检测到渲染(任何渲染)时被触发(例如背景改变,样式应用等)
CLS
(Cumulative Layout Shift
):累积布局偏移,(衡量网页元件视觉稳定性)⭐
累积布局偏移 (CLS
) 是测量视觉稳定性的一个以用户为中心的重要指标
CLS 较差的最常见原因为:无尺寸、动态注入
1.无尺寸的图像
2.无尺寸的嵌入和 iframe
3.动态注入的内容
优化手段
1. 除非是对用户交互做出响应,否则切勿在现有内容的上方插入内容
2. 倾向于选择transform
动画
TTI
(Time to Interactive
):变成完全交互式的时间
TTI
测量一个页面变成完全交互式需要多长时间
当页面显示
有用的内容(由First Contentful Paint衡量),
为大多数可见的页面元素注册了事件处理程序
并且页面在50毫秒内响应用户交互时,
页面被认为是完全交互式的。
TBT
(Total Blocking Time
):阻止响应时间
长时间任务(Long Tasks): 长时间任务是指在浏览器主线程上执行的任务,其持续时间超过50毫秒(通常)
后果:这可能会阻塞其他任务的执行,包括用户输入响应,JavaScript脚本的执行、DOM操作、CSS计算等。
它衡量了在页面加载过程中由于长时间任务(Long Tasks)的存在而导致的用户交互延迟。TBT总和是一个网页加载期间所有长时间任务的阻塞部分之和。
例如,如果检测到一个 70 毫秒长的任务,则阻塞部分将为 20 毫秒。
它将这些长时间任务的阻塞部分的持续时间相加,以获得用户可能面临的总延迟。这包括了从FCP到TTI之间的时间段内的所有长时间任务,因为这些任务可能会影响用户对页面的响应速度,从而影响了用户体验。
通过监测和最小化TBT总和,开发人员可以努力提高网页的性能,以确保用户能够更快地与页面进行互动,减少潜在的交互延迟。
SI
(Speed Index
):帧之间的视觉进展
SI
指数衡量内容在页面加载期间视觉显示的速度。Lighthouse
首先在浏览器中捕获页面加载的视频,并计算帧之间的视觉进展
优化手段:减少主线程工作 、JavaScript
的执行时间
首字节时间:网络后端的整体响应耗时
主文档返回第一个字节的时间,是页面加载性能比较重要的指标。对用户来说一般无感知,对于开发者来说,则代表访问网络后端的整体响应耗时。
window.performance API
性能 API 提供了许多与网页性能和资源加载有关的信息,这些信息以性能条目(Performance Entries)的形式存在。这些性能条目包括导航性能(如页面加载时间)、资源加载性能(如图像、脚本和样式表加载时间)
// 获取所有资源加载性能条目
const resourceEntries = performance.getEntriesByType('resource');// 遍历资源性能条目并打印
resourceEntries.forEach((entry) => {console.log('资源类型:', entry.initiatorType);console.log('资源 URL:', entry.name);console.log('加载时间:', entry.duration);
});
从性能API中获取"paint"类型的性能条目,然后查找并处理名称为'first-contentful-paint'的条目,将其描述为'FCP'并将开始时间转换为整数后添加到事务的页面上下文中。
// 获取性能信息并筛选出类型为 'paint' 的条目
window.performance.getEntries().filter((entry: Record<string, any>) => {return entry.entryType === 'paint';}).forEach((item: Record<string, any>) => {// 如果条目的名称是 'first-contentful-paint',则执行以下操作if (item.name === 'first-contentful-paint') {// 将信息添加到事务的页面上下文中transaction.addPageContext({description: 'FCP', // 描述为 'FCP'time: floatToInt(item.startTime) // 将开始时间转换为整数并添加到 'time' 属性});}});
Performance
是一个浏览器全局对象,提供了一组 API 用于编程式地获取程序在某些节点的性能数据。它包含一组高精度时间定义,以及配套的相关方法。我们可以直接在浏览器控制台打印window.performance
// 获取 performance 数据
var performance = {// memory 是非标准属性,只在 Chrome 有// 我有多少内存memory: {usedJSHeapSize: 16100000, // JS 对象(包括V8引擎内部对象)占用的内存,一定小于 totalJSHeapSizetotalJSHeapSize: 35100000, // 可使用的内存jsHeapSizeLimit: 793000000 // 内存大小限制},// 我从哪里来?navigation: {redirectCount: 0, // 如果有重定向的话,页面通过几次重定向跳转而来type: 0 // 0 即 TYPE_NAVIGATENEXT 正常进入的页面(非刷新、非重定向等)// 1 即 TYPE_RELOAD 通过 window.location.reload() 刷新的页面// 2 即 TYPE_BACK_FORWARD 通过浏览器的前进后退按钮进入的页面(历史记录)// 255 即 TYPE_UNDEFINED 非以上方式进入的页面},
// 核心时间相关timing: {// 在同一个浏览器上下文中,前一个网页(与当前页面不一定同域)unload 的时间戳,如果无前一个网页 unload ,则与 fetchStart 值相等navigationStart: 1441112691935,// 前一个网页(与当前页面同域)unload 的时间戳,如果无前一个网页 unload 或者前一个网页与当前页面不同域,则值为 0unloadEventStart: 0,// 和 unloadEventStart 相对应,返回前一个网页 unload 事件绑定的回调函数执行完毕的时间戳unloadEventEnd: 0,// 第一个 HTTP 重定向发生时的时间。有跳转且是同域名内的重定向才算,否则值为 0redirectStart: 0,// 最后一个 HTTP 重定向完成时的时间。有跳转且是同域名内部的重定向才算,否则值为 0redirectEnd: 0,// 浏览器准备好使用 HTTP 请求抓取文档的时间,这发生在检查本地缓存之前fetchStart: 1441112692155,// DNS 域名查询开始的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等domainLookupStart: 1441112692155,// DNS 域名查询完成的时间,如果使用了本地缓存(即无 DNS 查询)或持久连接,则与 fetchStart 值相等domainLookupEnd: 1441112692155,// HTTP(TCP) 开始建立连接的时间,如果是持久连接,则与 fetchStart 值相等// 注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接开始的时间connectStart: 1441112692155,// HTTP(TCP) 完成建立连接的时间(完成握手),如果是持久连接,则与 fetchStart 值相等// 注意如果在传输层发生了错误且重新建立连接,则这里显示的是新建立的连接完成的时间// 注意这里握手结束,包括安全连接建立完成、SOCKS 授权通过connectEnd: 1441112692155,// HTTPS 连接开始的时间,如果不是安全连接,则值为 0secureConnectionStart: 0,// HTTP 请求读取真实文档开始的时间(完成建立连接),包括从本地读取缓存// 连接错误重连时,这里显示的也是新建立连接的时间requestStart: 1441112692158,// HTTP 开始接收响应的时间(获取到第一个字节),包括从本地读取缓存responseStart: 1441112692686,// HTTP 响应全部接收完成的时间(获取到最后一个字节),包括从本地读取缓存responseEnd: 1441112692687,// 开始解析渲染 DOM 树的时间,此时 Document.readyState 变为 loading,并将抛出 readystatechange 相关事件domLoading: 1441112692690,// 完成解析 DOM 树的时间,Document.readyState 变为 interactive,并将抛出 readystatechange 相关事件// 注意只是 DOM 树解析完成,这时候并没有开始加载网页内的资源domInteractive: 1441112693093,// DOM 解析完成后,网页内资源加载开始的时间// 在 DOMContentLoaded 事件抛出前发生domContentLoadedEventStart: 1441112693093,// DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)domContentLoadedEventEnd: 1441112693101,// DOM 树解析完成,且资源也准备就绪的时间,Document.readyState 变为 complete,并将抛出 readystatechange 相关事件domComplete: 1441112693214,// load 事件发送给文档,也即 load 回调函数开始执行的时间// 注意如果没有绑定 load 事件,值为 0loadEventStart: 1441112693214,// load 事件的回调函数执行完毕的时间loadEventEnd: 1441112693215// 按照字母排序// connectEnd: 1441112692155,// connectStart: 1441112692155,// domComplete: 1441112693214,// domContentLoadedEventEnd: 1441112693101,// domContentLoadedEventStart: 1441112693093,// domInteractive: 1441112693093,// domLoading: 1441112692690,// domainLookupEnd: 1441112692155,// domainLookupStart: 1441112692155,// fetchStart: 1441112692155,// loadEventEnd: 1441112693215,// loadEventStart: 1441112693214,// navigationStart: 1441112691935,// redirectEnd: 0,// redirectStart: 0,// requestStart: 1441112692158,// responseEnd: 1441112692687,// responseStart: 1441112692686,// secureConnectionStart: 0,// unloadEventEnd: 0,// unloadEventStart: 0}
}
性能统计
Level 1 的规范(window.performance.timing)
- performance.timing 接口(定义了从 navigationStart 至 loadEventEnd 的 21 个只读属性)
- performance.navigation(定义了当前文档的导航信息,比如是重载还是向前向后等)
从当前浏览器窗口卸载旧页面开始,到新页面加载完成,整个过程一共被切分为 9 个小块:提示卸载旧文档、重定向/卸载、应用缓存、DNS 解析、TCP 握手、HTTP 请求处理、HTTP 响应处理、DOM 处理、文档装载完成。每个小块的首尾、中间做事件分界,取 Unix 时间戳,两两事件之间计算时间差,从而获取中间过程的耗时(精确到毫秒级别)。
level2规范(performance.getEntriesByType('navigation')[0])
确定统计起始点
页面性能统计的起始点时间,应该是用户输入网址回车后开始等待的时间
navigationStart:在URL输入栏回车或者页面按F5刷新
fetchStart:准备好使用 HTTP 请求获取文档
浏览器重定向、卸载页面的耗时对页面加载分析并无太大作用;通常使用 fetchStart 作为统计起始点。
性能数据计算公式
使用 performance.timing
简单计算出网页性能数据
FCP首屏:domContentLoadedEventEnd/Start - fetchStart
具备一定意义上的指标可以使用, domContentLoadedEventEnd - fetchStart,甚至使用loadEventStart - fetchStart,此时页面DOM树已经解析完成并且显示内容。
页面完全加载(首次渲染时间+DOM解析耗时+同步JS执行+资源加载耗时):
data['loadEventStart'] - data['fetchStart']
FP白屏:domInteractive - fetchStart
此时页面开始解析DOM树,页面渲染的第一个元素也会很快出现。
-
重定向耗时:redirectEnd - redirectStart
-
DNS 查询耗时:domainLookupEnd - domainLookupStart
-
TCP 链接耗时:connectEnd - connectStart
-
HTTP 请求耗时:responseEnd - responseStart
-
解析 dom 树耗时:domComplete - domInteractive
-
DOM ready 时间:domContentLoadedEventEnd - navigationStart
HTML 加载完成时间, 即 DOM Ready 时间
页面完全加载:首次渲染时间 + DOM 解析耗时 + 同步 JS 执行 + 资源加载耗时
//性能数据调用的api
data = window.performance.getEntriesByType('navigation')
//若上述api不支持则使用(将废弃)
data = window.performance.timing
-
onload:loadEventEnd - navigationStart
使用performance.getEntries()
获取所有资源请求的时间数据
let times = {};
let t = window.performance.timing;// 优先使用 navigation v2 https://www.w3.org/TR/navigation-timing-2/
if (typeof win.PerformanceNavigationTiming === 'function') {try {var nt2Timing = performance.getEntriesByType('navigation')[0]if (nt2Timing) {t = nt2Timing}} catch (err) {}
}//重定向时间
times.redirectTime = t.redirectEnd - t.redirectStart;//dns查询耗时
times.dnsTime = t.domainLookupEnd - t.domainLookupStart;//TTFB 读取页面第一个字节的时间
times.ttfbTime = t.responseStart - t.navigationStart;//DNS 缓存时间
times.appcacheTime = t.domainLookupStart - t.fetchStart;//卸载页面的时间
times.unloadTime = t.unloadEventEnd - t.unloadEventStart;//tcp连接耗时
times.tcpTime = t.connectEnd - t.connectStart;//request请求耗时
times.reqTime = t.responseEnd - t.responseStart;//解析dom树耗时
times.analysisTime = t.domComplete - t.domInteractive;//白屏时间
times.blankTime = (t.domInteractive || t.domLoading) - t.fetchStart;//domReadyTime
times.domReadyTime = t.domContentLoadedEventEnd - t.fetchStart;
获取所有资源请求的时间数据,这个函数返回一个按 startTime 排序的对象数组
我们直接面板输出一下。
使用performance.getEntriesByName(name)
获取特定名称的时间数据
FCP = performance.getEntriesByName("first-contentful-paint")[0].startTime - navigationStart
FP:item.startTime
window.performance.getEntries().filter((entry: Record<string, any>) => {return entry.entryType === 'paint'}).forEach((item: Record<string, any>) => {if (item.name === 'first-paint') {transaction.addPageContext({description: 'FP',time: floatToInt(item.startTime)})} })
LCP:创建性能观察器PerformanceObserver
// 创建性能观察器
const observer = new PerformanceObserver((entryList) => {// 从观察到的条目中提取性能条目const entries = list.getEntries();// 获取最后一个性能条目const lastEntry = perfEntries[perfEntries.length - 1];// 从最后一个性能条目中提取最大内容渲染时间 (LCP)const lcp = lastEntry.renderTime || lastEntry.loadTime;// 打印最大内容渲染时间 (LCP)console.log('LCP:', lcp);
});// 开始观察指定类型的性能条目(最大内容渲染)
observer.observe({ entryTypes: ['largest-contentful-paint'] });
使用performance.now()
精确计算程序执行时间
performance.now
方法返回当前网页自从performance.timing.navigationStart
到当前时间之间的微秒数(毫秒的千分之一)。也就是说,它的精度可以达到 100 万分之一秒。
那么我们可以通过两次调用 最后计算出 js 某种操作的精确耗时
const start = performance.now();
doTasks(); // 这里是耗时操作
const end = performance.now();
console.log("耗时:" + (end - start) + "微秒。");
使用performance.mark
以及performance.measure
手动测量性能
这块具体的代码示例 建议大家可以直接访问这里去查看
咱们如果想自定义搜集性能数据指标 做前端的性能监控系统 那么这两个 api 是非常给力的
Google performance 面板
let times = {};
let t = window.performance.timing;// 优先使用 navigation v2 https://www.w3.org/TR/navigation-timing-2/
if (typeof win.PerformanceNavigationTiming === 'function') {try {var nt2Timing = performance.getEntriesByType('navigation')[0]if (nt2Timing) {t = nt2Timing}} catch (err) {}
}//重定向时间
times.redirectTime = t.redirectEnd - t.redirectStart;//dns查询耗时
times.dnsTime = t.domainLookupEnd - t.domainLookupStart;//TTFB 读取页面第一个字节的时间(TTFB有多种计算方式,相减的参数可以是 requestStart 或者 startTime)
times.ttfbTime = t.responseStart - t.requestStart;//DNS 缓存时间
times.appcacheTime = t.domainLookupStart - t.fetchStart;//卸载页面的时间
times.unloadTime = t.unloadEventEnd - t.unloadEventStart;//tcp连接耗时
times.tcpTime = t.connectEnd - t.connectStart;//(Trans)内容传输耗时
times.transTime = t.responseEnd - t.responseStart;//解析dom树耗时
times.analysisTime = t.domComplete - t.domInteractive;//白屏时间
times.blankTime = (t.domInteractive || t.domLoading) - t.fetchStart;//domReadyTime
times.domReadyTime = t.domContentLoadedEventEnd - t.fetchStart;
SPA
页面内容渲染的时机被改变了,,在SPA应用中改变了url但不刷新页面的情况下是不会更新的。因此仅仅通过该api是无法获得每一个子路由所对应的页面渲染的时间。如果需要上报切换路由情况下每一个子页面重新render的时间,需要自定义上报。
数据上报方式
测量好时间后,就需要将数据发送给服务端。页面性能统计数据对丢失率要求比较低,且性能统计应该在尽量不影响主流程的逻辑和页面性能的前提下进行。
使用的img标签get请求
- 不存在AJAX跨域问题,可做跨源的请求
- 很古老的标签,没有浏览器兼容性问题
var i = new Image();
i.onload = i.onerror = i.onabort = function () {i = i.onload = i.onerror = i.onabort = null;
}
i.src = url;
navigator.sendBeacon(优先)
大部分现代浏览器都支持 navigator.sendBeacon方法。这个方法可以用来发送一些统计和诊断的小量数据,特别适合上报统计的场景。
- 数据可靠,浏览器关闭请求也照样能发
- 异步执行,不会影响下一页面的加载
- API使用简单
window.addEventListener('unload', logData, false);function logData() {navigator.sendBeacon("/log", analyticsData);
}
性能优化
解决服务器响应慢
{首字节时间|Time to First Byte}(TTFB
) :服务器响应时间
尽早连接:rel="preconnect"
使用rel="preconnect"
来告知浏览器您的页面打算尽快建立连接。
<link rel="preconnect" href="https://example.com" />
尽快DNS查找:rel="dns-prefetc"
(不支持preconnect时
)
<link rel="dns-prefetch" href="https://example.com" />
缓存
本地
静态HTML缓存到本地磁盘
Service Worker
会在浏览器后台运行,并可以拦截来自服务器的请求。此级别的程序化缓存控制使得缓存部分或全部 HTML 页面内容得以实现,并且只会在内容发生更改时更新缓存。
代理
配置反向代理(Varnish、nginx)来提供缓存内容
将用户路由到附近的CDN(内容分发网络)
将用户路由到附近的CDN(内容分发网络)是一种优化网络性能和用户体验的策略。这个过程通常涉及以下步骤:
-
CDN的选择和配置: 首先,网站所有者需要选择一个或多个CDN提供商,然后将其网站的内容部署到这些CDN上。这通常涉及将静态资源(如图像、脚本、样式表)复制到CDN服务器上。
-
全球CDN节点: CDN提供商通常在全球范围内拥有多个节点或服务器位置。这些节点分布在各个地理位置,通常位于不同的城市和国家。目的是接近最终用户,减少数据传输的延迟和提高内容加载速度。
-
DNS解析: 当用户尝试访问网站时,其浏览器会发出DNS(Domain Name System)请求,将域名解析为IP地址。这个步骤称为DNS解析。如果网站启用了CDN,DNS解析通常会返回一个与用户地理位置最近的CDN节点的IP地址。
-
内容缓存和交付: 一旦用户的请求路由到了附近的CDN节点,CDN服务器会负责响应该请求。如果CDN上已经缓存了所请求的内容,它将直接从缓存中提供内容,从而减少了请求的响应时间。如果内容尚未缓存,CDN服务器将从源服务器获取内容,然后将其缓存以供将来的请求使用。
-
内容交付到用户: CDN节点会将所请求的内容交付给用户的设备,通常通过最快的网络路径。这有助于减少网络延迟,提高内容加载速度。CDN还可以通过提供压缩、负载均衡和其他性能优化来进一步改善用户体验。
-
动态内容和边缘计算: 除了静态资源,一些CDN还提供边缘计算服务,允许在CDN节点上运行动态内容和应用程序逻辑,以加速动态内容的交付。
总的来说,将用户路由到附近的CDN节点有助于改善网站的性能,减少加载时间,提高用户体验,并降低网络流量成本。这种策略尤其对全球分布式的网站和应用程序非常重要,因为它可以减少地理距离对性能的不利影响。
阻塞渲染的 JavaScript 和 CSS
减少 CSS
阻塞时间:减少非必要字符css-minimizer-webpack-plugin
削减 CSS
: CSS
文件可以包含空格、缩进或注释等字符。这些字符对于浏览器来说都不是必要的,而对这些文件进行削减能够确保将这些字符删除。使用模块打包器或构建工具,那么可以在其中包含一个相应的插件来在每次构建时削减 CSS 文件:对于 webpack5
:css-minimizer-webpack-plugin i
延迟加载非关键 CSS
:rel="preload"
主线程启动之前
使用 Chrome
开发者工具中的代码覆盖率Coverage 查找网页上任何未使用的 CSS
。
对于任何初始渲染时不需要的 CSS
,使用 loadCSS
来异步加载文件,这里运用了rel="preload"
和onload
。<link rel="preload" href="stylesheet.css" as="style" onload="this.rel='stylesheet'">
<link>
元素的rel
属性的preload
值允许你在HTML的<head>
中声明获取请求,指定页面将很快需要的资源,你希望在页面生命周期的早期开始加载这些资源,在浏览器的主线程启动之前。
内联关键 CSS
:<head>
把用于首屏内容的任何关键路径 CSS 直接包括在<head>
中来将这些 CSS
进行内联。
减少 JavaScript
阻塞时间
缩小和压缩 JavaScript
文件:
缩小是删除空格和不需要的代码
从而创建较小但完全有效的代码文件的过程。Terser
是一种流行的 JavaScript
压缩工具;
压缩是使用压缩算法修改数据的过程
Gzip
是用于服务器和客户端交互的最广泛使用的压缩格式。Brotli
是一种较新的压缩算法,可以提供比 Gzip
更好的压缩结果。
静态压缩:WebpackPlugin
涉及提前压缩和保存资产。这会使构建过程花费更长的时间,尤其是在使用高压缩级别的情况下,但可确保浏览器获取压缩资源时不会出现延迟。如果您的 web
服务器支持 Brotli
,那么请使用 BrotliWebpackPlugin
等插件通过 webpack
压缩资产,将其纳入构建步骤。否则,请使用 CompressionPlugin
通过 gzip
压缩您的资产。
延迟加载未使用的 JavaScript
代码拆分
通过代码拆分减少 JavaScript
负载,- SplitChunksPlugin
最大限度减少未使用的 polyfill
缓慢的资源加载速度
优化和压缩图像
对于许多网站来说,在页面加载完毕后,图像会是视图中的最大元素。这种情况的常见示例包括首图、大型轮播或横幅图像
改善这些类型的图像进行加载和渲染所需的时间将直接提升 LCP 的速度。实现方式:
首先考虑不使用图像。如果图像与内容无关,请将其删除。
压缩图像(例如使用 Imagemin
)
将图像转换为更新的格式(JPEG 2000、JPEG XR 或 WebP)
使用响应式图像
考虑使用图像 CDN
预加载重要资源:rel="preload"
有时,在某个 CSS
或 JavaScript
文件中声明或使用的重要资源可能会比所期望的要晚一点被获取,例如深藏在应用程序众多 CSS 文件中的某个字体。
知道某个特定资源应该被优先获取,请使用<link rel="preload">
来更加及时地获取该资源
多种类型的资源都可以进行预加载,但您应该首先侧重于预加载关键资产,例如字体、首屏图像或视频,以及关键路径 CSS 或 JavaScript
<link rel="preload" as="script" href="script.js" />
<link rel="preload" as="style" href="style.css" />
<link rel="preload" as="image" href="img.png" />
压缩文本文件
压缩诸如 Gzip
和 Brotli
之类的算法可以显著缩减在服务器和浏览器之间传输的文本文件(HTML
、CSS
、JavaScript
)大小。所有浏览器都有效支持 Gzip
,而 Brotli
几乎可以在所有较新的浏览器中使用,并能提供更好的压缩结果。
首先,检查您的服务器是否已经自动压缩文件。大多数托管平台、CDN 和反向代理服务器在默认情况下都会对资产进行压缩编码,或者使您能够轻松配置资产。
如果您需要对服务器进行修改来使其压缩文件,请考虑使用 Brotli
,而不是 gzip
,因为 Brotli
可以提供更好的压缩率。
选择您要使用的压缩算法后,请在构建过程中提前压缩资产,而不是在浏览器请求时实时压缩资产。这样能够最大限度地减少服务器开销并防止在发出请求时出现延迟,尤其是在使用高压缩比的情况下。
使用 Service Worker
缓存资产
Service Worker
可用于缓存任何静态资源,并在收到重复请求时将资源直接提供给浏览器,而无需通过网络。
使用 Service Worker
预缓存关键资源可以显著减少资源加载时间