前端图片优化指南

954 阅读8分钟

1、你是否真的需要使用图片?

​ 在项目引入图片前,首先需要问问自己,是否真的需要引入此图片保证兼容性的前提下是不是可以使用如下替代方案:

1、iconfont

​ 字体图标是一种全新的设计方式,更为重要的是相比位图而言,使用字体图标可以不受限于屏幕分辨率,冲着这一点就具有非常强的优势,而且字体图标还具有一个优势是,只要适合字体相关的CSS属性都适合字体图标,包括但不限于:font-size、color、text-shadow

优势

  1. 很容易任意地缩放、改变颜色、产生阴影
  2. 可以拥有透明效果
  3. 浏览器兼容性较好
  4. 本身体积更小

劣势

  1. 只能被渲染成单色或CSS3的渐变色
  2. 创作字体图标很耗时间
  3. 字体文件体积过大,直接影响页面加载性能,特别是加载一个包含数百图标的Fonts,却只使用其中几个图标
  4. 不同的设备浏览器字体的渲染会略有差别

建议

​ 能使用字体图标尽量使用

2、css/css3

​ 使用css,可以绘制出一些简单的图标,由于CSS3的普及和在各大浏览器的不断增强支持,使CSS具有更大的可能性和能力去绘制更多样化,更复杂的图标。可绘制包括但不局限于:三角形、气泡、心形...可参考文章

优势

  1. 占用空间小
  2. 无需网络请求,展现更快

劣势

  1. 兼容性需参考 具体实现中所用到的css属性的兼容性

建议

对应简单的的、且字体图标库中没有的图标,可尝试css实现

对展现要求更高的场景,可使用css

3、svg

​ VG图标实际上是一个服务于浏览器的XML文件,而不是一个字体或像素的位图。它是由浏览器直接渲染XML,在任何大小之下都会保持图像清晰。除此之外,SVG图像也有过字体图标的一个主要优势:拥有多个彩色图像的能力。

优势

  1. 可以随意修改大小,而且不会影响图标质量
  2. 可以使用CSS样式来自定义图标颜色,比如颜色、尺寸等效果
  3. 可以使用gzip的方式把文件压缩到很小
  4. 可以很精细的控制SVG图标的每一部分

劣势

  1. 需要了解使用制作软件绘制SVG图形或专业的SVG图形编辑软件
  2. 浏览器支持性一般,IE8和Android 2.1以及其以下浏览器不支持。

建议

随意修改大小,不会影响图标质量的小图标使用svg

4、Base64

优势

  1. 不增加请求数

劣势

  1. 通常比图片要大不到10%,增大css文件大小
  2. 每次加载页面都需要解码
  3. 难以维护

建议

小于3kb的小图标,可使用工具自动转换为base64,但同时要注意控制css整体的大小

5、canvas

使用canvas也可绘制出一些图案

暂时应用不多,在此仅提及一下

2、图片优化方案

1、减小图片大小

1、选择合适的图片格式

格式介绍

  1. jpg有损压缩,压缩率高,不支持透明
  2. png支持透明,浏览器兼容好,无损压缩,体积大于jpg
  3. png8 —— 256色 + 支持透明
  4. png24 —— 2^24色 + 不支持透明
  5. png32 —— 2^24色 + 支持透明
  6. webp压缩程度更好,在ios webview有兼容性问题,pc端可在检测支持webp后使用 检测客户端是否支持webp
  7. gif 支持动图
类型 动画 压缩类型 透明度
jpg/jpeg 不支持 有损压缩 不支持
png 不支持 无损压缩 支持
gif 支持 无损压缩 支持
webP 不支持 无损压缩/有损压缩 支持

使用场景

  1. jpg —— 大部分不需要透明图片的业务场景
  2. png —— 大部分需要透明图片的业务场景
  3. webp —— 安卓全部、pc端可在检测支持webp后使用
  4. gif —— 体积不大的动画,体积过大,建议用视频代替gif图

决策过程

注:svg介绍可见上文

2、确定合适的尺寸

不同终端对同一个图片需求不一样,可以根据终端加载不同的图片来节省没必要的流量。

原理

  • 一倍图:当这个比率为1:1时,使用1个设备像素显示1个CSS像素。
  • 二倍图:当这个比率为2:1时,使用4个设备像素显示1个CSS像素。
  • 三倍图:当这个比率为3:1时,使用9(3*3)个设备像素显示1个CSS像素。

建议

  1. pc一般使用一倍图就可以
  2. 手机端建议使用二倍图

3、压缩图片

在线工具

  1. tinypng

    支持压缩png、jpg,压缩率高

  2. 智图

    腾讯出品,支持压缩png、jpeg、gif,并支持生成一张webp格式图片

  3. svg压缩

    支持压缩svg

