你们知道。咱们天天都在谈前端性能优化,天天都在背前端性能优化方案,然而,咱们殊不知道他背后的原理以及涉及那些知识储备。因此,我问了本身一些问题,css
当我问了本身一些问题之后,我从新学习性能优化的脉络就很清晰了,接下来咱们来咱们逐一解决。html
在前端的职业生涯中,咱们老是能一次次听到“性能”和“体验”这两个词。而在慢慢从菜鸟一点点打怪升级的过程当中,这两个词听到的频率在逐步上升。前端
而不少人只知道,这个东西,面试要考,因而开始背!背!背! 却从未想过,咱们作性能优化的初衷是什么。项目在什么阶段应该怎去作什么样的优化。怎样去权衡可维护性和优化的平衡点。这其实都是咱们去作性能优化须要思考的问题,那么有了大量的思考,其实咱们心中的答案也就能呼之欲出。这也是我要叮嘱本身的:作一些事情以前,要去思考他背后本质是什么,而不是流于表面,人云亦云。vue
那么咱们为何要去作性能优化呢?什么样的项目必需要去作性能优化?什么样的项目能够牺牲一些优化的点,而换取项目稳定性,和可维护性!node
咱们知道,一个网站最重要的的就是用户,有了用户你才能有业务,打个比方,你是一个电商网站,那么你必定但愿你的用户很是多,只有这样你才能有更多的人去浏览你的商品,从而在你的网站上花钱,买东西,这样你才能产生收益,在好比,为了获取更多的用户,你必需要在借助第三方的工具去推广你的网站,好比搜索引擎。而如今的搜索引擎,他都会去对你的网站作性能评估,从而可能影响你的排名!react
如此一来,你就会知道,咱们所谓的性能优化其实就是留住用户,以及获取用户,那么,基于以上思路,你就能根据当前项目,判断出我当前网站应该给予什么样的特殊优化方式,以及那些通用优化方式,而不是在网上找一通,而后对着优化!webpack
知道了上述问题以后,咱们则须要找寻一些标准,以及性能瓶颈。来达到优化的目的css3
记得亚马逊作个一个调查,他发现一个网站每100ms的延迟则致使1%的销量损失,那么,咱们的网站延迟要到什么地步才算性能好呢? 因此咱们要有一个标准!nginx
上述说过,每一个项目的状况不同,咱们不能跟亚马逊同样,作到极致,因此,大多数网站这须要遵循一个标准,咱们认为达到这个性能指标,就算能够了,在某个高频操做的点,在作针对的优化!web
测量页面的加载性能是一项艰难的任务。所以 Google Developers 正和社区一块儿致力于创建渐进式网页指标(Progressive Web Metrics,简称 PWM’s)。
PWM’s 都是些什么,咱们为何须要它们?
这就牵扯到浏览器的历史了。为了能彻底的讲明白这一块,咱们从头讲起,在好久好久之前,咱们有两个主要的点(事件)来测量性能:
DOMContentLoaded — 页面加载完成但脚本文件刚刚开始执行时触发这里指初始的 HTML 文档加载并解析完成,但不包括样式表、图像和子框架的加载完成,参考 MDN DOMContentLoaded 事件
load 事件在页面彻底加载后触发,此时用户已经可使用页面或应用。
拿掘金举例,最下部的地方就能够看见DOMContentLoaded、load
而时至今日在交互复杂,页面内容复杂的今天,你会发现DOMContentLoaded、load并不像之前那样能真实反映出用户的体验了。他跟你页面的复杂度、交互难度、动画多少、等都有不少联系,拿bilibili和掘金举例
你会发现,bilibili远比掘金的加载时间长的多。可是哔哩哔哩的性能却不比掘金差
在如今的web页面中,DOMContentLoaded 的问题在于不包含解析和执行 JavaScript 的时间,若是脚本文件太大,那么这个时间就会很是长。好比移动设备,在 3G 网络的限制下测量跟踪时间轴,就会发现要花费差很少十秒才能到达 load 点。 另外一方面,load 事件太晚触发,就没法分析出页面的性能瓶颈。 因此咱们可否依赖这些指标?它们到底给咱们提供了什么信息? 并且最主要的问题是,从页面开始加载直至加载完成,用户对这个过程的感知如何?
你刷新哔哩哔哩页面后,你会发现哔哩哔哩的体验很是好,除了有着好的设计以外,他还有着不少针对的性能优化。好比,快速加载首屏,其余屏幕懒加载,善于利用缓存等。
说了这么多什么叫作 PWM’s呢?
PWM’s 是一组用来帮助检测性能瓶颈的指标。除开 load 和 DOMContentLoaded,PWM's 给开发者提供了页面加载过程当中更多更详细的信息
其实在谷歌浏览器中,咱们就能使用devtools 来查看各类指标的加载时间!咱们仍是拿哔哩哔哩举例
首先打开performance 点击刷新按钮,
如此一来咱们就能看到一些关键的渲染节点
这是谷歌的开发者工具还给咱们提供了一个指标 - FP。这个指标表示页面绘制的时间点,换句话说它表示当用户第一次看到白屏的时间点,(fp还有另一个意思就是函数式),FP 事件在 Graphic Layer 进行绘制的时候触发,而不是文本、图片或 Canvas 绘制的时候,因此,这个时间点用来测量性能,着实有点棘手,因而,谷歌还给咱们提供了另外一个。
这是当用户看见一些“内容”元素被绘制在页面上的时间点。和白屏是不同的,它能够是文本的首次绘制,或者 SVG 的首次出现,或者 Canvas 的首次绘制等等。
FCP 事件在文本(正在等待字体文件加载的文本不计算在内)、图片、Canvas 等元素绘制时被触发。结果代表,FP 和 FCP 的时间差别可能从几毫秒到几秒不等。这个差异甚至能够从上面的图片中看出来。
因此你的内容绘制时间过长,这说明你的资源文件可能过大,或者网络拉胯。他能真是的反应网页性能方面的一些问题
LCP一种新的性能度量标准,LCP 是一种侧重于用户体验的性能度量标准,与现有度量标准相比,更容易理解与推理。 他与被舍弃的FMP不一样的是,FMP是有意义的内容绘制时间点,那么这个有意义的断定就放在了谷歌开发者工具这块,他是有争议的,并且并不能说明性能问题。
W3C Web性能工做组的讨论和Google的研究,发现度量页面主要内容的可见时间有一种更精准且简单的方法是查看 “绘制面积” 最大的元素什么时候开始渲染。这个最大的篇幅的内容渲染他极有多是主要内容。因此,瓜熟蒂落的代替了fmp
接下来咱们再来看这一张图
上面的一张张图片表示在各个时间点,当前网页渲染的快照,而蓝色的折线,则是当前网页的内存占用,从当前折线你能够清晰的看出垃圾回收(gc)在何时开始的,若是你看当前的蓝色折线是一直无限上升的,那么极有多是发生了内存泄露了,他能很好的帮你定位问题。
咱们点开这里面的main 选项,就能清晰的知道,当前页面中全部长任务,以及渲染任务的耗时,和执行顺序(所谓长任务:长任务就是指解析、编译或执行 JavaScript 代码块)咱们知道js和渲染是互斥的,因此在图中也能够清晰的体现到,他们互斥的关系以及谁妥协谁。这个图他还有一个洋气的名字叫--火焰图
那么经过他,仔细看
就能看到有个红色的三角,这就是谷歌工具给咱们的提示,表示性能不达标了!经过些就能分析出一些关键的性能瓶颈和可优化的点。
有不少小伙伴会就会说了,这又不是个人网站,我看他干啥,我只想看看这个网站到底性能如何!谷歌也给咱们提供了一个工具--lighthouse 以前须要下载,如今被开发者工具集成了
一样拿哔哩哔哩来举例,在开发者工具中测量一下,以后就会返回一堆数据,咱们只关注performance部分,首先他会有一个整体得分,而后就是各个项的时间,接下来逐个分析一下
在这里咱们只关注这四点 尤为须要关注第一点和第三点。 哔哩哔哩中咱们看他的first contentful paint性能还能够,若是是红色的那么就是超标了。 Speed Index 其实就是速度指数,在谷歌给的标准中,速度指数的标准是4秒 而哔哩哔哩的4.3秒也还凑活。
再往下看就是一些须要优化的项,包括http、js、css层面的优化。
在以前的first contentful paint 中,咱们发现他的时间消耗主要有两部分组成,第一个是渲染时间,另外一个就是网络加载时间。
咱们打开network 查看一下网页资源的加载图,一样的这个图也有一个很专业的名字,叫作瀑布图
他很是直观的描述了网站资源的加载时间和顺序,这个图呢有两个解读方法,一个是横向看,另外一个是纵向看。 横向看咱们能看到具体加载的资源
咱们看其实下载资源是最后一个步骤,他还包括等待时间,他须要排队,耽误了5毫秒,可能因为达到了浏览器最大请求数量阻塞了200毫秒。接下就是发送用了0.18毫秒。TTFB时间--是后台的处理时间以及网络传输时间,咱们才能下载
纵向看就能看到资源的加载顺序,那么我就有可能让某些时间长的请求,提早加载,并行请求,来达到优化的目标。
在理解了上面这些概念以后,咱们再来了解一下Chrome 团队提出了 RAIL 模型。
RAIL 是 response (响应)、 animation(动画)、idle(浏览器空置状态)和 load(加载)。
复制代码
固然这只是一个参考,在复杂的项目中,咱们只能尽可能的追赶,却很难达到,由于除了项目比较复杂以外,还有不少事情不是咱们能作主的。好比,网站支不支持http2,你也说的不算。若是idle 咱们须要达标,理论上说,洗数据的js计算,后端能作的毫不让前端去作,然而现实是,在个人职业生涯中,基本天天都在洗数据。
性能优化咱们天天都在谈,忽然有一天我我回过头来思考,咱们天天谈的性能优化,真的是在谈这些优化的点吗?不是否是。咱们说之因此能作性能优化,实际上是在深刻了解各个方向,好比 http工做机制、缓存机制、浏览器工做原理、工具链的优化策略、前端框架的原理以后总结出来的一个方案。因此咱们天天都在谈的的性能优化实际上是一个深不见底的池子,他须要你有完善的知识体系以及丰富的经验,毫不是背背优化点就能搞明白的。
如此一来,咱们之后在谈起性能优化,就不要在说这些优化点这些庸俗的东西。而是要深究他背后的原理、和总结出来这个方案的思考。 接下来,咱们一个个来攻克。
关于浏览器的工做原理这块以前写过一篇文章,基本涵盖到了一些细节知识点 重学前端(三)-聊聊咱们的浏览器的那些事
如上图所示,他其实就分为这么几步
整个的过程就是从 URL 转换为 Bitmap 的过程,先发送请求到服务器,而后服务器返回 HTML,浏览器解析 HTML,而后构建 DOM 树,计算 CSS 属性,而后进行排版,最后渲染成位图,而后通过操做系统或硬件的 API 完成视图的显示。
那么在这几个步骤中,有一个layout 和render 两个步骤,其实也就是布局(回流)和绘制,这也是浏览器关键渲染路径中两个很是重要的步骤,并且很是消耗浏览器资源。而咱们的性能优化其实就能够在这两个步骤中作文章。
对于布局而言,咱们须要改变的实际上是元素的几何信息,好比宽高,和位置,接下来我看看,有哪些操做能够出发布局,这样一来是,就有可能在我么的代码中去避免这个操做。从而达到性能优化的目的
添加删除元素
操做styles
display:none
offsetLeft、scrollTop、clientWidthd等
移动元素位置
修改浏览器大小、字体大小等
咱们仍是来看哔哩哔哩的火焰图。紫色的部分就是layout。而在布局的过程当中有一个很是经典的问题,叫作布局抖动,从而致使页面显得很是卡顿,其实所谓布局抖动就是连续发生layout 过程致使的
而咱们怎样能在保证效果的同时在这一步去作性能优化呢?
一、避免回流
好比修改元素位置,那么咱们可使用css动画去解决,利用复合步骤去解决问题,在好比利用vdom 最小限度的去改变元素的布局,
二、读写分离
其实就是利用浏览器的api--requestAnimationFrame去在当前针读数据。下一帧写数据,这样就能达到读写分离的效果了,在社区上有一个fastdom的库就能给咱们解决这个问题。
二、避免重绘 对于绘制而言,它只是影响元素的外观,风格,而不会影响布局的,好比background-color。
上图所示,绿色部分就是绘制步骤,而浏览器为了提升新能,在绘制的步骤上开发了复合线程,他就相似于ps 的图层,浏览器也将一些盒模型分为一个个图层,这样一来,修改一些图层,并不会影响其余页面的绘制个布局。
如上图所示,就是复合过程,他引发了样式计算,可是去没有走重绘,而是一个composite layers 过程,
那么咱们怎样尽可能的使用复合,而避免重绘呢?
三、减缓高频事件的触发 在复杂的网页交互中,好比拖动,滚动,高频点击,他的触发频率很是高,远远的高于60hz 因此,咱们就在想真的有必要吗?因此,咱们就须要有防抖、节流函数等来帮助咱们减缓高频事件!他的原理也很简单,其实就是利用定时器,来延时或者间断的处理事件回调
四、利用浏览器api减小页面抖动
咱们知道react16有个fiber的架构,他就是在底层运用浏览器的api requestIdCallback 来实现任务调度,从而最大限度的解决了页面卡顿问题。那么咱们在解决卡顿问题的时候,是否是也能够考虑使用这些requestIdCallback、requestAnimationFrame等这些api呢!
在框架横行的今天,react、vue、angular 三分天下,可是在框架的编程范式下,咱们每每忽略了本身代码层面的性能优化,总认为框架的做者会考虑这些问题,好比react fiber。然而,咱们不知道的是做为一个框架,让你的代码具备可维护性,和这个框架具备普适性,从而推广开来是很是重要的,因此,框架给咱们的保证是,在不须要手动优化的状况下,依然能够给提供过得去的性能。而不是很是好的性能,他其实本质上也是操做dom,只是这个事情框架给你作了,你只须要描述你的目的便可
举个例子:
如上图,我最近的vue项目中,就有着严重的性能瓶颈,咱们能够看到,在首次渲染以后,有两个很是长的长任务在阻塞页面渲染,显得很是卡顿,他其实本质缘由就是,table表格渲染,这种大数据量的渲染是很是容易出现性能瓶颈的。虽然vue 有虚拟dom 和diff 算法兜底,可是,他们也不是免费的,也有着不小的开销。因此,这时候就须要咱们去手动优化,好比加入虚拟滚动。
接下来仍是从底层去理解,js 执行的开销到底在哪?
如上图所示,js APP.js这个文件的的编译、解析时间有700多毫秒。
而再日后看的咱们发现除了有编译、解析脚本还有gc(垃圾回收)都有很多耗时。如此,咱们就有办法在这个步骤中去作文章,
接下来咱们逐一解析
前三个,都是一些比较明显的问题,就再也不赘述,咱们重点解释应该怎样写出迎合浏览器的代码呢?
咱们知道,在浏览器中,js的解析引擎叫作v8,其实呢,v8在底层解析的时候,是作了不少事情的,好比,咱们知道tcp 是流的传输,因而v8就有了脚本流的优化,解释起来大体意思就是,v8会给咱们作预解析,在脚本还没下载完成以后就开始解析代码,在好比,字节码缓存,以及懒解析,而咱们要作的就是,就是去迎合浏览器,好比:
以前在代码层面上,咱们列出了能够作的优化,然而它带来的收益甚微,可谓微不足道,其实,你仔细看一些vue、element 等知名的开源库,他们没有一个执行上述的优化手段的,由于,他们须要为了可维护性,来牺牲少量的性能是很是划算的。那么咱们在写代码时候,其实也须要作一些平衡以及取舍,你能够为了可维护性等理由,而放弃这些优化手段,可是,你却要必须知道,为啥要使用这些优化手段,这样才能在面试,以及未来某个项目中能合理的用上。
而咱们在资源方面优化,他是结结实实能看的见的都东西,而且能看到确切的效果。好比文件资源的压缩与合并,那图片格式浏览器解析最快,不须要当即加载的图片能不能懒加载,字体会不会影响性能?
咱们知道在网络协议层面,资源越小,那么就表明他的传输时间越少,因而,咱们就必需要在文件资源的层面去作一些优化,其实无论怎么优化,他们总遵循这一个原则:
那么围绕这两点,其实已经有了不少经优化手段了,我说几种,不少老前端,指定是难以忘怀,只不过在因为网速和技术的进步,他们被淹没在历史的长河中,好比雪碧图,使用gulp 压缩合并html、css 、js 资源,使用imagemin 优化图片大小。,固然这些问题其实,在工程化的今天,他实际上是掩盖了这些优化,咱们只须要专一开发,描述目的便可,可是有一些,问题咱们仍是要注意:
在上文中,咱们讲了一些优化方案,可是无论什么方案,他都绕不开工具链的合理使用,这也是咱们的性能优化中不可绕开的一个环节,由于工具的使用得当,能兼顾可维护性,和比较好的性能,那么提起工具链,就绕不开新一代的构建工具,webpack、rollup等。今天咱们就来探讨一下webpack这个老牌构建工具。
其实要提及的初衷实际上是为了开发者能使用上一些语言的新性能又让浏览器能运行开发者编写的代码而起到的中间人的一个身份。只不过因为自己的强大的插件和loder能力,顺带的给咱们的性能优化作了。具体的优化方式请移步我以前的文章webpack优化解决项目体积大、打包时间长、刷新时间长问题!
这个手段是收益最大的一个手段,上述的手段其实能力有限,而在传输层面作文章,其实才是事半功倍的的手段
咱们知道,虽然咱们的代码压缩了,可是他不是个压缩包啊,因而gzip横空出世,那么咱们就来看看gzip是个啥?
Gzip是若干种文件压缩程序的简称,HTTP协议上的GZIP编码是一种用来改进WEB应用程序性能的技术。大流量的WEB站点经常使用GZIP压缩技术来让用户感觉更快的速度。这通常是指WWW服务器中安装的一个功能,当有人来访问这个服务器中的网站时,服务器中的这个功能就将网页内容压缩后传输到来访的电脑浏览器中显示出来.通常对纯文本内容可压缩到原大小的40%.这样传输就快了,效果就是你点击网址后会很快的显示出来.固然这也会增长服务器的负载. 通常服务器中都安装有这个功能模块的。
复制代码
可是不是每一个浏览器都支持gzip的,若是知道客户端是否支持gzip呢,请求头中有个Accept-Encoding来标识对压缩的支持。客户端http请求头声明浏览器支持的压缩方式,服务端配置启用压缩,压缩的文件类型,压缩方式。当客户端请求到服务端的时候,服务器解析请求头,若是客户端支持gzip压缩,响应时对请求的资源进行压缩并返回给客户端,浏览器按照本身的方式解析,在http响应头,咱们能够看到content-encoding:gzip,这是指服务端使用了gzip的压缩方式。
使用方式也很是简单,在一些nginx 、node、等web服务器上启用便可。
HTTP协议的Keep-Alive意图在于短期内链接复用,但愿能够短期内在同一个链接上进行屡次请求/响应。
复制代码
一般一个网页可能会有不少组成部分,除了文本内容,还会有诸如:js、css、图片等静态资源,有时还会异步发起AJAX请求。只有全部的资源都加载完毕后,咱们看到网页完整的内容。然而,一个网页中,可能引入了几十个js、css文件,上百张图片,若是每请求一个资源,就建立一个链接,而后关闭,代价实在太大了。
基于此背景,咱们但愿链接可以在短期内获得复用,在加载同一个网页中的内容时,尽可能的复用链接,这就是HTTP协议中keep-alive属性的做用。
而在http1.1以后,keep-alive默认开启,也就是咱们不用特殊关注他了。
如上图所示,就是一个http 缓存的流程图,在我看来,全部的优化手段都比不上缓存所带来的体验,他直接省去了一些静态文件的请求资源的开销,从而在第二次请求是带来质的提高。而他遵循的规律只有两点:
而咱们还能够将缓存和工具链链接起来,从而给用户一个更好的体验,举个例子:webpack在打包时候,能够监听文件是否变化,从而若是文件变化,将改变当前文件名的hash值,其他不变,如此一来,在部署升级以后,用户也只请求到变更文件,从而减小资源的下载。从而达到性能的最优。
一个服务器与浏览器之间的中间人角色,若是网站中注册了service worker那么它能够拦截当前网站全部的请求,进行判断(须要编写相应的判断程序),若是须要向服务器发起请求的就转给服务器,若是能够直接使用缓存的就直接返回缓存再也不转给服务器。从而大大提升浏览体验。
复制代码
它有着两个特色
然而不幸的时,在2021年的今天,他的兼容性还堪忧。因此没有普及。
srr技术实际上是一个很是老牌的技术,他其实很早都存在,只不过因为vue 、和react 的大火、将传统的ssr革新掉,让前端也能参与到srr的这个浪潮中来。他的原理其实很是简单,就是将首屏内容在服务端拼接为字符串,放在客户端解析。从而减小了客户端的js执行时间。快速渲染页面。达到性能优化的目的
在上述全部的问题搞定以后,咱们在文章的开头,提出的最后两个问题,也就清晰了,原理方面其实也是阐述了个大概。
其实在每一个方向上都有值得深挖的知识,这些须要被深挖的知识在时时刻刻提醒咱们:咱们真的很菜,然而,我常常发现,不少人真的只是流于表面,他享受这互联网带来的红利和错觉,误认为本身很强,而且总爱指点江山。在经历了很长时间的思考之后,我问本身,若是被过渡抬高的互联网行业热度一过。我还剩下什么,其实也就会用vue、react的api而已。
故记录此文,并朝着各个方向慢慢攻克,但愿给你们在知识体系上添砖加瓦是不对之处。请批评指正!