Web性能优化之瘦身秘笈
Web 传输的内容当然是越少越好,最近一段时间的工作一直致力于 Web 性能优化,这是我近期使用过的一些缩减 Web 体积的手段
这些手段主要是为了减少 Web 传输的内容大小,只有干货
CSS
删除无用的样式
在使用 UI 库的时候,UI 库提供的样式并不是所有的都会使用到
例如一个 button 组件一般都会提供 default/primary/success/warning/danger 五颜六色好几款样式
但我们实际一个项目中也许只会用到其中的一两种,为了减少样式表的体积,需要将那些没有使用的样式挑选出来删除掉
使用 uncss 工具来删除无用的样式
该工具提供有在线版,只需要复制自己的 HTML 以及 CSS,点击按钮就可以生成精简后的样式
另外也可以通过浏览器工具 Coverage 挑选出未使用的样式,如下图

经过分析得出每个文件未使用样式的百分占比,其中红色标记的为未使用到的样式,从下图中可以看到具体未使用到的样式有哪些

️ 上面两种方法都是通过样式规则的选择器在页面上查找元素,如果能找到对应的元素,则说明该样式规则有被使用
随着在页面上进行各种操作,该百分比可能会降低,因为有些样式会在某些操作执行之后才会被使用到,比如:hover伪类相关的样式,在鼠标移入元素之前不会被标记为已使用的
所以,这两种方式都有一定的局限性,并不是挑选出的样式就一定是没有用的,也许某个样式是在用户执行相当复杂的操作后才会起作用,需要严格测试
删除重复的样式
CSS 全名 层叠样式表(Cascading Style Sheets),对同一个元素多次指定同一个样式只会让优先级高的覆盖优先级低的
在样式规则的选择器完全相同的情况下(比如这里 .selector-1 > .selector-2 和 .selector-1 > .selector-2 是完全相同的),被覆盖的样式可以安全地删除,如下
/* Before */
.selector-1 > .selector-2 {
display: none;
width: 200px !important;
}
.selector-1 > .selector-2 {
display: block;
width: 100px;
}
/* After */
.selector-1 > .selector-2 {
width: 200px !important;
}
.selector-1 > .selector-2 {
display: block;
}
通过浏览器的开发者工具可以轻松看到哪些样式被覆盖了