构建工具

  1. gulp
    1. gulp-imagemin:支持PNG, JPEG, GIF and SVG
  2. webpack
    1. image-webpack-loader:支持PNG,JPEG,GIF,SVG和WEBP图像
    2. imagemin-webpack-plugin:可以优化一些因为loader局限性优化不了的图像,具体可见作者描述

2、减小图片请求数量

1、雪碧图

雪碧图也叫css精灵图,原理是把多张图片合成一张,然后在css中通过background-position来使用,以减小图片网络请求数,可通过以下工具合成:

在线工具

  1. Toptal

    合成雪碧图,并给出css代码,比较方便

构建工具

  1. gulp

    1. gulp.spritesmith

      支持合成雪碧图,并输出less\sass\css\stylus文件

  2. webpack

    1. webpack-spritesmith

      gulp.spritesmith的webpack版

    2. postcss-sprites

      解析css文件,从而生成雪碧图

建议

因为雪碧图比较难以维护(比如雪碧图中的某个小图标不再使用,删除后可能会影响其他图标的位置),建议用构建工具生成

2、缓存图片资源

通过设置浏览器的缓存,来减少图片请求数量,需服务端设置。

  1. 强缓存

    1. Expires

      Expires: Wed, 25 Jul 2028 19:19:42 GMT
      

      表示资源会在2028-07-25 20:20:42后过期,到时候需要再次请求资源了。由于 Expires 是依赖于客户端系统时间,当修改了本地时间后,缓存可能会失效

    2. Cache-Control

      Cache-control: max-age=259200
      

      表示的是一段时间间距,比Expires更加可靠,同时启用的时候Cache-Control 优先级更高

  2. 协商缓存

    1. Last-Modified

      last-modified: Wed, 17 Jun 2020 10:10:40 GMT
      

      表示被请求资源在服务器端的最后一次修改时间,当再次请求该资源的时候,浏览器的request header中会带上If-Modified-Since,向服务器询问该资源是否有更新。

    2. ETag

      etag: "FkGAN1LpxOYPOdwfOezDUP72sjwC.gz"
      

      每次文件修改后服务端那边会生成一个新的 ETag ,是一个唯一文件标识符,当再次请求该资源时候,浏览器的request header中会带上If-None-Match ,这值就是之前返回的ETag ,把这个值发送到服务器,询问该资源 ETag 是否变动,有变动的话,说明该资源版本需要更新啦,客户端不能继续用缓存里的数据了。

      还可进行cdn缓存

3、懒加载图片

​ 将页面内未出现在可视区域内的图片先不做加载, 等到滚动到可视区域后再去加载。

原理

​ 先将img标签的src链接设为同一张图片(比如空白图片),然后给img标签设置自定义属性(比如 data-src),然后将真正的图片地址存储在data-src中,当JS监听到该图片元素进入可视窗口时,将data-src赋值给真正的src。

实现方式

  1. offsetTop方式

    图片是否出现在视窗内: offsetTop < clientHeight + scrollTop

  2. getBoundingClientRect方式

    图片是否出现在视窗内: element.getBoundingClientRect().top < clientHeight

  3. IntersectionObserver方式

    intersectionRatio:目标元素的可见比例,即 intersectionRect 占 boundingClientRect 的比例,完全可见时为 1 ,完全不可见时小于等于 0

  4. Chrome 原生 lazyload 属性

    <img src="https://source.unsplash.com/random/600" alt="" lazyload="on">
    

    非Chrome浏览器慎用

注意:监听滚动事件比较消耗性能,推荐进行节流

推荐阅读

  1. zhuanlan.zhihu.com/p/55311726
  2. zhuanlan.zhihu.com/p/49851113

3、其他

1、预加载图片

​ 针对我们将要用到的图片,可以选择预加载,这样可以提高用户体验。

实现方式

  1. css的 background属性

    // 设置图片在屏幕外
    .preload-01 { background: url(img.png) no-repeat -9999px -9999px; } 
    
  2. javaScript实现预加载

    /**
     * 加载图片
     *
     * @param {string} src 图片路径
     * @return: promise
     */
    
    function imgLoad(src) {
    	// 用promise进行包装
    	return new Promise((resolve, reject) => {
    		let img = new Image();
    		img.onload = () => {
    			resolve();
    		};
    		img.onerror = err => {
    			reject(err);
    		};
    		img.src = src;
    	});
    }
    

2、双刃剑:display:none

<div style="display: none; background:url('c.png')">
      <img src="a.png" style="display: none;">
      <div style="display: none">
          <img src="b.png">
      </div>
  </div>

经过测试,a.png、b.png、c.png在Chrome浏览器中都会加载

2、使用七牛云等第三方图片处理

如果项目使用了七牛云,建议使用七牛自带的图片处理api进行处理七牛云图片处理

建议:项目中的大图片可上传七牛

4、总结

所有,我们在拿到一张设计给的图片时,我们的思考过程应该是: