【前端性能优化——结合浏览器进行性能调优】

前端需要性能吗?

在大多数业务中,并没有认真考虑过前端性能。绘制符合UI设计的页面,满足用户交互,完善功能,没有bug就行。代码写的是否合理,空间复杂度和时间复杂度是否最优,第三方引入是否恰当时机,静态文件引入是否正确,图片格式选择是否合理等这些,都没有关注过。
随着前端的发展,浏览器性能越来越高,前端业务也逐渐变得复杂,前端页面数据量、交互量、渲染量越来越大,用户对前端要求也与时俱进,前端性能也就变得越来越重要。

前端应该从哪些地方优化——善用工具F12

首先在内心问自己一句:我会F12吗?做为一个前端开发者,我们每天都在F12,怎么可能不会呢?那么F12我们都用到的有哪些呢?

Element

1,elment 可以查看元素结构,对于不合理的布局,元素结构,可以直观的感受到。在鼠标选中元素的时候,页面上有相应的高亮部分,然我们能看到当前元素的布局位置,接触过部分前端开发,页面布局非常不合理,本该放在一个div中的内容,被硬生生分割开,然后用position去定位在一起,由于我主要是修改线上bug,做公共部分,每次去看他们代码的时候,都需要看很久才能将他们逻辑看明白,这种布局方式,完全是给下一个人挖坑。我的开发习惯是:先分析UCD的高保真,先对原型图进行大区域划分,再向每个大区域块中填充内容。这样能保证下一个开发者,快速定位。
在这里插入图片描述

如这个页面部分开发者,会在两个页面中,去重构两次数据统计 + 收拾栏+我的关注。而不是在页面页面中将 统计 + 收拾+关注 布局好,作为父页面,再将下面两个页面作为子页面嵌套进去。
2.同时,根据逻辑判断,判断元素是该用 display:none属性隐藏,还是用创建销毁来隐藏页面布局。在经常显示和隐藏的地方,建议使用 display:none隐藏。如:我们日常用到的自定义hover效果,一个人的头像,hover上去显示人的名称,职称,头衔等。页面上,hover查看是一个经常用到的,如果用创建销毁,需要每次hover都要创建销毁 + 回流+重绘。如果使用display属性,隐藏显示 hover内容,结合前端框架,通过虚拟dom,只需要更新显示的文字 + 回流 + 重绘。在hover显示内容较多的时候,相对于重新创建性能优势很大。
3.部分开发者,不习惯打开编辑器的校验,原本项目配置的校验,页会被开发者自己关闭。导致页面重复代码,重复id,元素中引用的方法,并没有在逻辑层声明。

Console

console 应该是我们使用的最多的调试工具,经常看报错 和数据格式。需要说明的是,不要过分依赖console,在很多地方debugger要比console.log好用的多,可能.console.log十次,没有一次 debugger效果好,一定debugger就能找到问题所在。
在console.log数据的时候,使用JSON拷贝一下,能更快定位数据格式问题。

Sources