️ 在选择器不相同的时候,也有可能会匹配到同一个元素,这个时候本条规则并不适用,需要注意
️ 有时候同一个样式属性反复出现只是为了兼容一些旧浏览器,也需要注意
使用复合属性
有些样式属性可以合并为一条,比如
/* Before */
.selector {
flex-direction: column;
flex-wrap: wrap;
}
/* After */
.selector {
flex-flow: column wrap;
}
合并之后字节数减少
删除过时的样式
有些样式是为了兼容一些老旧浏览器而提供的,当前已经不需要再兼容这些浏览器了,对应的样式可以删除掉,比如如下这些
- header {
- display: block;
- }
️ 使用 autoprefixer 删除过时的浏览器厂商前缀(比如 -moz-,-ms- 这些)
利用继承
部分样式会继承给后代元素,后代元素没有必要再写一遍,除非是确实需要覆盖的
之所以会有这条是因为之前在项目中看到随处可见的 box-sizing: border-box 属性其实可以主动设置为继承
*,
*:before,
*:after {
box-sizing: inherit;
}
html {
box-sizing: border-box;
}
这样所有元素都会继承这个属性,不用反复定义
提取公共样式
将多个规则集中相同的样式提取出来,并使用群组选择器放在一起,比如
/* Before */
.badge {
background-color: orange;
border-raidus: 5px;
color: #fff;
font-size: 13px;
}
.label {
background-color: orange;
border-raidus: 5px;
color: #fff;
font-size: 12px;
}
/* After */
.badge,
.label {
background-color: orange;
border-raidus: 5px;
color: #fff;
}
.badge {
font-size: 13px;
}
.label {
font-size: 12px;
}
csscss 可以用来分析冗余的 CSS 代码
这是一个 Ruby 工具,使用前需要先安装 ruby1.9 或以上版本
这个工具只是用来分析冗余样式的,并不会主动删除样式,需要自己手动调整
️ 在 CSS 中,样式的先后顺序是有意义的,随意移动样式规则可能会让样式出现问题,需要经过严格测试
️ csso 可以用来删除冗余,合并样式规则
压缩 CSS
压缩主要是删除无用的空白和注释
使用 cssnano 压缩 CSS
该工具提供 在线版
️ cssnano 自带 autoprefixer 工具帮助清理浏览器厂商前缀
JavaScript
删除无用的 JavaScript
浏览器的 Coverage 工具也能挑选出未使用的 JavaScript 代码,不再重复
️ 同样的,挑选出来的代码也不一定全是无用的,需要经过仔细测试
删除历史遗留代码
同 CSS 一样,JavaScript 也有一些代码是为了兼容旧浏览器而存在的
像 es5-shim.js 就是为了给那些不支持 ES5 的浏览器准备的,现在已经可以放心地从项目中去掉了,目前全球使用支持 ES5 的浏览器的用户占比高达98%
另外一些框架或库的新版本通常将不会包含那些兼容旧浏览器的代码,需要时保持更新即可,比如用 jQuery3.0 替换 jQuery1.12
删除功能重复的插件
一个项目经手的人多了之后,会出现一些匪夷所思的膨胀,比如同一个项目中引入了好几个功能相似的插件
找出相关代码,根据需求确定真正需要使用的插件,去掉其它多余的
️ 此条需要经过严格的测试
使用新的 API
随着 Web 标准的丰富以及浏览器的更新换代,越来越多的功能可以通过设备/浏览器原生的 API 来实现
比如 IntersectionObserver 可以用来探测 DOM 元素是否位于窗口可视区域内,这就不需要借助插件来实现这些功能了
相应的插件代码可以从项目中安全地删除,或者只为那些老旧设备/浏览器提供
压缩 JavaScript
主要是删除没用的空白和注释等等
使用 Terser 来压缩 JavaScript,通过 NPM 安装 npm install terser -g
执行命令 terser main.js -o main.min.js -c -m
字体
选择合适的格式
常用的字体格式有如下这些
WOFF2/WOFF
Web 开放字体格式(Web Open Font Format),加载快,压缩率高
WOFF2 是 WOFF 的升级版本,压缩率更高
SVG/SVGZ
矢量图形字体(Scalable Vector Graphics Font),仅有少部分浏览器支持(比如 iOS Safari 4.1-)
EOT
Embedded Open Type,IE 独占
TTF/OTF
OpenType Font 和 TrueType Font,浏览器支持范围最广的格式
根据目标设备选择合适的字体格式,不同的字体格式兼容的浏览器也是不一样的
下图是图一套字体的不同文件格式的大小对比

我们应该优先选用压缩率更高的 WOFF2 文件格式,如果浏览器不支持该格式,降级到 WOFF,甚至 OTF/TTF
下面是完整定义字体的方式,浏览器会根据优先顺序下载自身能识别但体积相对更小的字体文件
@font-face {
font-family: 'My Font';
src: url('path/my-font.eot');
src: url('path/my-font.eot?#iefix') format('embedded-opentype'),
url('path/my-font.woff2') format('woff2'),
url('path/my-font.woff') format('woff'),
url('path/my-font.ttf') format('truetype'),
url('path/my-font.svg#svgFontName') format('svg');
}
- TTF/OTF 的兼容性仅比 WOFF 多出一点点而已,已经到了可以忽略不计的地步
- SVG 字体和 EOT 是针对部分旧版本浏览器的兼容方案,目前已经没有太大使用的价值
所以上面的字体定义也可以精简为如下,基本可以满足市面上的主流浏览器
@font-face {
font-family: 'My Font';
src: url('path/my-font.woff2') format('woff2'),
url('path/my-font.woff') format('woff');
}
剔除多余的字体
在一个字体文件中不是所有字体都会使用到,特别是在使用图标字体的时候
里面有很多图标是我在项目中没有用到的,这种时候就需要编辑字体文件,删除那些没用上的字体
百度有个在线字体编辑工具 http://fontstore.baidu.com/static/editor/index.html 可以打开并编辑字体以及保存为其它格式
这是经过我编辑过后的文件大小对比,文件大小差距很大,确实用到的字体比较少

