浅谈前端的图片优化
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了浅谈前端的图片优化相关的知识,希望对你有一定的参考价值。
参考技术A 近期一直在和朋友讨论前端性能优化的问题,着重聊到了图片相关的优化,就想着总结记录一下。说到图片优化主要从两个点入手,分别是优化体积和合并请求,下面我分别展开说下这两点。
就优化体积来说,方法还是比较有限,无外乎压缩图片大小,根据视图需要选择合适的图片尺寸。
我们知道图片是有很多格式的,包括jpg、png、gif等,单就大小来说有损压缩的jpg格式一般是会比无损压缩的png要小的多的,但是它不支持透明;且单就png来说还区分png8、png24、png32,我们需要根据自身业务使用场景选择最优的格式,当然如果支持webp的话webp一定是最优解。
选择尺寸就更好理解了,比如视图是一个固定400*300的区域,你肯定不需要放一个4000*3000的图片上去,当然现代的前端项目或多或少都需要考虑不同端的适配,我这边的处理方式是通过公司私有的图片服务器处理,你只需要上传一张原图,获取图片时根据需要带长宽参数即可获得相应大小的图片。
请求合并是前端优化的必要手段,不止是针对图片,这里我只列举针对图片优化的方案,当然每种方案都有他的弊端。
用雪碧图来合并多张小图片的方案已经有很多年了,它成本低兼容好,确实能解决不少问题,但是使用雪碧图同时会带来一些副作用需要我们解决。
首先就是内存问题,由于我们把多张图片合并成一张大图,所以渲染时每次都会加载整张雪碧图,这样对于内存的开销来说实际上是更大的,解决的办法就是尽量减少图片间的留白,以及控制一张雪碧图的内容不要太多。
还有就是缩放的问题,我们都知道background-size是针对整张图片的缩放,在移动端适配多尺寸时再使用固定的px定位就会出现偏差,一般这个时候我们都会使用百分比或者rem的方式来处理,不过在部分机型上还是会出现边缘的情况,一个朋友提出了transform的解决方案,不过笔者目前还没有验证。
toDataUrl的方案相比于雪碧图晚了些,它可以通过base64字符串来描述图片本身从而减少图片的请求;它所带来的问题是会增大体积页面本身文件大小,而且由于是内联字符串会导致图片无法缓存。
最后就是通过iconfont/svg/CSS等方式来模拟图片展示,这个方案可以算是是最通用的兼容最好的方案,但是也不是绝对的完美方案;就iconfont来说只能是纯色的,不能像图片那样颜色丰富,而svg和css或多或少都有一些兼容性问题。
我们在优化图片的时候还可以设置一些缓存策略以及根据业务场景使用一些懒加载的策略,因为这两条属于所有请求通用的方案,这里就不展开说了。最后说一句我们在进行前端优化时需要根据自身需要定制优化方案,切不可为了优化而盲目优化。
前端优化带来的思考,浅谈前端工程化转
重复优化的思考
这段时间对项目做了一次整体的优化,全站有了20%左右的提升(本来载入速度已经1.2S左右了,优化度很低),算一算已经做了四轮的全站性能优化了,回顾几次的优化手段,基本上几个字就能说清楚:
传输层面:减少请求数,降低请求量
执行层面:减少重绘&回流
传输层面的从来都是优化的核心点,而这个层面的优化要对浏览器有一个基本的认识,比如:
① 网页自上而下的解析渲染,边解析边渲染,页面内CSS文件会阻塞渲染,异步CSS文件会导致回流
② 浏览器在document下载结束会检测静态资源,新开线程下载(有并发上限),在带宽限制的条件下,无序并发会导致主资源速度下降,从而影响首屏渲染
③ 浏览器缓存可用时会使用缓存资源,这个时候可以避免请求体的传输,对性能有极大提高
衡量性能的重要指标为首屏载入速度(指页面可以看见,不一定可交互),影响首屏的最大因素为请求,所以请求是页面真正的杀手,一般来说我们会做这些优化:
减少请求数
① 合并样式、脚本文件
② 合并背景图片
③ CSS3图标、Icon Font
降低请求量
① 开启GZip
② 优化静态资源,jQuery->Zepto、阉割IScroll、去除冗余代码
③ 图片无损压缩
④ 图片延迟加载
⑤ 减少Cookie携带
很多时候,我们也会采用类似“时间换空间、空间换时间”的做法,比如:
① 缓存为王,对更新较缓慢的资源&接口做缓存(浏览器缓存、localsorage、application cache这个坑多)
② 按需加载,先加载主要资源,其余资源延迟加载,对非首屏资源滚动加载
③ fake页技术,将页面最初需要显示Html&Css内联,在页面所需资源加载结束前至少可看,理想情况是index.html下载结束即展示(2G 5S内)
④ CDN
......
从工程的角度来看,上述优化点半数以上是重复的,一般在发布时候就直接使用项目构建工具做掉了,还有一些只是简单的服务器配置,开发时不需要关注。
可以看到,我们所做的优化都是在减少请求数,降低请求量,减小传输时的耗时,或者通过一个策略,优先加载首屏渲染所需资源,而后再加载交互所需资源(比如点击时候再加载UI组件),Hybrid APP这方面应该尽可能多的将公共静态资源放在native中,比如第三方库,框架,UI甚至城市列表这种常用业务数据。
拦路虎
有一些网站初期比较快,但是随着量的积累,BUG越来越多,速度也越来越慢,一些前端会使用上述优化手段做优化,但是收效甚微,一个比较典型的例子就是代码冗余:
① 之前的CSS全部放在了一个文件中,新一轮的UI样式优化,新老CSS难以拆分,CSS体量会增加,如果有业务团队使用了公共样式,情况更不容乐观;
② UI组件更新,但是如果有业务团队脱离接口操作了组件DOM,将导致新组件DOM更新受限,最差的情况下,用户会加载两个组件的代码;
③ 胡乱使用第三方库、组件,导致页面加载大量无用代码;
......
以上问题会不同程度的增加资源下载体量,如果听之任之会产生一系列工程问题:
① 页面关系错综复杂,需求迭代容易出BUG;
② 框架每次升级都会导致额外的请求量,常加载一些业务不需要的代码;
③ 第三方库泛滥,且难以维护,有BUG也改不了;
④ 业务代码加载大量异步模块资源,页面请求数增多;
......
为求快速占领市场,业务开发时间往往紧迫,使用框架级的HTML&CSS、绕过CSS Sprite使用背景图片、引入第三方工具库或者UI,会经常发生。当遇到性能瓶颈时,如果不从根源解决问题,用传统的优化手段做页面级别的优化,会出现很快页面又被玩坏的情况,几次优化结束后我也在思考一个问题:
前端每次性能优化的手段皆大同小异;代码的可维护性也基本是在细分职责;
既然每次优化的目的是相同的,每次实现的过程是相似的,而每次重新开发项目又基本是要重蹈覆辙的,那么工程化、自动化可能是这一切问题的最终答案
工程问题在项目积累到一定量后可能会发生,一般来说会有几个现象预示着工程问题出现了:
① 代码编写&调试困难
② 业务代码不好维护
③ 网站性能普遍不好
④ 性能问题重复出现,并且有不可修复之势
像上面所描述情况,就是一个典型的工程问题;定位问题、发现问题、解决问题是我们处理问题的手段;而如何防止同一类型的问题重复发生,便是工程化需要做的事情,简单说来,优化是解决问题,工程化是避免问题,今天我们就站在工程化的角度来解决一些前端优化问题,防止其死灰复燃。
文中是我个人的一些开发经验,希望对各位有用,也希望各位多多支持讨论,指出文中不足以及提出您的一些建议。
消灭冗余
我们这里做的第一个事情便是消除优化路上第一个拦路虎:代码冗余(做代码精简),单从一个页面的加载来说,他需要以下资源:
① 框架MVC骨架模块&框架级别CSS
② UI组件(header组件、日历、弹出层、消息框......)
③ 业务HTML骨架
④ 业务CSS
⑤ 业务Javascript代码
⑥ 服务接口服务
因为产品&视觉会经常折腾全站样式加之UI的灵活性,UI最容易产生冗余的模块。
UI组件
UI组件本身包括完整的HTML&CSS&Javascript,一个复杂的组件下载量可以达到10K以上,就UI部分来说容易导致两个工程化问题:
① 升级产生代码冗余
② 对外接口变化导致业务升级需要额外开发
UI升级
最理想的升级是保持对外的接口不变甚至保持DOM结构不变,但多数情况的UI升级其实是UI重做,最坏的情况是不做老接口兼容,这个时候业务同事便需要修改代码。为了防止业务抱怨,UI制作者往往会保留两个组件(UI+UI1),如果原来那个UI是核心依赖组件(比如是UIHeader组件),便会直接打包至核心框架包中,这时便出现了新老组件共存的局面,这种情况是必须避免的,UI升级需要遵守两个原则:
① 核心依赖组件必须保持单一,相同功能的核心组件只能有一个
② 组件升级必须做接口兼容,新的特性可以做加法,绝不允许对接口做减法
UI组成
项目之初,分层较好的团队会有一个公共的CSS文件(main.css),一个业务CSS文件,main.css包含公共的CSS,并且会包含所有的UI的样式:
半年后业务频道增,UI组件需求一多便容易膨胀,弊端马上便暴露出来了,最初main.css可能只有10K,但是不出半年就会膨胀至100K,而每个业务频道一开始便需要加载这100K的样式文件页面,但是其中多数的UI样式是首屏加载用不到的。
所以比较好的做法是,main.css只包含最核心的样式,理想情况是什么业务样式功能皆不要提供,各个UI组件的样式打包至UI中按需加载:
如此UI拆分后,main.css总是处于最基础的样式部分,而UI使用时按需加载,就算出现两个相同组件也不会导致多下载资源。
拆分页面
一个PC业务页面,其模块是很复杂的,这个时候可以将之分为多个模块:
一经拆分后,页面便是由业务组件组成,只需要关注各个业务组件的开发,然后在主控制器中组装业务组件,这样主控制器对页面的控制力度会增加。
业务组件一般重用性较低,会产生模块间的业务耦合,还会对业务数据产生依赖,但是主体仍然是HTML&CSS&Javascript,这部分代码也是经常导致冗余的,如果能按模块拆分,可以很好的控制这一问题发生:
按照上述的做法现在的加载规则是:
① 公共样式文件
② 框架文件,业务入口文件
③ 入口文件,异步加载业务模块,模块内再异步加载其它资源
这样下来业务开发时便不需要引用样式文件,可以最大限度的提升首屏载入速度;需要关注的一点是,当异步拉取模块时,内部的CSS加载需要一个规则避免对其它模块的影响,因为模块都带有样式属性,页面回流、页面闪烁问题需要关注。
一个实际的例子是,这里点击出发后的城市列表便是一个完整的业务组件,城市选择的资源是在点击后才会发生请求,而业务组件内部又会细分小模块,再细分的资源控制由实际业务情况决定,过于细分也会导致理解和代码编写难度上升:
如果哪天需求方需要用新的城市选择组件,便可以直接重新开发,让业务之间使用最新的城市列表即可,因为是独立的资源,所以老的也是可以使用的。
只要能做到UI级别的拆分与页面业务组件的拆分,便能很好的应付样式升级的需求,这方面冗余只要能避过,其它冗余问题便不是问题了,有两个规范最好遵守:
1 避免使用全局的业务类样式,就算他建议你使用 2 避免不通过接口直接操作DOM
冗余是首屏载入速度最大的拦路虎,是历史形成的包袱,只要能消除冗余,便能在后面的路走的更顺畅,这种组件化编程的方法也能让网站后续的维护更加简单。
资源加载
解决冗余便抛开了历史的包袱,是前端优化的第一步也是比较难的一步,但模块拆分也将全站分成了很多小的模块,载入的资源分散会增加请求数;如果全部合并,会导致首屏加载不需要的资源,也会导致下一个页面不能使用缓存,如何做出合理的入口资源加载规则,如何合理的善用缓存,是前端优化的第二步。
经过几次性能优化对比,得出了一个较优的首屏资源加载方案:
① 核心框架层:mvc骨架、异步模块加载器(require&seajs)、工具库(zepto、underscore、延迟加载)、数据请求模块、核心依赖UI(header组件消息类组件)
② 业务公共模块:入口文件(require配置,初始化工作、业务公共模块)
③ 独立的page.js资源(包含template、css),会按需加载独立UI资源
④ 全局css资源
这里如果追求极致,libs.js、main.css与main.js可以选择合并,划分结束后便可以决定静态资源缓存策略了。
资源缓存
资源缓存是为二次请求加速,比较常用的缓存技术有:
① 浏览器缓存
② localstorage缓存
③ application缓存
application缓存更新一块不好把握容易出问题,所以更多的是依赖浏览器以及localstorage,首先说下浏览器级别的缓存。
时间戳更新
只要服务器配置,浏览器本身便具有缓存机制,如果要使用浏览器机制作缓存,势必关心一个何时更新资源问题,我们一般是这样做的:
原文地址: http://www.cnblogs.com/yexiaochai/p/4901341.html
以上是关于浅谈前端的图片优化的主要内容,如果未能解决你的问题,请参考以下文章