页面里有用武之地吗,前端图片引入方式神演算

图片资源 Base64 化在 H5 页面里有用武之地吗

2016/12/15 · HTML5 ·
Base64

原文出处:
凹凸实验室   

图片 1

将图片资源转至base64格式后可直接放入页面作为首屏直出,也可以放入css文件中,减少请求,以加快首屏的呈现速度。
不过图片base64化,将带来一个臃肿的html或css文件,是否会影响页面的渲染性能,浏览器又支持如何呢?

前端图片引入方式神演算

2017/01/11 · 基础技术 ·
图片

原文出处: 沐洒(@Musa沐洒)   

图片 2

| 导语
本文只提供推理方式和分析方法,不保证样本及计算的精准性,慎读!!!

先阐述一下背景:

我们团队对于图片的使用方式有一个明文规定如下:

  1. 凡是需要合并雪碧图,或是编码base64的图片,均放入slice目录下对应模块目录里,gulp-postcss会统一编译处理。
  2. 直接以单图形式引入页面的图片,放在page/aaa/bbb/img目录下(aaa表示业务单元,bbb表示具体页面),使用相对路径./xxx.png直接引用。
  3. 全局复用的单图,放入common/img目录下。

目录结构大概是这个样子:

图片 3

这就是我们今天的议题。

众所周知,页面内图片的引入方式一般有这3种:雪碧图,base64内联,普通单图。(canvas,svg等非常规方式不在此次议题里),先简单分析一下三种方式的优劣势:

图片 4

嗯,大概的情况是这样的,然后我来稍微扩展解释一下:

1.
base64图本身确实无法缓存,但是base64图一般是存在于css里的,那么就可以随着css被缓存而实现间接缓存,所以它的缓存属性不是“无”。说它“差”是因为并不是直接被当做图片缓存。当然如果是直接写在html里的,那就没法缓存了,这个要注意。

2.
base64额外增加html/css大小并不是主要问题,问题是,因此造成的渲染堵塞有时候是致命的!而作为图片文件加载则不存在这个问题,因为图片是不会堵塞到html和css加载的,因此也不会影响首屏渲染。(当然了,你非要把img标签写在style前面那我只能说,哥,我服~~~~)

了解了三种方式的优劣势之后,对于使用场景简单归纳一下:

  1. 页面自身独有的图片,全部合并成一张雪碧图。

2.
公共模块或者公共组件,如果包含多张图片,则各自为阵合并各自的雪碧图;如果只有一两个图片,或者包含有可以被其他模块、组件、页面复用的图片,则使用灵活性好的单图模式或base64模式。

不过这种说法遗留了一个问题:例如所有页面都有的吊顶区域,假如那里有一个小图,注意,是一个喔(如果是很多的话就合并啦),这种时候是直接单图引入呢?还是base64内嵌到吊顶的css里?

好像二者都可以是吧,用单图的好处就是我在首页缓存后,逛其他页面时候就不用再加载了,当然了首页就会多一个请求;而用base64模式,会少一个图片请求,但会增加吊顶css的文件大小,从而间接增加了首页的渲染堵塞时间。好吧,又TMD陷入了纠结。。。

别急!

下面我们再对base64模式做一个简单的分析:

先明确我们对于base64图片劣势的控诉点在于,1:丫会增大原始图片文件;2:植入css之后会增大css文件大小。

做一个简单的实验,我把几个全局经常出现的小图标,用base64编码,结果:

平均增大35%

图片 5

但是!

gzip压缩后 —— 4%~40%,平均增大22%

图片 6

当然样本少是一个问题,但大概的我们还是能看出来一些端倪:base64确实会增大文件,而且即使做了gzip后增幅还是居高不下。这也是为什么我们一般不会对大图片进行base64编码的原因,假如对一张100KB的图片编码,将会增加20-30KB!这是蛮恐怖的了。但我们现在说的是小图片呀,一个小图片1KB左右,即使增大30%也就增加三百多字节而已。

我们思考的更进一步,究竟怎么样的文件大小增幅,是我们可以接受的?

一个常识,普通人的肉眼可识别的视觉暂留是50ms。而根据多年前端实战经验,对于网页渲染速度,肉眼可敏感识别的渲染时长间隔是500ms,所以一般一个css3过渡效果,transition-duration
为0.3s和0.8s才会有显著区别,而0.3s和0.5s的区别,除了号称“像素眼”的重构同学和有细节控的设计师能感知外,一般人很难明显感知。

