理论部分内容参考自:Web页面全链路性能优化指南,如果仅仅想进行优化不想花费太多时间了解原理可以直接跳到第二部分

一、理论部分

1.浏览器渲染原理

我们需要知道浏览器是如何渲染一个页面的,我们才能知道如何对页面进行性能优化,所以这里我们对一些基础知识进行讲解。

1.1 进程与线程

浏览器有多种进程,其中最主要的5种进程如下:

p1

  1. 浏览器进程 负责界面展示、用户交互、子进程管理、提供存储等
  2. 渲染进程 每个页面都有一个单独的渲染进程,用于渲染页面,包含webworker线程
  3. 网络进程 主要处理网络资源加载(HTML、CSS、JS、IMAGE、AJAX等)
  4. GPU进程 3D绘制,提高性能
  5. 插件进程 chrome插件,每个插件占用一个进程

1.2 输入url到页面展示完整过程

p2

1.2.1 用户输入

用户在浏览器进程输入并按下回车健后,浏览器判断用户输入的url是否为正确的url,如果不是,则使用默认的搜索引擎将该关键字拼接成url。

1.2.2 卸载原页面并重定向到新页面

然后浏览器会将现有页面卸载掉并重定向到用户新输入的url页面,也就是图中【Process Unload Event】和【Redirect】流程。

此时浏览器会准备一个渲染进程用于渲染即将到来的页面,和一个网络进程用于发送网络请求。

1.2.3 处理Service Worker

如果当前页面注册了Service Worker那么它可以拦截当前网站所有的请求,进行判断是否需要向远程发送网络请求。也就是图中【Service Worker Init】与【Service Worker Fecth Event 】步骤

如果不需要发送网络请求,则取本地文件。如果需要则进行下一步。

1.2.4 网络请求

p3

  1. 根据url查询本地是否已经有强制缓存,如果有则判断缓存是否过期,如果没过期则直接返回缓存内容,也就是图1中【HTTP Cache】步骤
  2. 如果没有强制缓存或者缓存已过期,则将该请求加入队列进行排队准备发送网络请求,也就是图2中【正在排队】,然后进入DNS解析阶段,也就是图1中【DNS】以及图2中的【DNS查找】,DNS根据域名解析出对应的IP地址。(DNS基于UDP)。
  3. 使用IP寻址找到对方,然后根据IP地址+端口号创建一个TCP连接(三次握手),也就是图1中【TCP】以及图2中的【初始连接】创建完成后利用TCP连接来传输数据。(TCP会将数据拆分为多个数据包,进行有序传输,如果丢包会重发,TCP的特点是可靠、有序)
  4. 判断当前协议是否为https,如果为https,则进行SSL协商,将数据进行加密,如果为http协议则不进行加密(明文传输),也就是图2中的【SSL】。
  5. 开始发送http请求(请求行/请求头/请求体),也就是图1中【Request】以及图2中的【已发送请求】。HTTP协议有多个版本,目前使用最多的版本为HTTP/1.1,HTTP/1.1发送完成后默认不会断开。keep-alive 默认打开,为了下次传输数据时复用上次创建的连接。每个域名最多同时建立6个TCP连接,所以同一时间最多发生6个请求。
  6. 服务器收到数据后解析HTTP请求(请求行/请求头/请求体),处理完成后生成状态码和HTTP响应(响应行/响应头/响应体)后返回给客户端,也就是图2的【等待中】在做的事情。
  7. 客户端接收到HTTP响应后根据状态码进行对应的处理,如果状态码为304则直接代表协商缓存生效,直接取本地的缓存文件。如果不是则下载内容。也就是图1中【Response】以及图2中的【下载内容】步骤。

1.2.5 服务端响应

在网络请求第6步中,服务器收到HTTP请求后需要根据请求信息来进行解析,并返回给客户端想要的数据,这也就服务端响应。

服务端可以响应并返回给客户端很多种类型的资源,这里主要介绍html类型。

目前前端处理服务端响应html请求主要分为SSR服务端渲染与CSR客户端渲染,CSR就是返回一个空的HTML模版,然后浏览器加载js后通过js动态渲染页面。SSR是服务端在接受到请求时事先在服务端渲染好html返回给客户端后,客户端再进行客户端激活。

在打开一个站点的首屏页的完整链路中,使用SSR服务端渲染时的速度要远大于CSR客户端渲染,并且SSR对SEO友好。所以对于首屏加载速度比较敏感或者需要优化SEO的站点来说,使用SSR是更好的选择。

1.2.6 浏览器渲染详细流程

浏览器渲染详细流程主要在网络请求中的地7步。浏览器下载完html内容后进行解析何渲染页面的流程。
p4
渲染流程分为4种情况:

  1. HTML中无任何CSS相关标签
  2. CSS相关标签在HTML最顶部,且在解析到内容标签(<div />)时已经解析完CSS相关标签
  3. CSS相关标签在HTML最顶部,但在解析到内容标签(<div />)时CSS相关标签尚未解析完
  4. CSS相关标签在HTML最底部
    (略)

1.3 浏览器处理每一帧的流程

