前端性能优化——图片篇

本文在github作了收录 github.com/Michael-lzg…javascript

在类电商类项目,每每存在大量的图片,如 banner 广告图,菜单导航图,美团等商家列表头图等。图片众多以及图片体积过大每每会影响页面加载速度,形成不良的用户体验,因此对图片进行优化势在必行。css

咱们先来看一个页面启动时加载的图片信息。html

http.png

如图所示,这个页面启动时加载了几十张图片(甚至更多),而这些图片请求几乎是并发的,在 Chrome 浏览器,最多支持的并发请求次数是有限的,其余的请求会推入到队列中等待或者停滞不前,直到上轮请求完成后新的请求才会发出。因此至关一部分图片资源请求是须要排队等待时间的,过多的图片必然会影响页面的加载和展现。vue

选择合适的图片格式

JPEG

JPEG 是由 Joint Photographic Experts Group 所开发出的一种图片。它最大的特色是 有损压缩。这种高效的压缩算法使它成为了一种很是轻巧的图片格式。另外一方面,即便被称为“有损”压缩,JPG 的压缩方式仍然是一种高质量的压缩方式:当咱们把图片体积压缩至原有体积的 50% 如下时,JPG 仍然能够保持住 60% 的品质。此外,JPG 格式以 24 位存储单个图,能够呈现多达 1600 万种颜色,足以应对大多数场景下对色彩的要求,这一点决定了它压缩先后的质量损耗并不容易被咱们人类的肉眼所察觉。java

优势webpack

  • JPEG 格式的图片能够呈现数百万种颜色。因此每当网站须要呈现色彩丰富的图片,JPEG 老是最佳选择。
  • 有损压缩,你能够经过压缩大大的减小图片的体积,通常图片用 60%级别比较合适,若是选择大于 75%的压缩等级,则会使图片有明显的质量降低。
  • 无兼容性问题,因此开发者能够放心随意使用。

使用场景css3

  • JPG 适用于呈现色彩丰富的图片,在咱们平常开发中,JPEG 图片常常做为大的背景图、轮播图或 Banner 图出现。
  • 可是有损压缩后的图片确实很难露出马脚,当它处理矢量图形和 Logo 等线条感较强、颜色对比强烈的图像时,人为压缩的图片模糊会至关明显。
  • JPEG 图像不支持透明度处理,透明图片可选择使用 PNG。

PNG

PNG(可移植网络图形格式)是由 W3C 开发的图片格式,是一种无损压缩的高保真的图片格式。它同时支持 8 位和 24 位,这里都是二进制数的位数。按照咱们前置知识里提到的对应关系,8 位的 PNG 最多支持 256 种颜色,而 24 位的能够呈现约 1600 万种颜色。git

PNG 图片具备比 JPEG 更强的色彩表现力,对线条的处理更加细腻,对透明度有良好的支持。它弥补了上文咱们提到的 JPEG 的局限性,惟一的缺点就是 体积太大github

应用场景web

  • PNG 在处理线条和颜色对比度方面的优点,咱们主要用它来呈现小的 Logo、颜色简单且对比强烈的图片或背景等。
  • 支持透明度处理,透明图片可选择使用 PNG

GIF

GIF 是一种最多支持 256 种颜色的 8 位无损图片格式。这个限制让 GIF 格式对于多颜色或者摄影图片的展现无能为力。

优势

  • 支持 256 中颜色,文件体积一般都很小
  • 支持透明

应用场景

  • 支持动画,适合去展现一些无限循环的动画,好比图标、表情、广告栏等。
  • 对于一些只有简单色彩的图片很是合适。

WebP

WebP 是一种同时提供了有损压缩与无损压缩(可逆压缩)的图片文件格式,派生自影像编码格式 VP8。它像 JPEG 同样对细节丰富的图片信手拈来,像 PNG 同样支持透明,像 GIF 同样能够显示动态图片,集多种图片文件格式的优势于一身。

WebP 最初在 2010 年发布,目标是减小文件大小,但达到和 JPEG 格式相同的图片质量,但愿可以减小图片档在网络上的发送时间。根据 Google 较早的测试,WebP 的无损压缩比网络上找到的 PNG 档少了 45%的文件大小,即便这些 PNG 档在使用 pngcrush 和 PNGOUT 处理过,WebP 仍是能够减小 28%的文件大小。

