直接进入正题吧,原文出处

2019-11-07 18:28栏目:前端开发
TAG:

图片资源 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

作为一名只会喊666的咸鱼,只能作为伸手党,参(chao)照(xi)一波别人的劳动成果来加深自己的记忆 *其实是自己肚子没墨水* 。既然是参照,直接进入正题吧。

如何统计?

通过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 //页面接收到数据开始到呈现完毕的总时间。

图片 12

场景1,内嵌至css文件中

避免阻塞

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

一张大小为50kbjpg格式图片,应用到9×15=135个dom做背景图,模拟雪碧图的模式,多个节点引用同一张图片做背景,(示例)如图。
图片 13
测试环境: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(总时间受网络状态等因素影响,数据做比较用)
图片 14

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

图片 15

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

1. 优化HTTP请求

1 - 减少请求次数:

  1. 合并代码。

  2. sprite化图片。

  3. 小于8kb的用base64作为src源。

4. 缓存ajax,对于每次请求返回内容都相同的ajax,可以设置cache属性进行缓存。

  1. 剔除重复性的脚本。

2 - 减少请求体积:

  1. 让后台dalao们使用GZIP。

  2. 压缩代码,减少文件的空白字符。

  3. 优化图片,压缩IMG-PNG8格式,压缩图片。

3 - 减少请求资源自带cookie的体积,也叫cookie隔离,使用CDN就好,有兴趣可以看看Cookie

  • Free Domains技术。

4 - 减少页面中空引用的href与src标签,因为你什么都不写,浏览器还是会对服务器发起一个空的HTTP请求,会对服务器产生一些不必要的压力。

5 - 突破请求限制,同一时刻向一个域名请求下载的并行数有限,可以使用不同域名分别存放静态资源,增大并行下载量。

6 - 使用大杀器,CDN。

7 - 不要滥用post请求,post翻译过来是邮递的意思,获取数据还是用get请求。而且在大部分浏览器中post还是有分步操作,而get是一步到位,虽然效率上差别不会很明显,但这不是混淆语义的理由。

顺便附上一个get与post区别的link: get与post的区别。

8 - css置顶,script置底,如需在头部放置script,可以使用defer与async异步的script引入。

9 - 避免使用css表达式。

10 - 使用懒加载。

11 - 减少DNS查找。每次DNS查询都会有30-120ms的耗时,可以使用DNS预加载。

12 - 减少页面重定向。每次重定向都会有资源的损耗,尽量减少不必要的重定向。

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

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

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

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

图片 17

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

渲染

1 - 减少DOM树的深度,DOM树越深,越吃浏览器的渲染资源。尽量减少不必要的DOM层级。

2 - 优化体积过大的css文件,css体积过大需要更多的时间加载,关键的资源需要进行拆分渲染,保证首屏加载速度,提高用户体验。

3 - 避免在HTML中直接缩放图片,在HTML中直接缩放图片会使页面内容重绘,会影响页面中其他操作的效率,可能会产生卡顿。

4 - 优化js加载,耗时的script应当在html底部渲染,或者使用异步加载,与首屏无关的js也应当延迟加载。

5 - 避免使用<table>与<iframe>:

table是将整个table元素渲染完毕再一次性绘制到页面上,特别吃性能,相比而言可以用其他列表元素代替,譬如ul、dl。

iframe会因为加载源页面(即src='xxx'),而造成父级进入渲染阻塞的状态,等待iframe的源页面加完完毕才会回到父级继续往下渲染,如果是必要时,可以用异步的方式动态加载iframe。

6. 避免使用css滤镜,与css表达式,太吃性能的玩意儿暂时不要玩儿吧,浏览器hold不住。

3、调整图片体积

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

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

图片 18

缓存

4、调整引用次数

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

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

图片 19

预加载

1 - 使用DNS预解析,像天猫阿里的元信息里面都有用到:

图片 20

使用dns-prefetch的姿势。

分析和小结:

在OSX+Chrome环境下,将50kb的图片base64后放入样式中,build过程拉长了约20倍,使用Timeline工具可以看到,计算样式阻塞了整个过程。

图片 21

  1. 比起直接引入图片地址,css文件中引入base64格式的图片对样式渲染的性能消耗明显,如果大量使用,会带来耗电和发热的问题,需谨慎使用。
  2. Rendering消耗的时间同css文件大小、引用次数几乎成正比(未测试其它极限情况),在网络条件优质的4G环境,50~70ms的RTT(往返时延)情况下,通常移动网络的状况会更差,对于首屏优化,合适的使用还是很值得的。
  3. 图片转成base64编码后,文档大小较原文件大了一些,而经过 gzip 后两者几乎没有区别。

场景2,内嵌至js文件中

1、原生方式直接加载多张图片

大小10~70kb共9张图片。总大小约300kb

关闭缓存:build: 300ms | complete: 310ms

图片 22
开启缓存:build: 110ms | complete: 120ms

图片 23

测试环境 build(单位:ms) complete(单位:ms)
OS X+Chrome 110 120
iOS+微信 50 100
OS X+Safari 148 150
Android+微信 50 100

2、转换成base64格式,合并请求

将上面的图片转成base64后,放在js文件中,加载进来。

关闭缓存:build: 0ms | complete: 400ms

图片 24

开启缓存:build: 0ms | complete: 80ms

图片 25

测试环境 build(单位:ms) complete(单位:ms)
OSX+Chrome 110 120
iOS+微信 0 35
OS X+Safari 7 70
Android+微信 0 250

3、比较不同网速下同步请求和合并请求的加载效率

使用上述1、2的测试demo分别在3G、4G网速条件下测试结果如下:

  • 在网络环境差的情况下,合并请求明显缩短了整个加载时间;
  • 在网络环境较好的WIFI和4G下则差别不大。
测试环境 图片直接加载 complete(单位:ms) base64合并请求 complete(单位:ms)
3G 6000 4500
4G 450 400
WIFI 320 340

图片 26

分析和小结:

base64后的的js资源达381kb,在一个线程里加载,消耗大量时间,从统计结果看,在渲染性能差异上并没有场景1那么明显。
但有缓存的情况下,页面渲染完成的速度甚至更快。
从Timeline里看到细节,解析这个近400kb的js文件对整个渲染过程造成了一定压力,不过总共40ms的解析时间是完全可以接受的。

图片 27

  1. 从html里直直接引用图片链接和base64图片对渲染性能的影响几乎没有区别,在网络条件差的情况下,合并请求却能大大提高加载效率;
  2. 直接引用至html,无法缓存,将base64后的图片资源放在js文件中管理,方便设置缓存。
  3. 有一个缺点就是图片资源base64化需要扩展构建工具来支持。

使用建议

  1. 图片资源的base64编码进css文件会带来一定的性能消耗,需谨慎使用。
  2. 将图片资源编码进js文件中,管理和预加载H5应用的图片资源,合理的合并请求可以大大提高页面体验。

    1 赞 1 收藏 评论

图片 28

版权声明:本文由大奖888-www.88pt88.com-大奖888官网登录发布于前端开发,转载请注明出处:直接进入正题吧,原文出处