浏览器在渲染完页面之后还需要不间断的处理很多内容的,比如动画、用户事件、定时器等。因此当浏览器渲染完页面后,还会在之后的每一帧到来时执行以下的流程。
p5

  1. 【Input events】处理用户事件,先处理【阻塞事件Blocking】包括touch和wheel事件,后处理【非阻塞事件Non-blocking】包括click和keypress。
  2. 【JS】处理完用户事件后执行【定时器Timers】
  3. 【Begin frame】处理完定时器后开始进行【每帧事件Per frame events】的处理,包括窗口大小改变、滚动、媒体查询的更改、动画事件。
  4. 【rAF】处理完帧事件后执行requestAnimationFrame回调函数和IntersectionObserver回调函数。
  5. 【Layout】然后【重新计算样式Recalc style】、【更新布局Update layout】、【调整Observer回调的大小Resize Observer callbacks】
  6. 【Paint】然后【合成更新Compositing update】、【Paint invalidation】、【Record】

2.Chrome性能优化相关工具

了解完浏览器渲染原理,我们还需要知道根据哪些指标才能判断一个页面性能的好坏,在Chrome中这些指标应该怎么获取。以及Chrome都为我们提供了哪些性能相关的工具,如何使用。

2.1 Chrome Performance(性能)

Performance既是一个Chrome工具,可用于性能检测。
同样又是一套JS API,可在Chrome中执行。

2.1.1 Chrome Performance 工具的使用

打开Chrome调试面板选择Performance,中文版为性能,点击刷新按钮,Performance会刷新并录制当前页面,然后我们就可以在面板中看到如下的各种性能相关细节。
p6

2.1.2 使用Performance API获取性能相关指标

接下来我们来了解一下目前常用的性能指标,并且我们需要知道其中一些关键指标如何用Performance API获取。

  1. TTFB(Time To First Byte)首字节时间:从发送请求到数据返回第一个字节所消耗的时间。
  2. FP (First Paint) 首次绘制: 第一个像素绘制到页面上的时间。
  3. FCP (First Contentful Paint) 首次内容绘制: 标记浏览器渲染来自 DOM 第一位内容的时间点,该内容可能是文本、图像、SVG 甚至 元素。
  4. FMP(First Meaningful Paint) 首次有效绘制: 例如,在 YouTube 观看页面上,主视频就是主角元素。
    图片可以没加载完成,但整体的骨架已经加载完成了。
    1秒内完成FMP的概率超过80%,那就代表这个网站是一个性能较好的网站。
  5. TTI (Time to Interactive) 可交互时间 : DOM树构建完毕,可以绑定事件的时间
  6. LCP (Largest Contentful Paint) 最大内容渲染: 代表在viewport中最大的页面元素加载的时间。LCP的数据会通过PerformanceEntry对象记录, 每次出现更大的内容渲染, 则会产生一个新的PerformanceEntry对象(2019年11月新增)。
  7. DCL (DomContentloaded): 当 HTML 文档被完全加载和解析完成之后,DOMContentLoaded 事件被触发,无需等待样式表、图像和子框架的完成加载。
  8. L (onLoad), 当依赖的资源(图片、文件等), 全部加载完毕之后才会触发。
  9. L (onLoad), 当依赖的资源(图片、文件等), 全部加载完毕之后才会触发。
  10. TBT (Total Blocking Time) 页面阻塞总时长: TBT汇总所有加载过程中阻塞用户操作的时长,在FCP和TTI之间任何long task中阻塞部分都会被汇总。
  11. CLS (Cumulative Layout Shift) 累积布局偏移: 总结起来就是一个元素初始时和其hidden之间的任何时间如果元素偏移了, 则会被计算进去, 具体的计算方法可看这篇文章 https://web.dev/cls/
  12. SI (Speed Index): 指标用于显示页面可见部分的显示速度, 单位是时间。

2.2 Coverage(覆盖率)

获取代码未使用占比。获取代码未使用占比
p7

2.3 Lighthouse

获取性能报告并查看推荐优化项。可以在本地安装命令行工具来使用,也可以通过Chrome来使用。
p8

2.4 Network(网络)

2.4.1 网络请求中的Timing(时间)

能获取网络请求的时间消耗细节,可以根据耗时来决定优化策略。优先优化耗时最长的。
p9

  • 正在排队:网络请求队列的排队时间
  • 已停止:阻塞住用于处理其他事情的时间
  • DNS查找:用于DNS解析IP地址的时间
  • 初始连接:创建TCP连接时间
  • SSL:用于SSL协商的时间
  • 已发送请求:用于发送请求的时间
  • 等待中:请求发出至接收响应的时间也可以理解为服务端处理请求的时间
  • 下载内容:下载响应的时间

2.4.2 网络请求的优先级

浏览器会根据资源的类型决定优先请求哪些资源,优先级高的请求能够优先被加载。
p10
不同资源类型的优先级排序如下:

  • 最高:html、style
  • 高:font、fetch、script
  • 低:image、track

2.4.3 网页总资源信息

p11

  • 58个请求:网页一共多少个请求
  • 6.9 MB 项资源:网页资源一共6.9MB大小
  • DOMContentLoaded 454 毫秒:DOM加载完毕的时长
  • 加载时间 1.02 秒:onload完毕的时长

2.4.4 Network配置

p12

3.网页性能优化

