欢迎你们前往腾讯云技术社区,获取更多腾讯海量技术实践干货哦~css
导语 : 从事前端有6年+的时间了,从最开始的美工到重构再到偏向js逻辑开发的前端开发,一直在前端这个行业里面摸索和学习,我如今将本身这些年的一个心得体会来个系统性的梳理写成一篇关于性能优化的主题文章,但愿对你们有点帮助,也欢迎你们提出各类意见和建议。html
做者:刘勇刚 前端
前端工程师是一个最近这5-6年才开始慢慢被互联网公司重视起来的一个职业,能够说是一个新兴行业,我用一张简单的思惟导图带你们回顾一下前端技术发展的历程以及将来一个展望:html5
1.0时代没什么说的,html、css打天下的时代,那个时候你会用js开发个计算器就牛逼到不行。2.0时代是最好的时代,新技术、新思想蓬勃发展,堪称前端的工业革命,前端人员的地位获得了充分承认,门槛也有必定的提高。前端性能优化的涉及点从服务器到协议再到宿主环境自己都要有比较深入的认识,业界目前主要仍是以雅虎总结出来35条前端性能优化的黄金军规(http://www.cnblogs.com/siqi/p...) 为参考。今天我想将这些年对前端的性能优化的经验思考总体来个串烧,带你们鸟瞰一下前端性能优化目前的一些通行作法以及这么作的出发点。文章初衷主要是对一些性能优化基础知识回顾和体系梳理,不对具体技术点作深刻分析,点到为止,我的理解不对的地方欢迎各位大神拍砖,抛砖引玉。node
引入话题前我仍是先从一个老生常谈的话题开始:webpack
“从用户输入URl到页面展现给用户浏览器客户端的过程当中发生了什么?”css3
这里用个图表简单描述一下几个步骤:web
web优化的目标就是如何让用户更快、更简单易用、更流畅的使用咱们的服务,对于前端开发而言就是如何让咱们的资源体量更小、数量更精简、内容更早呈现、交互更加人性化。ajax
web性能优化有个你们比较公认的二八原则,就是资源从服务器处理完下发到客户端的浏览器上(上图第6步)所占的时间比例大概是整个过程的20%,也就是说服务器端能够优化的空间的效率提高并不会很明显,前端性能优化成为web性能优化重点考虑的领域,我下面将会从如下几个维度去作了本身的一个思考(跟35条军规有必定重叠)和总结:算法
一、突破单线程解析渲染阻塞限制
浏览器是一个单线程解析模式去解析渲染从服务器端拿到的html文本,css加载的过程当中会对后续的脚本资源加载形成阻塞,脚本的加载也会阻塞后续DOM结构的解析形成页面的留白时间增加,雅虎的35条军规中有一条就是样式文件放在头部,脚本文件放在DOM节点最末尾,减小阻塞。这里还有几个针对脚本文件的优化:
针对不须要DOM操做(主要考虑是须要操做DOM的脚本每每须要获取一些样式信息)的Js脚本能够采用动态建立script的方式载入,动态载入的脚本不阻塞后续资源的加载。
脚本文件加载能够加上defer或者async属性标识防止阻塞(关于二者区别能够参考)
二、利用事件冒泡特性
浏览器的事件模型的冒泡的特性(浏览器事件模型不清楚的自行搜索了解)我以为是最牛逼的设计之一,解决了浏览器由于解析DOM模型不一样步致使开发者往DOM对象注册事件回调找不到对象的问题。
浏览器事件注册有3个级别定义,DOM 0级事件注册(利用DOM元素行内事件属性onclick注册事件回调),DOM 1级事件注册(利用DOM元素对象的onclick API 在外部注册事件回调),DOM 2级事件注册(利用利用DOM元素对象的addEventListner/attachEvent API 在外部注册事件回调)。这里性能优化的建议就是利用DOM2级在目标DOM的父标签(大部分框架是在body标签统一注册事件监听)注册回调,收拢事件监听入口同时节约了DOM节点引用开销。
三、避开Cookie性能bug
Cookie是前端做为先后台登陆态校验最一般用的缓存方案,但鉴于浏览器在每次都会往同域的任何资源的http请求中自动带上cookie信息的状况,这里有必要进行优化一下,由于像css、js、image这些资源请求是不须要cookie信息的,会无故形成请求带宽的浪费(想象一下咱们的cookie大小假设为10K,100个请求就是近1M的大小,高并发下以咱们现行网络带宽也是蛮大的一笔负担了)。Cookie free性能优化方案的处理方式是CDN异域静态资源服务器部署咱们的前端css、js、image资源。
以本身目前负责的香港跨境汇款为例
页面路径下的资源的请求:
CDN资源加载的请求:
经过对比CDN分开部署的资源请求并无带上cookie信息。
四、突破浏览器并发链接限制
浏览器针对domain,而非页面page作并发链接限制的特性,domain hash的技术优化方案的处理方式是将资源划分域分开部署,但由于过多的域划分会增长多余的DNS开销,这里通行的数量是3个之内。目前咱们的港菲汇款业务只有两个域名分开部署,一个主站,一个CDN,我我的建议能够将CDN中的图片资源再单独再分一个域名部署会更好些,为何单独把图片抽出来,后面会讲到。
五、利用GPU硬件加速浏览器渲染
针对一些界面渲染过程比较耗时的状况下,能够利用CSS3属性开启GPU来加速渲染咱们的DOM,开启很简单通常我是用-webkit-transform:translateZ(0)假3D属性来唤起系统GPU加速渲染功能,关于为何会这样,我这里作个简单的解释:
对于咱们的浏览器而言,拿到咱们的html文本串开始按顺序解析成DOM树,并与同步解析出来的CSS匹配生成渲染树(跟DOM树的节点不是一一对应,好比display:none的节点就不会插入渲染树)
图片来源:https://segmentfault.com/a/11...
浏览器将渲染树的节点用一个图层表示,这样层层叠加在一块儿生成layout,有点像ps的图层叠加的概念(能够经过火狐浏览器开发者工具3维展现更直观),通常状况下对节点的任何涉及尺寸的改变都会引发layout的重排重绘(重排和重绘是形成浏览器渲染的最大性能损耗的因素),但有种开小灶的状况Composite Layers(复合图层)直接交给咱们GPU中单独的合成器进程处理,自身变化不会引发其余层的位置变化,不会引发重排重绘。tranform 3d属性是能够悄悄的告诉咱们的浏览器把元素解析做为复合图层交给单独进程去处理的。
注:这里有个原则,不能滥用咱们的加速,由于过多开启硬件加速会消耗更多的用户内存空间,也会比较耗电,通常针对css3动画建议开启
一、减小http请求数量
a、通行解决方案
css、script合并:gulp、webpack都可以很简单的经过任务脚本的方式去自动化解决,目前咱们团队是用咱们自研的前端构建工具配合咱们的Dust库作的发布前的资源打包任务,核心就是用的gulp。
css sprites雪碧图:将网站经常使用的一些小图片整合到一张大图上来,样式里面经过background-position二维坐标定位找到本身的图片。这里有个原则,通常是将网站复用率较高的,不太容易变更的图标和图片,好比按钮、平铺背景小图片等。
font-icon字体图标:字体图标库的使用,是一个很是有创新的方式,由于是矢量的,解决位图像素放大变虚的问题,体验很好,相比一样矢量的SVG来讲使用更简单,一个css的font-family就能够像平时设置字体同样使用,淘宝是国内这方面的先行者,有本身的一套很开放的矢量图标库平台。淘宝自身的许多小图标都是用的字体图标来展现的。
图片base64编码传输:图片base64编码后,可让浏览器减小自身的一次http请求,但由于自身的一些缺陷,不能滥用(即便一个很小的图片编码后都会有一大串字符,增长了咱们CSS体积,性能不降反升),个人建议是针对那些全站通用或者体积很小很差整合到雪碧图里面的图标进行编码,固然还有不少不一样的场景你们本身权衡。
图片延时加载:主要是为了减小首屏一次性图片的加载量。具体作法是给图片或者标签设置一个私有行内属性data-image(固然能够本身随便定义)存放目标图片地址信息,监听浏览器的滚动事件,标签到了浏览器可视区域就将图片地址放入图片的src属性中或者做为标签的样式的背景图片中展现。淘宝首页的作法是用一个div来作延时图片加载,经过背景图片来展现最后的图片。
图片展现前:
图片展现后:
b、缓存机制
协议缓存方案:利用http缓存协议头cache-control作304缓存,或者更精确的ETAG设置依据资源的修改时间来设置缓存方案。但目前更有效或者极端的作法是利用max-expire-time,设置资源的最大缓存时间假设为1年的长缓存,更新采用非覆盖式更新的方式是目前大公司通行的作法。这样每次资源请求的时候都是只从客户端缓存读取(status:200,size:from
cache),而不是还要跑一次http请求到服务器端拿到304状态。仍是以一张淘宝首页图片长缓存的截图为例:
appCache应用缓存方案:离线应用缓存是h5提供一个比较有效的离线应用方案,利用navigator.online
、window.applicationCache对象、服务器.appcache(之前是.manifest)配置文件保证在脱机下的移动web应用照常能用,若是要作数据的离线还要加上window.localStorage作离线数据的保存。这里简单说一下接入离线应用须要的几个步骤:
一、给须要作离线缓存的页面html标签设定manifest属性,指定缓存的配置文件 cache.appcahe(能够设定任何扩展名,只要在服务器端配置mime-type为text/cache-manifest就行)。
二、建立上一步指定的cache.appcache配置文件,按如下截图说明来配置资源
三、在服务器端配置配置文件的扩展名映射的mime-type为text/cache-manifest
appcache离线方案诟病太多,目前接入的很少,有种慢慢变弃儿的趋势,这里提出来让你们权衡
**PWA(Progressive Web
Apps)方案**:谷歌提出的一套全新的离线web方案,利用manifest.json配置文件、window.serviceWorker对象来实现类原生app体验的离线应用方案,能够说是浏览器应用缓存的一个脱胎升级方案(鉴于文章篇幅,这里不作介绍了)。
二、减轻http数据请求大小
a、通行解决方案
css、script、图片压缩:这些能够gulp或者webpack自动化脚本里面定义脚本任务来完成。
服务器开启gzip压缩:通常如今服务器都有开启Gzip压缩,压缩率一般都是30%以上,效果仍是不错的。
原图:
Gzip压缩后:
图片服务器动态响应方案:这个方案对应上面宿主环境维度domain hash单独出来一个独立域名部署图片资源的方案介绍。图片资源是网站请求资源中一个很是大头的开销,之前你们能够在静态资源服务器中建个image目录存放就完事,随着网站服务发展,图片不只面临多样化、高并发带来的压力,在移动端wap站点中更是要针对不一样的分辨率屏幕下图片尺寸动态适配的场景觉得了节省带宽的需求。图片服务器的单独架构有必定的复杂度(若是考虑到高并发下的容灾、缓存机制的话不亚于一个大型web网站集群的搭建,这里有篇文章推荐你们阅读),这里只讨论一下其中负责切图服务部分的服务器(简称切图服务器)功能,切图服务器对外提供一个restful的url调用,好比http://www.xxx.com/xxx图片路...“xxx图片路径”下的xxx图片等比压缩成130*120的图片尺寸并返回,这块服务可使用咱们前端比较熟悉的node建立,固然也能够用PHP来提供。
b、页面切片预加载方案
性能优化静态资源维度最后一块内容就是针对页面,如何尽早输出页面模块,减小留白时间是一个思考点。facebook应用的BigPipe方案是个很不错的借鉴思想,还有淘宝也有首页作了相应的切片方案,对页面合理的分块,在服务器和客户端创建某种对应机制,让各个页面块并行的在服务器端拼接完成并吐出来,目前我对这块没有太深的了解,这里只是提出bigPipe的方案供你们参考。
TCP链接中的3次握手、慢启动的一些特性注定了链接通道的利用效率成为制约性能的一个很大的因素。由于http是基于TCP的应用协议,TCP层维度考虑还得从http几个版本的发展历史来看:
http1.0时期:tcp链接是基于一种单通道顺序等待请求响应方式(客户端每发一个请求都要从新创建链接),特定历史背景下产生的,低效率很难跟上时代发展,99年在1.0基础上修订出1.1版本,并沿用至今。
http1.1时期:在请求头信息加入keep Alive保持链接的必定活性(固然也加入了100
Status节约带宽、cache特性等),容许在一个链接通道生命期内重复发送不一样的应用请求,必定程度上减轻了链接资源利用效率问题,但当用户浏览网页时间大于链接活性周期再次请求的时候仍然要从新创建这些请求,在大型科技公司对高并发高可用性下资源高效利用的背景下,1.1版本仍是难知足大公司对高性能条件下网络资源的高效率利用的要求。
谷歌(叒是谷歌,牛逼)率先在09年基于TCP开发出全新SPDY应用协议,解决了多路复用请求优化、服务器推送的痛点问题,也为后面http2.0的推出奠基了基础。
咱们能够作的优化:减小一些没必要要的请求(扫除404死链接、304请求用咱们的长缓存机制)去优化,尽可能减小一些没必要要的链接请求数。
鉴于js语言自己的灵活性,以及每一个人的开发习惯,很难有很好的一个方式去验证开发者的代码实现的效率(目前更多的是用打点测速的方式去监控代码的执行时间),更多的是一种建议,你们有更好的建议能够提出来分享。
单线程限制:利用异步回调&多线程API突破js语言单线程带来的内存开销利用不充分的问题,现有能够利用的一些异步方式的回调均可以尝试利用好比settimeout,setinterval,requestAnimationframe(推荐用它),多线程的API方式有WebWork。这里推荐日本的一个开发者开发的一个多线程前端库Concurrent.Thread.js(它是做者用setTimeout和setInterval来模拟的多线程,能够自行网上搜索了解)。
重点优化代码中的循环结构体:就代码自己而言影响执行效率的大部分是循环体结构和算法设计不合理致使的时间复杂度和空间复杂度的增长。这里放两段实际项目中的代码截图来对比:
实例功能须要:实现输入框每4个字符一个空格隔开的效果
低效实现方式,用for循环:
改进后的方式:
合理使用设计模式优化代码结构:设计模式的合理利用(不能滥用)能够起到内存优化提升执行效率效果,好比单例和简单工厂在建立xhr请求对象中的利用:建立一个简单工厂向外面提供xhr对象,工厂内部用闭包开辟一个数组队列单例用来存放xhr对象,当调用者须要xhr对象时工厂就从队列里取出readyState状态为空闲(0/4)的xhr对象不然从新建立并放入队列中。在有大量ajax对象请求的应用下能够最大限度节约建立xhr消耗的内存开销,这里用个简图来描述一下思路:
其余一些优化建议:好比减小js频繁操做dom节点的次数(这个原本想放在宿主环境维度)减小浏览器的重排重绘;好比针对dom标签对象(主要是针对ie下dom对象的引用会被GC回收遗漏的问题)、闭包内部的引用类型的变量用完事后记得要及时释放,避免形成内存泄漏。
性能优化通常都是从技术角度去入手,但咱们的目标之一“让用户简单易用”也是性能优化的一环。当技术性能缺陷难于避免的时候,做为前端交互实现的执行者,更应该配合产品和交互设计师提出一个咱们认为更好的交互逻辑体验方案去让咱们的数据加载不那么让用户有等待的感知,让咱们的提示更加的人性舒服。(交互设计师更加专业,我这里不敢班门弄斧)
Web3.0时代的前端展望:
文章最后对Web3.0时代作个本身的猜测,web3.0目前在业内尚未很明肯定义,你们能够大胆猜测前端行业将来形态。我在这先YY一下,人工智能、大数据普遍应用应该会成为推进前端进入3.0的时代的最好契机,以此引起的前端新的革命:
浏览器成为一个系统生态(至于哪一个浏览器如今很差说,如今谷歌浏览器PWA方案提供给前端类app开发方案就有这个趋势,之后都不须要装系统了)。前端再也不是数据的搬运工,在web领域颇有可能除了底层维护数据库的工程师们继续深耕外(主要切入云计算领域),其它的均可以转到前端作浏览器系统生态(哎妈呀,有种翻身农奴作主人的感受O(∩_∩)O~~)。
传统的html语义性的超文本标记语言已经很难承载更多的信息化数据,html5继续深度发展,再也不只是浏览器专用的标记语言,可能会成为跨平台标准的信息表示层,信息表示多样化史无前例,可能到时候不叫html了。
http2.0成为一个普遍试用的标准,并进一步深化,安全校验层作到相似区块链去中心化的思路,作到极致安全(https估计能够下岗了)。
js成为跨平台公认的标准实现语言(目前前端跨平台基础形态已经有了),随着Es6的普遍推广和深度改进,可能就不会像如今这么灵活,更加像一个合格的标准“高级”脚本语言。
(以上猜测纯属我的理解,没有权威认证,你们能够畅所欲言)
结语:刚来鹅厂不久,做为前端攻城狮进入到国内顶尖互联网公司感到骄傲,性能优化是一个永恒的话题,每一个阶段都有聊不完的性能优化的话题,我将我这些年的一些不成文的理解整理了一下,但愿对你们有点帮助。第一次发文章,有错误的地方请你们指点一二。
Web 前端性能优化 : 如何有效提高静态文件的加载速度
使用 Skeleton Screen 提高用户感知体验
盒子端 CSS 动画性能提高研究
此文已由做者受权腾讯云技术社区发布,转载请注明文章出处
原文连接:https://cloud.tencent.com/com...