虽然 webP 有诸多优势,可是它不能彻底替代 JPEG 和 PNG,由于浏览器对 WebP 支持并不广泛。特别是移动端 IOS 系统基本不支持。

webp.png

图片压缩

咱们再来看一下一张图片的加载过程:

load.png

图片众多以及图片体积过大每每会影响页面加载速度,形成不良的用户体验,有部分图片达到几百 kB,甚至 2M(这锅必须运营背,非得上传高清大图不可?),直接致使了加载时间过长。因此对于体积过大的图片,在保持图片在可接受的清晰度范围内可适当对图片大小进行压缩。

图片压缩又分为有损压缩和无损压缩。

有损压缩

有损压缩指在压缩文件大小的过程当中,损失了一部分图片的信息,也即下降了图片的质量(即图片被压糊了),而且这种损失是不可逆的。常见的有损压缩手段是按照必定的算法将临近的像素点进行合并。压缩算法不会对图片全部的数据进行编码压缩,而是在压缩的时候,去除了人眼没法识别的图片细节。所以有损压缩能够在同等图片质量的状况下大幅下降图片的体积。例如 jpg 格式的图片使用的就是有损压缩。

无损压缩

无损压缩指的是在压缩图片的过程当中,图片的质量没有任何损耗。咱们任什么时候候均可以从无损压缩过的图片中恢复出原来的信息。压缩算法对图片的全部的数据进行编码压缩,能在保证图片的质量的同时下降图片的体积。例如 png、gif 使用的就是无损压缩。

下面是各类图片格式的压缩类型

image.png

工具压缩

webpack 压缩

工程化的项目能够在 webpack 里面配置 image-webpack-loader 进行图片压缩

  1. 安装依赖
npm install --save-dev image-webpack-loader
复制代码
  1. 配置 webpack
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 和产品会说压缩出来的效果图片不是他们想要的。

使用雪碧图

雪碧图,CSS Sprites,国内也叫 CSS 精灵,是一种 CSS 图像合成技术,主要用于小图片显示。

浏览器请求资源的时候,同源域名请求资源的时候有最大并发限制,chrome 为 6 个,就好比你的页面上有 10 个相同 CDN 域名小图片,那么须要发起 10 次请求去拉取,分两次并发。第一次并发请求回来后,发起第二次并发。若是你把 10 个小图片合并为一张大图片的画,那么只用一次请求便可拉取下来 10 个小图片的资源。减小服务器压力,减小并发,减小请求次数。

优势

把诸多小图片合成一张大图,利用 backround-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(字体图标),即经过字体的方式展现图标,多用于渲染图标、简单图形、特殊字体等。

优势

  • 像使用字体同样,设置大小、颜色及其余样式,不失真
  • 轻量,易修改
  • 有效减小 HTTP 请求次数

推荐使用阿里的字体图标库:iconfont

使用 base64 格式

原理:将图片转换为 base64 编码字符串 inline 到页面或 css 中。

优势

  • 提高性能: 网页上的每个图片,都是须要消耗一个 http 请求下载而来的, 图片的下载始终都要向服务器发出请求,要是图片的下载不用向服务器发出请求,base64 能够随着 HTML 的下载同时下载到本地.减小 https 请求。

  • 加密: 让用户一眼看不出图片内容 , 只能看到编码。

  • 方便引用: 在多个文件同时使用某些图片时, 能够把图片转为 base64 格式的文件, 把样式放在全局中, 好比 common.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 达成,这样能减小图片的请求,达到优化的目的。

缺点

  • 受限于 css 的浏览器的兼容性
  • 对于较复杂的图案就无能为力了,写也麻烦,开发成本大

使用 CDN 图片

CDN 的全称是 Content Delivery Network,即内容分发网络。CDN 是构建在网络之上的内容分发网络,依靠部署在各地的边缘服务器,经过中心平台的负载均衡、内容分发、调度等功能模块,使用户就近获取所需内容,下降网络拥塞,提升用户访问响应速度和命中率。CDN 的关键技术主要有内容存储和分发技术。