上面我们分别讲解了进程与线程、浏览器打开一个页面的完整过程、浏览器处理每一帧时的流程、Chrome性能相关的各种工具以及性能相关的各种指标。以上内容都掌握之后我们再考虑性能优化遍有了思路,我们在页面展示的任意一个步骤中对其进行优化都能对整个网页的展示性能产生影响。

下面列出了一个页面能优化的所有内容,读者可根据自己的业务情况结合性能工具来做适合自己的优化方式。

3.1 网络优化策略

3.1.1 减少HTTP请求数

合并JS、合并CSS、合理内嵌JS和CSS、使用雪碧图。

3.1.2 使用HTTP缓存

使用强制缓存可以不走网络请求,直接走本地缓存数据来加载资源。

使用协商缓存可以减少数据传输,当不需要更新数据时可通知客户端直接使用本地缓存。

3.1.3 使用 HTTP/2.0

HTTP/2.0使用同一个TCP连接来发送数据,他把多个请求通过二进制分贞层实现了分贞,然后把数据传输给服务器。也叫多路复用,多个请求复用同一个TCP连接。

HTTP/2.0会将所有以:开头的请求头做一个映射表,然后使用hpack进行压缩,使用这种方式会使请求头更小。

服务器可主动推送数据给客户端。

3.1.4 避免重定向

301、302 重定向会降低响应速度。

3.1.5 使用 dns-prefetch

DNS请求虽然占用的带宽较少,但会有很高的延迟,由其在移动端网络会更加明显。

使用 dns-prefetch 可以对网站中使用到的域名提前进行解析。提高资源加载速度。

通过DNS预解析技术可以很好的降低延迟,在访问以图片为主的移动端网站时,使用DNS预解析的情意中下页面加载时间可以减少5%。

3.1.6 使用域名分片

在HTTP/1.1中,一个域名同时最多创建6个TCP连接,将资源放在多个域名下可提高请求的并发数。

3.1.7 CDN

静态资源全上CDN,CDN能非常有效的加快网站静态资源的访问速度。

3.1.8 压缩

gzip压缩、html压缩、js压缩、css压缩、图片压缩。

3.1.9 使用contenthash

contenthash可以根据文件内容在文件名中加hash,可用于浏览器缓存文件,当文件没有改变时便直接取本地缓存数据。

3.1.10 合理使用preload、prefetch

1
2
3
4
5
6
7
<link rel="preload" as="style" href="/static/style.css">
<link rel="preload" as="font" href="/static/font.woff">
<link rel="preload" as="script" href="/static/script.js">

<link rel="prefetch" as="style" href="/static/style.css">
<link rel="prefetch" as="font" href="/static/font.woff">
<link rel="prefetch" as="script" href="/static/script.js">

两者都不会阻塞onload事件,prefetch会在页面空闲时候再进行加载,是提前预加载之后可能要用到的资源,不一定是当前页面使用的,preload预加载的是当前页面的资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="preload" as="style" href="./preload.css">
<title>Document</title>
</head>
<body>
<article></article>
</body>
</html>

如上代码,预加载了css但并没有使用。浏览器在页面onload完成一段时间后,发现还没有引用预加载的资源时,浏览器会在控制台输出下图的提示信息。
p13

preload和prefetch可根据资源类型决定资源加载的优先级,详细优先级如代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 最高 -->
<link rel="preload" as="style" href="./file.xxx">

<!-- 高 -->
<link rel="preload" as="font" href="./file.xxx">
<link rel="preload" as="fetch" href="./file.xxx">
<link rel="preload" as="script" href="./file.xxx">

<!-- 低 -->
<link rel="preload" as="image" href="./file.xxx">
<link rel="preload" as="track" href="./file.xxx">
<title>Document</title>
</head>
<body>
<article></article>
</body>
</html>

p14

3.2 浏览器渲染优化策略

3.2.1 关键渲染路径

当通过JS或者其他任意方式修改DOM后,浏览器会进入如下流程。

【JS通过API修改DOM】>【计算样式】>【布局(重排)】>【绘制(重绘)】>【合成】。

Reflow 重排:重排在Chrome Performance中叫做布局,通常添加或删除元素、修改元素大小、移动元素位置、获取位置信息都会触发页面的重排,因为重排可能会改变元素的大小位置等信息,这样的改变会影响到页面大量其它元素的大小位置信息,会耗费掉大量的性能,所以在实际应用中我们应该尽可能的减少重排。

Repaint 重绘:重绘在Chrome Performance中叫做绘制,通常样式改变但没有影响位置时会触发重绘操作,重绘性能还好,但我们也需要尽量减少重绘,如果需要做一些动画,我们尽量使用CSS3动画,CSS3动画只需要在初始化时绘制一次,之后的动画都不会触发重绘操作。

3.2.2 强制同步布局问题

在同一个函数内,修改元素后又获取元素的位置时会触发强制同步布局,影响渲染性能。

强制同步布局会使js强制将【计算样式】和【布局(重排)】操作提前到当前函数任务中,这样会导致每次运行时执行一次【计算样式】和【重排】,这样一定会影响页面渲染性能,而正常情况下【计算样式】和【重排】操作会在函数结束后统一执行。

