🔝 静态资源优化的整体思路 | 🔙 上一站 - CSScss
优质的图片能够有效吸引用户,给用户良好的体验,因此随着互联网的发展,愈来愈多的产品开始使用图片来提高产品体验。相较于页面其余元素,图片的体积不容忽视。下图是截止 2019 年 6 月 HTTP Archive[1] 上统计的网站上各种资源加载的体积:前端
能够看到,图片占据了半壁江山。一样,在一篇 2018 年的文章中,也提到了图片在网站中体量的平均占比已经超过了 50%[2]。然而,随着平均加载图片总字节数的增长,图片的请求数却再减小,这也说明网站使用的图片质量和大小正在不断提升。webpack
因此,若是单纯从加载的字节数这个维度来看性能优化,那么不少时候,优化图片带来的流量收益要远高于优化 JavaScript 脚本和 CSS 样式文件。下面咱们就来看看,如何优化图片资源。git
图片能够合并么?固然。最为经常使用的图片合并场景就是雪碧图(Sprite)[3]。github
在网站上一般会有不少小的图标,不经优化的话,最直接的方式就是将这些小图标保存为一个个独立的图片文件,而后经过 CSS 将对应元素的背景图片设置为对应的图标图片。这么作的一个重要问题在于,页面加载时可能会同时请求很是多的小图标图片,这就会受到浏览器并发 HTTP 请求数的限制。我见过一个没有使用雪碧图的页面,首页加载时须要发送 20+ 请求来加载图标。将图标合并为一张大图能够实现「20+ → 1」的巨大缩减。web
雪碧图的核心原理在于设置不一样的背景偏移量,大体包含两点:gulp
background-url
设置为合并后的雪碧图的 uri;background-position
来展现大图中对应的图标部分。你能够用 Photoshop 这类工具本身制做雪碧图。固然比较推荐的仍是将雪碧图的生成集成到前端自动化构建工具中,例如在 webpack 中使用 webpack-spritesmith,或者在 gulp 中使用 gulp.spritesmith。它们二者都是基于于 spritesmith 这个库,你也能够本身将这个库集成到你喜欢的构建工具中。浏览器
咱们知道,通常来讲咱们访问一个页面,浏览器加载的整个页面实际上是要比可视区域大不少的,也是什么咱们会提出“首屏”的概念。这就致使其实不少图片是不在首屏中的,若是咱们都加载的话,至关因而加载了用户不必定会看到图片。而图片体积通常都不小,这显然是一种流量的浪费。这种场景在一些带图片的长列表或者配图的博客中常常会遇到。缓存
解决的核心思路就是图片懒加载 —— 尽可能只加载用户正在浏览或者即将会浏览到的图片。实现上来讲最简单的就是经过监听页面滚动,判断图片是否进入视野,从而真正去加载图片:安全
function loadIfNeeded($img) {
const bounding = $img..getBoundingClientRect();
if (
getComputedStyle($img).display !== 'none'
&& bounding.top <= window.innerHeight
&& bounding.bottom >= 0
) {
$img.src = $img.dataset.src;
$img.classList.remove('lazy');
}
}
// 这里使用了 throttle,你能够实现本身的 throttle,也可使用 lodash
const lazy = throttle(function () {
const $imgList = document.querySelectorAll('.lazy');
if ($imgList.length === 0) {
document.removeEventListener('scroll', lazy);
window.removeEventListener('resize', lazy);
window.removeEventListener('orientationchange', lazy);
return;
}
$imgList.forEach(loadIfNeeded);
}, 200);
document.addEventListener('scroll', lazy);
window.addEventListener('resize', lazy);
window.addEventListener('orientationchange', lazy);
复制代码
对于页面上的元素只须要将本来的 src
值设置到 data-src
中便可,而 src
能够设置为一个统一的占位图。注意,因为页面滚动、缩放和横竖方向(移动端)均可能会改变可视区域,所以添加了三个监听。
固然,这是最传统的方法,现代浏览器还提供了一个更先进的 Intersection Observer API[4] 来作这个事,它能够经过更高效的方式来监听元素是否进入视口。考虑兼容性问题,在生产环境中建议使用对应的 polyfill。
若是想使用懒加载,还能够借助一些已有的工具库,例如 aFarkas/lazysizes、verlok/lazyload、tuupola/lazyload 等。
在使用懒加载时也有一些注意点:
对于占位图这块能够再补充一点。为了更好的用户体验,咱们可使用一个基于原图生成的体积小、清晰度低的图片做为占位图。这样一来不会增长太大的体积,二来会有很好的用户体验。LQIP (Low Quality Image Placeholders)[5] 就是这种技术。目前也已经有了 LQIP 和 SQIP(SVG-based LQIP) 的自动化工具能够直接使用。
若是你想了解更多关于图片懒加载的内容,这里有一篇更详尽的图片懒加载指南[6]。
除了对于 <img>
元素的图片进行来加载,在 CSS 中使用的图片同样能够懒加载,最多见的场景就是 background-url
。
.login {
background-url: url(/static/img/login.png);
}
复制代码
对于上面这个样式规则,若是不该用到具体的元素,浏览器不会去下载该图片。因此你能够经过切换 className 的方式,放心得进行 CSS 中图片的懒加载。
还有一种方式是将图片转为 base64 字符串,并将其内联到页面中返回,即将原 url 的值替换为 base64。这样,当浏览器解析到这个的图片 url 时,就不会去请求并下载图片,直接解析 base64 字符串便可。
可是这种方式的一个缺点在于相同的图片,相比使用二进制,变成 base64 后体积会增大 33%。而所有内联进页面后,也意味着本来可能并行加载的图片信息,都会被放在页面请求中(像当因而串行了)。同时这种方式也不利于复用独立的文件缓存。因此,使用 base64 须要权衡,经常使用于首屏加载 CRP 或者骨架图上的一些小图标。
使用合适的图片格式不只能帮助你减小没必要要的请求流量,同时还可能提供更好的图片体验。
图片格式是一个比较大的话题,选择合适的格式[7]有利于性能优化。这里咱们简单总结一些。
1) 使用 WebP:
考虑在网站上使用 WebP 格式[8]。在有损与无损压缩上,它的表现都会优于传统(JPEG/PNG)格式。WebP 无损压缩比 PNG 的体积小 26%,webP 的有损压缩比同质量的 JPEG 格式体积小 25-34%。同时 WebP 也支持透明度。下面提供了一种兼容性较好的写法。
<picture>
<source type="image/webp" srcset="/static/img/perf.webp">
<source type="image/jpeg" srcset="/static/img/perf.jpg">
<img src="/static/img/perf.jpg">
</picture>
复制代码
2) 使用 SVG 应对矢量图场景:
在一些须要缩放与高保真的状况,或者用做图标的场景下,使用 SVG 这种矢量图很是不错。有时使用 SVG 格式会比相同的 PNG 或 JPEG 更小。
3) 使用 video 替代 GIF:
在兼容性容许的状况下考虑,能够在想要动图效果时使用视频,经过静音(muted)的 video 来代替 GIF。相同的效果下,GIF 比视频(MPEG-4)大 5~20 倍。Smashing Magazine 上有篇文章[9]详细介绍使用方式。
4) 渐进式 JPEG:
基线 JPEG (baseline JPEG) 会从上往下逐步呈现,相似下面这种:
而另外一种渐进式 JPEG (progressive JPEG)[10] 则会从模糊到逐渐清晰,令人的感觉上会更加平滑。
不过渐进式 JPEG 的解码速度会慢于基线 JPEG,因此仍是须要综合考虑 CPU、网络等状况,在实际的用户体验之上作权衡。
图片的压缩通常能够分为有损压缩(lossy compression)和无损压缩(lossless compression)。顾名思义,有损压缩下,会损失必定的图片质量,无损压缩则可以在保证图片质量的前提下压缩数据大小。不过,无损压缩通常能够带来更可观的体积缩减。在使用有损压缩时,通常咱们能够指定一个 0-100 的压缩质量。在大多数状况下,相较于 100 质量系数的压缩,80~85 的质量系数能够带来 30~40% 的大小缩减,同时对图片效果影响较小,即人眼不易分辨出质量效果的差别。
处理图片压缩可使用 imagemin 这样的工具,也能够进一步将它集成至 webpack、Gulp、Grunt 这样的自动化工具中。
因为移动端的发展,屏幕尺寸更加多样化了。同一套设计在不一样尺寸、像素比的屏幕上可能须要不一样像素大小的图片来保证良好的展现效果;此外,响应式设计也会对不一样屏幕上最佳的图片尺寸有不一样的要求。
以往咱们可能会在 1280px 宽度的屏幕上和 640px 宽度的屏幕上都使用一张 400px 的图,但极可能在 640px 上咱们只须要 200px 大小的图片。另外一方面,对于现在盛行的“2 倍屏”、“3 倍屏”也须要使用不一样像素大小的资源。
好在 HTML5 在 <img>
元素上为咱们提供了 srcset
和 sizes
属性,可让浏览器根据屏幕信息选择须要展现的图片。
<img srcset="small.jpg 480w, large.jpg 1080w" sizes="50w" src="large.jpg" >
复制代码
具体的使用方式能够看这篇文章[11]。
你也许不知道,不少图片含有一些非“视觉化”的元信息(metadata),带上它们可会致使体积增大与安全风险[12]。元信息包括图片的 DPI、相机品牌、拍摄时的 GPS 等,可能致使 JPEG 图片大小增长 15%。同时,其中的一些隐私信息也可能会带来安全风险。
因此若是不须要的状况下,可使用像 imageOptim 这样的工具来移除隐私与非关键的元信息。
在 2.1. 中提到,合适的场景下可使用 SVG。针对 SVG 咱们也能够进行一些压缩。压缩包括了两个方面:
首先,与图片不一样,图片是二进制形式的文件,而 SVG 做为一种 XML 文本,一样是适合使用 gzip 压缩的。
其次,SVG 自己的信息、数据是能够压缩的,例如用相比用 <path>
画一个椭圆,直接使用 <ellipse>
能够节省文本长度。关于信息的“压缩”还有更多能够优化的点[13]。SVGGO 是一个能够集成到咱们构建流中的 NodeJS 工具,它能帮助咱们进行 SVG 的优化。固然你也可使用它提供的 Web 服务。
与其余静态资源相似,咱们仍然可使用各种缓存策略来加速资源的加载。
图片做为现代 Web 应用的重要部分,在资源占用上一样也不可忽视。能够发现,在上面说起的各种优化措施中,同时附带了相应的工具或类库。平时咱们主要的精力会放在 CSS 与 JavaScript 的优化上,所以在图片优化上可能概念较为薄弱,自动化程度较低。若是你但愿更好得去贯彻图片的相关优化,很是建议将自动化工具引入到构建流程中。
除了上述的一些工具,这里再介绍两个很是好用的图片处理的自动化工具:Sharp 和 Jimp。
好了,咱们的图片优化之旅就暂时到这了,下面就是字体资源了。
目前内容已所有更新至 ✨ fe-performance-journey ✨ 仓库中,陆续会将内容同步到掘金上。若是但愿尽快阅读相关内容,也能够直接去该仓库中浏览。