举个简单的例子:

之前买火车票你们都只能去火车站买,后来咱们买火车票就能够在楼下的火车票代售点买了。

基本原理

CDN 的基本原理是普遍采用各类缓存服务器,将这些缓存服务器分布到用户访问相对集中的地区或网络中,在用户访问网站时,利用全局负载技术将用户的访问指向距离最近的工做正常的缓存服务器上,由缓存服务器直接响应用户请求。

基本思路

CND 的基本思路是尽量避开互联网上有可能影响数据传输速度和稳定性的瓶颈和环节,使内容传输的更快、更稳定。经过在网络各处放置节点服务器所构成的在现有的互联网基础之上的一层智能虚拟网络,CDN 系统可以实时地根据网络流量和各节点的链接、负载情况以及到用户的距离和响应时间等综合信息将用户的请求从新导向离用户最近的服务节点上。其目的是使用户可就近取得所需内容,解决 Internet 网络拥挤的情况,提升用户访问网站的响应速度。

CDN 的优点

  • CDN 节点解决了跨运营商和跨地域访问的问题,访问延时大大下降;
  • 大部分请求在 CDN 边缘节点完成,CDN 起到了分流做用,减轻了源站的负载。

图片懒加载

懒加载是一种网页性能优化的方式,它能极大的提高用户体验。图片一直是影响网页性能的主要元凶,如今一张图片超过几兆已是很常常的事了。若是每次进入页面就请求全部的图片资源,那么可能等图片加载出来用户也早就走了。因此进入页面的时候,只请求可视区域的图片资源。

总结出来就是:

  • 减小资源的加载,页面启动只加载首屏的图片,这样能明显减小了服务器的压力和流量,也可以减少浏览器的负担。
  • 防止并发加载的资源过多而阻塞 js 的加载,影响整个网站的启动,影响用户体验
  • 浪费用户的流量,有些用户并不想所有看完,所有加载会耗费大量流量。

原理

图片懒加载的原理就是暂时不设置图片的 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)
复制代码

经过上面例子的实现,咱们要实现懒加载都须要去监听 scroll 事件,尽管咱们能够经过函数节流的方式来阻止高频率的执行函数,可是咱们仍是须要去计算 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 的 background 的 url 属性里面,一种是经过 javascript 的 Image 对象设置实例对象的 src 属性实现图片的预加载。

一、用 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 的 background 属性将图片预加载到屏幕外的背景上。当它们在 web 页面的其余地方被调用时,浏览器就会在渲染过程当中使用预加载(缓存)的图片。该方法虽然高效,但仍有改进余地。使用该法加载的图片会同页面的其余内容一块儿加载,增长了页面的总体加载时间。
为了解决这个问题,咱们增长了一些 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)
复制代码

二、使用 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 的媒体查询(media query)。

@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 属性进行响应式处理。方法以下:

  1. 建立 picture 标签。
  2. 放置多个 source 标签,以指定不一样的图像文件名,进而根据不一样的条件进行加载。
  3. 添加一个回退的元素
<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>
复制代码

须要注意的是:如今不少浏览器对于 picture 这个标签还不支持,使用的时候须要加以注意。

picture.png

渐进式图片

渐进式图片的意思是在高画质图像加载完以前会先显示低画质版本。低画质版本因为画质低、压缩率高,尺寸很小,加载很快。在二者之间咱们也能够根据须要显示不一样画质的版本。

cat.png

渐进式图片可让用户产生图片加载变快的印象。用户再也不盯着一片空白区域等待图片加载,而能看到图像变得愈来愈清晰,这样对用户体验也是友好的。

骨架屏技术也是相似的原理。

总结

  1. 选择合适的图片格式和压缩大图,可从根源上截图大图加载过慢的问题。
  2. 使用雪碧图,iconfont,base64,css 代替图片等可减小图片 http 请求,提升页面加载速度。
  3. 使用 CDN 图片可达到分流的效果,减小服务券压力。
  4. 图片懒加载,预加载,渐进式图片等可不一样程度减小白屏时间,提升产品体验。

推荐文章

相关文章
相关标签/搜索