3.2.3 如何减少重排与重绘

  1. 脱离文档流(绝对定位、固定定位),脱离文档流的元素进行重排不会影响到其他元素。
  2. 图片渲染时增加宽高属性,宽高固定后,图片不会根据内容动态改变高度,便不会触发重排。
  3. 尽量用CSS3动画,CSS3动画能最大程度减少重排与重绘。
  4. 使用will-change: transform;将元素独立为一个单独的图层。(定位、透明、transform、clip都会产生独立图层)。

3.3 静态文件优化策略

3.3.1 图片优化

3.3.1.1 图片格式
  1. jpg(jpeg):适合色彩丰富的图、Banner图。不适合:图形文字、图标、不支持透明度。
  2. png:适合纯色、透明、图标,支持纯透明和半透明。不适合色彩丰富图片,因为无损储存会导致储存体积大于jpeg。
  3. gif:适合动画、可以动的图标。支持纯透明但不支持半透明,不适合色彩丰富的图片。埋点信息通常也会使用gif发送,因为1x1的gif图发送的网络请求比普通的get请求要小一些。
  4. webp:支持纯透明和半透明,可以保证图片质量和较小的体积,适合Chrome和移动端浏览器。不适合其他浏览器。
  5. svg:矢量格式,大小非常小,但渲染成本过高,适合小且色彩单一的图标。
3.3.1.2 优化策略
  • 减少图片资源的尺寸和大小,节约用户流量。
  • 设置alt=”xxx”属性,图像无法显示时会显示alt内容。
  • 图片懒加载, loading=”lazy”为原生,建议使用IntersectionObserver自己做懒加载。
  • 不同环境加载不同尺寸和像素的图片srcset与sizes的使用。
  • 采用渐进式加载 先加载占位图,然后加载模糊小图,最后加载真正清晰的图。
  • 使用Base64URL 减少图片请求数。
  • 采用雪碧图合并图片,减少请求数。

3.3.2 HTML优化

  • 语义化HTML,代码简洁清晰,利于SEO,便于开发维护。
  • 减少HTML嵌套关系,减少DOM节点数量。
  • 提前声明字符编码,让浏览器快速确定如何渲染网页内容<html lang="en"> <meta charset="UTF-8">
  • 删除多余空格、空行、注释、无用属性。
  • 减少iframe,子iframe会阻塞父级的onload事件。可以使用js动态给iframe赋值,就能解决这个问题。
  • 避免table布局。

3.3.3 CSS优化

  • 减少伪类选择器,减少选择器层数、减少通配符选择器、减少正则选择器。
  • 避免css表达式background-color: expression(...)
  • 删除空格、空行、注释、减少无意义的单位、css压缩。
  • css外链,能走缓存。
  • 添加媒体字段,只加载有效的css文件。
1
2
<link rel="stylesheet" href="./small.css" media="screen and (max-width:600px)" />
<link rel="stylesheet" href="./big.css" media="screen and (min-width:601px)"/>
  • 使用css contain属性,能控制对应元素是否根据子集元素的改变进行重排。
  • 减少@import使用,因为它使用串行加载。

3.3.4 JS优化

  • 通过script的asyncdefer属性异步加载,不阻塞DOM渲染。
  • 减少DOM操作,缓存访问过的元素。
  • 不直接操作真实DOM,可以先修改,然后一次性应用到DOM上。(虚拟DOM、DOM碎片节点)。
  • 使用webworker解决复杂运算,避免复杂运算阻塞主线程,webworker线程位于渲染进程。
  • 图片懒加载,使用IntersectionObserver实现。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<style>
img {
height: 200px;
display: block;
}
</style>
<title>Document</title>
</head>
<body>
<img src="./loading.gif" data-src="./01.jpg" />
<img src="./loading.gif" data-src="./02.jpg" />
<img src="./loading.gif" data-src="./03.jpg" />
<img src="./loading.gif" data-src="./04.jpg" />
<img src="./loading.gif" data-src="./05.jpg" />
<img src="./loading.gif" data-src="./06.jpg" />
<img src="./loading.gif" data-src="./07.jpg" />
<img src="./loading.gif" data-src="./08.jpg" />
<img src="./loading.gif" data-src="./09.jpg" />
<img src="./loading.gif" data-src="./10.jpg" />

<script>
const intersectionObserver = new IntersectionObserver((changes) => {
changes.forEach((item, index) => {
if (item.intersectionRatio > 0) {
intersectionObserver.unobserve(item.target)
item.target.src = item.target.dataset.src
}
})
});

const domImgList = document.querySelectorAll("img");
domImgList.forEach((domImg) => intersectionObserver.observe(domImg));
</script>
</body>
</html>
  • 虚拟滚动
  • 使用requestAnimationFrame来做动画,使用requestIdleCallback来进行空闲时的任务处理。
  • 尽量避免使用eval,性能差。
  • 使用事件委托,能减少事件绑定个数。事件越多性能越差。
  • 尽量使用canvascss3动画。
  • 通过chrome覆盖率(Coverage)工具排查代码中未使用过的代码并将其删除。
  • 通过chrome性能(Performance)工具查看每个函数的执行性能并优化。

3.3.5 字体优化

FOUT(Flash of Unstyled Text)等待一段时间,如果没加载完成,先显示默认。加载 后再进行切换。

FOIT(F1ash of Invisib1e Text) 字体加载完毕后显示,加载超时降级系统字体(白屏)。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style>
@font-face {
font-family: 'hagan';
src: url('./font.ttc');
font-display: swap;
/* b1ock 35 内不显示,如果没加载完毕用默认的 */
/* swap 显示老字体 在替换*/
/* fa11back 缩短不显示时间,如果没加载完毕用默认的,和b1ock类似*
/* optional 替换可能用字体 可能不替换*/
}
article {
font-family: hagan;
}
</style>
<title>Document</title>
</head>
<body>
<article>ABC abc</article>
</body>
</html>

3.4 浏览器储存优化策略

cookie在过期之前一直有效,最大储存大小为4k,限制字段个数,不适合大量的数据储存,每次请求会携带cookie,主要用来做身份校验。
优化方式:

  1. 需要合理设置cookie有效期。
  2. 根据不同子域划分cookie来减少cookie传输。
  3. 静态资源域名和cookie域名采用不同域名,避免静态资源请求携带cookie。

3.4.2 LocalStorage

Chrome下最多储存5M,除非手动清除,否则一直存在。可以利用localStorage储存静态资源。比如储存网页的.js.css,这样会使页面打开速度非常快。例如 百度

1
2
3
4
5
6
// index.js
const name = 'hagan'
function showName () {
console.log(name)
}
showName()
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
<!-- index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Document</title>
</head>
<body>
<script src="https://lib.baomitu.com/axios/0.26.1/axios.js"></script>
<script>
cacheFile('/index.js')

async function cacheFile (url) {
const fileContent = localStorage.getItem(url)
if (fileContent) {
eval(fileContent)
} else {
const { data } = await axios.get(url)
eval(data)
localStorage.setItem(url, data)
}
}
</script>
</body>
</html>

3.4.3 SessionStorage

会话级别储存,可用于页面间的传值。

3.4.4 IndexDB

浏览器的本地数据库,大小几乎无上限。

3.5 其他优化策略

  • 关键资源个数越多,首次页面加载时间就会越长。
  • 关键资源的大小,内容越小下载时间越短。
  • 优化白屏,合理使用内联css、js。
  • 预渲染,打包时进行预渲染,生成静态HTML文件,用户访问时直接返回静态HTML。
  • 服务端渲染同构,加速首屏速度(耗费服务端资源),有利于SEO优化。首屏使用服务端渲染,后续交互使用客户端渲染。

3.6 使用PWA提高用户体验

webapp用户体验差的一大原因是不能离线访问。用户粘性低的一大原因是无法保存入口,PWA就是为了解决webapp的用户体验问题而诞生的。使用PWA能令站点拥有快速、可靠、安全等特性。

  1. Web App Manifest 将网站添加到电脑桌面、手机桌面,类似Native的体验。
  2. Service Worker 配合Cache API,能做到离线缓存各种内容。
  3. Push API 配合 Notification API,能做到类似Native的消息推送与实时提醒。
  4. App Shell 配合 App Skeleton,能做App壳与骨架屏。

二、优化小技巧实操

1.图片压缩

1.1 图片多级压缩

目前从网上下载的图片主流格式是JPG或者PNG,当分辨率比较高的时候体积往往比较大,因此要想提高网站的速度就有必要将图片体积压缩,这里参考店长的推荐,无损压缩推荐Tinypng,有损压缩推荐imagine

TinyPNGImagine
特点无损压缩有损压缩,以牺牲图像质量, 降低图像色彩来达到缩减图像大小的目的。
优点能够完全保留图像色彩,不损伤图像质量1. 压缩程度高,支持转pngjpgwebp,进一步减少图片大小 2. 软件支持一次处理多张图片,无上限,处理完成支持直接批量覆盖原文件或重命名
缺点单次仅能上传20张图片,每张限制大小为5MB,无法处理更大图片。PNG转其他格式为不可逆操作(指覆盖保存后),且可能反而会增加图片大小。

在这里还给大家推荐一个非常实用的网站:imagesTool

这个网站不仅仅支持将PNGJPG格式的图片批量(完全没有数量限制那种)转化为webpavif(chrome推荐的一种压缩率更高的编码方式),而且还能改变图片的原始分辨率,甚至将图片压缩到指定的大小,下面演示了一张图压缩之路!

image-20221025131104569

我们选择了将PNG转化为webp格式,画质为High,图片规模缩小为80%,压缩率达到94%;而选择将PNG转化为avif格式,画质为High,图片规模缩小为80%,压缩率达到95%,这样下来我们可以把所有图片几乎都可以控制在500KB以下,而且肉眼观察画质没有明显的不同。

image-20221025130942155

使用建议:如果没有特别要求,直接使用Imagine进行有损压缩即可。虽然说是有损压缩,但是默认压缩会自主计算压缩程度,一般默认程度就能节省70%空间,且肉眼几乎发现不了图片压缩情况。同时我个人是比较推荐一步到位直接压缩成webp格式,虽然在一些未适配的浏览器(例如IE浏览器)上可能出现无法查看图片的情况,但是压缩率是真的高,图片一路下来压缩个80%毫无压力。如果对图片分辨率要求不太高或者图片分辨率原本就太高了,可以考虑缩小分辨率,结合上面几项,甚至可以压缩90%的大小!

1.2 图片懒加载

图片懒加载是指只加载目前能看到及其附近的页面上的图片,有节省流量与提高性能的作用。Butterfly主题内置了图片懒加载的选项,在_config.butterfly.yml文件下将对应项设置为如下即可:

1
2
3
4
5
6
7
# Lazyload (圖片懶加載)
# https://github.com/verlok/vanilla-lazyload
lazyload:
enable: true
field: site # site/post 选择在哪些地方启用图片懒加载
placeholder: # 你自己喜欢的图片
blur: true # 模糊效果

2.字体二级压缩

我们从网上下载的字体通常都是TTF格式,ttf(TrueTypeFont)是一种字库名称,是Apple公司和Microsoft公司共同推出的字体文件格式,也是最常用的格式之一,但是一些好看的字体往往体积都会很大,网站每次进去都要加载一个这么大的文件,速度肯定会很慢!例如下面这个筑紫A丸2.0的标准体ttf格式的字体文件,体积约为23MB。

image-20221025131822607

这部分参考:有效的压缩字体方法

一个有效的压缩方法是:fonttools编码压缩+转woff2格式二级压缩

2.1 使用 fonttools 压缩字体文件

需要 Python 环境,并已安装好 pip,使用 pip 来安装 fonttools

1
pip install fonttools

下载已经整理好的字体编码列表文件sc_unicode.txt 点这里进行下载

将你要压缩的字体ttf文件和刚刚下载的sc_unicode.txt编码文件放进去同一个文件夹,进入到该文件夹的控制台如下命令然后回车

1
pyftsubset ZhuZiAWan-3.ttf --unicodes-file=sc_unicode.txt

image-20221025133032806

image-20221025133103786

然后就会得到图片所示的subset结尾的编码压缩后的ttf文件,体检体积已经从23MB下降到3MB。

2.2 利用CloudConvert将TTF格式转换成woff2 格式

进去国外平台CloudConvert(国内速度可能会慢一些),将TTF格式的字体文件转化为woff2格式

image-20221025133728970

最后压缩成从23MB的文件压缩成1.5MB,足足压缩了十几倍,压缩率十分高!

3.css和js文件优化

3.1 压缩css与js文件

  • 如果是用VSCODE进行网站搭建的小伙伴,可以下载插件JS & CSS Minifier (Minify)

  • 该插件支持将jscss文件的格式进行压缩,降低文件体积

  • 使用方法:安装插件后,在jscss文件界面右键,选择Minify Document,插件会生成一个文件名为xxx.min.css或者xxx.min.js的文件,这就是压缩后的文件,在引入的时候直接引用这个压缩后的文件即可。

image-20221025134609440

3.2 gulp批量压缩

这是压缩全局静态资源的教程,可以参考店长的文章:

3.3 减少加载项

当网站的东西多起来了之后,会越来越卡顿,这时候推荐以下几个优化方向:

  • 减少卡片的出场动画wowjsLive2Dcanvas元素等(其中Live2D是比较消耗资源的)
  • 如果部分js和css文件只有部分页面需要引用,尽量不全局引用,同时删除空格、空行、注释、减少无意义的单位
  • css文件尽量添加media字段指定加载设备,js文件的引入可以加入 的 asyncdefer 属性异步加载,不阻塞 DOM 渲染

4.无服务器下的资源加速策略

4.1 可用的免费CDN选择

1.staticaly

官网地址:https://www.staticaly.com
轻松地从GitHub / GitLab / Bitbucket等加载您的项目 没有流量限制或限制。

文件通过超快速全球CDN提供。 在URL(不是分支)中使用特定标记或提交哈希。
根据URL永久缓存文件。 除master分支外,文件在浏览器中缓存1年。 具体用法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
# GitHub CDN
https://cdn.staticaly.com/gh/:user/:repo/:tag/:file
https://cdn.staticaly.com/gh/growvv/growvv.github.io/master/README.md

# GitLab CDN
https://cdn.staticaly.com/gl/:user/:repo/:tag/:file

# Bitbucket CDN
https://cdn.staticaly.com/bb/:user/:repo/:tag/:file

# WordPress CDN
https://cdn.staticaly.com/wp/c/:version/wp-includes/:file
https://cdn.staticaly.com/wp/p/:plugin_name/:version/:file
https://cdn.staticaly.com/wp/t/:theme_name/:version/:file

# Imgpx CDN
https://cdn.staticaly.com/img/:image_url

# Favicons CDN
https://cdn.staticaly.com/favicons/:favicon_url

2.githack

直接从GitHub,Bitbucket或GitLab提供原始文件
官网地址:http://raw.githack.com/ 具体用法和上面的staticaly很类似

1
2
3
4
5
6
# Github CDN
//主分支
https://rawcdn.githack.com/liub1934/LB-Blog/master/wp-content/themes/Memory/emoji/xiaodianshi/baiyan.png

//版本分支
https://rawcdn.githack.com/liub1934/LB-Blog/8806f440d3f9a7cc3e6125d7d75564e40262c6a8/wp-content/themes/Memory/emoji/xiaodianshi/baiyan.png

3.TianliCDN

https://tianli-blog.club/jsd/

轻量级私人CDN,申请见:https://tianli-blog.club/jsd/

4.UNPKG

见:https://unpkg.com/

