本文首发于政采云前端团队博客 : 为你从新系统梳理下, Web 体验优化中和图有关的那些事(万字长文)
Web 页面性能优化,解决了图片相关,问题就解决了大半。本文从 Web 常见的图片格式入手,引出与图片优化相关的有效方案,指望对你们能有一点帮助。javascript
注:以下说明整理于网络公开信息。
无损压缩:数据通过压缩后,信息不受损失,还能够彻底恢复到压缩前的样子。无损压缩技术通常是经过两个步骤来完成:css
PNG:PNG 是一种无损压缩的位图图形格式,支持索引、灰度、RGB 三种颜色方案以及 Alpha 通道等特性。PNG 的开发目标是改善并取代 GIF 做为适合网络传输的格式而不需专利许可。名称由来一个是 Portable Network Graphics(便携式网络图形),还有一个非正式的由来是 "Png is Not Gif"。使用场景是带有透明、半透明背景的图片,须要在网络传输中显示预览效果后展现全貌。上古时期的 IE6 不支持 PNG 半透明,须要用 hack 方法解决。PNG 体积比较大,非必须可用 JPG 替代。PNG 有 png八、png2四、png32 之分。html
GIF:图像互换格式(Graphics Interchange Format)是一种位图图形文件格式,无损压缩、索引色。原始版本为 87a,1989 年发布 89a 版本,支持多帧动画和透明色。1995 年 Netscape Navigator 2.0 发布,定义了动画循环多少次或是否无限次播放,如今聊天的动图都是基于该版本的 GIF。GIF 的特性以下:前端
更快、更简单、更稳定是咱们每个前端工程师的追求,HTTP/2 的出现让这些美好的词汇都汇聚在一块儿。首先来一个 demo 感觉一下牛逼哄哄的 HTTP/2,HTTP/1.1 vs HTTP/2vue
HTTP/2 全部性能加强的核心在于新的二进制分帧层,它定义了如何封装 HTTP 消息并在客户端与服务器之间传输。java
从这张图比较清晰地看出 HTTP/1.x 和 HTTP/2的区别。HTTP/1.x 协议以换行符做为纯文本的分隔符,而 HTTP/2 将全部传输的信息分割为更小的消息和帧,并采用二进制格式对它们编码。react
通常状况下,客户端须要啥东西会告诉服务端,而后服务端返回对应的资源到客户端。HTTP/2 新增的另外一个强大的新功能是,服务器能够对一个客户端请求发送多个响应。换句话说,服务端能够先于客户端检测到将要请求的资源,提早推送到客户端,不发送全部资源的实体,只发送资源的 URL。客户端接到后会进行验证缓存,若是发现须要这些资源,则正式发起请求。webpack
每一个 HTTP 传输都承载一组标头,这些标头说明了传输的资源及其属性。在 HTTP/1.x 中,这些元数据始终以纯文本形式,一般会给每一个传输增长 500–800 字节的开销。若是使用 HTTP Cookie,增长的开销有时会达到上千字节。 为了减小此开销和提高性能,HTTP/2 使用 HPACK 压缩格式压缩请求和响应标头元数据,这种格式采用两种简单可是强大的技术:ios
在 HTTP/2 中,请求和响应标头字段的定义保持不变,仅有一些微小的差别:全部标头字段名称均为小写,请求行如今拆分红各个 :method
、:scheme
、:authority
和 :path
伪标头字段。git
每一个 TCP 链接只能发送一个请求, HTTP/1.x 在前面的请求没有完成前,后面的请求将会阻塞。
其实在 HTTP/2 以前,咱们在写代码的时候也用过相似的思想,好比
HTTP/2 的出现又可让咱们省掉很多麻烦。多路复用容许同时经过单一的HTTP请求多个响应。
只加载可视区的内容,当页面向下滚动时,再继续加载后面的内容。
图片懒加载的原理其实很是简单,咱们先不设置图片的 src 属性,将图片的真实路径放到一个浏览器不认识的属性中(好比 data-src),而后咱们去监听 scroll 事件。当页面的 scrollTop 与浏览器的高度之和大于图片距页面顶端的 Y (注意是整个页面不是浏览器窗口)时,说明图片已经进入可视区域,这是把 data-src 的值放到 src 中便可。
关于如何实现本文不作过多阐述,成熟的方案社区比比皆是。这边推荐几个比较好用的轮子。
安装
npm install --save lozad
使用
<img class="lozad" data-src="image.png" />
const observer = lozad(); // 默认会去找 .lozad 这个class observer.observe();
安装
npm i vue-lazyload -S
使用
Vue.use(VueLazyload)
用高阶组件去包裹
安装
npm install --save react-lazyload
使用
import React from 'react'; import ReactDOM from 'react-dom'; import LazyLoad from 'react-lazyload'; import MyComponent from './MyComponent'; const App = () => { return ( <div className="list"> <LazyLoad height={200}> <img src="tiger.jpg" /> </LazyLoad> <LazyLoad> <MyComponent /> </LazyLoad> </div> ); }; ReactDOM.render(<App />, document.body);
缓存是一种保存资源副本并在下次请求时直接使用该副本的技术,所以使用HTTP缓存是WEB性能优化中必不可少的,也是每位前端开发工程师的必修课。
浏览器和服务器之间使用的缓存策略能够分为强缓存、协商缓存两种:
有这样一种场景,浏览器检查本地缓存找到以前响应的文件发现已通过期,只能去服务端请求,可是服务器的资源没有发生变化,能够说是浪费了一次请求。
Etag的出现很好地解决了这个问题,其为一个哈希值,浏览器甚至不用去关系这个值是怎么来的,在第一次请求时,浏览器生成Etag并发送到服务端。浏览器下一次请求时发现这个值未变,就跳过请求。
浏览器自动在 If-None-Match
HTTP 请求头内提供 ETag。 服务器根据当前资源核对令牌,若是它未发生变化,服务器将返回 304 Not Modified
响应。这样一个来回避免了浏览器再次去请求资源,即省钱又省时间。
Cache-Control是强缓存的一种,每一个资源均可经过 Cache-Control 定义其缓存策略,Cache-Control来控制谁能够缓存、缓存多久。
无需和服务端通讯的请求是最佳的,经过本地副本消除全部网络延迟、节省流量。
Cache-Control 是在 HTTP/1.1 规范中定义的,取代了以前用来定义响应缓存策略的头部(例如 Expires)。 全部现代浏览器都支持 Cache-Control,所以,用他就足够了。
指令 | 说明 |
---|---|
max-age | 指令指定从请求的时间开始,容许提取的响应被重用的最长时间(单位:秒 |
private | 只为单个用户缓存,不容许任何中间缓存对其进行缓存 |
no-cache | 先与服务器确认返回的响应是否发生了变化,走协商缓存 |
no-store | 禁止浏览器以及全部中间缓存存储任何版本的返回响应 |
实际开发中每每不存在什么固定的最优解,咱们须要根据不一样的业务场景制定相应的策略。
雪碧图,CSS Sprites,国内也叫 CSS 精灵,是一种 CSS 图像合成技术,主要用于小图片显示。
在网页中,为了更好的展示效果,常常会采用一些小图标来替代文字。经常使用的方式包括 Icon Fonts、SVG Icons、小图片,其中 Icon Fonts 只支持单色,SVG Icons 需 IE9+。
注:如不考虑低版本浏览器兼容性,推荐使用 SVG Icons,有兴趣能够阅读 张鑫旭-SVG Sprites技术介绍
若是采用小图片,须要考虑一个问题:每张小图片独立请求,加载时都会产生一个 HTTP 请求,而小图片体积(1 ~ 2 kb)就比较小,对比上 HTTP 请求链接、请求头内容、响应等的开销,就显得很是不必了,那有没有可能将多张小图片合并成一张图?
将小图标合并成一张图片,利用 backround-position
属性值来肯定图片呈现的位置便可。以下图所示不一样尺寸、位置:
经过 CSS 定位,能够展示对应的图标。
.icon-alipay { background-image: url(sprite.png); background-position: 0px -131px; width: 81px; height: 73px; } .icon-taobao { background-image: url(sprite.png); background-position: -177px 0px; width: 114px; height: 114px; } .icon-wechat { background-image: url(sprite.png); background-position: 0px 0px; width: 177px; height: 131px; } .icon-xinlang { background-image: url(sprite.png); background-position: -81px -131px; width: 72px; height: 72px; }
每次修改或者新增图片时,都须要对雪碧图进行修改,若是依靠人工维护,成本过高,可否自动生成雪碧图和样式呢?可使用 spritesmith,该工具可自动合并图片,并获得图片在合并以后的相对位置。简单使用示例代码以下:
const fs = require('fs') const path = require('path'); const Spritesmith = require('spritesmith'); const baseDir = './images'; const files = fs.readdirSync(baseDir) const sprites = files.map(file => path.join(baseDir, file)) Spritesmith.run({src: sprites}, (err, result) => { if (err) { console.error(err) } else { console.info(result); } })
运行的输出结果以下:
{ coordinates: { 'images/alipay.png': { x: 0, y: 131, width: 81, height: 73 }, 'images/taobao.png': { x: 177, y: 0, width: 114, height: 114 }, 'images/wechat.png': { x: 0, y: 0, width: 177, height: 131 }, 'images/xinlang.png': { x: 81, y: 131, width: 72, height: 72 } }, properties: { width: 291, height: 204 }, image: <Buffer 89 50 4e 47 0d 0a 1a 0a 00 00 00 0d 49 48 44 52 00 00 01 23 00 00 00 cc 08 06 00 00 00 38 45 c5 ce 00 00 40 06 49 44 41 54 78 01 ec c1 0b 98 e6 05 61 ... 22705 more bytes> }
其中
coordinates
:每张图片对应的尺寸和位置properties
:生成的图片尺寸image
:文件的Buffer,可用于生成图片对于现有的经常使用的构建工具,已有现成的插件可直接使用:
如下为 gulp
配合 gulp.spritesmith
的示例代码:
const gulp = require('gulp'); const spritesmith = require('gulp.spritesmith'); // gulp任务定义 gulp.task('sprite', () => { return gulp.src('images/*.png') .pipe(spritesmith({ imgName: 'sprite.png', cssName: 'sprite.css' }) ).pipe(gulp.dest('./')); });
运行后会获得sprite.png
、sprite.css
两个文件,最后打包的时候将文件对应打包进去便可。
background-size
属性来使得最终显示正常,可参考以上插件的retina
相关配置参数;iconfont 译为字体图标,即经过字体的方式展现图标,多用于渲染图标、简单图形、特殊字体等。
使用 iconfont 时,因为只须要引入对应的字体文件,针对加载图片张数较多的状况,可有效减小 HTTP 请求次数,并且通常字体体积较小,因此请求传输数据量较少。与直接引入图片不一样,iconfont 能够像使用字体同样,设置大小和颜色,还能够经过 CSS 设置特殊样式,且由于其是矢量图,不存在失真的状况。
那么,怎么使用 iconfont 呢?请参照如下 demo:
根据开发需求,按需引入不一样格式的字体文件(eot / ttf / woff / svg)
<style> @font-face { font-family: "iconfont"; src: url('iconfont.eot'); /* IE9*/ src: url('iconfont.eot?#iefix') format('embedded-opentype'), /* IE6-IE8 */ url('iconfont.woff') format('woff'), url('iconfont.ttf') format('truetype'), /* chrome, firefox, opera, Safari, Android, iOS 4.2+*/ url('iconfont.svg?#iconfont') format('svg'); /* iOS 4.1- */ } .iconfont { font-family: "iconfont"; } </style> <body> //  是一个字符的 unicode 码,在该 iconfont 字体文件中对应某个图标 <i class="iconfont"></i> </body>
关于 @font-face 的说明可参考 mozilla 官方文档。
在平时开发工做中,可以使用如下经常使用图标字体库:
Base64 是网络上最多见的用于传输 8Bit 字节码的编码方式之一,可将图片编码为特定的字符串,由 52 个大小字母和 10 个数字,以及 +、/、= 三个字符组成,详见 wiki.
使用 Base64 编码渲染图片有如下优势:
凡事皆有利弊,使用 Base64 编码同时也会带来一些问题:
如需将图片转换为 Base64 编码,可使用 JavaScript API FileReader.readAsDataURL
,详细可参考文档。
图片体积压缩就是在图片保持在可接受的清晰度范围内同时减小文件大小,图片体积压缩能够借助工具实现。
阿里云 OSS 能够经过配置参数形式对图片进行处理,支持缩放、裁剪、旋转、效果、格式转换、水印等操做,详细信息点此 文档 查看。
示例:将图强制缩略成宽度为 100,高度为 100。
http://image-demo.oss-cn-hangzhou.aliyuncs.com/example.jpg?x-oss-process=image/resize,m_fixed,h_100,w_100
本文做者之一@明明 亦作过一个小工具,经过配置缩放参数、压缩质量、格式等属性后自动生成 OSS 后缀地址,具体如何使用参考文档。
七牛云的图片处理服务和阿里云 OSS 功能相似,也能够对图片进行缩放、裁剪、旋转、缩略等操做,详细文档
示例:将图强制缩略成宽度为 100,高度为 100。
https://odum9helk.qnssl.com/resource/Ship.jpg?imageView2/2/w/100/h/100
Web 开发中常见的图片包括 JPG,PNG,GIF,webP,选择合适的格式以及压缩质量能够在保证视觉效果的状况下,加速网站的呈现。下面针对不一样图片格式的特性来作一下对比:
类型 | 动画 | 压缩类型 | 浏览器支持 | 透明度 |
---|---|---|---|---|
GIF | 支持 | 无损压缩 | 全部 | 支持 |
PNG | 不支持 | 无损压缩 | 全部 | 支持 |
JPEG | 不支持 | 有损压缩 | 全部 | 不支持 |
webP | 不支持 | 无损压缩或有损压缩 | Chrome、Opera、Firefox、Edge、Android | 支持 |
回忆本文开头介绍的不一样图片格式的特色,你们能够参考下图选择合适的使用场景:
在 Retina 视网膜屏幕面世以前人们不多关注像素密度与设备像素比,随着 Retina 屏在移动设备中愈来愈普遍地应用,为了保证图片在不一样 DPR(设备像素比)的设备上显示足够清晰,开发者须要针对不一样设备适配不一样倍数的图片。
window.devicePixelRatio
来获取。了解以上的概念,咱们不难理解:
针对一张 30px 30px 的图片,在 1 倍屏上,按照 1 : 1 平铺,图片质量并不损失。可是在 2 倍屏、3 倍屏上,分别经过 60 60 与 90 * 90 个物理像素渲染这张图片就会出现模糊、失真的现象,从而影响用户体验。因此,咱们须要根据不一样 DPR 去加载不一样倍数的图片:
在高分辨率显示屏如 2x 上,在页面中使用二倍图能够保证清晰度,可是当此页面在低 DPR 设备打开时,咱们只须要 50% 长宽的图片就能保证显示效果,而此时带宽开销倒是同样的。因此为了解约传输流量,咱们须要告诉浏览器,根于不一样的 DPR 加载不一样尺寸的图片,一般有如下三种方法:
<picture> <source srcset="photo@3x.jpg" media="(min-width: 800px)"> <source srcset="photo@2x.jpg" media="(min-width: 600px)"> <img srcset="photo.jpg"> </picture>
<img src="photo.png" srcset="photo@2x.png 2x, photo@3x.png 3x" alt="photo" />
background-image: image-set("photo.png" 1x, "photo@2x.png" 2x, "photo@3x.png" 3x);
根据世界健康组织的统计,全球约有 2.85 亿视力障碍人士,仅美国就有 810 万网民患视力障碍,而在中国,这个数字要被乘以 2。不一样于咱们浏览网页的方式是看,视力障碍人员浏览互联网信息主要是靠听 —— 靠屏幕阅读器读出网页的有效信息,经过听这些信息来获知内容。对于前端工程师而言,关注到一些细节的优化,能更好的服务于这些视力障碍人士。
最基础的方式,是装饰性图片归类到背景图,经过 CSS 背景图进行设置;功能性图片放到 HTML 中,经过 img
标签引入,且要设置 alt
属性,以便被屏幕阅读器识别并阅读。图片 alt
信息应简短,介绍图片信息便可,避免内容冗余。图片的长信息介绍应被放到 longdesc
属性中:
<img src="" alt="图片说明" /> <img src="" longdesc="一段很长的文字一段很长的文字一段很长的文字一段很长的文字" />
更多无障碍相关,可参考《腾讯网无障碍说明》。
“高对比度模式” 是一种 Windows 系统的设置主题,其用意是为了保证视力受损的用户,在查看 Web 信息时提供方便。它经过使用对比鲜明的色彩和字号来提升文本的可读性,高对比度模式下网页的背景默认会变成全黑。
CSS Image Sprites(CSS 雪碧图)是一项用来减小网页中图片 HTTP 请求数的技术,但其会致使在 Windows 高对比度模式下背景图片消失,其服务的 Web 应用性能的提高和对无障碍体验被破坏之间的矛盾,已经引发了 Web 开发者的关注。Sprite 技术的使用的确为更多网站的优化加载速度的体验贡献甚大,但咱们要认可,这个过程当中咱们忽略并损害了使用高对比度模式用户的体验。因为 <img>
元素能够在高对比度模式下显示,故而在此场景下,使用基于 <img>
标签的 Sprite 技术,能够获得比基于 CSS 背景图的 Sprite 更多的收益。
关于 IMG Sprite 技术的应用,能够在此文中学习到 《Foreground Sprites – High Contrast Mode Optimization》
就前端性能优化而言,图片优化可谓是其必不可少的环节。可是与其说是在作优化,不如说是在作“权衡”。一些操做是以牺牲一部分红像质量为代价的。咱们的主要任务,是尽量的去寻求一个质量与性能之间的平衡点,并在不一样业务场景下,作好图片方案的选型工做。
政采云前端团队(ZooTeam),一个年轻富有激情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、杭电等校的应届新人。团队在平常的业务对接以外,还在物料体系、工程平台、搭建平台、性能体验、云端应用、数据分析及可视化等方向进行技术探索和实战,推进并落地了一系列的内部技术产品,持续探索前端技术体系的新边界。
若是你想改变一直被事折腾,但愿开始能折腾事;若是你想改变一直被告诫须要多些想法,却无从破局;若是你想改变你有能力去作成那个结果,却不须要你;若是你想改变你想作成的事须要一个团队去支撑,但没你带人的位置;若是你想改变既定的节奏,将会是“5 年工做时间 3 年工做经验”;若是你想改变原本悟性不错,但老是有那一层窗户纸的模糊… 若是你相信相信的力量,相信平凡人能成就非凡事,相信能遇到更好的本身。若是你但愿参与到随着业务腾飞的过程,亲手推进一个有着深刻的业务理解、完善的技术体系、技术创造价值、影响力外溢的前端团队的成长历程,我以为咱们该聊聊。任什么时候间,等着你写点什么,发给 ZooTeam@cai-inc.com