前言:这篇文章我在掘金翻译计划中跟着一块儿翻译的文章(感谢掘金翻译),我翻译了第三部分,而后校对了第二部分,这篇文章对于前端性能优化的技术仍是比较新颖和全面的,因此决定本身阅读一遍英文原文,而后又用思惟导图整理了下重点,英文原文仍是挺长的,因此javascript
推荐你们时间充裕的话能够本身阅读英文原文,此文是根据掘金翻译的四篇文章 + 其余的翻译文章 + 本身翻译修改得出的。由于翻译你们懂得,会有一些错误的地方,欢迎你们指出,本人不保证翻译没有错误,但已经尽力去翻译了,因此欢迎你们直接阅读英文原文!!欢迎你们直接阅读英文原文!!欢迎你们直接阅读英文原文!!php
咱们都知道性能很重要,可是,咱们是否真的一直都知道咱们性能优化的瓶颈在哪?是代价昂贵的 JavaScript?是 web 字体很慢?仍是比较大的图片?或者是渲染速度过于迟缓?tree-shaking(无用代码移除)、scope hoisting(做用域提高)、code-splitting(按需加载)、 intersection observer 以及 clients hints、CSS containment、HTTP/2 和 service workers 这些技术都是有利于性能优化的。而且,最重要的是,咱们要从哪里开始提高咱们的性能呢?而且要怎么创建长久有效的性能机制。css
译者注:html
tree-shaking
:tree-shaking
是 Webpack 2 引入的新功能,tree-shaking
是无用代码移除(DCE, dead code elimination)的一个方法,但和传统的方法不太同样。tree-shaking
找到须要的代码,灌入最终的结果;传统 DCE 找到执行不到的代码,从 AST 里清除。—— 如何评价 Webpack 2 新引入的 Tree-shaking 代码优化技术?scope hoisting
:scope hoisting
是 Webpack 3 的新功能,又译做“做用域提高”。Webpack 将全部模块都用函数包裹起来,而后本身实现了一套模块加载、执行与缓存的功能,使用这样的结构是为了更容易实现 Code Splitting(包括按需加载)、模块热替换等功能。—— Webpack 3 的新功能:Scope Hoistingcode-splitting
:对于大型的 web 应用而言,把全部的代码放到一个文件的作法效率不好,特别是在加载了一些只有在特定环境下才会使用到的阻塞的代码的时候。Webpack有个功能会把你的代码分离成Chunk,后者能够按需加载。这个功能就是code-splitting
。—— 在Webpack中使用Code Splitting实现按需加载intersection observer
:能够自动"观察"元素是否可见,因为可见(visible)的本质是,目标元素与视口产生一个交叉区,因此这个 API 叫作"交叉观察器"。—— IntersectionObserver API 使用教程clients hints
:自动响应式图片 —— Automatic responsive images with Client HintsCSS containment
:新的 CSS 属性 Containment 容许开发者限制浏览器样式、布局和 paint 工做的范围。—— CSS Containment in Chrome 52service workers
:实现离线页面 ——Service worker concepts and usage在之前,性能优化常常是在项目完成才去考虑的,常常被推迟到项目的末期,这样优化的范围就会缩小,优化的内容一般是优化静态文件和调整一些服务器配置文件。可是如今,性能优化发生了很大的改变,这些是远远不够的。前端
性能不只仅是一个技术问题:重要的是,当将它放入工做流中的时候,作出的设计决策必须受性能的提示。性能必须被测量、监测和完善,而网络日益增加的复杂性也使得跟踪性能的度量原来越难,由于度量会根据设备、浏览器、协议、网络类型和延迟而显着变化(CDN,ISP,缓存,代理,防火墙,负载均衡器和服务器都在性能上发挥做用)。vue
因此,咱们就必须时刻记住性能优化 —— 从项目开始的时候直到网站最后发布。因此咱们就建立了一个性能优化列表。(但愿是公正的和客观的)2018 前端性能检查表 —— 能够帮助你解决你网站的优化问题,例如,响应时间如何更快,流畅的用户交互和不会流失用户的带宽。java
(你也能够仅下载 2018 前端性能优化清单 的 PDF 版本(0.129 MB)或者下载 Apple Pages 版本(0.236 MB))node
微小的优化对于保持性能也是十分重要的,可是时刻记着咱们必需要有一个明确的目标,可度量的目标将影响整个过程当中的任何决策。这里有几种不一样的模式,而且下面的方法都是从自身角度出发,因此你要尽早确认你本身优化的优先级。react
在不少团队中,前端开发者确切的知道常见的潜在问题是什么,应该用什么样的加载模块来修复。然而,只要开发或者设计那边有一方跟市场的意见不合,性能就不会长时间持续下去。因此,研究用户常见的抱怨的地方,看看提升性能如何帮助解决这些常见问题。webpack
不管是在移动设备上仍是在桌面上都须要运行性能实验并测量结果。 它会创建一个用真实数据为大家公司量身定制的案例研究。此外,要善于使用 WPO Stats 上发布的案例研究和实验中的数据,能够帮助提升你对性能和业务以前潜在关系的认识,以及性能对用户体验和业务度量的影响。仅仅说明绩效是不够的 - 你还须要创建一些可衡量和可追踪的目标来观察性能。
根据 心理学研究,若是你想要用户感受你的网站比你竞争者的网站快,那么你至少须要比它们快 20%。研究你的主要竞争者,收集他们在手机和桌面上的性能,而且设置一个你能超过他们的临界值。要获取准确的结果和目标,首先要研究你的分析结果,看看你的用户都在作什么。你能够模拟第百分之九十的用户经验进行测试,收集数据,而后创建一个 表格,而后再减去 20% 就是你的目标(即 性能预算)。如今你在测试以前就有了一些可度量的数据。
若是你比较在乎预算并尝试去用最小的脚原本得到最快的交互时间,那么你就开始了你的“前端优化之旅”。Lara Hogan 关于如何使用性能预算来处理设计的 指南 能够为设计人员提供有用的指引,而 性能预算计算器 和 Browser Calories 里均可以帮助建立预算(感谢 Karolina Szczur 提供)。
除了性能外,还要考虑对你业务最有利的客户的行为。 设置和讨论关键行动的可接受时间阈值,并设置一个整个团队已经达成一致的 “UX ready”用户时间标记。 大部分状况下,用户的行为涉及许多不一样部门,所以,按照设置的可接受的时间安排会避免不少问题。确保额外的资源和功能的额外成本其余成员都是可见和知晓的。
此外,正如 Patrick Meenan 所建议的那样,最好在设计的过程当中设置一个加载队列而且要知道这些顺序会存在哪些利弊。你须要优先处理更重要的优先级,而且定义一个它们优先级的顺序,你也应该清楚哪些是能够推迟的。理想的状况,该队列也应该反映出 JS 和 CSS 的加载顺序,只要设计好了,那么在构建过程当中处理它们就会很简单了。另外,还须要考虑的是在页面还在加载时页面的“中间状态”(例如,当 web 字体没有加载完时)。
计划计划计划,重要的事情说三遍!若是早期进行优化那么会很容易实现目标,可是没有计划或者没有制定切合实际的、为公司量身定制的业绩目标那么就很难保持性能。
不是全部的度量都同样重要。 研究哪些度量对于你的应用程序最重要:一般,这与你可以以多快的速度开始渲染最重要的像素(以及它们的效果)以及如何为这些渲染像素提供输入最快的响应速度有关。 这能够帮助你为后续的工做提供最佳的优化结果。 无论怎样,不要专一于整个页面加载时间(例如 onLoad
和 DOMContentLoaded
时间),而是要优先按照用户能够感知到的页面加载。 也就是说要关注一组稍微不一样的度量。
首次有内容渲染,视觉完整和可交互时间之间的区别。来自于:@denar90
你能够参考下面这样度量:
Steve Souders 对 每一个度量都进行了详细的解释。在许多状况下,根据你的应用程序的使用场景,可交互时间和输入响应 会是最关键的。但这些度量可能会不一样:例如,对于 Netflix 电视的用户界面来讲,关键输入响应、内存使用和可交互时间更为重要。
为了收集准确的数据,咱们须要尽量全的选择要测试的设备。最好是 Moto G4,或者一款中档的三星设备又或者是一个 Nexus 5X 这样的普通的设备。若是你手边没有设备,能够使用节流 CPU(5× 减速)来限制网速(例如,150 ms 的往返时延,1.5 Mbps 如下和0.7 Mbps 以上)实如今桌面设备上模拟移动设备的体验。最终,切换到常规的 3G,4G 和 Wi-Fi。为了使性能体验的影响更明显,你甚至能够使用 2G 或 一个节流的 3G 网络,以便进行更快的测试。
幸运的是,有不少优秀的选项能够帮助你自动收集数据,并根据这些度量衡量你的网站在一段时间内的性能。 请记住,良好的性能度量是须要被动和主动监控工具的组合:
前者在开发过程当中特别有用,由于它能够在使用产品时持续跟踪。 后者在长期维护很是有用,由于它能够帮助你了解在实际访问站点时发生的性能瓶颈。经过使用导航定时、资源定时、绘图定时、长时间任务等内置的 RUM API,被动和主动性能监视工具一块儿提供应用程序性能的完整画面。 例如,你能够使用PWMetrics,Calibre,SpeedCurve,mPulse,Boomerang 和 Sitespeed.io,这些都是性能监测工具的绝佳选择。
注意:选择浏览器外部的网络级别的限制器老是比较安全的,例如 DevTools 因为实现的方式而存在与 HTTP/2 推送交互的问题(感谢Yoav!)。
为了不误解,要确保你团队里的每一个同事都对清单很熟悉。每一个决策都对性能有影响。项目将极大地受益于前端开发人员正确地将性能价值传达给整个团队。从而使每一个人都对它负责,而不只仅是前端开发人员。根据绩效预算和清单中定义的优先顺序来设计决策。
为了让交互感受起来很顺畅,接口有 100ms 来响应用户的输入。任何比它长的时间,用户都会认为该应用程序很慢。RAIL,一个以用户为中心的性能模型会为你提供健壮的目标。为了让页面达到小于 100ms 的响应,页面必需要在小于 50ms 前最迟将控制权返回给主线程。预计输入延迟时间 告诉咱们,若是咱们能达到这个门槛,在理想状况下,它应该低于 50ms。对于像动画这样性能消耗比较大的地方,最好的作法是,在可以优化的地方,尽可能优化到极致;在不能优化的地方,让性能开销降至最低。
同时,每一帧动画应该要在 16 毫秒内完成,从而达到 60 帧每秒(1秒 ÷ 60 = 16.6 毫秒) —— 最好能够在 10 毫秒完成。由于浏览器须要时间将新框架绘制到屏幕上,你的代码应该在触发 16.6 毫秒之内完成。保持乐观和明智地利用空闲时间。显然,这些目标适用于运行时的性能,而不是加载性能。
虽然这可能很难实现,一个好的最终目标是首次有效渲染低于 1 秒而且速度指标的值低于 1250。由于咱们是以 200 美金为基准的 Android 手机(如 Moto G4)和一个缓慢的 3G 网络上,模拟 400ms 的往返延时和 400kb 的传输速度,因此咱们的目标是可交互时间低于 5s,而且再次访问的时间低于 2s。
请注意,当谈到可交互时间时,最好来区分一下首次交互和连续交互以免对它们之间的误解。前者是在主要内容已经渲染出来后最先出现的点(窗口至少须要 5s,页面才开始响应)。后者是指望页面能够一直进行输入响应的点。
HTML 的前 14~15kb 加载是是最关键的有效载荷块 —— 也是第一次往返(这是在400 ms 往返延时下 1 秒内所获得的)预算中惟一能够交付的部分。通常来讲,为了实现上述目标,咱们必须在关键的文件大小内进行操做。最高预算压缩以后 170 Kb(0.8-1MB解压缩),它已经占用多达 1s (取决于资源类型)来解析和编译。稍微高于这个值是能够的,可是要尽量地下降这些值。
尽管如此,仍是能够提升绑定的规模预算。例如,你能够在浏览器主线程的活动中设置性能预算,例如,在开始渲染前的绘制时间或者跟踪前端 CPU 。像 Calibre,SpeedCurve 和 Bundlesize 这些工具能够帮助你保持你的预算控制,并集成到你的构建过程。
现现在不要太在乎那些很酷的技术栈。根据你的项目使用你的构建工具,不管是 Grunt,Gulp,Webpack,Parcel,仍是工具间的组合。只要你能快速的获得结果,而且保证你的构建过程没问题。那么,你就能够选择该构建工具。
安全的选择是将 渐进式加强 做为前端架构和项目部署的指导原则。首先设计和构建核心体验,而后为有能力的浏览器使用高级特性加强体验,创造 弹性 体验。若是你的网站是在一个网络不佳的而且有个糟糕的显示屏上糟糕的浏览器上运行,速度还很快的话,那么,当它运行在一个快速网络下快速的浏览器的机器上,它只会运行得更快。
有这么多未知因素影响加载 —— 网络、热保护、缓存回收、第三方脚本、解析器阻塞模式、磁盘的读写、IPC jank、插件安装、CPU、硬件和内存限制、L2/L3缓存、RTTS、图像、Web字体加载行为的差别 —— JavaScript 的代价是最大的,web 字体阻塞默认渲染和图片的加载消耗了大量的内存。随着性能瓶颈从 服务器端转移到客户端,做为开发人员,咱们必须更仔细地考虑全部这些未知因素。
在 170kb 的预算中,已经包括了关键路径的 HTML / CSS / JavaScript、路由器、状态管理、实用程序、框架和应用程序逻辑,咱们必须完全检查网络传输成本,解析/编译时间和运行时间来选择咱们的框架。
正如 Seb Markbage 所 指出的,测量框架的启动成本的好方法是首先渲染视图,再删除它,而后再渲染,由于它能够告诉你框架是如何扩展的。
第一次渲染倾向于预热一堆编译迟缓的代码,当它扩展时,更大的分支能够从中受益。第二次渲染基本上是仿效页面上的代码重用是如何随着页面复杂度的增加来影响性能特征。
并非每一个项目都须要框架。事实上,某些项目 能够彻底移除某些框架并从中受益。一旦选择了一个框架,你最少会使用好几年。因此,若是你须要使用它,确保你的选择是通过深思熟虑的 而且 对其彻底了解。在进行选择前,至少要考虑总大小的成本 + 初始解析时间:轻量级的选项像 Preact,Inferno,Vue,Svelte 或者 Polymer 都作得很好。框架的大小基线将为你的应用程序代码定义约束条件。。
请记住,在移动设备上,与台式计算机相比,预计速度会有 4-5 倍的降低。由于移动设备的 GPU、CPU、内存及电池特性都不一样。在手机上的解析时间比桌面设备的要高 36%。因此总在一个最能表明广泛用户的平均的设备上测试。
不一样的框架将会对性能产生不一样的影响,而且须要不一样的优化策略。所以,你必须清楚地了解你所依赖的框架的全部细节。当建立一个 web 应用的时候,参考 PRPL 模式 和 应用程序 shell 体系结构。这个想法很简单: 用最少的代码来将初始路由的交互快速呈现,而后使用 service worker 进行缓存和预缓存资源,而后使用懒加载异步加载所需的路由。
PRPL 表明的是保持推送关键资源,渲染初始路由,预缓存剩余路由和延迟加载必要的剩余路由。
应用程序 shell 是最小的 HTML、CSS 和 JavaScript 驱动的用户界面。
译者注:AMP 即加速移动页面,是一种制做可快速加载的轻量型网页的方法,特别适合于移动设备。
依赖于你的组织优先级和战略,你能够考虑使用谷歌的 AMP 和 Facebook 的 Instant Articles 或者苹果的 Apple News。若是不使用它们,你也能够实现很好的性能,可是 AMP 确实提供了一个免费的内容分发网络(CDN)的性能框架,而 Instant Articles 将提升你在 Facebook 上的知名度和表现。
对于用户而言,这些技术主要的优点是确保性能,因此有时他们更喜欢 AMP-/Apple News/Instant Pages 连接,而不是“常规”和潜在的臃肿页面。对于那些之内容为主的网站,主要处理不少第三方法内容,这些选择极大地加速渲染的时间。
对于站长而言,这些样式在各个平台可发现性而且 加强在搜索引擎中的可见性。你也能够从新使用 AMP 做为你的 PWA 数据源来构建渐进的 Web AMPs。有什么缺点呢?显然,在一个有围墙的区域里,开发者能够创造并维持与内容分离的单独版本,防止 Instant Articles 和 Apple News 没有实际的URLs。(谢谢,Addy,Jeremy)
根据你拥有的动态数据量,你能够将部份内容外包给静态站点生成工具 如 Jekyll、Hexo 生成的静态文件,接着把静态文件推到 CDN 中,最后 CDN 只是提供静态文件的静态版本。因此这样就能够避免发起对数据库的读写请求。你甚至能够选择一个基于 CDN 的静态主机平台,(这样就能够)经过给页面添加可交互组件的方式来丰富你的页面,并以此做为性能提高的标志 (JAMStack)。
请注意,CDN 也是能够托管并卸载(offload)动态内容的,因此我们没有必要把 CDN 的服务范围限定在静态资源。(另外须要你记住的是),无论你的 CDN 是否执行内容压缩(GZip)、内容转换、HTTP/2 传输以及 ESI(一种标记语言,能够用它把网页划分为单独的可缓存的实体)等操做,咱们仍是须要复核上述操做的,这是由于上述操做不只会在 CDN 的 edge 处(服务器最接近用户的地方)聚合页面中的静态以及动态内容,也还会执行其它任务。
你应该知道优先处理什么。运行你全部静态资源(JavaScript、图片、字体、第三方脚本和页面中“昂贵的”模块,好比:轮播图、复杂的图表和多媒体内容),并将它们划分红组。
先搞清楚资源(assets)能够分为几类,大体能够分为:
咱们在“Improving Smashing Magazine's Performance”上发布了一篇文章,上面详细描述了该方法。
虽然这个技术已经很老了,但咱们仍然能够使用 cutting-the-mustard 技术 使传统浏览器使用核心功能并加强对现代浏览器的体验。严格限制加载的资源:优先加载核心功能,而后是提高的,最后是其余的。注意:该技术能够从浏览器版本中推断出设备功能,而如今咱们已经再也不这样作了。
例如:在发展中国家,廉价的安卓手机主要运行 Chrome,他们的内存和 CPU 有限。PRPL 模式 就是一个好的选择。最终,使用 Device Memory Client Hints Header,咱们就可以更可靠地识别出低端设备。如今,只有在 Blink 中才支持 header (Blink 支持client hints)。由于设备存储也有一个在 Chrome 中能够调用的 JavaScript API,一种选择是基于 API 的特性检测,只在不支持的状况下回退到“符合标准”技术(谢谢,Yoav!)。
当咱们处理单页面应用时,在你的页面渲染以前你须要初始化应用。寻找模块和技术加快初始化渲染时间(例如:如何调试 React 性能,以及如何提升 Angular 性能),由于大多数性能问题来自于启动应用程序的初始解析时间。
JavaScript 有成本,但不必定是文件大小影响性能。解析和执行时间的不一样很大程度依赖设备的硬件。在一个普通的手机上(Moto G4),仅解析 1MB (未压缩的)的 JavaScript 大概须要 1.3-1.4 秒,会有 15 - 20% 的时间耗费在手机的解析上。在执行编译过程当中,只是用在 JavaScript 准备平均须要 4 秒,在手机上页面展现其主要内容所需的时间(First Meaningful Paint)须要 11 秒。解释:在低端移动设备上,解析和执行时间能够轻松提升 2 至 5 倍。
Ember 最近作了一个实验,使用二进制模板(binary templates )巧妙的避免解析开销的方式。这些模板不须要解析。(感谢,Leonardo!)
这就是检查每一个 JavaScript 依赖性的关键,像 webpack-bundle-analyzer、Source Map Explorer 和 Bundle Buddy 这样的工具能够帮助你完成这些。度量 JavaScript 解析和编译时间。Etsy 的 DeviceTiming,一个小工具可让你的 JavaScript 测量任何设备或浏览器上的解析和执行时间。重要的是,虽然文件的大小重要,但它不是最重要的。解析和编译时间并非随着脚本大小增长而线性增长。
Webpack Bundle Analyzer visualizes JavaScript dependencies.
使用 ahead-of-time 编译器 来 减轻从客户端 到 服务端的渲染 的开销,所以快速输出有用的结果。最后,考虑使用 Optimize.js 经过包装可快速调用的函数来实现(在app初始时可以)快速载入(尽管,它可能不须要)。
Tree-shaking 是一种清理构建过程的方法经过只加载生产中实际使用的代码并清除 在 Webpack 中 未使用的 import
。使用 Webpack 3 和 Rollup,咱们还能够使用 scope hoisting(做用域提高),scope hoisting
容许工具检测哪些 import
能够被提高或者能够转换成一个内联函数。有了 Webpack 4,你如今能够使用 JSON Tree Shaking。UnCSS 或者 Helium 能够帮助你去删除未使用 CSS 样式。
并且,你须要考虑 如何编写有效的 CSS 选择器 以及 如何避免编写臃肿和开销浪费的样式。你也能够使用 Webpack 缩短类名和在编译时使用独立做用域来 动态地重命名 CSS 类
Code-splitting 是 Webpack 的另外一个特性,可将你的代码分解为按需加载的“块”。并非全部的 JavaScript 都是必须下载、解析和编译的。一旦你在代码中肯定了分割点,Webpack 会处理这些依赖关系和输出文件。在应用发送请求的时候,这样基本上确保初始的下载足够小而且实现按需加载。另外,考虑使用 preload-webpack-plugin 获取代码拆分的路径,而后使用 <link rel="preload">
or <link rel="prefetch">
提示浏览器预加载它们。
在哪里定义分离点?经过追踪哪些 CSS/JavaScript 块被使用和哪些没有被使用。Umar Hansa 解释了你如何使用 Devtools 的 Code Coverage 来实现。
若是你没有使用 Webpack,那么相比于 Browserify 的输出结果, Rollup 的输出更好一些。当使用 Rollup 时,推荐你了解下 Rollupify,它能够将 ECMAScript 2015 modules 转化为一个大的 CommonJS module —— 由于小的模块会有使人惊讶的高性能开销(取决于打包工具和模块加载系统的选择)。
Addy Osmani 的从快速默认:现代加载的最佳实践。幻灯片76。
最后,随着现代浏览器对 ES2015 支持愈来愈好,考虑 使用babel-preset-env
只转换现代浏览器不支持的 ES2015+ 的特性。而后设置两个构建,一个为 ES6 一个为 ES5。咱们能够 使用script type="module"
让具备 ES 模块浏览器支持加载文件,而老的浏览器能够加载传统的 script nomodule
。
对于 loadsh,使用 babel-plugin-lodash
将只加载你仅在源码中使用的模块。这可能会为你节省至关多的 JavaScript 负载。
研究 JavaScript 引擎在用户基础中占的比例,而后探索优化它们的方法。例如,当优化的 V8 引擎是用在 Blink 浏览器,Node.js 运行和 Electron 的时候,对每一个脚本使用脚本流。一旦下载开始,它容许 async
或 defer scripts
在一个单独的后台线程进行解析,所以在某些状况下,提升 10% 的页面加载时间。实际上,在 <head>
中 使用 <script defer>
,以便 浏览器更早地能够发现资源,而后在后台线程中解析它。
Caveat:Opera Mini 不支持 defement 脚本,若是你正在印度和非洲从事开发工做,defer
将会被忽略,致使阻塞渲染直到脚本加载(感谢 Jeremy)!。
渐进引导:使用服务器端呈现得到首次有效绘制,但也包含一些最小必要的 JavaScript 来保持实时交互来接近首次有效绘制。
在两种场景下,咱们的目标应该是创建 渐进引导:使用服务端呈现得到首次有效绘制,并且还要包含一些最小必要的 JavaScript 来保持实时交互来接近首次有效绘制。若是 JavaScript 在首次有效绘制没有获取到,那么浏览器可能会在解析时锁住主线程,编译和执行最新发现的 JavaScript,从而对站点或应用程序的交互性形成限制。
为了不这样作,老是将执行函数分离成一个个,异步任务和可能用到 requestIdleCallback
的地方。考虑 UI 的懒加载部分使用 WebPack 动态 import
支持,避免加载、解析和编译开销直到用户真的须要他们(感谢 Addy!)。
在本质上,交互时间(TTI)告诉咱们导航和交互之间的时间。度量是经过在窗口初始内容呈现后的第一个五秒来定义的,在这个过程当中,JavaScript 任务都不超过 50ms。若是发生超过 50ms 的任务,则从新开始搜索五秒钟的窗口。所以,浏览器首先会假定它达到了交互式,只是切换到冻结状态,最终切换回交互式。
一旦咱们达到交互式,而后,咱们能够按需或等到时间容许,启动应用程序的非必需部分。不幸的是,随着 Paul Lewis 提到的,框架一般没有优先级的概念,所以渐进式引导很难用大多数库和框架实现。若是你有时间和资源,使用该策略能够极大地改善前端性能。
随着全部性能优化的到位,咱们经常没法控制来自业务需求的第三方脚本。第三方脚本的度量不受用户体验的影响,因此,一个单一的脚本经常会以调用使人讨厌的,长长的第三方脚本为结尾,所以,破坏了为性能专门做出的努力。为了控制和减轻这些脚本带来的性能损失,仅异步加载(可能经过 defer)和经过资源提示,如:dns-prefetch
或者 preconnect
加速他们是不足够的。
正如 Yoav Weiss 在他的必须关注第三方脚本的通讯中解释的,在不少状况下,下载资源的这些脚本是动态的。页面负载之间的资源是变化的,所以咱们不知道主机是从哪下载的资源以及这些资源是什么。
这时,咱们有什么选择?考虑 经过一个超时来使用 service workers 下载资源,若是在特定的时间间隔内资源没有响应,返回一个空的响应告知浏览器执行解析页面。你能够记录或者限制那些失败的第三方请求和没有执行特定标准请求。
另外一个选择是创建一个 内容安全策略(CSP) 来限制第三方脚本的影响,好比:不容许下载音频和视频。最好的选择是经过 <iframe>
嵌入脚本使得脚本运行在 iframe 环境中,所以若是没有接入页面 DOM 的权限,在你的域下不能运行任何代码。Iframe 能够 使用 sandbox
属性进一步限制,所以你能够禁止 iframe 的任何功能,好比阻止脚本运行,阻止警告、表单提交、插件、访问顶部导航等等。
例如,它可能必需要容许脚本运行 <iframe sandbox="allow-scripts">
。每个限制均可以经过多种容许值在 'sandbox' 属性中(几乎到处支持)解除,因此将它们限制在容许作的最低限度。考虑使用 Safeframe 和 Intersection Observer;这将使广告嵌入 iframe 的同时仍然调度事件或须要从 DOM 获取信息(例如广告知名度)。注意新的策略如 特征策略)、资源的大小限制、CPU 和带宽优先级限制损害的网络功能和会减慢浏览器的脚本,例如:同步脚本,同步 XHR 请求,document.write 和超时的实现。
要对第三方进行 压力测试,在 DevTools 上自底向上概要地检查页面的性能,测试在请求被阻止或超时后会发生什么状况,对于后者,你能够使用 WebPageTest 的 Blackhole 服务器 72.66.115.13
,你能够在你的 hosts
文件中指定特定的域名。最好是最好是自主主机并使用单个主机名,可是同时生成一个请求映射,当脚本变化时,暴露给第四方调用和检测。
再次检查一遍 expires
,cache-control
,max-age
和其余 HTTP cache 头部都是否设置正确。一般,资源应该是可缓存的,无论是短期的(它们是否极可能改变),仍是无限期的(它们是不是静态的)。 你能够在须要更新的时候,改变它们 URL 中的版本便可。在任何资源上禁止头部 Last-Modified
都会致使一个 If-Modified-Since
条件查询,即便资源在缓存中。与 Etag
同样,即便它在使用中。
使用 Cache-control: immutable
,(实际上是为了解决fingerprinted静态资源的缓存问题而被设计出来的,解决了客户端revalidation问题(截至 2017年12月,在 FireFox,Edge 和 Safari 中支持;只有 FireFox 在 HTTPS 中支持)。你也能够使用 Heroku 的 HTTP 缓存头部,Jake Archibald 的 "Caching Best Practices" ,以及 Ilya Grigorik 的 HTTP caching primer 做为指导。并且,注意不一样的头部,尤为是在关系到 CDN 时,而且注意而且要注意关键头文件,有助于避免在新请求稍有差别时进行额外的验证,但从之前请求标准,并非必要的(感谢,Guy!)。
在 2005 年,Google 推出了 Brotli,一个新的开源无损数据压缩格式,如今已经 被全部的现代浏览器所支持。实际上,Brotli 比 Gzip 和 Deflate 更有效。压缩速度可能会很是慢,取决于设置信息。可是缓慢的压缩过程会提升压缩率,而且仍然能够快速解压。固然,解压缩速度很快。
只有当用户经过 HTTPS 访问网站时,浏览器才会采用。Brotli 如今还不能预装在某些服务器上,并且若是不本身构建 NGINX 和 UBUNTU 的话很难部署。不过这也并不难。实际上,一些 CDN 是支持的,甚至 也能够经过服务器在不支持 CDN 的状况下启用 Brotli。
在最高级别的压缩下,Brotli 的速度会变得很是慢,以致于服务器在等待动态压缩资源时开始发送响应所花费的时间可能会使咱们对文件大小的优化无效。可是,对于静态压缩,高压缩比的设置比较受欢迎 —— (感谢 Jeremy!)
或者,你能够考虑使用 Zopfli 的压缩算法,将数据编码为 Deflate,Gzip 和 Zlib 格式。Zopfli 改进的 Deflate 编码使得任何使用 Gzip 压缩的文件受益,由于这些文件大小比 用Zlib 最强压缩后还要小 3% 到 8%。问题在于压缩文件的时间是原来的大约 80倍。这就是为何虽然 使用 Zopfli 是一个好主意可是变化并不大,文件都须要设计为只压缩一次能够屡次下载的。
比较好的方法是你能够绕过动态压缩静态资源的成本。Brotli 和 Zopfli 均可以用于明文传输 —— HTML,CSS,SVG,JavaScript 等。
有什么方法呢?在最高等级和 Brotli 的 1-4 级动态压缩 HTML 使用 Brotli+Gzip 预压缩静态资源。同时,检查 Brotli 是否支持 CDN,(例如 KeyCDN,CDN77,Fastly)。确保服务器可以使用 Brotli 或 gzip 处理内容。若是你不能安装或者维护服务器上的 Brotli,那么请使用 Zopfli。
尽量经过 srcset
,sizes
和 <picture>
元素使用 响应式图片。也能够经过 <picture>
元素使用 WebP 格式的图像(Chrom,Opera,Firefox soon支持),或者一个 JPEG 的回调(见 Andreas Bovens 的 code snippet)或者经过使用内容协商(使用 Accept
头信息)。
Sketch 自己就支持 WebP 而且 WebP 图像能够经过使用 WebP 插件 从 PhotoShop 中导出。也有其余选择能够使用,若是你使用 WordPress 或者 Joomla,也有能够轻松支持 WebP 的扩展,例如 Optimus 和 Cache Enabler(经过 Cody Arsenault)
你能够仍然使用 client hints,但仍须要得到一些浏览器支持。没有足够的资源支持响应式图片?使用 断点发生器 或者相似 Cloudinary 这样的服务自动优化图片。一样,在许多状况下,只使用 srcset
和 sizes
会有不错的效果。
响应图像断点生成器自动生成图像和标记生成。
如今有一个相当重要着陆页,有一个特定的图片的加载速度很是关键,确保 JPEGs 是渐进式的而且使用 Adept、 mozJPEG (经过操纵扫描级来改善开始渲染时间)或者 Guetzli 压缩,谷歌新的开源编码器重点是可以感官的性能,并借鉴 Zopfli 和 WebP。惟一的 不足 是:处理的时间慢(每百万像素 CPU 一分钟)。至于 png,咱们能够使用 Pingo,和 svgo,对于 SVG 的处理,咱们使用 SVGO 或 SVGOMG
每个图像优化的文章会说明,但始终会提醒要保持矢量资源干净和紧密。确保清理未使用的资源,删除没必要要的元数据,并减小图稿中的路径点数量(从而减小SVG代码)。(感谢,Jeremy!)
到目前为止,这些优化只涵盖了基础知识。 Addy Osmani 已经发布了 一个很是详细的基本图像优化指南,深刻到图像压缩和颜色管理的细节。 例如,您能够模糊图像中没必要要的部分(经过对其应用高斯模糊滤镜)以减少文件大小,最终甚至能够开始移除颜色或将图像变成黑白色,以进一步缩小图像尺寸。 对于背景图像, 从Photoshop 导出的照片质量为 0 到 10% 也是绝对能够接受的。
那么 GIF 图片呢?咱们能够使用 循环的 HTML5 视频,而不是影响渲染性能和带宽的重度 GIF 动画,而使用循环的 HTML5 视频,虽然 <video>
会使得 浏览器的性能很慢,可是与图像不一样的是,浏览器不会预先加载 <video>
内容。咱们也能够使用 Lossy GIF, gifsicle 或者 giflossy 添加有损压缩 GIF。
好 消息: 但愿不久之后咱们能够使用 <img src=".mp4">
来加载视频, 早期的测试代表 img
标签比同等大小的 GIF 显示速度的要快 20 多倍,解析速度要快 7 倍多。
还不够好?那么,你也能够使用 多个背景图像技术 提升图像的感知性能。 记着,减小对比度 和模糊没必要要的细节(或消除颜色)也能够减少文件的大小。 你须要放大一个小照片而不失真?考虑使用 Letsenhance.io
Zach Leatherman 的 字体加载策略综合指南 提供了十几种更好的网页字体发送选项
首先须要问一个问题,你是否能不使用 UI 系统字体。 若是不能够,那么你有很大可能使用 Web 网络字体,会包含字形和额外的功能以及用不到的加粗。你能够向字体设计公司获取网络字体子集或子集,若是您使用的是开源字体(例如,经过仅包含带有某些特殊的重音字形的拉丁语),则能够只选择部分 Web 字体来减小其文件大小。
WOFF2 的支持很是好,对于不支持WOFF2的浏览器,你能够使用 WOFF 和 OTF 做为不支持它的浏览器的备选。另外,从 Zach Leatherman 的《字体加载策略综合指南》(代码片断也能够做为 Web字体加载片断)中选择一种策略,并使用服务器缓存持久地缓存字体。是否是感受小有成就?Pixel Ambacht 有一个 快速教程和案例研究,让你的字体按顺序排列。
若是你没法从你的服务器拿到字体并依赖于第三方主机,请确保使用 字体加载事件(或对不支持它的浏览器使用 Web字体加载器)FOUT 要优于 FOIT; 当即开始渲染文本,并异步加载字体 —— 也能够使用 loadCSS。 你也能够 摆脱本地安装的操做系统字体,也能够使用 可变的 字体。
怎么才能是一个无漏洞的字体加载策略? 从 font-display
开始,而后到 Font Loading API,而后到 Bram Stein 的 Font Face Observer(感谢 Jeremy!)若是你有兴趣从用户的角度来衡量字体加载的性能, Andreas Marschke 探索了 使用 Font API 和 UserTiming API 进行性能跟踪
此外,不要忘记包含 font-display:optional
描述符来提供弹性和快速的字体回退,unicode-range
将大字体分解成更小的语言特定的字体,以及Monica Dinculescu 的字体样式匹配器 用来解决因为两种字体之间的大小差别,最大限度地减小了布局上的震动的问题。
当用户请求页面时,浏览器获取 HTML 并构造 DOM,而后获取 CSS 并构造 CSSOM,而后经过匹配 DOM 和 CSSOM 生成一个渲染树。若是有任何的 JavaScript 须要解决,浏览器将不会开始渲染页面,直到 JavaScript 解决完毕,这样就会延迟渲染。 做为开发人员,咱们必须明确告诉浏览器不要等待并当即开始渲染页面。 为脚本执行此操做的方法是使用 HTML 中的 defer
和 async
属性。
事实证实,咱们 应该把 async
改成 defer
(由于 ie9 及如下不支持 async)。 另外,如上所述,限制第三方库和脚本的影响,特别是使用社交共享按钮和嵌入的 <iframe>
嵌入(如地图)。 大小限制 有助于防止 JavaScript 库过大:若是您不当心添加了大量依赖项,该工具将通知你并抛出错误。 你能够使用 静态社交分享按钮(如经过 SSBG )和 静态连接 来代替交互式地图。
若是您须要延迟加载图片、视频、广告脚本、A/B 测试脚本或任何其余资源,则能够使用 Intersection Observer API,它提供了一种方法异步观察目标元素与 祖先元素或顶层文档的视口。基本上,你须要建立一个新的 IntersectionObserver 对象,它接收一个回调函数和一组选项。 而后咱们添加一个目标来观察。
当目标变得可见或不可见时执行回调函数,因此当它拦截视口时,能够在元素变得可见以前开始采起一些行动。 事实上,咱们能够精确地控制观察者的回调什么时候被调用,使用 rootMargin
和 threshold
(一个数字或者一个数字数组来表示目标可见度的百分比)。Alejandro Garcia Anglada 发表了一个 简单的教程 关于如何实际实施的教程。
你甚至能够经过向你的网页添加 渐进式图片加载 来将其提高到新的水平。 与 Facebook,Pinterest 和 Medium 相似,你能够首先加载低质量或模糊的图像,而后在页面继续加载时,使用 Guy Podjarny 提出的 LQIP (Low Quality Image Placeholders) technique(低质量图像占位符)技术替换它们的清晰版本。(能够参考知乎)
若是技术提升了用户体验,观点就不同了,但它确定会提升第一次有意义的绘画的时间。咱们甚至能够经过使用 SQIP 建立图像的低质量版本做为 SVG 占位符来实现自动化。 这些占位符能够嵌入 HTML 中,由于它们天然能够用文本压缩方法压缩。 Dean Hume 在他的文章中 描述了 如何使用 Intersection Observer 来实现这种技术。
浏览器支持程度如何呢?Decent,与 Chrome,火狐,Edge 和 Samsung Internet 已经支持了。 WebKit 目前 正在开发中。若是浏览器不支持呢? 若是不支持Intersection Observer,咱们仍然能够 延迟加载 一个 polyfill 或当即加载图像。甚至还有一个 library。
一般,咱们会使用懒加载来处理全部代价较大的组件,如字体,JavaScript,轮播,视频和 iframe。 你甚至能够根据网络质量调整内容服务。网络信息 API,特别是 navigator.connection.effectiveType
(Chrome 62+)使用 RTT 和下行链路值来更准确地表示链接和用户能够处理的数据。 您能够使用它来彻底删除视频自动播放,背景图片或 Web 字体,以便链接速度太慢。
为确保浏览器尽快开始渲染页面,一般 会收集开始渲染页面的第一个可见部分所需的全部 CSS(称为 “关键CSS” 或 “首屏 CSS”)并将其内联添加到页面的 <head>
中,从而减小往返。 因为在慢启动阶段交换包的大小有限,因此关键 CSS 的预算大约是 14 KB。
若是超出这个范围,浏览器将须要额外往返取得更多样式。 CriticalCSS 和 Critical 能够作到这一点。 你可能须要为你使用的每一个模板执行此操做。 若是可能的话,考虑使用 Filament Group 使用的 条件内联方法。
使用 HTTP/2,关键 CSS 能够存储在一个单独的 CSS 文件中,并经过 服务器推送 来传递,而不会增大 HTML 的大小。 问题在于,服务器推送是很 麻烦,由于浏览器中存在许多问题和竞争条件。 它一直不被支持,并有一些缓存问题(参见 [Hooman Beheshti介绍的文章](Hooman Beheshti's presentation) 114 页内容)。事实上,这种影响多是 负面的,会使网络缓冲区膨胀,从而阻止文档中的真实帧被传送。 并且,因为 TCP 启动缓慢,彷佛服务器推送在热链接上 更加有效。
即便使用 HTTP/1,将关键 CSS 放在根目录上的单独文件中也是有 好处的,有时甚至比缓存和内联更为有效。 Chrome 请求这个页面的时候会再发送一个 HTTP 链接到根目录,从而不须要 TCP 链接来获取这个 CSS(感谢 Philip!)
须要注意的一点是:和 preload
不一样的是,preload
能够触发来自任何域的预加载,而你只能从你本身的域或你所受权的域中推送资源。 一旦服务器获得来自客户端的第一个请求,就能够启动它。 服务器将资源压入缓存,并在链接终止时被删除。 可是,因为能够在多个选项卡之间重复使用 HTTP/2 链接,因此推送的资源也能够被来自其余选项卡的请求声明(感谢 Inian!)。
目前,服务器并无一个简单的方法得知被推送的资源 是否已经存在于用户的缓存中,所以每一个用户的访问都会继续推送资源。所以,您可能须要建立一个 缓存监测 HTTP/2 服务器推送机制。若是被提取,您能够尝试从缓存中获取它们,这样能够避免再次推送。
但请记住,新的 cache-digest
规范 无需手动创建这样的 “缓存感知” 的服务器,基本上在 HTTP/2 中声明的一个新的帧类型就能够表达该主机的内容。所以,它对于 CDN 也是特别有用的。
对于动态内容,当服务器须要一些时间来生成响应时,浏览器没法发出任何请求,由于它不知道页面可能引用的任何子资源。 在这种状况下,咱们能够预热链接并增长 TCP 拥塞窗口大小,以便未来的请求能够更快地完成。 并且,全部内联配置对于服务器推送都是较好的选择。事实上,Inian Parameshwaran 对 HTTP/2 Push 和 HTTP Preload 进行了比较 深刻的研究,内容很不错,其中包含了您可能须要的全部细节。服务器究竟是推送仍是不推送呢?你能够阅读一下 Colin Bendell 的 Should I Push?。
底线:正如 Sam Saccone 所说,preload
有利于将资源的开始下载时间更接近初始请求, 而服务器推送是一个完整的 RTT(或 更多,这取决于您的服务器反应时间 —— 若是你有一个服务器能够防止没必要要的推送。
你使用 流响应 吗?经过流,在初始导航请求中呈现的 HTML 能够充分利用浏览器的流式 HTML 解析器。
streams 常常被遗忘和忽略,它提供了异步读取或写入数据块的接口,在任何给定的时间内,只有一部分数据可能在内存中可用。 基本上,只要第一个数据块可用,它们就容许原始请求的页面开始处理响应,并使用针对流进行优化的解析器逐步显示内容。
咱们能够从多个来源建立一个流。例如,您可让服务器构建一个壳子来自于缓存,内容来自网络的流,而不是提供一个空的 UI 外壳并让它填充它。 正如 Jeff Posnick 指出的,若是您的 web 应用程序由 CMS 提供支持的,那么服务器渲染 HTML 是经过将部分模板拼接在一块儿来呈现的,该模型将直接转换为使用流式响应,而模板逻辑将从服务器复制而不是你的服务器。Jake Archibald 的 The Year of Web Streams 文章重点介绍了如何构建它。对于性能的提高是很是明显的。
流式传输整个 HTML 响应的一个重要优势是,在初始导航请求期间呈现的 HTML 能够充分利用浏览器的流式 HTML 解析器。 在页面加载以后插入到文档中的 HTML 块(与经过 JavaScript 填充的内容同样常见)没法利用此优化。
浏览器支持程度如何呢? 详情请看这里 Chrome 52+、Firefox 5七、Safari 和 Edge 支持此 API 而且服务器已经支持全部的 现代浏览器.
Save-Data
存储数据吗?特别是在新兴市场工做时,你可能须要考虑优化用户选择节省数据的体验。 Save-Data 客户端提示请求头 容许咱们为成本和性能受限的用户定制应用程序和有效载荷。 实际上,您能够将 高 DPI 图像的请求重写为低 DPI 图像,删除网页字体和花哨的特效,关闭视频自动播放,服务器推送,甚至更改提供标记的方式。
该头部目前仅支持 Chromium,Android 版 Chrome 或 桌面设备上的 Data Saver 扩展。最后,你还能够使用 service worker 和 Network Information API 来提供基于网络类型的低/高分辨率的图像。
使用 资源提示 来节约时间,如 dns-prefetch
(在后台执行 DNS 查询),preconnect
(告诉浏览器在后台进行链接握手(DNS, TCP, TLS)),prefetch
(告诉浏览器请求一个资源) 和 preload
(预先获取资源而不执行他们)。
最近,咱们至少会使用 preconnect
和 dns-prefetch
,咱们会当心使用 prefetch
和 preload
;前者只能在你很是肯定用户后续须要什么资源的状况下使用(相似于采购渠道)。注意,prerender
已被弃用,再也不被支持。
请注意,即便使用 preconnect
和 dns-prefetch
,浏览器也会对它将并行查找或链接的主机数量进行限制,所以最好是将它们根据优先级进行排序(感谢 Philip!)。
事实上,使用资源提示多是最简单的提升性能的方法,它确实颇有效。何时该使用呢?Addy Osmani 已经作了解释,咱们应该预加载肯定将在当前页面中使用的资源。预获取可能用于将来页面的资源,例如用户还没有访问的页面所需的 Webpack 包。
Addy 的关于 Chrome 中加载优先级的文章展现了 Chrome 是如何精确地解析资源提示的,所以一旦你决定哪些资源对页面渲染比较重要,你就能够给它们赋予比较高的优先级。你能够在 Chrome DevTools 网络请求表格(或者 Safari Technology Preview)中启动“priority”列来查看你的请求的优先级。
DevTools 中的 "Priority" 列。图片来源于:Ben Schwarz,重要的请求
例如,因为字体一般是页面上的重要资源,因此最好使用 preload
请求浏览器下载字体。你也能够 动态加载 JavaScript ,从而有效的执行延迟加载。一样的,由于 <link rel="preload">
接收一个 media
的属性,你能够基于 @media
查询规则来有选择性地优先加载资源。
须要注意的一些问题是:preload
能够 将资源的下载时间移到请求开始时,可是这些缓存在内存中的预先加载的资源是绑定在所发送请求的页面上,也就是说预先加载的请求不能被页面所共享。并且,preload
与 HTTP 缓存配合得也很好:若是缓存命中则不会发送网络请求。
所以,它对后发现的资源也很是有用,如:经过 background-image 加载的一幅 hero image,内联关键 CSS (或 JavaScript),并预先加载其余 CSS (或 JavaScript)。此外,只有当浏览器从服务器接收 HTML,而且前面的解析器找到了 preload
标签后,preload
标签才能够启动预加载。因为咱们不等待浏览器解析 HTML 以启动请求,因此经过 HTTP 头进行预加载要快一些。早期提示将有助于进一步,在发送 HTML 响应标头以前启动预加载。
请注意:若是你正在使用 preload
,as
必须定义不然什么都不会加载,还有,预加载字体时若是没有 crossorigin
属性将会获取两次
使用 CSS containment 隔离昂贵的组件 - 例如,限制浏览器样式、用于非画布导航的布局和绘画工做,第三方组件的范围。确保在滚动页面没有延迟,或者当一个元素进行动画时,持续地达到每秒 60 帧。若是这是不可能的,那么至少要使每秒帧数持续保持在 60 到 15 的范围。使用 CSS 的 will-change
通知浏览器哪一个元素的哪一个属性将要发生变化。
此外,评估运行时渲染性能(例如,使用 DevTools)。能够经过学习 Paul Lewis 免费的关于浏览器渲染优化的 Udacity 课程和 Emily Hayman 的文章优化网页动画和交互来入门。
一样,咱们有 Sergey Chikuyonok 这篇文章关于如何正确使用 GPU 动画。注意:对 GPU-composited 层的更改是代价最小的,若是你能经过“不透明”和“变形”来触发合成,那么你就是在正确的道路上。
组件以何种顺序显示在页面上以及咱们如何给浏览器提供资源当然重要,可是咱们一样也不能低估了感知性能的角色。这一律念涉及到等待的心理学,主要是让用户在其余事情发生时保持忙碌。这就涉及到了感知管理,优先开始,提早完成和宽容管理。
这一切意味着什么?在加载资源时,咱们能够尝试始终领先于客户一步,因此将不少处理放置到后台,响应会很迅速。让客户参与进来,咱们能够用骨架屏幕(实例演示),而不是当没有更多优化可作时、用加载指示,添加一些动画/过渡欺骗用户体验。
在谷歌提出向更安全的网页进军以及Chrome 会把(全部使用 HTTP 的)网页认定为“不安全”的大环境下,迁移到HTTP/2是不可避免的。HTTP/2 从目前来看支持得很是好,而且,在某些场景下,使用 HTTP/2 会让你大力出奇迹。一旦运行在 HTTPS 上,你至少可以在 service workers 和 server push 方面得到显著的性能提高。
最终,谷歌计划将全部 HTTP 页面标记为不安全的,并将有问题的 HTTPS 的 HTTP 安全指示器更改成红色三角形。(图片来源)
最耗时的任务将是迁移到 HTTPS,取决于你的 HTTP/1.1 用户基础有多大(即便用旧版操做系统或浏览器的用户),你将不得不为旧版的浏览器性能优化发送不一样的构建版本,这须要你采用不一样的构建流程。注意:开始迁移和新的构建过程可能会很棘手,并且耗费时间。接下来所讲的内容,都是针对以前切过 HTTP/2 环境或者如今正准备切 HTTP/2 环境(的读者)来展开的。
再次强调一下,须要对现阶段正如何提供在你开始使用 HTTP/2 请求资源以前,须要搞清楚你之前是如何请求资源的。另外须要你在载入大模块以及并行载入小模块之间找到一个平衡点。 。最终,仍然是最好的请求就是没有请求,然而咱们的目标是在快速传输资源和缓存之间找到一个好的平衡点。
一方面,你可能想要避免合并全部资源,而是把整个界面分解成许多小模块,而后在构建过程当中压缩这些小模块,最后经过 “scount” approach 的方法引用和并行加载这些小模块。这样的话,一个文件的更改就不须要从新下载整个样式表或 JavaScript。这样还能够 最小化解析时间 ,并将单个页面的负荷保持在较低的水平。
另外一方面,打包仍然很重要。首先,压缩将获益。在压缩大文件的过程当中,借助目录重用的特色,达到优化性能的目的,而小的单独的文件则不会。有标准的工做来解决这个问题,但如今还远远不够。其次,浏览器还没有为这种工做流优化。例如,Chrome 将触发进程间通讯(IPCs),与资源的数量成线性关系,所以页面中若是包含数以百计的资源将会形成浏览器性能损失。
为了得到使用 HTTP/2 最好的效果,能够考虑使用渐进地加载 CSS,正如 Chrome 的 Jake Archibald 所推荐的。
你能够尝试渐进地加载 CSS。显然,经过这样作,这种作法不利于 HTTP/1.1 的用户,所以在部署 HTTP/2 的过程当中,你可能须要针对不一样的浏览器建立并发送该浏览器支持的 HTTP 协议的报头,这还只是部署过程当中稍微复杂的地方。你能够使用 HTTP/2 的 connection coalescing,可让你在 HTTP/2 环境使用域名共享)来避开这些,但在实践中实现这一目标是很困难的。
怎么作呢?若是你运行在 HTTP/2 之上,发送 6-10 个包是个理想的折中(对旧版浏览器也不会太差)。对于你本身的网站,你能够经过实验和测量来找到最佳的折中。
不一样的服务和 CDNs 可能对 HTTP/2 的支持状况不同,可是能够使用TLS 来查看你的可选服务,或者快速的查看你的服务的性能以及你想要其支持的特性。 TLS工具功能以下:
Is TLS Fast Yet? 容许你检查你转换到 HTTP /2 时你的服务器和 CDN 的选项。
经过在你的服务上启动 OCSP stapling,你能够加速 TLS 握手。在线证书状态协议(OCSP)的提出是为了替代证书注销列表(CRL)协议。两个协议都是用于检查一个 SSL 证书是否已被撤回。可是,OCSP 协议不须要浏览器花时间下载而后在列表中搜索认证信息,所以减小了握手时间。
由于 IPv4 即将用完以及主要的移动网络正在迅速采用 IPv6(美国已经达到50% 的 IPv6 使用阈值),将你的 DNS 更新到 IPv6 以应对将来是一个好的想法。只要确保在网络上提供双栈支持,就可让 IPv6 和 IPv4 同时运行。毕竟,IPv6 不是向后兼容的。研究显示,也是正由于 IPv6 自带 NDP 以及路由优化,因此才可以让网站的载入速度提高10%到15%。
若是你使用 HTTP/2,请再次检查,确保您的服务针对 HTTP 响应头部实现 HPACK 压缩以减小没必要要的开销。因为 HTTP/2 服务相对较新,它们可能不彻底支持该规范,HPACK 就是一个例子。能够使用 H2spec 这个伟大的(若是技术上很详细)工具来检查。HPACK做品。
H2spec (View large version) (Image source)
全部实现了 HTTP/2 的浏览器都在 TLS 上运行的大背景下,若是你遇到如下问题:
那么,你须要
没有什么网络性能优化能快过用户机器上的本地缓存。若是你的网站运行在 HTTPS 上,那么请参考使用 “Service Workers 的实用指南”。使用 service worker 中缓存静态资源并存储离线资源(甚至离线页面)的目的,并且还会教你如何从用户的设备里面拿数据,也就是说,你如今是不须要经过网络的方式去请求以前的数据。同时,参考 Jake 的 Offline Cookbook 和 Udacity 免费课程“离线 Web 应用程序”。浏览器支持?如上所述,它获得了普遍支持 (Chrome、Firefox、Safari TP、Samsung Internet、Edge 17+),但无论怎么说,它都是网络。它有助于提升性能吗?是的,Service Workers确实会提高性能。
在 Chrome 和 Firefox 中进行测试是不够的。你应该去了解你的网站在代理浏览器和旧版浏览器中是如何工做的。例如,UC 浏览器和 Opera Mini,这些浏览器 在亚洲有大量的市场份额 (达到 35%)。在你感兴趣的国家测量平均网络速度从而避免在将来发现“大惊喜”。测试网络节流,并仿真一个高 DPI 设备。BrowserStack 很不错,但也要在实际设备上测试。
k6 可让你像写单元测试同样编写性能测试用例。
有一个WebPagetest私人的实例老是有利于快速和无限的测试。可是,一个带有自动警报的连续监视工具将会给您提供更详细的性能描述。设置您本身的用户计时标记来度量和监视特定的业务度量。同时,考虑添加自动化性能回归警报来监控随着时间而发生的变化。
使用 RUM 解决方案来监视性能随时间的变化。对于自动化的类单元测试的负载测试工具,您能够使用 k6 脚本 API。此外,能够了解下 SpeedTracker、Lighthouse 和 Calibre。
这个列表很是全面,完成全部的优化可能须要很长时间。因此,若是你只有一个小时的时间来进行重大的改进,你会怎么作?让咱们把这一切归结为10个低挂的水果。显然,在你开始以前和完成以后,测量结果,包括开始渲染时间以及在 3G 和电缆链接下的速度指数。
测量实际环境的体验并设定适当的目标。一个好的目标是:第一次有意义的绘制 < 1 s,速度指数 < 1250,在慢速的 3G 网络上的交互 < 5s,对于重复访问,TTI < 2s。优化渲染开始时间和交互时间。
为您的主模板准备关键的 CSS,并将其包含在页面的 <head>
中。(你的预算是 14 KB)。对于 CSS/JS,文件大小不超过 170 KB gzipped(解压后 0.8-1 MB)。
延迟加载尽量多的脚本,包括您本身的和第三方的脚本——特别是社交媒体按钮、视频播放器和耗时的 JavaScript 脚本。
添加资源提示,使用 dns-lookup
、preconnect
、prefetch
和 preload
加速传输。
分离 web 字体,并以异步方式加载它们(或切换到系统字体)。
优化图像,并在重要页面(例如登陆页面)中考虑使用 WebP。
检查 HTTP 缓存头和安全头是否设置正确。
在服务器上启用 Brotli 或 Zopfli 压缩。(若是作不到,不要忘记启用 Gzip 压缩。)
若是 HTTP/2 可用,启用 HPACK 压缩并开启混合内容警告监控。若是您正在运行 LTS,也能够启用 OCSP stapling。
在 service worker 缓存中尽量多的缓存资产,如字体、样式、JavaScript 和图像。
记住了这个清单,您就已经为任何类型的前端性能项目作好了准备。请随意下载该清单的打印版PDF,以及一个可编辑的苹果页面文档,以定制您须要的清单:
若是你须要其余选择,你也能够参考 Rublic 的前端清单和 Jon Yablonski 的“设计师的 Web 性能清单”。
一些优化可能超出了您的工做或预算范围,或者因为须要处理遗留代码而显得过分滥用。没问题!使用这个清单做为一个通用(而且但愿是全面的)指南,并建立适用于你的环境的你本身的问题清单。但最重要的是,测试和权衡您本身的项目,以在优化前肯定问题。祝你们 2018 年的性能大涨!
很是感谢 Guy Podjarny, Yoav Weiss, Addy Osmani, Artem Denysov, Denys Mishunov, Ilya Pukhalski, Jeremy Wagner, Colin Bendell, Mark Zeman, Patrick Meenan, Leonardo Losoviz, Andy Davies, Rachel Andrew, Anselm Hannemann, Patrick Hamann, Andy Davies, Tim Kadlec, Rey Bango, Matthias Ott, Mariana Peralta, Philipp Tellis, Ryan Townsend, Mohamed Hussain S H, Jacob Groß, Tim Swalling, Bob Visser, Kev Adamson, Aleksey Kulikov and Rodney Rehm 对这篇文章的校对,一样也感谢咱们出色的社区,分享了他们在性能优化工做中学习到的技术和经验,供你们使用。大家真正的很是了不得!