4.2 免费图床推荐

  1. 目前我是用的Github公共仓库+Vercel部署加速,速度还可以,关键是支持增量更新和自动部署,不过前期的配置比较麻烦点,你也可以选用Github公共仓库+Cloudflare部署加速,速度和Vercel接近。

  2. 大概步骤就是建立一个公共仓库,把你本地的图片推上这个仓库,然后将这个仓库部署托管于Vercel,再用一个二级域名绑定这个部署,那就可以通过二级域名+路径的方式访问到图片与资源了。每次仓库更新导致指定分支更新,都会触发Vercel重新部署,实现自动化流程。(这部分教程以后有机会详细写)

  3. 如果不想折腾或者只想存少量不太重要的图片,那么完全可以用公共的免费图床,下面列举了一些免费的图床:

4.3 域名绑定的好处

目前Vercel的域已经被GFW墙了,如果想用Vercel托管网站又想在国内访问又不想科学上网,只能绑定国内域名,还有想用Vercel来进行资源加速也必须是有国内域名的,同时域名还可以拥有自己的域名邮箱与网站标识符,总之域名还是很有用的。对于静态网站来说你可以没有服务器,但是你不能没有域名。

4.4 托管平台的选择

目前能够托管静态网站的平台有以下几个

  1. Vercel(不限量、速度较快) 推荐指数:🌟🌟🌟🌟🌟
  2. Cloudflare(不限量、速度还可以) 推荐指数:🌟🌟🌟🌟
  3. 4EVERLAND(流量限制100GB、速度还可以、适合做备用线路) 推荐指数:🌟🌟🌟
  4. Netlify(流量限制100GB、速度还可以、适合做备用线路) 推荐指数:🌟🌟🌟
  5. Github Pages(不限量、速度较慢) 推荐指数:🌟🌟
  6. Gitee Pages(不限量、速度还可以、但是Gitee与国外平台兼容性差) 推荐指数:🌟🌟
  7. 还有其他的例如GitLab Pages等就不说了,估计没啥人用

5.实用工具

5.1 Lighthouse

Lighthouse 是一个网站性能测评工具, 它是 Google Chrome 推出的一个开源自动化工具,能够对 PWA 和网页多方面的效果指标进行评测,并给出最佳实践的建议以帮助开发者改进网站的质量。它的使用方法也非常简单,我们只需要提供一个要测评的网址,它将针对此页面运行一系列的测试,然后生成一个有关页面性能的报告。通过报告我们就可以知道需要采取哪些措施来改进应用的性能和体验。

在高版本(应该是 >= 60)的 Chrome 浏览器中,Lighthouse 已经直接集成到了调试工具 DevTools 中了,因此不需要进行任何安装或下载。按 F12 打开开发者工具,可以看到在 console、security 等选项后面有一个 Audits (安装了lighthouse插件或者是高版本的谷歌浏览器可能显示的是 lighthouse)选项,选择该选项,然后点击 generate report 即可。

image-20221025173341316

image-20221025173705571

使用 Lighthouse 对网站进行测评后,我们会得到一份评分报告,它包含了性能(Performance),访问无障碍(Accessibility),最佳实践(Best Practice),搜索引擎优化(SEO),PWA(Progressive Web App)五个部分:

5.1.1 性能(Performance)

性能评分的分值区间是0到100,如果出现0分,通常是在运行 Lighthouse 时发生了错误,满分100分代表了网站已经达到了98分位值的数据,而50分则是75分位值的数据。

影响评分的性能指标:性能测试结果会分成 Metrics,Diagnostic,Opportunities 三部分,但只有 Metrics 部分的指标项会对分数产生直接影响。

Lighthouse 会衡量以下 Metrics 性能指标项:

  • 首次内容绘制(First Contentful Paint)。即浏览器首次将任意内容(如文字、图像、canvas 等)绘制到屏幕上的时间点。
  • 首次有效绘制(First Meaningful Paint)。衡量了用户感知页面的主要内容(primary content)可见的时间。对于不同的站点,首要内容是不同的,例如:对于博客文章,标题及首屏文字是首要内容,而对于购物网站来说,图片也会变得很重要。
  • 首次 CPU 空闲(First CPU Idle)。即页面首次能够对输入做出反应的时间点,其出现时机往往在首次有效绘制完成之后。该指标目前仍处于实验阶段。
  • 可交互时间(Time to Interactive)。指的是所有的页面内容都已经成功加载,且能够快速地对用户的操作做出反应的时间点。该指标目前仍处于实验阶段。
  • 速度指标(Speed Index)。衡量了首屏可见内容绘制在屏幕上的速度。在首次加载页面的过程中尽量展现更多的内容,往往能给用户带来更好的体验,所以速度指标的值约小越好。
  • 输入延迟估值(Estimated Input Latency)。这个指标衡量了页面对用户输入行为的反应速度,其基准值应低于 50ms。

Metrics 部分的指标项会直接影响分数,可以作为我们的主要参考点。

另外的两部分中, Opportunities 指的是优化机会,它提供了详细的建议和文档,来解释低分的原因,帮助我们具体进行实现和改进。Diagnostics 指的是现在存在的问题,为进一步改善性能的实验和调整给出了指导。这两者不会纳入分数的计算。

每一项性能指标对评分的贡献都有其计算逻辑,Lighthouse 会将原始的性能值映射成为 0-100 之间的数字。