那么因此我们是不是粗略的得出一个结论:对于首屏渲染时间的减少或增加,用户可明显感知的变化范围是50ms-500ms之间,也就是说,即便你优化做得再好,小于50ms的变化,是不会被感知的,另一方面,如果你因为某个原因增加了首屏渲染时间500ms,就会产生一个很大的感官变化。

好了,这么说来,我们能接受的文件大小增幅,所导致的首屏渲染时间增加,应该控制在500ms内。对于身处公司内网的我们而言,M/s的速度显然不用在意这些细节,500ms可以轻轻松松加载几MB的资源,就算是普通用户,现在宽带整体速度都6得飞起,500ms加载几百KB应该不成问题吧。

但是!我们不能这么想啊,做产品的会把用户当做小白,我们做开发优化是不是也应该假设用户还停留在拨号上网时代?哈哈哈,开玩笑了,这倒不至于,但我们确实可以假设用户网速很一般,100kb/s的网页加载速度,对自己够狠了吧我。

基于网速100kb/s的假设,500ms可以加载50kb的资源。。。。等等!总感觉哪里不对!

一个文件的加载,应该包含了这些个过程:

图片 7

所以我们理论上“500ms可以加载50kb的资源”,指的是download这里的速度而已,但是一个小图片从请求到渲染,需要经过请求排队,请求堵塞,等待响应,下载等众多环节。。。那么500ms我们到底能加载多大的文件呢?这个问题我真的回答不了,因为这涉及到的环境变量太多了,请求堵塞,网速抖动,浏览器版本,服务器速度,dns解析等等都有可能影响到这个结果。这。。。文章写不下去了怎么办。。。不能放弃治疗啊!那么我们干脆就更加大概一点估算好了,就假设这500ms中,只有250ms是给我们用来下载资源的,那么100kb/s的速度我们可以下载25kb的资源,嗯。。。。看起来还蛮是合理的呢。。。。

我们多找几张小图看一下timing的分布:(10kb以内)

图片 8

有没有发现一个规律?对于10kb以下的小图而言,下载时间其实几乎可以忽略不计(1%左右),而真正占用贷款的是这一次次请求经历的漫长的流程(请求排队,请求堵塞,等待响应….)

补充验证:当图片大小增加到100kb以上时,下载耗时平均是总耗时的50%不到。

经过上面一大推的推演和样本测试后,我们得到了一些相对合理的参数值,接下来要抛大招(计算公式)了!

图片 9

终于!我们拿到了我们想要的计算结果!2.6倍base64图片总大小的下载时间,是我们增加的首屏负荷。之前我们已经说了,在不影响用户感官明显变化的情况下,我们仁慈的允许多500ms的下载时间,在100kb/s的弱网条件下,最终计算出,允许内嵌的base64图片大小是20kb!20kb!20kb!这和我们刚刚大概估算的25kb很接近啊!看来有些时候计算无力的情况下估算还点靠谱的。。。

机智的我经过一系列估算后,得出了一个拙劣但相当有意义的答案!意义在于,我终于知道什么大小的图片叫做小图片啦!!!不知道这个历史性难题难倒了多少重构GG!

图片 10

好吧,别打我,我知道我的计算有点暴力。。。。

Anyway!我在文章副标题里就说了,

本文只提供推理方式和分析方法,不保证样本及计算的精准性,慎读!!!

讲真,我的切入点和分析方法应该是没有问题的对吧各位?只是这其中需要计算的数值实在涉及到太多不确定性,我表示暂时受到那么一丢丢困扰,所以就先估算之,感兴趣的同学可以按照此方法重新计算哈。

做这些蛋疼的研究,终归还是要回归到业务上的,那么我们文章开始的疑问是不是已经解决了呢?经过我们一步步的推演和深入浅出,问题基本解决了。

下面简单归纳一下不同场景所应该使用的图片引入方式:(正经脸 -_- !!!)

  • 全局通用的,非特定页面或模块独有的图片,采用单图或base64方式引入,二者区别如下:
    • 若该图片在多处使用或图片本身较大(这类图总体积大于20kb),则使用单图模式
    • 若该图片只有少数地方使用且图片本身较小(这类图总体积小于20kb),则使用base64模式

  • 公共模块/组件里的图片(假设该模块名为mod-prd)
    • 模块内有N(N>=3)个图片,则全部放入**slice/mod/prd**里,使用雪碧图模式,否则参考全局通用图片处理方式

  • 页面自身独有的图片,全部合并成一张雪碧图

