[译] 2018 前端性能优化清单 - 第 2 部分

2018 前端性能优化清单 - 第 2 部分

下面是前端性能问题的概述,你能够参考以确保流畅的阅读本文。javascript


  1. 你会在你的项目中使用 AMP 和 Instant Articles 么?

依赖于你的组织优先性和战略性,你可能想考虑使用谷歌的 AMP 和 Facebook 的 Instant Articles 或者苹果的 Apple News。没有它们,你能够实现很好的性能,可是 AMP 确实提供了一个免费的内容分发网络(CDN)的性能框架,而 Instant Articles 将提升你在 Facebook 上的知名度和表现。css

对于用户而言,这些技术主要的优点是确保性能,可是有时他们宁愿喜欢 AMP-/Apple News/Instant Pages 链路,也不肯是“常规”和潜在的臃肿页面。对于之内容为主的网站,主要处理不少第三方法内容,这些选择极大地加速渲染的时间。html

对于网站的全部者而言优点是明显的:在各个平台规范的可发现性和增长搜索引擎的可见性。你也能够经过把 AMP 做为你的 PWA 数据源来构建渐进加强的 Web 体验。缺点?显然,在一个有围墙的区域里,开发者能够创造并维持其内容的单独版本,防止 Instant Articles 和 Apple News 没有实际的URLs。(谢谢 Addy,Jeremy前端

  1. 明智地选择你的 CDN

根据你拥有的动态数据量,你能够将部份内容外包给静态站点生成器,将其放在 CDN 中并从中提供一个静态版本。所以能够避免数据的请求。你甚至能够选择一个基于 CDN 的静态主机平台,将交互组件做为加强来充实你的页面 (jamstack)。java

注意,CDN 也能够服务(卸载)动态内容。所以,限制你的 CDN 到静态资源是没必要要的。仔细检查你的 CDN 是否进行压缩和转换(好比:图像优化方面的格式,压缩和调整边缘的大小),智能 HTTP/2 交付,边侧包含,在 CDN 边缘组装页面的静态和动态部分(好比:离用户最近的服务端),和其余任务。node

构建优化

  1. 分清轻重缓急

知道你应该优先处理什么是个好主意。管理你全部资产的清单(JavaScript,图片,字体,第三方脚本和页面中“昂贵的”模块,好比:轮播图,复杂的图表和多媒体内容),并将它们划分红组。react

创建电子表格。针对传统的浏览器,定义基本的_核心_体验(好比:彻底可访问的核心内容),针对多功能浏览器_提高_体验(好比:丰富多彩的,完美的体验)和其余的(不是绝对须要并且能够被延迟加载的资源,如 Web 字体、没必要要的样式、旋转木马脚本、视频播放器、社交媒体按钮、大型图像。)。咱们在“Improving Smashing Magazine's Performance”发布了一篇文章,上面详细描述了该方法。android

  1. 考虑使用“cutting-the-mustard”模式

虽然很老,但咱们仍然可使用 cutting-the-mustard 技术将核心经验带到传统浏览器并加强对现代浏览器的体验。严格要求加载的资源:优先加载核心传统的,而后是提高的,最后是其余的。该技术从浏览器版本中演变成了设备功能,这已经不是咱们如今能作的事了。webpack

例如:在发展中国家,廉价的安卓手机主要运行 Chrome,尽管他们的内存和 CPU 有限。这就是 PRPL 模式能够做为一个好的选择。所以,使用设备内存客户端提示头,咱们将可以更可靠地针对低端设备。在写做的过程当中,只有在 Blink 中才支持 header(Blink 支持客户端提示)。由于设备存储也有一个在 Chrome 中能够调用的 JavaScript API,一种选择是基于 API 的特性检测,只在不支持的状况下回退到 “符合标准”技术(谢谢Yoav!)。ios

  1. 解析 JavaScript 的代价很大,应保持其较小

但咱们处理单页面应用时,在你能够渲染页面时,你须要一些时间来初始化 app。寻找模块和技术加快初始化渲染时间(例如:这里是如何调试 React 性能,以及如何提升 Angular 性能),由于大多数性能问题来自于启动应用程序的初始解析时间。

JavaScript 有成本,但不必定是文件大小会影响性能。解析和执行时间的不一样很大程度依赖设备的硬件。在一个普通的手机上(Moto G4),仅解析 1MB (未压缩的)的 JavaScript 大概须要 1.3-1.4 秒,会有 15 - 20% 的时间耗费在手机的解析上。在执行编译过程当中,只是用在JavaScript准备平均须要 4 秒,在手机上绘排须要 11 秒。解释:在低端移动设备上,解析和执行时间能够轻松提升 2 至 5 倍

Ember 最近推出了一个实验,一种使用二进制模板巧妙的避免解析开销的方式。这些模板不须要解析。(感谢Leonardo!

这就是检查每一个 JavaScript 依赖性的关键,工具像 webpack-bundle-analyzerSource Map ExplorerBundle Buddy 能够帮助你完成这些。度量 JavaScript 解析和编译时间。Etsy 的 DeviceTiming,一个小工具容许您指示 JavaScript 在任何设备或浏览器上测量解析和执行时间。重要的是,虽然大小重要,但它不是一切。解析和编译时间并非随着脚本大小增长而线性增长

<figure class="video-container"><iframe src="https://player.vimeo.com/video/249525818" width="640" height="384" frameborder="0" webkitallowfullscreen="" mozallowfullscreen="" allowfullscreen=""></iframe>
复制代码

Webpack Bundle Analyzer visualizes JavaScript dependencies.

  1. 你使用预编译器么?

使用预编译器减轻从客户端服务端的渲染的开销,所以快速输出有用的结果。最后,考虑使用 Optimize.js 更快的加载,用快速地调用的函数(尽管,它可能不须要)。

  1. 你使用 tree-shaking,scope hoisting,code-splitting 么

Tree-shaking 是一种经过只加载生产中确实被使用的代码和在 Webpack 中清除无用部分,来整理你构建过程的方法。使用 Webpack 3 和 Rollup,咱们还能够提高做用域容许工具检测 import 连接以及能够转换成一个内联函数,不影响代码。有了 Webpack 4,你如今可使用 JSON Tree ShakingUnCSS or Helium 能够帮助你去删除未使用 CSS 样式。

并且,你想考虑学习如何编写有效的 CSS 选择器以及如何避免臃肿和开销浪费的样式。感受好像超越了这个?你也可使用 Webpack 缩短类名和在编译时使用做用域孤立来动态地重命名 CSS 类名

Code-splitting 是另外一种 Webpack 特性,能够基于“chunks”分割你的代码而后按需加载这些代码块。并非全部的 JavaScript 必须下载,解析和编译的。一旦在你的代码中肯定了分割点,Webpack 会全权负责这些依赖关系和输出文件。在应用发送请求的时候,这样基本上确保初始的下载足够小而且实现按需加载。另外,考虑使用 preload-webpack-plugin 获取代码拆分的路径,而后使用 <link rel="preload"> or <link rel="prefetch"> 提示浏览器预加载它们。

在哪里定义分离点?经过追踪使用哪些 CSS/JavaScript 块和哪些没有使用。Umar Hansa 解释了你如何可使用 Devtools 代码覆盖率来实现。

若是你没有使用 Webpack,值得注意的是相比于 Browserify 输出结果 Rollup 展示的更加优秀。当使用 Rollup 时,咱们会想要查看 Rollupify,它能够转化 ECMAScript 2015 modules 为一个大的 CommonJS module ——由于取决于打包工具和模块加载系统的选择,小的模块会有使人惊讶的高性能开销

Addy Osmani 的'默认快速:现代负载最佳实践'

Addy Osmani 的从[快速默认:现代加载的最佳实践](https://speakerdeck.com/addyosmani/fast-by-default-modern-loading-best-practices)。幻灯片76。

最后,随着现代浏览器对 ES2015 支持愈来愈好,考虑使用babel-preset-env 只有 transpile ES2015+ 特点不支持现代浏览器的目标。而后设置两个构建,一个在 ES6 一个在 ES5。咱们能够使用script type="module"让具备 ES 模块浏览器支持加载文件,而老的浏览器能够加载传统的创建script nomodule

对于 loadsh,使用 babel-plugin-lodash将会加载你仅仅在源码中使用的。这样将会很大程度减轻 JavaScript 的负载。

  1. 利用目标 JavaScript 引擎的优化。

研究 JavaScript 引擎在用户基础中占主导地位,而后探索优化它们的方法。例如,当优化的 V8 引擎是用在 Blink 浏览器,Node.js 运行和电子,对每一个脚本充分利用脚本流。一旦下载开始,它容许 asyncdefer scripts 在一个单独的后台线程进行解析,所以在某些状况下,提升页面加载时间达 10%。实际上,在 <head>使用 <脚本延迟>,以至于浏览器更早地能够发现资源,而后在后台线程中解析它。

CaveatOpera Mini 不支持 defement 脚本,若是你正在为印度和非洲开发,defer 将会被忽略,致使阻塞渲染直到脚本已经评估了(感谢 Jeremy)!_。

渐进引导

渐进引导:使用服务器端呈现得到第一个快速的有意义的绘排,并且还要包含一些最小必要的 JavaScript 来保持实时交互来接近第一次的绘排。

  1. 客户端渲染或者服务端渲染?

在两种场景下,咱们的目标应该是创建渐进引导:使用服务器端呈现得到第一个快速的有意义的绘排,并且还要包含一些最小必要的 JavaScript 来保持实时交互来接近第一次的绘排。若是 JavaScript 在第一次绘排没有获取到,那么浏览器可能会在解析时锁住主线程,编译和执行最新发现的 JavaScript,所以限制互动的网站或应用程序

为了不这样作,老是将执行函数分离成一个个,异步任务和可能用到 requestIdleCallback的地方。考虑 UI 的懒加载部分使用 WebPack 动态 import 支持,避免加载,解析,和编译开销直到用户真的须要他们(感谢 Addy!)。

在本质上,交互时间(TTI)告诉咱们导航和交互之间的时间长度。度量是经过在初始内容呈现后的第一个五秒窗口来定义的,在这个过程当中,JavaScript 任务没有操做 50ms 的。若是发生超过 50ms 的任务,寻找一个五秒的窗口从新开始。所以,浏览器首先会假定它达到了交互式,只是切换到冻结状态,最终切换回交互式。

一旦咱们达到交互式,而后,咱们能够按需或随时间所容许的,启动应用程序的非必需部分。不幸的是,随着 Paul Lewis 提到的,框架一般没有优先出现的概念能够向开发人员展现,所以渐进式引导很难用大多数库和框架实现。若是你有时间和资源,使用该策略能够极大地改善前端性能。

  1. 你限制第三方脚本的影响么?

尽管全部的性能获得很好地优化,咱们不能控制来自商业需求的第三方脚本。第三方脚本度量不受终端用户体验的影响,因此,一个单一的脚本经常会以调用使人讨厌的,长长的第三方脚本为结尾,所以,破坏了为性能专门做出的努力。为了控制和减轻这些脚本带来的性能损失,仅异步加载(可能经过 defer)和经过资源提示,如:dns-prefetch 或者 preconnect 加速他们是不足够的。

正如 Yoav Weiss 在他的必须关注第三方脚本的通讯中解释的,在不少状况下,下载资源的这些脚本是动态的。页面负载之间的资源是变化的,所以咱们没必要知道主机是从哪下载的资源以及这些资源是什么。

这时,咱们有什么选择?考虑 经过间隔下载资源来使用 service workers,若是在特定的时间间隔内资源没有响应,返回一个空的响应告知浏览器执行解析页面。你能够记录或者限制那些失败的第三方请求和没有执行特定标准请求。

另外一个选择是创建一个 内容安全策略(CSP) 来限制第三方脚本的影响,好比:不容许下载音频和视频。最好的选择是经过 <iframe> 嵌入脚本以至于脚本运行在 iframe 环境中,所以若是没有接入页面 DOM 的权限,在你的域下不能运行任何代码。Iframe 能够 使用 sandbox 属性进一步限制,所以你能够禁止 iframe 的任何功能,好比阻止脚本运行,阻止警告、表单提交、插件、访问顶部导航等等。

例如,它可能须要容许脚本运行 <iframe sandbox="allow-scripts">。每个限制均可以经过'容许'值在 'sandbox' 属性中(几乎到处支持)解除,因此把他们限制在最低限度的容许他们去作的事情上。考虑使用 Safeframe 和交叉观察;这将使广告嵌入 iframe 的同时仍然调度事件或须要从 DOM 获取信息(例如广告知名度)。注意新的策略如特征策略),资源的大小限制,CPU 和带宽优先级限制损害的网络功能和会减慢浏览器的脚本,例如:同步脚本,同步 XHR 请求,document.write 和超时的实现。

为了压测第三方,在 DevTools 上自底向上概要地检查页面的性能,测试若是一个请求被阻塞了会发生什么或者对于后面的请求有超时限制,你可使用 WebPageTest's Blackhole 服务器 72.66.115.13,同时能够在你的 hosts 文件中指定特定的域名。最好是自我主机和使用一个单一的主机名,可是同时生成一个请求映射,当脚本变化时,暴露给第四方调用和检测。

请求块

图片信用:Harry Roberts

  1. HTTP cache 头部设置是否合理?

再次检查一遍 expirescache-controlmax-age 和其余 HTTP cache 头部都是否设置正确。一般,资源应该是可缓存的,无论是短期的(若是它们极可能改变),仍是无限期的(若是它们是静态的)——你能够在须要更新的时候,改变它们 URL 中的版本便可。在任何资源上禁止头部 Last-Modified 都会致使一个 If-Modified-Since 条件查询,即便资源在缓存中。与 Etag 同样,即便它在使用中。

使用 Cache-control: immutable,该头部针对被标记指纹的静态资源设计,避免资源被从新验证(截至 2017年12月,在 FireFox,Edge 和 Safari 中支持;只有 FireFox 在 HTTPS 中支持)。你也可使用 Heroku 的 HTTP 缓存头部,Jake Archibald 的 "Caching Best Practices" ,以及 Ilya Grigorik 的 HTTP caching primer 做为指导。并且,注意不一样的头部,尤为是在关系到 CDN 时,而且注意关键头部有助于避免在新请求稍有差别时进行额外的验证,但从之前请求标准,并非必要的(感谢Guy!)。


掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 AndroidiOS前端后端区块链产品设计人工智能等领域,想要查看更多优质译文请持续关注 掘金翻译计划官方微博知乎专栏

相关文章
相关标签/搜索