例如,FMP(First Meaningful Paint)的原始值是从页面初始化开始到主要内容渲染成功的耗时,根据真实站点的数据,顶级性能的站点的 FMP 值约为 1220ms,这个值会被映射成 Lighthouse 的 99 分。

针对不同的评分,Lighthouse 用了不同的颜色进行标注,分值区间和颜色的对应关系如下:

  • 0 - 49(慢):红色
  • 50 - 89(平均值): 橙色
  • 90 - 100(快): 绿色

5.1.2 访问无障碍(Accessibility)

访问无障碍评分的分值由相关指标的加权平均值计算而来。可以在评分详情查阅每项指标的具体权重。同理,较大权重的指标项对分数的影响较大。

无障碍性的每个指标项测试结果为pass或者fail,与性能指标项的计算方式不同,当页面只是部分通过某项指标时,页面的这项指标将不会得分。例如,如果页面中的一些元素有屏幕阅读器友好的命名,而其他的元素没有,那么这个页面的 screenreader-friendly-names 指标项得分为0。

5.1.3 最佳实践(优化)(Best Practice)

最佳实践评分的分数区间为0-100。影响这项评分的指标项的权重都是相同的。

比如:推荐使用 https,跨域的跳转链接需要使用 rel 标识,不能使用废弃的 API等等。

5.1.4 搜索引擎优化(SEO)

比如:图片元素使用 alt 属性等等提高搜索引擎搜索排名,便于搜索引擎能找到你这个网站。

5.1.5 PWA(Progressive Web App)

Lighthouse 使用 PWA 基准检查项列表(Baseline PWA Checklist)进行测评,测评结果将这些指标项分成了四个类别,共包含12个自动测试项和3个手动测试项,其中各个自动测试项的评分权重是相同的。PWA 的评测指标对我们来说非常重要,我们可以从这四个类别详细了解一下基准指标项。

  • 快速可靠:

    1. 页面在移动网络条件下能够快速加载。
    2. 在离线条件下页面能够返回状态码200。这里我们可以通过 Service Worker 来实现离线可用。
    3. start_url 在离线条件下返回状态码200。start_url 是前面章节我们提到过的 manifest.json 中的一个属性,它指定了用户打开该 PWA 时加载的 URL。
  • 可安装:

    1. 始终使用 HTTPS。
    2. 注册 Service Worker 来缓存页面以及 start_url。
    3. 使用 manifest 文件来实现安装 PWA 的需求,浏览器能够主动通知用户将应用添加到桌面,增加留存率。
  • PWA 优化:

    1. 将 HTTP 流量重定向到 HTTPS。
    2. 配置自定义启动画面。
    3. 设置地址栏主题颜色。
    4. 页面内容针对视口大小自适应,对移动用户的展示更友好。
    5. 使用了标签,并设置了 width 或 initial-scale 属性。
    6. 当 JavaScript 文件不可用时,提供降级措施,页面能显示基本内容而不出现白屏。
  • 手动测试项:

    1. 站点跨浏览器可用,如主流浏览器 Chrome, Edge, Firefox 及 Safari 等。
    2. 页面间切换流畅,即使在较差的网络环境下,切换动画也应该简洁顺畅,这是提高用户感知体验的关键。
    3. 保证每个页面都有独一无二的 URL,能够在新的浏览器窗口打开,且方便在社交媒体上进行分享。

除了上述基准指标项之外,为了让 PWA 的体验更加完美,还有一些 Lighthouse 未实现检查的进阶指标,也就是可以作为示范性参考的 PWA 的指标,比如用户体验、缓存、推送通知等等。

5.2 灵雀监控

灵雀应用监控平台最国内老牌知名免费网站统计51LA网站统计旗下的一个 Web 性能与 bug 监控工具平台,发布后在站长圈内也有一些较好的评价,访问地址:https://perf.51.la。51LA灵雀应用监控平台,专注于帮您及时发现异常问题,帮助运营人员发现网站风险,协助开发者修复网页 bug,从而提升用户体验。产品面向产品,运营以及开发人员作为网站性能及异常信息收集工具。开发人员可以通过页面统计获取用户平均的页面统计信息和网页 Performance 的各个性能指标进行分析,如DNS 解析时间,TTFB、服务器响应时间、白屏时间、页面完全加载时长等是否达标等判断。灵雀应用监控平台为开发人员提供了包含程序异常收集、性能分析、资源异常分析、页面外链跳转分析、可疑脚本/iframe 监控等网站优化和用户体验改进所需的信息。

特点:

  • 大家是否有遇到过下面这些情况:
  • 网站打开速度慢找不到原因;
  • 部分访客无法打开网站又不能及时得到反馈;
  • 网站突然无流量或被k 却无从查起起因;

1.异常分析,精准定位

image-20221025174453208

实时监控网站发生的程序错误、网络请求异常、资源加载异常等潜在问题,精准采集错误上报的页面、错误发生时间、UA等详细的用户设备数据,帮您快速定位异常,提高debug效率。

2.性能监测,定向优化

image-20221025174556202

采用51LA V6网站统计的多维分析技术,详细展示分析网站性能加载状况,确定网站体验优化方向。

3.外链监控,规避暗链

image-20221025174628427

提供24小时全方位监控站内外链的功能,定位异常跳转,助您快速识别暗链链接,降低网站被k风险,提升网站安全性。