本文收录于github github.com/Michael-lzg...
在电商项目中,经常会出现大量的图片,比如Banner广告图片、菜单导航图片、美团等店铺列表头部图片。 图片过多、图片过大往往会影响页面加载速度,导致用户体验不佳,因此对图片进行优化势在必行。
我们先看一下页面启动时加载的图片信息。
如图所示,页面启动时会加载几十张(甚至更多)图片,而且这些图片请求几乎是并发的。 在Chrome浏览器中,支持的最大并发请求数是有限的,其他请求会在队列中等待或者原地踏步,直到上一轮请求完成后发出新的请求。 因此webpack图像压缩,相当一部分图片资源请求需要排队等待,过多的图片必然会影响页面的加载和显示。
选择正确的图像格式
JPEG
JPEG是由联合图像专家组开发的图片。 它最大的特点就是有损压缩。 这些高效的压缩算法使其成为一种非常轻量级的图像格式。 另一方面,虽然被称为“有损”压缩,但JPG的压缩方式一直是一种高质量的压缩方式:当我们将图像体积压缩到原始体积的50%以下时,JPG仍然可以保持60%。 % 质量。 据悉,JPG格式以24位存储单张图像,最多可以呈现1600万种颜色,足以满足大多数场景下的色调要求。 这就决定了压缩前后的质量损失对于我们人眼来说是不容易的。 意识到的。
优势
使用场景
巴布亚新几内亚
PNG(便携式网络图形格式)是 W3C 开发的一种图像格式。 它是一种无损压缩的高保真图像格式。 它支持 8 位和 24 位数字,这里是二进制补码数字的位数。 根据我们后知识中提到的对应关系,8位PNG最多支持256种颜色,而24位PNG可以显示大约1600万种颜色。
PNG图像比JPEG有更强的色调表现力,腰线的处理也更简单,对透明度有很好的支持。 它填补了我们上面谈到的JPEG的局限性,唯一的缺点是体积太大。
应用场景
动图
GIF 是一种 8 位无损图像格式,最多支持 256 种颜色。 这一限制使得 GIF 格式无法显示多色或照片图像。
优势
应用场景
网络P
WebP 是一种提供有损压缩和无损压缩(可逆压缩)的图像文件格式,源自图像编码格式 VP8。 它可以像JPEG一样处理细节丰富的图片,像PNG一样支持透明度,可以像GIF一样显示动态图片,并且集成了多种图像文件格式的优点。
WebP 最初于 2010 年发布,目标是减小文件大小,但实现与 JPEG 相同的图像质量,并希望还能减少通过网络发送图像文件所需的时间。 根据谷歌之前的测试webpack图像压缩,WebP 的无损压缩与网上找到的 PNG 文件相比,文件大小减少了 45%。 虽然这个PNG文件是用pngcrush和PNGOUT处理的,但WebP仍然可以将文件大小减少28%。
虽然webP有很多优点,但它并不能完全取代JPEG和PNG,因为浏览器对WebP的支持并不普遍。 特别是联通的IOS系统基本不支持。
图像压缩
我们再看一下一张图片的加载过程:
图片过多、图片尺寸过大往往会影响页面加载速度,导致用户体验不佳。 有的图片达到几百kB,甚至2M(这锅需要运算来背,非得上传高清大图吗?),直接导致Loading时间过长。 因此,对于尺寸过大的图片,可以适当压缩图片的尺寸,同时将图片保持在可接受的清晰度范围内。
图像压缩分为有损压缩和无损压缩。
有损压缩
有损压缩是指在压缩文件大小的过程中,丢失了部分图像信息,即图像质量提高了(即图像变得模糊),但这些损失是不可逆的。 常见的有损压缩方法是按照一定的算法将相邻像素进行组合。 压缩算法并不是对图片中的所有数据进行编码压缩,而是在压缩时去除图片中人眼难以识别的细节。 因此,有损压缩可以在相同画质的情况下大幅增加图片的体积。 jpg 等格式的图像使用有损压缩。
无损压缩
无损压缩是指在不损失图片质量的情况下压缩图片的过程。 我们可以随时从无损压缩图片中恢复原始信息。 压缩算法对图片的所有数据进行编码压缩,可以在保证图片质量的同时增加图片的体积。 如png和gif使用无损压缩。
以下是各种图像格式的压缩类型
工具压缩
网页包压缩
工程项目可以在webpack上配置image-webpack-loader进行图像压缩
安装依赖项
npm install --save-dev image-webpack-loader
复制代码
配置网络包
module.exports = {
...
module: {
rules: [
{
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
use: [
{
loader: 'file-loader',
options: {
name: '[name].[hash:7].[ext]'
},
},
{
loader: 'image-webpack-loader',
options: {
mozjpeg: {
progressive: true,
quality: 50,
},
optipng: {
enabled: true,
},
pngquant: {
quality: [0.5, 0.65],
speed: 4,
},
gifsicle: {
interlaced: false,
},
webp: { // 不支持WEBP就不要写这一项
quality: 75
},
},
},
],
},
],
},
}
复制代码
至于是否使用插件手动压缩,那就见仁见智了,因为有些UI和产品会说压缩后的治愈图片不是自己想要的。
使用精灵
精灵图,CSSSprites,国外也叫CSS sprite,是一种CSS图像合成技术,主要用于小图片显示。
浏览器请求资源时,对同源域名请求资源有最大并发限制。 Chrome是6。例如,您的页面上有10张同一个CDN域名的小图片,那么您需要发起10个拉取请求,分两批并发。 第一个并发请求返回后,发起第二个并发请求。 如果将10张小图合并为一张大图,那么只需一次请求就可以拉取10张小图的资源。 减轻服务器压力,减少并发,减少请求数。
优势
将很多小图片组合成一张大图片,利用background-position属性值来确定图片呈现的位置,可以有效减少请求次数,并且在不影响开发体验的情况下,使用创建插件可以透明的开发商。 适合页面图片较多且丰富的场景。
缺点
生成的镜像体积较大,减少请求数量,减小镜像大小,不合理的分割也不利于并行加载。
复合精灵
在webpack中,有相应的插件提供了手动合成精灵图像的功能,但是你可以手动生成相应的样式文件——webpack-spritesmith,用法如下
var path = require('path')
var SpritesmithPlugin = require('webpack-spritesmith')
module.exports = {
// ...
plugins: [
new SpritesmithPlugin({
src: {
cwd: path.resolve(__dirname, 'src/ico'),
glob: '*.png',
},
target: {
image: path.resolve(__dirname, 'src/spritesmith-generated/sprite.png'),
css: path.resolve(__dirname, 'src/spritesmith-generated/sprite.styl'),
},
apiOptions: {
cssImageRef: '~sprite.png',
},
}),
],
}
复制代码
通过前面的配置,可以将src/ico目录下的所有png文件合成为精灵图片,而是输出到对应的目录,同时可以生成对应的样式文件。 样式文件的句型将根据您配置的样式文件后缀动态生成。
使用图标字体
iconfont(字体图标),以字体的形式显示图标,多用于渲染图标、简单图形、特殊字体等。
优势
推荐使用阿里的字体图标库:iconfont
使用base64格式
原理:将图像转换为base64编码的字符串内联到页面或css中。
优势
但需要注意的是,如果图片较大且图片的色调层次比较丰富,这些方法就不适合使用,因为图片的base64编码的字符串非常大,会明显减少HTML 页面的大小,从而影响加载速度。
最常见的 base64 化用于 url-loader。
module.exports = {
...
module: {
rules: [
{
test: /.(png|jpe?g|gif|svg)(?.*)?$/,
loader: 'url-loader',
options: {
limit: 10240,
name: utils.assetsPath('img/[name].[hash:7].[ext]'),
}
},
],
},
}
复制代码
这样可以将项目中大于10kb的图片转换成base64应用到页面中
使用css来替换图片。
比如要实现装饰效果,如半透明、边框、圆角、阴影、渐变等,在目前主流浏览器中都可以用CSS来实现,这样可以降低对图片的要求,达到优化的目的。
缺点
使用CDN镜像
CDN的全称是ContentDeliveryNetwork,即内容分发网络。 CDN是建立在互联网上的内容分发网络。 借助部署在各地的边缘服务器,通过中心平台的负载均衡、内容分发、调度等功能模块,用户可以就近获取所需内容,减少网络串扰。 ,提高用户访问响应率和点击率。 CDN的关键技术主要包括内容存储和分发技术。
举一个简单的例子:
以前,火车票只能在火车站购买。 后来我们买火车票的时候,就可以在楼下的火车票销售点购买。
基本的
CDN的基本原理是广泛采用各种类型的缓存服务器,并将此类缓存服务器分布在用户访问相对集中的区域或网络中。 在缓存服务器上,缓存服务器直接响应用户请求。
基本思想
CND的基本思想是尽可能避开互联网上可能影响数据传输速率和稳定性的困难和链路,从而使内容传输更快、更稳定。 通过在网络各处放置节点服务器,在现有互联网的基础上形成一层智能虚拟网络,CDN系统可以实时根据网络流量和各节点的连接情况、负载状况以及到用户的距离根据时间、响应时间等综合信息,将用户的请求重定向到距离用户最近的服务节点。 其目的是使用户能够就近获取所需内容,解决Internet网络拥塞情况,提高用户访问网站的响应率。
CDN 的优点
图片延迟加载
延迟加载是网页性能优化的一种形式,可以极大地改善用户体验。 图像仍然是网页性能的主要原因,如今图像超过几兆字节的情况并不罕见。 如果每次进入页面时都请求所有的图片资源,那么用户可能在图片加载完成后就已经离开了。 所以进入页面时,只请求可见区域的图片资源。
总结起来就是:
原则
图片延迟加载的原理不是暂时设置图片的src属性,而是隐藏图片的url,比如先写在data-src上,然后把图片的真实url放入当前图片在可见区域之后的src属性上面,从而实现图片的延迟加载。
function lazyload() {
let viewHeight = document.body.clientHeight //获取可视区高度
let imgs = document.querySelectorAll('img[data-src]')
imgs.forEach((item, index) => {
if (item.dataset.src === '') return
// 用于获得页面中某个元素的左,上,右和下分别相对浏览器视窗的位置
let rect = item.getBoundingClientRect()
if (rect.bottom >= 0 && rect.top < viewHeight) {
item.src = item.dataset.src
item.removeAttribute('data-src')
}
})
}
// 可以使用节流优化一下
window.addEventListener('scroll', lazyload)
复制代码
通过前面反例的实现,如果我们想要实现延迟加载,就需要监听滚动风暴。 虽然我们可以使用函数节流的方法来避免高频执行函数,并且仍然需要估计scrollTop、offsetHeight等属性,但是简单吗? 不需要估计这个属性的方法,答案是肯定的 - IntersectionObserver
const imgs = document.querySelectorAll('img[data-src]')
const config = {
rootMargin: '0px',
threshold: 0,
}
let observer = new IntersectionObserver((entries, self) => {
entries.forEach((entry) => {
if (entry.isIntersecting) {
let img = entry.target
let src = img.dataset.src
if (src) {
img.src = src
img.removeAttribute('data-src')
}
// 解除观察
self.unobserve(entry.target)
}
})
}, config)
imgs.forEach((image) => {
observer.observe(image)
})
复制代码
图片预加载
图片预加载是指在一些需要显示大量图片的网站上,提前将图片加载到本地缓存中,从而提高用户体验。
常用的方法有两种,一种是隐藏在css背景的url属性上,另一种是通过javascript的Image对象设置实例对象的src属性,实现图像的预加载。
1.用CSS和JavaScript实现预加载
#preload-01 {
background: url(http://domain.tld/image-01.png) no-repeat -9999px -9999px;
}
#preload-02 {
background: url(http://domain.tld/image-02.png) no-repeat -9999px -9999px;
}
#preload-03 {
background: url(http://domain.tld/image-03.png) no-repeat -9999px -9999px;
}
复制代码
通过 CSS 背景属性在离屏背景上预加载图像。 当它们在网页的其他地方被调用时,浏览器在渲染期间使用预加载(缓存)的图像。 这种方法实际上是有效的,但仍有改进的空间。 使用此方法加载的图像将与页面上的其他内容一起加载,从而减少页面的整体加载时间。
为了解决这个问题,我们减少了一些 JavaScript 代码来延迟预加载时间,直到页面加载完毕。
function preloader() {
if (document.getElementById) {
document.getElementById('preload-01').style.background =
'url(http://domain.tld/image-01.png) no-repeat -9999px -9999px'
document.getElementById('preload-02').style.background =
'url(http://domain.tld/image-02.png) no-repeat -9999px -9999px'
document.getElementById('preload-03').style.background =
'url(http://domain.tld/image-03.png) no-repeat -9999px -9999px'
}
}
function addLoadEvent(func) {
var oldonload = window.onload
if (typeof window.onload != 'function') {
window.onload = func
} else {
window.onload = function () {
if (oldonload) {
oldonload()
}
func()
}
}
}
addLoadEvent(preloader)
复制代码
2.使用JavaScript实现预加载
function preloader() {
if (document.images) {
var img1 = new Image()
var img2 = new Image()
var img3 = new Image()
img1.src = 'http://domain.tld/path/to/image-001.gif'
img2.src = 'http://domain.tld/path/to/image-002.gif'
img3.src = 'http://domain.tld/path/to/image-003.gif'
}
}
function addLoadEvent(func) {
var oldonload = window.onload
if (typeof window.onload != 'function') {
window.onload = func
} else {
window.onload = function () {
if (oldonload) {
oldonload()
}
func()
}
}
}
addLoadEvent(preloader)
复制代码
响应式图像加载
什么是响应式图像加载? 虽然是为了在不同码率的设备上显示不同规格的图片,防止资源浪费。
常用的方法是CSS3媒体查询(mediaquery)。
@media screen and (min-width: 1200px) {
img {
background-image: url('1.png');
}
}
@media screen and (min-width: 992px) {
img {
background-image: url('2.png');
}
}
@media screen and (min-width: 768px) {
img {
background-image: url('3.png');
}
}
@media screen and (min-width: 480px) {
img {
background-image: url('4.png');
}
}
复制代码
据悉,还可以利用HTML5的图片属性进行响应式处理。 方式如下:
创建图片标签。 放置多个源标签来指定不同的图像文件名,从而根据不同的条件加载它们。添加一个后备元素
<picture>
<source srcset="src/img/l.png" media="(min-width: 1200px)" />
<source srcset="src/img/2.png" media="(min-width: 992px)" />
<source srcset="src/img/4.png" media="(min-width: 768px)" />
<img src="src/img/4.png" />
</picture>
复制代码
需要注意的是,现在很多浏览器都不支持图片标签,所以使用时需要注意。
渐进图像
渐进式图像意味着在加载更高质量的图像之前显示较低质量的版本。 低画质版本帧率低、压缩率高、规格小,因此加载速度快。 在两者之间我们还可以根据需要显示不同音质的版本。
渐进式图像给用户的印象是图像加载速度更快。 用户不用盯着空白区域等待图像加载,而是可以看到图像变得越来越清晰,这对用户体验也很友好。
骨架屏技术也是类似的原理。
总结
选择合适的图片格式,对大图进行压缩,就可以从症结上解决大图截图加载太慢的问题。 使用sprite、iconfont、base64、css代替图片可以减少图片的http请求,提高页面加载速度。 使用CDN图片可以达到分流的疗效,减轻服务优惠券的压力。 图片延迟加载、预加载、渐进式图片等可以不同程度的减少死区时间,提升产品体验。