Web 传输的内容固然是越少越好,最近一段时间的工做一直致力于 Web 性能优化,这是我近期使用过的一些缩减 Web 体积的手段javascript
这些手段主要是为了减小 Web 传输的内容大小,只有干货css
在使用 UI 库的时候,UI 库提供的样式并非全部的都会使用到html
例如一个 button 组件通常都会提供 default/primary/success/warning/danger 五光十色好几款样式java
但咱们实际一个项目中也许只会用到其中的一两种,为了减小样式表的体积,须要将那些没有使用的样式挑选出来删除掉git
使用 uncss 工具来删除无用的样式github
该工具提供有在线版,只须要复制本身的 HTML 以及 CSS,点击按钮就能够生成精简后的样式web
另外也能够经过浏览器工具 Coverage 挑选出未使用的样式,以下图npm
通过分析得出每一个文件未使用样式的百分占比,其中红色标记的为未使用到的样式,从下图中能够看到具体未使用到的样式有哪些api
⚠️ 上面两种方法都是经过样式规则的选择器在页面上查找元素,若是能找到对应的元素,则说明该样式规则有被使用
随着在页面上进行各类操做,该百分比可能会下降,由于有些样式会在某些操做执行以后才会被使用到,好比: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 能够用来删除冗余,合并样式规则
压缩主要是删除无用的空白和注释,或用更简短的写法代替
推荐使用工具 cssnano 来压缩 CSS
该工具还提供了 在线版
这是压缩前的代码
这是压缩后
@charset "utf-8";h1:before{margin:10px 20px;color:red;font-weight:400;background-position:100% 100%;quotes:"«" "»";background:linear-gradient(180deg,#ffe500,#ffe500 50%,#121 0,#121);min-width:0}
体积减小了一半
ℹ️ cssnano 自带 autoprefixer 工具帮助清理浏览器厂商前缀
浏览器的 Coverage 工具也能挑选出未使用的 JavaScript 代码,再也不重复
⚠️ 一样的,挑选出来的代码也不必定全是无用的,须要通过仔细测试
同 CSS 同样,JavaScript 也有一些代码是为了兼容旧浏览器而存在的
像 es5-shim.js 就是为了给那些不支持 ES5 的浏览器准备的,如今已经能够放心地从项目中去掉了,目前全球使用支持 ES5 的浏览器的用户占比高达98%
另一些框架或库的新版本一般将不会包含那些兼容旧浏览器的代码,须要时保持更新便可,好比用 jQuery3.0 替换 jQuery1.12
一个项目经手的人多了以后,会出现一些匪夷所思的膨胀,好比同一个项目中引入了好几个功能类似的插件
找出相关代码,根据需求肯定真正须要使用的插件,去掉其它多余的
⚠️ 此条须要通过严格的测试
随着 Web 标准的丰富以及浏览器的更新换代,愈来愈多的功能能够经过设备/浏览器原生的 API 来实现
好比 IntersectionObserver 能够用来探测 DOM 元素是否位于窗口可视区域内,这就不须要借助插件来实现这些功能了
相应的插件代码能够从项目中安全地删除,或者只为那些老旧设备/浏览器提供
主要是删除没用的空白和注释等等
使用 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'); }
因此上面的字体定义也能够精简为以下,基本能够知足市面上的主流浏览器
@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>