本地开发和测试环境一定要把sourceMap开启,方便查看报错,查看报错位置,定位错误原因。source中的断点 和 console.log结合查找问题。
凡是在源码调试过长中,感觉很不顺畅,代码逻辑组织不起来的,代码基本上都有问题。前端开发人员的面向对象开发能力比JAVA开发人员弱。在JAVA开发中,万物皆对象,所有方法都是对象对方法的调用。而前端开发人员,习惯用嵌套的方式如:
emu = [{name: ‘总计’, options: [{name: ‘手机’, value: 10, type: [{}]},{}], function(){}},{name: ‘第一季度’ options: [], function(){}, {name: ''第二季度 options: [], function(){}],前端页面使用的数据,很习惯,用一个对象包裹,为了方便定位代码,把对这个对象处理的方法,也包裹在其中。
在数据不复杂的时候,这种方式和好用。 但是在数据结构复杂的时候, 当需要定义一条数据的时候,肯能会需要 a.b.c[1].d[0].v这样去复制取值,这样去赋值,或者在自己写的过长中,自己能理解自己的代码。但是,需要考虑的是,需求一直在变更,代码不会一直是自己维护。如果当需求变更了,或者下一个人去维护的时候,a.b.c[1].d[0].v 他根本看不懂是什么意思。每一次取值的时候,都需要去看定义这个地方是怎么定义的。特别是遇到下标的时候,他还需要去看一下嵌套数据的位置。

方法拆分是一个很好的习惯,部分前端开发不喜欢方法提纯,方法拆分。一个方法里面两三百行代码,看着都头大。开发范式中有一条:方法的单一职责,大概意思一个方法里面只干一件事情。定义一个方法叫imgToBase64(img): string;那么这个方法就做一件事,将图片转换成base64。很多时候,前端需要对数据进行处理,加工。这个时候,我们可以定一个一个专门处理数据的方法,然后再在将数据处理方法拆分成多个。如:
在这里插入图片描述
比如我们处理一个列表数据,涉及到数据组装,页面上,我们选择的title是个复选框,是一个数组,后端为了方便存储,只存了一个字符串。我们就需要对数据进行处理。这个时候,整个数据处理函数,内部代码进行拆分。
当然这个案例,只是一个简单演示,实际处理的时候,要比这个复杂的多,设计数据格式转换,字段组装,后端不需要字段的删除(节约带宽,减少网络传输时间)

Network

我们开发过长中,使用netWork是最多的。作为一个前端开发人员,network,谁都会用。但是很多人都用不好。
我们日常开发过长中,使用netWork最多的是查看接口是否正常调用,查看数据返回格式是否正常,查看资源是否正常加载。
很多人都会忽略其中的瀑布流,调用顺序。
1.接口调用顺序很关键。如:前端经常会查询一个人是否具有该页面的操作权限,再根据操作权限查询数据。a接口中的数据与B接口入参,是否调用有关,B接口返回的数据和C接口有关,这样就形成了一个链式调用。
在这里插入图片描述
其中横坐标为 network中的时间。可以看出,链式调用,所用的时间为 第一个接口开始调用,到最后一个接口掉完。前端接口用时 = 开始调用时间 + 排队时间 + 暂停时间 + tcp 连接时间 + 后端相应时间 + 下载时间 + 前端处理数据 + 根据处理的数据绘制页面。如果存在这么多链式调用的时候,整个调用时间,就变得不可控,任何一个环节出现问题,如网络波动,服务器响应慢,前端浏览器卡,最后都会给用户带来不好的感官,如果是商业项目,会导致用户流失。
其中举例为自己遇到的情况: A先查询权限, 根据查询回来的权限判断,用户具有那个部门权限,再根据具有的部门权限,查询B 部门配置的下拉选项,C接口根据部门查询回来下拉选项 ,默认选择第一个选项,查询业务数据。D接口,业务数据查询回来又一个关联id在里面,再根据关联id查询一个统计数据。
这个页面的接口调用,就这样a -> b -> c ->d。在正常网络情况下还比较好(我们服务器在成都),成都用户调用非常快,没有啥子问题。而深圳和广州、北京用户调用就变得非常慢。
现在的前端项目,webpack在打包的时候,做的配置都满满足一般情况。而我们性能优化更多的时候,就是为了应对网络波动、网络链路超长的情况。在网络链路变成之后,从浏览器中输入网址到页面load之后,平均耗时6s。
怎么优化呢?
业务接口后端集成权限,前端调用权限不做业务权限判断,仅仅是用来对一些按钮做disable、显示隐藏控制。如没有删除权限,就没有删除按钮,这个前端需要单独掉权限接口。是否显示当前页面数据,这个权限判断放到查询数据接口里面,由后端控制。

分析接口之间的依赖关系,判断是否可以同时调用。有个接口可能没有数据依赖关系,可能是开发人员太多,经手人太多,后面的开发者不清楚前面的业务逻辑,在历史迭代中,变成了链式调用该。这个时候需要所有开发者,将代码抽离好,写好注释,认真阅读前面的代码,找BA要到历史需求文档,找领导要到历史开发文档。确认每个接口具体要干的事情,判断是否需要链式调用。

合并部分接口,部分接口可以由后端内部调用,然后在同一个接口中返回。不再拆分。

利用缓存。比如举例中的下拉配置,并不是一个经常会变动的内容,是否可以做到前端。如果不能做到前端d代码中,是否可以存在 localstorage中, 每次有限区localstorage中的配置,等该页面接口调用完成之后,再掉获取配置接口,判断是否更新localstorage中的配置。

2.瀑布流中的时间很重要
在这里插入图片描述
在network中,找到瀑布(waterfall)栏位,选择关系的接口,将鼠标放上去,就会弹出一个很多时间的弹框。从这个弹框的颜色,我们就能感受到,这个弹框包含了很多信息。
stalled:排队时间、waiting for server: 等待服务器响应时间、content Download: 下载内容时间。这三个时间是我们最关心的。
排队:当存在多个请求的时候,浏览器处理不过来,就会把多余的请求放到队列中(谷歌浏览器同一个域下 能请求六个,操作六个就要排队,前面处理一个,队列就抽一个去处理)。
服务器响应:这个大家都知道,后端处理数据也需要时间,需要喊后端的同事去优化,有个接口,测试环境服务器响应就要2s,这个就是后端问题,一般情况测试环境控制在500ms以下。
下载内容:这个根网络和资源大小有关。资源大小又根后端有关,也需要后端同事一起优化,在优化的时候,前端可以对不需要使用的数据,做出标识,发给后端,后端将不必要的数据,就不再接口中返回。
可以看到,该图片中,还有一个发送请求消耗的时间,正常情况,大家的请求头都比较下。但是部分系统的cookie非常非常长,复制到text中,一整页。别看发送请求头,每次消耗不了多少时间,但是量大啊。当请求头很大的时候,每次来个 100ms,十次就是 1s。请求头,只带需要带的东西,用户的名字,工号,id,都不用了。只需要把健全id传递到后端,后端自己在鉴权的时候就可以拿到。

Performance(性能)

在这里插入图片描述
性能分析的时候,一定要结合浏览器的这个功能。
该功能,会对当前页面的 DOMContentLoaded、Onload、First Contentful Paint、Largest Contentful Paint 的时间做一个统计。
同时集成了network ,证明network非常重要。
Frames:影响性能,用户体验
Animation: 尽量减少不需要的动画,动画很消耗性能。一个静态显示页面,仅仅展示了几个移动的光标,cpu就爆满。
Main: 应该是渲染进程(包含了JS引擎线程、GUI渲染线程、事件处理线程、定时器处理线程、http请求线程)。主要用来做 脚本执行、样式计算、布局计算、绘制页面。
在这里插入图片描述
其中不同的颜色,代表的含义和下面摘要中颜色代表的含义一样。横坐标代表时间。加上红色斜线的,表示该任务执行,是一个长任务。由于渲染进程中,GUI和JG线程是互斥线程,执行GUI的时候不能执行JS,执行JS不能执行GUI,如果一个JS代码片段执行过长,就会导致页面渲染不出来。需要等到JS代码执行完,浏览器才会执行GUI绘制页面。页面白屏,或者卡顿会导致用户感知特别不好,对长任务代码需要适当分割。
如promise.all([]) 里面掉用10个接口,每个接口回来的数据要处理30ms,那么等待10个接口全部返回再处理,最低就需要 300ms。这300ms内,浏览器就卡着,不能进行页面绘制。
如果我们在 每个接口里面包装一个处理数据的 promise,那么不会有这个问题
在这里插入图片描述
这样,每个接口的处理,就在当前接口调用回来就会处理,处理完成,如果其他接口没有回来,浏览器就可以去干其他事情。
GUP:图像处理器,现在部分浏览器会用他来将GUI处理好的页面,绘制到屏幕。这地方可以看到,绘制页面到浏览器的耗时。
调用树,和事件日志:这两个还是非常重要,如果当前代码的执行,具体是耗时在什么地方,在什么方法里面,都可以在这两个里面找到。对于耗时的方法,在这里面找到之后,再去做优化。在帮助其他组排查问题的时候,发现了一个300ms的调用,最后排查出来是 全局引入了 echarts,首屏加载都会调用这个。改成局部引用之后,只会在直接进入使用这个echats的地方,才会加载。

Application:

这大家都知道,数据存储的地方,唯一需要注意的是:不同站点存储同一个key,获取的时候,会出现异常。

Memory:

前端开发者,关注内存变化的很少。但是内存对前端开发也很重要,清空变量,到底是定义局部变量还是全局变量,销毁监听器,主动销毁组件。正常情况下,react、vue、angular都会自动销毁组件、回收组件内部变量所有用到的内存。我们重点需要关注的是 自定义监听器的回收。
遇到过一个页面,刚刚点进去没有任何问题,在这个页面和其他页面反复切换几次之后,整个浏览器就会卡死。通过分析:该页面,自定义了很多监听, 在页面销毁的时候没有进行释放。多次,进入该页面之后,监听器一直累加,导致内存消耗完毕。
那个页面有很多卡片,卡片是用canvas绘制的,卡片的数量也是根据后端数据控制。每个卡片上都需要监听点击事件、鼠标移动事件。开发者通过window.addEventListener为每个卡片增加了三个监听事件。
最后的解决方法:将所有的 卡片放到一个canvas中绘制,绘制测原有的样式,只在一个canvas中增加监听,通过坐标计算鼠标经过是原来对应的数据。在destroy中对自定义监听进行移除。

在写页面的时候,很多开发者喜欢一股脑的把所有的变量都定义为当前页面的全局变量,不管这个变量是否只使用一次。这种做法很少出现问题,因为前端数据量大的时候都会做分页查询。但是最好还是合理安排这些变量的声明位置,变量声明要遵循就近原则——声明了就马上使用,以后不会在使用的变量可以手动 赋值为 null
可以在浏览器内存的快照中,对当前路由下所占用内存进行查看,也可以对当前页面具体使用了哪些对象进行查看,占用内存当然是越小越好。
在这里插入图片描述

Performance insights:

和performance 由点相似,主要是针对当前页面各个阶段执行时间做了一个统计。由一个播放的按钮,可以只管看到在某个时间,当前页面处于什么状态下。比起performance,这个地方更加直观一点,可以一眼看出,当前页面耗时地方
在这里插入图片描述

Lighthouse:

是浏览器对我们页面打分的一个工具,通过打分规则和最后的分数,我们可以清晰认识到当前页面还有哪些需要改进的地方,与此同时,lighthouse还会对当夜页面的优化做一些建议。
在这里插入图片描述
其中主要的打分规则由:First Contentful Paint、Largest Contentful Paint、Total Blocking Time、Cumulative Layout Shift、Speed Index。这几个规则也是对用户感知影响最大的,所用的时间也是越少越好,
在打分规则下面,还会对问题代码做统计,对问题代码做建议(这个建议可以不采纳,需要开发者结合实际情况进行处理)
在这里插入图片描述其中的View Original Trace 点击之后会跳转到 performance里面
View Treemap 点击之后,可以看到整个代码的应用树
在这里插入图片描述
viewTreempa中会对代码片对的使用率做一个统计,如果代码片段使用率过低,就表示当前代码需要拆分。代码的使用率在下面的 Coverage 中也会专门统计

Coverage

专门做代码使用率统计的,在每个代码片段中,都有代码使用率。
点击需要查看的片段,会在资源中打开当前代码文件,会对使用情况标颜色(这个颜色有点不准,要么标的位置不对,要么标的数量不对,开发者结合实际情况使用)
对一些使用率低的代码进行分包分片,按需引入。不要用全局引入。
在这里插入图片描述

Performance monitor性能监视

性能监视,主要看当前页面实时内存使用情况,js堆情况、dom节点数
在这里插入图片描述
通过测试发现:动画、回流、重排重绘都会拉升CPU使用率。开发页面的时候,需要结合页面,对页面布局做合理调整,减少回流的情况,对动画处理进行优化。

写在最后

整个前端性能优化结合浏览器分析的介绍就到此结束了,前端性能更多的时候需要靠后端,遇到数据处理,一定要优先喊后端处理,千万不要前端自己给处理了。你处理一次,后面就会有第二次,由第二次就会有第三次。可能这个后端同事在和其他前端联调的时候,也不处理数据,然后对其他前端说: 那个XXX都自己处理数据,为啥你不行。
浏览器的情况很复杂,浏览器的使用权是在用户手上,你永远不知道用户在开了多少个窗口,永远不知道用户的电脑打开多个应用。寄托前端处理数据,然后还要保证性能,简直是痴人说梦。

前端性能处理:拆、减、缓存、图片、http2。
拆:按需引入,一个大代码片段拆分,通过打包工具按需引入到需要的地方。
减:减少资源请求数,减少资源请求量。
缓存:缓存真的很快,本地磁盘写入,的时候。网络连接可能还没有三次握手成功。
图片:svg,webpb, 这两个格式的图片都很小,在切图的时候,优先选择webpb、svg。同样的图片,webpb要比png小差不多一半。
http2:大部分浏览器都是支持http2的,如果能升级http2,多次请求互用一个 TCP连接,蚊子再小也是肉,一个首屏加载可能需要七八十个请求,这就变成了很大一坨肉。

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值