图像
在 Web 网页中,图像的体积占了大头,减少图像可以大幅增加性能
选择适合的图像格式
不同文件格式的图像其文件大小,图像质量是不一样的,根据具体情况选择合适的图像格式
常用 Web 图像格式
| 格式 | 透明 | 动画 | 说明 | 浏览器支持 |
|---|---|---|---|---|
| GIF | ️ | ️ | 颜色较少 | 全 |
| JPEG | 有损格式,常用于照片 | 全 | ||
| PNG | ️ | 无损 | 全 | |
| WebP | ️ | ️ | 支持无损/有损压缩,比JPEG,PNG和GIF更好的压缩效果 | 较新 |
| AVIF | ️ | ️ | 比WebP,JPEG,PNG和GIF更好的压缩效果 | 最新 |
| JPEGXL | ️ | ️ | 无损压缩,更快的解码和其他各种改进 | 暂无 |
一些新的图像格式拥有较高的性能,比如 AVIF 和 WebP
不过这些新的图像格式不是所有浏览器都支持,此时可以使用一个 <picture> 元素来包裹 <img> 元素,再通过使用 <source> 元素来为 <img> 元素提供多个备胎资源供其自行选择
<source> 元素可以有多个,srcset 属性是必须的(注意是 srcset)
<picture>
<source type="image/avif" srcset="logo.avif">
<source type="image/webp" srcset="logo.webp">
<img alt="logo" src="logo.png">
</picture>
浏览器会自行忽略不支持的格式,如果浏览器支持 AVIF 格式就使用 logo.avif,如果支持 WebP 格式就使用 logo.webp
如果上面俩都不支持,就会使用 logo.png,不支持 <picture> 元素的浏览器会直接显示 <img> 元素
值得一提的是 <picture> 元素内部必须包含一个 <img> 元素,否则图像不会显示(因为 <picture> 元素并不是一个独立显示的元素,而是为 <img> 元素服务的)
还有 <img> 元素始终都不应该忘记的 alt 属性,当任何图像格式都无法显示或者图像下载失败的时候,至少还能显示替代的文字说明
要在 CSS 中使用这些新的格式通常用 JavaScript 来判断浏览器是否支持
创建一个 Image 对象,然后加载一张较小的需要判断格式的图像,如果加载成功则说明浏览器支持该格式,下面是 Google 提供的判断浏览器是否支持 WebP 的方法
const img = new Image()
img.onload = img.onerror = () => {
document.body.classList.add(img.height > 0 ? 'webp' : 'no-webp')
}
img.src = 'data:image/webp;base64,UklGRhoAAABXRUJQVlA4TA0AAAAvAAAAEAcQERGIiP4HAA=='
如果该浏览器支持,则给 <body> 元素添加 webp 类,否则添加 no-webp,在 CSS 中就可以这样写
.webp .logo {
background: url(./logo.webp);
}
.no-webp .logo {
background: url(./logo.png);
}
这样就能根据该浏览器是否支持 webp 格式加载不同格式的图像了
️ 转换图像格式可以使用 Sqoosh,在图像大小和质量之间手动调整权衡
使用响应式图像
在 CSS 中使用媒体查询结合 image-set 可以依据设备/浏览器的宽度以及像素比显示不同分辨率的图像
️ 为了方便一眼看出来,图像的名称包含了图像的真实宽度,比如 logo-240.png 表示这张图像宽度为 240 像素
.logo {
background-image: url(./images/logo-120.png);
background-image: -webkit-image-set(url(./images/logo-120.png) 1x, url(./images/logo-240.png) 2x);
background-image: image-set(url(./images/logo-120.png) 1x, url(./images/logo-240.png) 2x);
}
@media (min-width: 600px) {
.logo {
background-image: url(./images/logo-240.png);
background-image: -webkit-image-set(url(./images/logo-240.png) 1x, url(./images/logo-480.png) 2x);
background-image: image-set(url(./images/logo-240.png) 1x, url(./images/logo-480.png) 2x);
}
}
@media (min-width: 1200px) {
.logo {
background-image: url(./images/logo-480.png);
background-image: -webkit-image-set(url(./images/logo-480.png) 1x, url(./images/logo-960.png) 2x);
background-image: image-set(url(./images/logo-480.png) 1x, url(./images/logo-960.png) 2x);
}
}
根据 移动优先 的原则,使用媒体查询应该从小往大
️ 不支持 image-set 的浏览器将会使用前面定义的传统 url 路径
️ image-set 目前还在草案中,需要添加对应的浏览器厂商前缀,示例已添加
️ Safari 只支持 url 路径和 1x/2x 这样的设备像素比
而 <img> 元素通过其新增的 srcset 和 sizes 属性来实现响应式图像
<img alt="avator"
src="avator-120.jpg"
srcset="avator-120.jpg 120w, avator-240.jpg 240w, avator-480.jpg 480w"
sizes="(max-width: 600px) 120px, 240px">
srcset 属性为图像提供多个源供设备/浏览器自行选择,其中图像路径后面的 120w/240w/480w 用于告诉设备/浏览器每张图像的实际宽度
sizes 属性为图像提供渲染尺寸,可以通过媒体查询提供多个渲染尺寸以及一个默认尺寸(这里 240px 就是默认的渲染尺寸)
设备/浏览器会根据这些信息选择最合适的图像加载显示
当设备/浏览器宽度在 600 像素以下时图像将占据 120 像素的宽度,此时如果设备像素比为 1 则显示 avator-120.jpg,如果设备像素比为 2 则显示 avator-240.jpg,为 4 则应该显示 avator-480.jpg
当设备/浏览器宽度大于 600 像素的时候图像将占据 240 像素的宽度,此时如果设备像素比为 1 则显示 avator-240.jpg,如果设备像素比为 2 则显示 avator-480.jpg
| 浏览器宽度 | 设备像素比 | 显示哪张图像 |
|---|---|---|
| <= 600px | 1 | avator-120.jpg |
| - | 2 | avator-240.jpg |
| - | 4 | avator-480.jpg |
| > 600px | 1 | avator-240.jpg |
| - | 2 | avator-480.jpg |
️ 设备像素比也有可能是小数,比如 1.5,设备/浏览器会选择它自己认为最合适的那张图像来显示
️ 其中 src 属性是给不支持 srcset 和 sizes 属性的浏览器提供的
图像压缩
有些格式的图像往往还会包含一些没有用的信息,清理掉这些信息有助于缩小图像体积
这通常使用工具来进行
使用 imagemin 压缩图像
图像懒加载
页面上有很多图像我们一开始是看不到的,有的在我们滚动页面之后才会出现在屏幕上,又有的在某个对话框弹出后才能看到
对于这类图像,我们可以推迟它们的加载时机,等到它们需要真正展示在屏幕上的时候才加载,而不是在页面一开始时就加载,这将大大节省页面初始化时加载的资源大小
使用浏览器原生的懒加载方案,这非常简单,只需要给图像元素添加一个 loading="lazy" 属性即可
<img alt="avator" loading="lazy" src="avator.jpg">
目前该属性只得到一部分浏览器的支持,不支持的浏览器会忽略

