前端性能优化.docx
《前端性能优化.docx》由会员分享,可在线阅读,更多相关《前端性能优化.docx(34页珍藏版)》请在冰点文库上搜索。
![前端性能优化.docx](https://file1.bingdoc.com/fileroot1/2023-6/19/56f1bd8f-3ad2-4d8e-8d8a-ce554d24ede1/56f1bd8f-3ad2-4d8e-8d8a-ce554d24ede11.gif)
前端性能优化
当我们在讨论前端性能时我们在谈些什么?
性能的瓶颈又到底在哪儿?
是昂贵的JavaScript开销,耗时的网络字体下载,超大的图片还是迟钝的页面渲染?
摇树(tree-shaking)、作用域提升(scopehoisting)、代码分割(code-splitting),以及各种酷炫的加载模式,包括交叉观察者模式(intersectionobserver)、服务端推送(serverpush)、客户端提示(clientshints)、HTTP/2、serviceworker以及edgeworker,研究这些真的有用吗?
还有,最重要的,当我们着手处理前端性能的时候,我们该从哪里开始,该如何去建立一个长期的性能优化体系?
早些时候,性能都是所谓的“后顾之忧”。
直到项目快结束的时候,它会被归结为代码压缩(minification)、拼接(concatenation)、静态资源优化(assetoptimization)以及几行服务器配置的调整。
现在回想一下,情况似乎已经全然不同了。
性能问题不仅仅是技术上的考量,当它被整合进工作流时,在设计的决策中也需要考量性能的因素。
性能需要持续地被检测、监控和优化。
同时,网络在变得越来越复杂,这带来了新的挑战,简单的指标追踪变得不再可行,因为不同的设备、浏览器、协议、网络类型和延迟都会使指标发生明显变化。
(CDN、ISP、缓存、代理、防火墙、负载均衡和服务器,这些都得考虑进去。
)
因此,如果我们想囊括关于性能提升的所有要点—从一开始到网站最后发布,那么最终这个清单应该长啥样呢?
以下是一份(但愿是无偏见的、客观的)2019前端性能优化年度总结,“介是你没有看过的船新版本”,它几乎包括所有你需要考虑的要点,来确保你的网站响应时间够短、用户体验够流畅、同时不会榨干用户的带宽。
起步:
计划与指标
对于持续跟踪性能,“微优化”(micro-optimization)是个不错的主意,但是在脑子里有个明晰的目标也是很必要的—量化的目标会影响过程中采取的所有决策。
有许多不同的模型可以参考,以下讨论的都基于我个人主观偏好,请根据个人情况自行调整。
1.建立性能评估规范
在很多组织里面,前端开发者都确切地知道哪有最有可能出现问题,以及应该使用何种模式来修正这些问题。
然而,由于性能评估文化的缺失,每个决定都会成为部门间的战场,使组织分裂成孤岛。
要想获得业务利益相关者的支持,你需要通过具体案例来说明:
页面速度会如何影响业务指标和他们所关心的KPI。
没有开发、设计与业务、市场团队的通力合作,性能优化是走不远的。
研究用户抱怨的常见问题,再看看如何通过性能优化来缓解这些问题。
同时在移动和桌面设备上运行性能基准测试,由公司真实数据得到定制化的案例研究(casestudy)。
除此以外,你还可以参考WPOStats上展示的性能优化案例研究及其实验数据来提升自己对性能优化的敏感性,了解为什么性能表现如此重要,它对用户体验和业务指标会产生哪些影响。
光是明白性能表现很重要还不够,你还得设立量化的、可追溯的目标,时刻关注它们。
2.目标:
比你最快的竞争对手快至少20%
根据一项心理学研究,如果你希望你的用户感觉到你们的网站用起来比竞争对手快,那么你需要比他们快至少20%。
研究你的主要对手,收集他们的网站在移动和桌面设备上的性能指标,确定超越他们的最低要求。
为了得到准确的结果和目标,首先去研究你们产品的用户行为,之后模仿90%用户的行为来进行测试。
为了更好地了解你的对手的性能表现,你可以使用ChromeUXReport(CrUX,一组现成的RUM数据集,IlyaGrigorik的视频介绍),SpeedScorecard(可同时估算性能优化将如何影响收入),真实用户体验测试比较(RealUserExperienceTestComparison)或者SiteSpeedCI(基于集成测试)。
注意:
如果你使用PageSpeedInsights(是的,它还没被抛弃),你可以得到指定页面详细的CrUX性能数据,而不是只有一些粗略的综合数据。
在为具体页面(如“首页”、“产品列表页面”)设立性能目标时,这些数据会非常有用。
另外,如果你正在使用CI来监测性能预算,当使用CrUX来确立目标时,你需要确保测试环境与CrUX一致。
(感谢PatrickMeenan!
)
收集数据,建立一个表格,削减掉20%,以此建立你的目标性能预算。
那么现在你有了量化的对照组样本。
事情正逐步走向正轨,只要你时刻把这份预算记在心里,并且每次都交付尽可能少的代码以缩短可交互时间。
需要些资料来上手?
AddyOsmani写了一篇非常详细的文章解释如何开始做性能预算,如何量化新特性带来的影响,以及当超出预算时,你应该怎么做。
LaraHogan有一份考虑到性能预算时的产品设计指南,可以对设计师们提供一些有用的提示。
另外,通过建立带有报告打包体积图表的仪表盘,来可视化展示性能预算和当前的性能指标。
有很多工具可以帮你做到这一点,SiteSpeed.iodashboard(开源),SpeedCurve和Calibre只是其中几个,你可以在perf.rocks找到更多工具。
一旦确立好合适的性能预算,你就可以借助WebpackPerformanceHintsandBundlesize、LightouseCI,PWMetrics、SitespeedCI把它们整合进打包流程中,在请求合并时强制检测性能预算,并在PR备注中注明得分记录。
如果你需要个性化定制,你可以使用webpagetest-charts-api,它提供了一系列可以从WebPagetest的结果生成图表的API。
举个例子,正如Pinterest一样,你可以创建一个自定义的eslint规则,禁止导入重依赖(dependency-heavy)的文件和目录,从而避免打包文件变得臃肿。
设定一个团队内共享的“安全”依赖包列表。
除了性能预算外,仔细考虑那些对你们业务价值最大的关键用户操作。
规定并讨论可接受的关键操作响应时间阈值,并就“UX就绪”耗时评分在团队内达成共识。
大多数情况下,用户的操作流程会涉及到许多不同公司部门的工作,因此,就“时间阈值”达成共识可以为今后关于性能的沟通提供支持,避免不必要的讨论。
确保对新增资源和功能带来的资源开销了如指掌。
另外,正如PatrickMeenan提议的,在设计过程中,规划好加载的顺序和取舍是绝对值得的。
如果你预先规划好哪部分更重要,并确定每部分出现的顺序,那么同时你也会知道哪些部分可以延迟加载。
理想情况下,这个顺序也会反映出CSS和JavaScript文件的导入顺序,因此在打包阶段处理它们会变得更容易些。
除此以外,还得考虑页面加载时中间态的视觉效果(比方说,当网络字体还没有加载完全时)。
规划,规划,规划。
尽管在早期就投入那些能起到立竿见影效果的优化似乎相当有吸引力—这对需要快速决胜的项目而言可能是个不错的策略,但是如果没有务实的规划和因地制宜的性能指标,很难保证性能优先能一直受到重视。
首次绘制(FirstPaint)、首次有内容绘制(FirstContentfulPaint)、首次有意义绘制(FirstMeaningfulPaint)、视觉完备(VisualComplete)、首次可交互时间(TimeToInteractive)的区别。
3.选择合适的指标
并不是所有的指标都同等重要。
研究哪个指标对你的应用最重要,通常来说它应该与开始渲染你的产品中最重要的那些像素的速度以及提供输入响应所需的时间相关。
这个要点将为你指明最佳的优化目标,提供努力的方向。
不管怎样,不要总是盯着页面完整载入的时间(比方说onload和DOMContentLoaded),要站在用户的角度去看待页面加载。
也就是说,需要关注一组稍微不同的指标。
事实上,“选择正确的指标”是没有绝对完美方案的。
根据TimKadlec的研究和MarcosIglesias在他的演讲中提到的,传统的指标可以归为几种类型。
通常,我们需要所有的指标来构建完整的性能画像,但是在特定场景中,某些指标可能比其他的更重要些。
基于数量的指标衡量请求数量、权重和性能评分等。
对于告警和监控长期变化很有用,但对理解用户体验帮助不大。
里程碑式指标使用加载过程中的各个状态来标记,比如:
首位字节时间(TimeToFirstByte)和首次可交互时间(TimeToInteractive)。
对于描述用户体验和指标很有用,但对了解加载过程中的情况帮助不大。
渲染指标可以估计内容渲染的时间,例如渲染开始时间(StartRender)和速度指数(SpeedIndex)。
对于检测和调整渲染性能很有用,但对检测重要内容何时出现、何时可交互帮助不大。
自定义指标衡量某个特定的、个性化的用户事件,比如Twitter的首次发推时间(TimeToFirstTweet),Pinterest的收藏等待时间(PinnerWaitTime)。
对准确描述用户体验很有用,但不方便规模化以及与竞品比较。
为了使性能画像更加完整,我们通常会在所有类型中都选择一些有用的指标。
一般来说,最重要的是以下几个:
首次有效绘制(FirstMeaningfulPaint,FMP)
反映主要内容出现在页面上所需的时间,也侧面反映了服务器输出任意数据的速度。
FMP时间过长一般意味着JavaScript阻塞了主线程,也有可能是后端/服务器的问题。
首次可交互时间(TimetoInteractive,TTI)
在此时间点,页面布局已经稳定,主要的网络字体已经可见,主线程已可以响应用户输入—基本上意味着只是用户可以与UI进行交互。
是描述“网站可正常使用前,用户所需要等待的时长”的关键因素。
首次输入延迟(FirstInputDelay,FID或Inputresponsiveness)
从用户首次与页面交互,到网站能够响应该交互的时间。
与TTI相辅相成,补全了画像中缺少的一块:
在用户切实与网站交互后发生了什么。
标准的RUM指标。
有一个JavaScript库可以在浏览器中测量FID耗时。
速度指数(SpeedIndex)
衡量视觉上页面被内容充满的速度,数值越低越好。
速度指数由视觉上的加载速度计算而得,只是一个计算值。
同时对视口尺寸也很敏感,因此你需要根据目标用户设定测试配置的范围。
(感谢Boris!
)
CPU耗时
描述主线程处理有效负载时繁忙程度的指标,显示在绘制、渲染、运行脚本和加载时,主线程被阻塞的频次和时长。
高的CPU耗时明显地意味着卡顿的用户体验。
利用WebPageTest,你可以在“Chrome”标签页上选择“CaptureDevToolsTimeline”选项来暴露出可能的主线程崩溃(得益于WebPageTest可以在任何设备上运行)。
广告的影响(AdWeightImpact)
如果你的站点的利润主要来源于广告,那么追踪广告相关代码的体积就很有用了。
PaddyGanti的脚本可以构筑两条URL(一条有广告,一条没有),并且利用WebPageTest生成一个比较视频,并显示区别。
偏离度指标(Deviationmetrics)
正如Wikipedia的工程师所指出的,你的结果中数据的变化在一定程度上可以反映出设施的可靠性,以及你该花多少精力来关注这些偏离度和极端值。
过大的变化意味着你很可能需要对目前设施的配置做一些调整,它也能帮助我们了解有某些页面是难以可靠地用指标衡量的,例如因为第三方脚本而导致的明显变化。
另外,追踪浏览器版本也是个不错的主意,它可能帮助你获悉新版浏览器可以带来的性能变化。
自定义指标(Custommetrics)
自定义指标可由具体业务和用户体验的需要专门设置。
它需要你对重要像素、关键脚本、必要CSS样式和相关静态资源有个清晰的概念,并能够测算用户需要多长时间来下载它们。
关于这点,你可以使用HeroRenderingTimes或PerformanceAPI,为重要业务事件创建时间戳。
另外,你也可以通过在WebPageTest测试完成后运行自定义的脚本来收集自定义的指标。
SteveSouders写了一篇文章详细地介绍了各个指标。
需要注意的是:
首次交互时间是在实验环境下通过自动化审查得到的,而首次输入延迟则表示真实用户在使用中感受到的实际延迟。
总而言之,始终观测和追踪这两个指标会是个好主意。
不同的应用,偏好的指标可能会不同。
举个例子,对于NetflixTV的UI界面而言,关键输入响应、内存使用和首次可交互时间会更重要些,而对于Wikipedia,首末视觉变化和CPU耗时指标会显得更重要些。
4.在目标用户的典型设备上收集数据
为了得到准确的数据,我们需要选择合适的测试设备。
MotoG4会是一个不错的选择,或者是Samsung的一款中端产品,又或者是一款如Nexus5X一样中庸的设备,以及Alcatel1X这样的低端设备。
你可以在opendevicelab找到这些。
如果想在更慢的设备上测试,你可以花差不多$100买一台Nexus2。
如果你手上没有合适的设备,你可以通过网络限速(比如:
150msRTT,下行1.5Mbps,上行0.7Mbps)以及CPU限速(慢5倍)在电脑上模拟移动端体验。
然后,再切换到普通3G、4G和WIFI网络进行测试。
为了使性能影响更加明显,你甚至可以引入2G星期二,或者为了更方便测试,在办公室限制3G网络。
时刻记着:
在移动设备上,运行速度应该会比在桌面设备上慢4-5倍。
移动设备具有不同的GPU、CPU、内存、电池特性。
如果说慢速网络制约了下载时间的话,那么手机较为慢速的CPU则制约了解析时间。
事实上,移动设备上的解析时间通常要比桌面设备长36%。
因此,一定要在一部平均水准的设备上进行测试—一部你的用户中最具代表性的设备。
在一周中选择一天让网速变慢。
Facebook就有2G星期二来提高对低速网络的关注。
(图片来源)
幸运的是,有很多工具可以帮你自动化完成数据收集、评估上述性能指标随时间变化趋势。
记住,一个好的性能画像应该包括一套完整的性能指标、实验数据和实际数据。
∙集成测试工具可以在预先规定了设备和网络配置的可复制环境中收集实验数据。
例如:
Lighthouse、WebPageTest
∙真实用户监测(RUM)工具可以持续评估用户交互,收集实际数据。
例如,SpeedCurve、NewRelic,两者也都提供集成测试工具。
前者在开发阶段会非常有用,它可以帮助你在开发过程中发现、隔离、修复性能问题。
后者在维护阶段会很有用,它可以帮助你了解性能瓶颈在哪儿,因为这都是真实用户产生的数据。
通过深入了解浏览器内置的RUMAPI,如NavigationTiming、ResourceTiming、PaintTiming、LongTasks等,集成测试和RUM两者搭配构建出完整的性能画像。
你可以使用PWMetrics、Calibre,SpeedCurve、mPulse和Boomerang、Sitespeed.io来进行性能监测,它们都是不错的选择。
另外,利用ServerTimingheader,你甚至可以同时监测后端和前端性能。
注意:
建议使用浏览器外部的网络节流器,因为浏览器的DevTools可能会存在一些问题,比如:
由于实现方法的原因,HTTP/2push可能会有问题。
(感谢Yoav和Patrick!
)对于MacOS,我们可以用NetworkLinkConditioner;对于Windows,可以用WindowsTrafficShaper;对于Linux,可以用netem;对于FreeBSD,可以用dummynet。
5.为测试设立“纯净”、“接近真实用户”的浏览器配置(Profile)
使用被动监控工具进行测试时,一个常见的做法是:
关闭反病毒软件和CPU后台任务,关闭后台网络连接,使用没有安装任何插件的“干净的”浏览器配置,以避免结果失真。
(Firefox、Chrome)。
然而,了解你的用户通常会使用哪些插件也是个不错的主意,然后使用精心设计的“接近真实用户的”浏览器配置进行测试。
事实上,某些插件可能会给你的应用带来显著的性能影响。
如果你有很多用户在使用这些插件,你可能需要考虑这些影响。
“干净的”用户浏览器配置可能有些过于理想化了,可能会与实际情况大相径庭。
6.与团队其他成员分享这份清单
确保你的每一位同事都充分熟悉这份清单,从而避免在以后出现误解。
每一个决策都会带来性能影响,整个项目会从前端开发者正确地对待性能问题而获益良多,从而使得团队中的每一个人都负起责任来,而不仅仅只是前端。
根据性能预算和清单中定义的优先级来制定设计决策。
设置切实可行的目标
7.100毫秒响应时间,60fps
为了使用户感觉交互流畅,界面的响应时间不得超过100ms。
如果超过了这个时间,那么用户将会认为该应用程序是卡顿的。
RAIL,一个以用户为中心的性能模型为你提供了健康的目标:
为了达到<100毫秒的响应,页面必须在每50毫秒内将控制权交还给主线程。
预计输入延迟时间可以告诉我们是否到达了这个阈值,理想情况下,它应该小于50毫秒。
对于像动画这样的(性能)高压点,如果可以,最好不要做任何事情。
RAIL,一个以用户为中心的性能模型。
此外,每一帧动画应在16毫秒内完成,从而达到每秒60帧(1秒÷60=16.6毫秒)——最好在10毫秒以下。
由于浏览器需要时间将新帧绘制到屏幕上,因此你的代码应在到达16.6毫秒的标记之前执行完成。
我们开始讨论120fps(例如iPad的新屏幕以120Hz运行),而Surma已经覆盖了一些120fps的渲染性能解决方案,但这可能不是我们目前正关注的目标。
对性能预期持悲观态度,但要在界面设计上保持乐观并明智地使用空闲时间。
显然,这些目标适用于运行时性能,而不是加载性能。
8.速度指数<1250,TTI(交互时间)<5s(3G),关键文件大小<170KB(gzip压缩后)
虽然很难实现,但最好将终级目标定为,首次绘制时间1秒以内,速度指数的值限制在1250以下。
由于基准是模拟在价值200美元的Android手机(如MotoG4)上,网络为slow3G,400msRTT和400kbps的传输速度,目标是交互时间低于5秒,对于重复访问,目标是低于2秒(只能通过serviceworker实现)。
请注意,当谈到互动指标时,最好区分FirstCPUIdle以及TimetoInteractive,以避免误解。
前者是主要内容渲染后的最早点(其中页面至少有5秒的响应时间)。
后者是页面可以始终响应输入的时间。
(感谢PhilipWalton!
)
我们有两个主要限制因素,限制我们制定一个合理的目标来保证网络内容的快速传输。
一方面,由于TCP慢启动,我们有着网络传输的限制。
HTML的前14KB是最关键的有效负载块——并且是第一次往返中唯一可以提供的预算(由于手机唤醒时间,这是在400msRTT情况下1秒内获得的)
另一方面,内存和CPU有硬件限制(稍后我们将详细讨论它们),原因是JavaScript的解析时间。
为了实现第一段中所述目标,我们必须考虑JavaScript关键文件大小的预算。
关于预算应该是多少有很多不同的意见(这应该由你的项目的本身决定),但是gzip压缩后预算为170KB的JavaScript已经需要花费1s才能在普通手机上进行解析和编译。
假设解压缩时170KB扩展到3倍大小,那么解压缩后(0.7MB)时,那已经可能是MotoG4或Nexus2上“用户体验的丧钟”。
当然,你的数据可能显示你的客户没有使用这些设备,但是也许因为低下的性能导致你的服务无法访问,他们根本没有出现在你的分析中。
事实上,Google的AlexRussels建议将gzip压缩后大小为130-170KB作为一个合理的上限,当超出这个预算时,你应该进行慎重考虑。
在现实世界中,大多数产品都不是很接近(这个标准);当今的bundle平均大小约为400KB,与2015年末相比增长了35%。
在中等水平的移动设备上,Time-To-Interactive占30-35秒。
我们当然也可以超过bundle的大小预算。
例如,我们可以根据浏览器主线程的活动设置性能预算,即在开始渲染之前进行绘制,或跟踪前端CPU热点。
Calibre,SpeedCurve以及Bundlesize等工具能够帮你控制预算,并且可以集成到你的构建过程中。
此外,性能预算可能不应该是固定值。
由于依赖网络连接,性能预算应该(对不同的网络条件)进行适配,但无论他们如何使用,慢速连接上的负载更加“昂贵”。
定义环境
9.选择并设置你的构建工具
不要过分关注那些炫酷的东西。
坚持你自己的构建环境,无论是Grunt,Gulp,Webpack,Parcel还是工具组合。
只要你获得了所需结果,并且构建过程中没有任何问题,这就可以了。
在构建工具中,Webpack似乎是最成熟的工具,有数百个插件可用于优化构建大小。
入门Webpack可能会很难。
所以如果你想要入门,这里有一些很棒的资源:
∙Webpackdocumentation——显然,一个很好的起点,Webpack也是如此。
RajaRao写的Webpack—TheConfusingBits以及AndrewWelch写的AnAnnotatedWebpackConfig也是。
∙SeanLearkin有一个名为Webpack:
TheCoreConcepts的免费课程,JeffreyWay有一个名为Webpackforeveryone的免费课程。
这两个课程都是深入Webpack的好资料。
∙WebpackFundamentals是一个时长为4h的非常全面的免费课程,由SeanLarkin创作,发布在FrontendMasters。
∙如果你稍微高级一点,RowanOulton已经发布了一门FieldGuideforBetterBuildPerformancewithWebpack并且BenediktRötsch进行了一项关于优秀的研究puttingWebpackbundleonadiet。
∙Webpackexamples包含数百个可以立即使用的Webpack配置,按主题和目的分类。
还额外提供了一个Webpack配置生成器,可以生成基本配置文件。
∙awesome-webpack是一个实用的Webpack资源,库和工具的精选列表,包括Angular,React和框架无关项目的文章,视频,课程,书籍和示例。
10.默认使用渐进增强
保持渐进增强作为前端架构和部署的指导原则是一个安全的选择。
首先设计和构建核心体验,然后使用高级特性为支持的浏览器提升体验,创建弹性体验。
如果你的