装逼结束,轻喷~

2 赞 3 收藏
评论

图片 11

如何统计?

通过Navigation
Timing记录的关键时间点来统计页面完成所用的时间,并通过Chrome开发工具来跟踪细节

JavaScript

var timing = window.performance.timing timing.domLoading
//浏览器开始解析 HTML 文档第一批收到的字节 timing.domInteractive //
浏览器完成解析并且所有 HTML 和 DOM
构建完毕timing.domContentLoadedEventStart //DOM
解析完成后,网页内资源加载开始的时间 timing.domContentLoadedEventEnd
//DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)
timing.domComplete //网页上所有资源(图片等) 下载完成,且准备就绪的时间

1
2
3
4
5
var timing = window.performance.timing
timing.domLoading //浏览器开始解析 HTML 文档第一批收到的字节
timing.domInteractive // 浏览器完成解析并且所有 HTML 和 DOM 构建完毕timing.domContentLoadedEventStart //DOM 解析完成后,网页内资源加载开始的时间
timing.domContentLoadedEventEnd //DOM 解析完成后,网页内资源加载完成的时间(如 JS 脚本加载执行完毕)
timing.domComplete //网页上所有资源(图片等) 下载完成,且准备就绪的时间

以上定义来自chrome官方文档,在其它环境下也许会有差异,从测试结果看,下面的build时间在android+微信环境中一直是0,对此可能是因为渲染机制差别,此处不做深入测试。除osx+chrome之外环境的数据仅作参考。

JavaScript

build = timing.domComplete – timing.domContentLoadedEventStart
//间隔记录网页内资源加载和呈现时间。 complete = timing.domComplete –
timing.domLoading //页面接收到数据开始到呈现完毕的总时间。

1
2
build = timing.domComplete – timing.domContentLoadedEventStart //间隔记录网页内资源加载和呈现时间。
complete = timing.domComplete – timing.domLoading //页面接收到数据开始到呈现完毕的总时间。

场景1,内嵌至css文件中

1、原生引入图片链接做背景图

一张大小为50kbjpg格式图片,应用到9×15=135个dom做背景图,模拟雪碧图的模式,多个节点引用同一张图片做背景,(示例)如图。
图片 12
测试环境:Mac OS X EI Capitan 10.xx + Chrome 48.xx
其它辅助测试机器: iPhone 6 plus iOS 9.xx; 魅族Note Android 4.xx

实际使用过程中,其它版本和机型的Android手机还有待测试

关闭缓存状态下,build:150ms | complete:
200ms(总时间受网络状态等因素影响,数据做比较用)
图片 13

开启缓存状态下,build: 7ms | complete:
59ms(包括以下稳定状态下多次测试的平均值,截图为最接近平均值的状态,默认数据来自Mac+Chrome[48.XX版本])

图片 14

测试环境 build(单位:ms) complete(单位:ms)
OS X+Chrome 7 59
iOS+微信 45 90
OS X+Safari 50 100
Android+微信 0 120

2、引入base64格式图片做背景图

将上面50kb大小的jpg图片转换为base64格式,加在css文件中。

关闭缓存状态下,build:80ms | complete: 280ms

图片 15
开启缓存状态下,build: 160ms | complete: 210ms

图片 16

测试环境 build(单位:ms) complete(单位:ms)
OS X+chrome 160 210
iOS+微信 35 100
OS X+Safari 9 90
Android+微信 12 150

3、调整图片体积

调整上面图片的(压缩品质)体积,base64化后,对应的css文件大小也跟着改变

图片大小 10kb 20kb 45kb 100kb 180kb
对应css文件大小 27kb 42kb 76kb 150kb 260kb
Rendering时间 30ms 46ms 81ms 156ms 258ms

图片 17

4、调整引用次数

50kb大小的图片,base64化后,调整引用图片做背景图的dom的个数

引用次数 10 20 50 100 135
Rendering时间 15ms 19ms 44ms 74ms 83ms

图片 18

发表评论

电子邮件地址不会被公开。 必填项已用*标注