该属性的 polyfill
还可以使用 JavaScript 插件,市面上有不少这类型的插件
️ 引入一个插件会增加 JavaScript 的代码量,但是延迟了部分图像的加载时机,具体需要权衡
使用其它方案替换图像
减少图像最好的办法就是没有图像
使用 SVG 替换图像

该图像格式为 png 大小为 1.46kb
如果使用 SVG 来表示同样的图像则只有 300 多字节,体积大幅度减小
<svg viewBox="0 0 100 100" xmlns="http://www.w3.org/2000/svg">
<path d="M50 92.5H6.09a4.47 4.47 0 01-3.87-6.71l22-38 22-38a4.46 4.46 0 017.74 0l22 38 22 38a4.47 4.47 0 01-3.87 6.71z" fill="##ff7f00"></path>
<path d="M57.41 78.1A7.41 7.41 0 1150 70.7a7.39 7.39 0 017.41 7.4zm-2.14-14.89H44.81l-1.72-36h13.82z" fill="#fff"></path>
</svg>
SVG 既可以改颜色,也可以任意放大缩小
SVG 可以使用 SVGO 来优化
使用纯样式替换图像
比如下面这个 loading 效果就是纯样式写的
相对于图像来说,纯代码的字节数就少得多了
@keyframes spin {
to {
transform: rotate(1turn);
}
}
.loading {
animation: spin 1.2s infinite linear;
border: 4px solid rgba(0, 0, 0, 0.1);
border-left-color: #46aaff;
border-radius: 50%;
height: 30px;
width: 30px;
}
<div class="loading"></div>
Web性能优化之瘦身秘笈的更多相关文章
- 性能优化7--App瘦身
1. 前言 如果你对App优化比较敏感,那么Apk安装包的大小就一定不会忽视.关于瘦身的原因,大概有以下几个方面: 对于用户来说,在功能差别不大的前提下,更小的Apk大小意味更少的流量消耗,也意味着更 ...
- 关于WEB 性能优化 (摘抄)
压缩源代码和图片 JavaScript文件源代码可以采用混淆压缩的方式,CSS文件源代码进行普通压缩,JPG图片可以根据具体质量来压缩为50%到70%,PNG可以使用一些开源压缩软件来压缩,比如24色 ...
- Web性能优化-合并js与css,减少请求
Web性能优化已经是老生常谈的话题了, 不过笔者也一直没放在心上,主要的原因还是项目的用户量以及页面中的js,css文件就那几个,感觉没什么优化的.人总要进步的嘛,最近在被angularjs吸引着,也 ...
- web性能优化——浏览器相关
简介 优化是一个持续的过程.所以尽可能的不要有人为的参与.所以能自动化的或者能从架构.框架级别解决的就最更高级别解决. 这样即能实现面对开发人员是透明的.不响应,又能确保所有资源都是被优化过的. 场景 ...
- Web性能优化系列
web性能优化之重要,这里并不打算赘述.本系列课程将带领大家认识.熟悉.深刻体会并且懂得如果去为不同的站点做性能优化 同时,本系列将还会穿插浏览器兼容性相关问题的解决方案,因为在我看来,兼容性同样属于 ...
- 移动web性能优化笔记
移动web性能优化 最近看了一些文章,对移动web性能优化方法,做一个简单笔记 笔记内容主要出自 移动H5前端性能优化指南和移动前端系列——移动页面性能优化
- web性能优化 来自《web全栈工程师的自我修养》
最近在看<web全栈工程师的自我修养>一书,作者是来自腾讯的前端工程师.作者在做招聘前端的时候问应聘者web新能优化有什么了解和经验,应聘者思索后回答“在发布项目之前压缩css和 Java ...
- web性能优化之--合理使用http缓存和localStorage做资源缓存
一.前言 开始先扯点别的: 估计很多前端er的同学应该遇到过:在旧项目中添加新的功能模块.或者修改一些静态文件时候,当代码部署到线上之后,需求方验收OK,此时你送了一口气,当你准备开始得意于自己的ma ...
- Web 性能优化:Preload与Prefetch的使用及在 Chrome 中的优先级
摘要: 理解Preload与Prefetch. 原文:Web 性能优化:Preload,Prefetch的使用及在 Chrome 中的优先级 作者:前端小智 Fundebug经授权转载,版权归原作者所 ...
随机推荐
- Infinite Maze
从起点开始走,对于可以走到的位置,都必定能从这个位置回到起点.这样,对地图进行搜索,当地图中的某一个被访问了两次,就能说明这个地图可以从起点走到无穷远. 搜索的坐标(x,y),x的绝对值可能大于n,的 ...
- CF-1333F Kate and imperfection
F. Kate and imperfection 假设一个一个的往集合里面放元素,显然在放某个元素之前,我们不想让它的倍数已经在集合里面.因为在这之前,我们不如先把这个数放进去,再把它的倍数放进去更优 ...
- HDU-3240(卡特兰数+分解质因数后求逆元)
卡特兰数相关公式 : \(H_n = {C_{2n}^n \over n+1)}\) \(H_n = {(4n-2)\over n+1}\times H_{n-1}\) \(H_n = C_{2n}^ ...
- P1714 切蛋糕 单调队列
题目: 题目描述 今天是小Z的生日,同学们为他带来了一块蛋糕.这块蛋糕是一个长方体,被用不同色彩分成了N个相同的小块,每小块都有对应的幸运值. 小Z作为寿星,自然希望吃到的第一块蛋糕的幸运值总和最大, ...
- codeforces B. Pasha and String
Pasha got a very beautiful string s for his birthday, the string consists of lowercase Latin letters ...
- ACM#学习心得0
加入实验室也有些日子了,这是第一个近来的小小学习心得 1.在之前的训练题和考核题以及平时刷过的题中,我发现自己对字符串这一块的基础知识掌握还是比较差的,总是不能正确的接收的字符或字符串. 这两个星期, ...
- python代理池的构建2——代理ip是否可用的处理和检查
上一篇博客地址:python代理池的构建1--代理IP类的构建,以及配置文件.日志文件.requests请求头 一.代理ip是否可用的处理(httpbin_validator.py) #-*-codi ...
- C# TCP应用编程二 同步TCP应用编程
不论是多么复杂的TCP 应用程序,双方通信的最基本前提就是客户端要先和服务器端进行TCP 连接,然后才可以在此基础上相互收发数据.由于服务器需要对多个客户端同时服务,因此程序相对复杂一些.在服务器端, ...
- 升级到WLS2
WLS2相对WSL1有重大改变,其中最重要的是subsystem linux可以说是真正意义上的linux发行版了,当然也提升了i/o性能. 1. 升级windows WSL 2 is only av ...
- sdutoj2887
#include <stdio.h> #include <math.h> int main(){ int px,tx;double alpha; int T;scanf(&qu ...