原文做者:Addy Osmanihtml
译者:UC 国际研发 Jothy前端
写在最前:欢迎你来到“UC国际技术”公众号,咱们将为你们提供与客户端、服务端、算法、测试、数据、前端等相关的高质量技术文章,不限于原创与翻译。react
这是一篇关于性能优化的文章,是一篇很是值得你阅读的文章,文章的内容很是丰富,大概你花5-10分钟阅读。webpack
在过去的一年里,咱们忙于试图弄清楚如何让网络更快、更高效。所以就有了咱们但愿在本文中与你分享的新工具,方法和库。在第一部分中,咱们将向你展现咱们在开发 The Oodles Theater App 时使用的一些优化技术。在第二部分中,咱们将讨论咱们的预测加载实验和新的 Guess.js 计划。
git
Tip:你能够在 Youtube 观看视频 https://www.youtube.com/watch?v=Mv-l3-tJgGkgithub
互联网每一年都在变得愈来愈重。若是咱们检查网页的状态,咱们能够看到一个中等大小的移动端页面大约为 1.5MB,其中大部分是 JavaScript 和图片。
web
网站规模不断扩大,加上其余因素,如网络延迟,CPU 限制,渲染阻塞模式或多余的第三方代码,都会致使复杂的性能难题。
面试
大多数用户把速度当成用户体验须要层次理论(User Experience Hierarchy of Needs,见下图)的最高层。这并不太使人惊讶,由于在页面加载完成以前,你有不少事情都作不了。你没法从页面中获取价值,你没法欣赏它的美学。
算法
图1. 速度对用户有多重要?
shell
咱们知道性能对用户很重要,但它又像是一个寻找从哪里开始优化的秘密。幸亏有一些工具能够帮助咱们。
Lighthouse 属于 Chrome DevTools 的一部分,它容许你对你的网站进行审查,并提供优化建议。
咱们最近推出了一系列新的性能审查(https://developers.google.com/web/updates/2018/05/lighthouse),它们在平常开发工做流中很是有用。
图2. 新 Lighthouse 审查
让咱们来探索一下如何在一个实际的例子中利用它们:Oodles Theatre App。这是一个小型的 demo web app,你能够在里面试用咱们最爱的一些交互式 Google Doodle,甚至玩一两局游戏。
在构建 App 时,咱们但愿确保它尽量高效。优化的起点是 Lighthouse 报告。
图3. Oodles App 的 Lighthouse 报告
咱们的 App 在 Lighthouse 报告中的初始表现很是糟糕。使用 3G 网络时,用户须要等待 15 秒才能得到第一个有意义的(页面)绘制,或者(说)让 App 可交互。 Lighthouse 高亮标识了咱们网站的一堆问题,而 23 分的总体性能得分充分反映了这一点。
页面大小约3.4MB - 咱们迫切须要减减肥了。
咱们的首次性能挑战由此开始:找到能够轻松删除的内容,同时不影响总体体验。
有一些显而易见的东西能够安全删除掉:空白和注释。
图4. Minify和压缩 JavaScript 和 CSS
Lighthouse 在 Unminified CSS & JavaScript 审查中突出了这个机会。程序使用 webpack 进行构建,所以为了缩小体积,咱们选择了 Uglify JS 插件。
缩小体积是一项常见任务,所以你得找到适合你的构建过程的现成解决方案。
该项目中另外一个有用的审查是启用文本压缩。咱们没有理由发送未压缩的文件,并且如今大多数 CDN 都开箱即用地支持这个。
咱们使用 Firebase Hosting 来托管咱们的代码,Firebase 默认启用 gzip,所以,经过在合理的 CDN 上托管咱们的代码,咱们免费得到了这个功能。
虽然 gzip 是一种很是流行的压缩方式,但其余机制如 Zopfli 和 Brotli 也愈来愈吸引人。 Brotli 受大多数浏览器支持,你能够在将资源发送到服务器以前使用二进制方式对其进行预压缩。
咱们的下一步是确保在没必要要的状况下咱们不会重复发送资源。
Lighthouse 中的低效缓存策略审查使咱们注意到,咱们能够经过优化缓存策略实现这一目标。经过在咱们的服务器中设置 max-age 过时头,咱们能够确保在重复访问状况下,用户能够重用他们以前下载的资源。
理想状况下,你应该在尽量长的时间内尽量安全地缓存尽量多的资源,并提供验证令牌,以便高效地从新验证已更新的资源。
到目前为止,咱们删除了非必要下载文件的显要部分,那不太明显的部分呢?例如,未使用的代码。
图5.检查代码覆盖率
有时咱们会在 App 中包含没必要要的代码。这种状况尤为是在你的 App 开发了很长一段时间,你的团队或你的依赖项发生了变化,有时“孤儿库”被遗忘的状况下。这正是咱们经历过的事情。
最初咱们使用 Material Components 库来快速构建咱们的 App 原型。随着时间的推移,咱们转向了更加定制化的外观,咱们彻底忘记了这个库。幸运的是,代码覆盖率检查帮助咱们在 bundle 中从新发现了它。
你能够在 DevTools 中检查代码覆盖率状态,包括运行时和应用加载时间。你能够在底部截图中看到两个大的红色横条 - 咱们有超过 95% 的 CSS 是未使用的,还有一大堆 JavaScript。
Lighthouse 在未使用的 CSS 规则审查中也提到了这个问题。它表示咱们可能节省超过 400kb。因此咱们回到代码里,删除了该库的 JavaScript 和 CSS 部分。
图6. 弃用 MVC 适配器,咱们的样式降低到了 10KB!
这使咱们的 CSS bundle 减小了 20倍,这对于一个很小的,两行长的 commit 来讲太赞了。
固然,它使咱们的性能得分上升,并且可交互时间也获得优化。
可是,经过这样的更改,仅仅检查指标和分数是不够的。删除实际代码毫不是零风险的,所以你应该始终注意发掘潜在的风险。
咱们的代码在 95% 的代码中未被使用 - 在某处仍有 5% 在使用。显然咱们的某个组件仍在使用该库中的样式 - 涂鸦滑块中的小箭头。由于它过小了,咱们能够手动将这些样式合并到按钮中。
图7.一个组件仍在使用已删除的库
所以,若是您删除代码,请确保您拥有适当的测试工做流,以帮助您防范潜在的可见的风险回归。
咱们知道大量资源可能会减慢网页加载速度。他们可能会消耗用户的钱,他们可能会对他们的数据计划产生重大影响,所以注意这一点很是重要。
经过使用巨大的网络负载审查,Lighthouse 可以检测到咱们的某些网络负载存在的问题。
图8.检测巨大的网络负载
从这里能够看到,咱们有超过 3mb 的代码被传输 - 这不是通常的多,特别是在移动设备上。
在这个列表的最顶部,Lighthouse 告诉咱们有一个2mb大小的未压缩JavaScript vendor 包。这也是 webpack 高亮显示的问题。
俗话说:最快的请求是还没发出的请求。
理想状况下,你应该衡量你发送给用户的每一项资源的价值,衡量这些资源的性能,并根据初始经验判断它是否真的值得被传输。由于有时这些资源能够在空闲时间延迟发送,懒加载或处理。
在咱们的例子中,由于咱们处理的是大量的 JavaScript 包,因此咱们很幸运,由于 JavaScript 社区拥有丰富的 JavaScript 包审查工具。
图9. JavaScript 包审查
咱们开始使用 webpack bundle analyzer,它告诉咱们,咱们有一个名为 unicode 的依赖项,它是 1.6mb 的已解析 JavaScript,很是大的文件。
而后咱们转到咱们的编辑器,并使用为可视化代码准备的 Import Cost 插件,透过它咱们可以看到咱们导入的每一个模块的成本。它能帮助咱们发现哪一个组件包含引用此模块的代码。
而后咱们切换到另外一个工具 BundlePhobia。这个工具容许你输入任何 NPM 包的名称,并实际看到它通过压缩和 gzip 后的预计大小。咱们找到了一个很好的替代方案,咱们使用的 slug 模块只有 2.2kb,因此咱们用了它。
这对咱们的性能产生了很大的影响。在此更改和发现其余减小 JavaScript 包大小的机会之间,咱们节省了 2.1mb 的代码。
综合考虑到这些 bundle 的压缩和缩小尺寸,咱们整体上看到了 65% 的提高。咱们发现这确实值得去作。
所以,总的来讲,尝试去消除你的网站和应用中没必要要的下载吧。对资源进行清点并衡量其性能影响,这可能会带来面目一新的改变,所以请确保按期审查你的资源。
虽然大型网络负载可能对咱们的应用程序产生重大影响,但还有另外一样东西也能产生巨大影响——那就是 JavaScript。
JavaScript 是你最昂贵的资产。在移动设备上,若是您发送了大量的 JavaScript,它可能会延迟用户与界面组件交互的时间。这意味着他们点击 UI 的操做多是毫无心义的。所以,了解 JavaScript 成本为什么如此之高显得极为重要。
这是浏览器处理 JavaScript 的方式。
图10. JavaScript 处理
咱们首先必须下载该脚本,咱们的 JavaScript 引擎须要解析该代码,编译并执行它。
如今,这些(处理)阶段在台式机或笔记本等高端设备、甚至是高端手机并不用花很长时间。但在中等配置手机上,这个过程可能要花上 5 到 10 倍的时间。这正是延迟交互的缘由,所以把它降下来很是关键。
为了帮你发现 App 的这些问题,咱们向 Lighthouse 引入了一个新的 JavaScript 启动时间审查工具。
图11. JavaScript 启动时间审查
在 Oodle App 的例子中,它显示咱们花了 1.8 秒来启动 JavaScript。而这段时间所作的,是将全部路由和组件静态导入到一整个 JavaScript 包中。
解决此问题的方法之一是使用代码分割。
代码分割的概念是,不要一次把一整个披萨的 JavaScript 给到你的用户,不妨一次给只他们所须要的一片?
代码分割能够应用于路由级别或组件级别。它适用于 React 和 React Loadable,Vue.js,Angular,Polymer,Preact 以及其余多个库。
咱们将代码分割合并到咱们的应用中,从静态导入切换为动态导入,实现了咱们所需的异步懒加载代码。
图13.使用动态导入的代码分割
这种影响既缩小了 bundle 的大小,也节省了咱们的 JavaScript 启动时间。它把时间降到了 0.78 秒,为应用提速 56%。
一般,若是你正在构建你的 JavaScript 体验,请务必作到仅向用户发送他们所须要的代码。
利用代码分割等概念,发掘 tree shaking 等思路,查看 webpack-libs-optimizations 仓库,了解在使用 webpack 时如何减少库的体积的方法。
图片加载性能的笑话
在 Oodle App 中,咱们使用了大量图片。不幸的是,Lighthouse 对它的热情远远低于咱们。事实上,咱们在全部三个与图片相关的审查上都挂掉了。
咱们忘了对图片进行优化,没有正确处理好它们的体积,咱们也本可使用其余图片格式来优化。
图14. Lighthouse 图像审查
咱们开始对图片进行优化。
针对一次性的优化,你可使用 ImageOptim 或 XNConvert 等可视化工具。
更自动化的方法是使用像 imagemin 这样的库,在构建过程当中优化图片。
这样,你就能够确保未来添加的图片自动获得优化。一些 CDN,例如 Akamai 或 Cloudinary 或 Fastly 等第三方解决方案,也提供了全面的图像优化解决方案。因此你也能够放心地把图片托管到这些服务上。
若是因为成本或延迟问题而不想这么干,Thumbor 或 Imageflow 等项目也提供自托管替代方案。
图15.优化先后
咱们的背景 PNG 图片在 webpack 中被标记为大,事实的确如此。在将调整到 viewport 大小,并经过 ImageOptim 优化后,咱们把体积降到了 100kb,还能够接受。
咱们对网站上的图片重复执行了此操做,由此显着下降了总体页面的体积。
GIF 的代价极其昂贵。使人惊讶的是,GIF 格式从未打算成为动画平台。所以,切换到更合适的视频格式能够大大节省文件大小。
在 Oodle App 中,咱们在主页上使用了 GIF 做为介绍动画。根据 Lighthouse 的说法,切换到更高效的视频格式能节省超过 7mb。咱们的动画就差很少 7.3mb 了,这对于任何网站来讲都太大了,因此咱们把它变成了一个包含两个源文件的视频元素——mp4 和 WebM,以兼容更多浏览器。
图16.用视频替换动画 GIF
咱们使用 FFmpeg 工具将动画 GIF 转换为 mp4 文件。 WebM 格式则节免得更多——ImageOptim API 能够作这种转换。
这种转换为咱们节省了超过 80% 的整体积。这让咱们降到了 1mb 左右。
尽管如此,1mb 仍算是网络传输中的大型资源,特别是对于带宽受限的用户而言。幸亏,咱们可使用 Effective Type API 来检测到它们处于慢网络,而后给它们提供体积更小的 JPEG。
此接口使用高效的往返时间和下降值来判断用户所用的网络类型。它只返回一个字符串,像 slow 2G,2G,3G 或 4G。所以,根据此值,对 4G 如下的用户,咱们使用图像替换掉了视频元素。
它确实牺牲了一点点用户体验中,但至少在慢网络中,该站点也是可用的了。
轮播动画,滑块或很是长的页面一般会加载图像,即便用户并不能当即在页面上看到它们。
Lighthouse 将在屏幕外图像审核中标记此行为,您也能够在 DevTools 的网络面板中自行查看。若是你看到不少图像传入,但它们之中只有少数可见,则意味着你能够考虑延迟加载它们。
浏览器自己尚不支持懒加载,所以咱们必须使用 JavaScript 来添加此功能。咱们使用 Lazysizes 库为咱们的 Oodle 封面添加懒加载行为。
Lazysizes 很是智能,由于它不只能够跟踪元素的可见性变化,还能够主动预获取视图附近的元素,以得到最佳的用户体验。它还提供了 IntersectionObserver 的可选集成,为你带来很是高效的可见性查找。
在此更改后,咱们的图片将按需提取。若是你想深刻了解该主题,请查看 images.guide - 一个很是方便和全面的资源。
images.guide: https://images.guide/
并不是每一个经过网络发送到浏览器的字节都具备同等的重要性,浏览器知道这一点。许多浏览器都有试探性方法来决定他们应该首先获取什么。因此有时它们会在获取图片或脚本以前获取 CSS。
可能有用的东西是做为页面做者的咱们,能够告诉浏览器对咱们来讲真正重要的是什么。值得庆幸的是,在过去几年中,浏览器已经添加了许多功能来帮助咱们实现这一功能,例如:使用 link rel=preconnect,preload 或 prefetch 实现资源提示。
这些 Web 平台的功能能够帮助浏览器在正确的时间获取正确的资源,而且它们会比使用脚本完成的一些自定义加载,基于逻辑的方法更有效。
让咱们看看 Lighthouse 如何实际指导咱们有效地使用这些功能。
Lighthouse 让咱们作的第一件事就是避免屡次与任一源的昂贵往返请求。
图17.避免屡次与任一源的昂贵往返请求
对于 Oodle App,咱们实际上重度使用 Google 字体。每当在页面中使用 Google Fonts 样式表时,它最多会链接两个子域。Lighthouse 告诉咱们,若是咱们可以预热这种链接,咱们能够在初次链接时节省最多 300 毫秒。
利用 link rel preconnect,咱们能够有效地屏蔽链接延迟。
特别是对像 Google Fonts 这种把字体 CSS 托管在 googleapis.com 上,以及把字体资源托管在 Gstatic 上的资源,这可能会产生很大的影响。因此咱们应用了这个优化,优化了几百毫秒。
Lighthouse 建议的下一件事是预先加载关键请求。
图18.预加载关键请求
<link rel=preload> 很是强大,它通知浏览器当前导航中须要该资源,而且尝试让浏览器尽快获取它。
如今,Lighthouse 告诉咱们,咱们应该预加载咱们的关键网络字体资源,由于咱们正在加载两种网络字体。
预加载网络字体以下所示 - 指定rel = preload,将字体类型传入 as 字段,而后指定要加载的字体类型,例如 woff2。
这对你的页面产生的影响将很是明显。
图19.预加载资源的影响
一般,若是不使用 link rel preload,若是 Web 字体刚好对你的页面相当重要,那么浏览器必须作的是首先获取 HTML,解析 CSS,以及接下来的其余资源,最后才去获取你的网络字体。
而使用了 link rel preload,一旦浏览器解析了 HTML,它就能够提前开始获取这些网络字体。对咱们的 App 来讲,这能够减小咱们使用 Web 字体渲染文本所花费的时间。
如今,若是你想尝试使用 Google Fonts 预加载字体,那还没那么简单,咱们还有一个问题。
咱们在样式表中的字体中指定的 Google Font URL 刚好是 Google 字体团队按期更新的内容。这些 URL 可能会过时或按期更新,所以,若是你但愿彻底控制字体加载体验,咱们建议你自行托管你的 Web 字体。这也很棒,由于它让你能够访问 link rel preload 等内容。
在咱们的例子中,咱们发现 Google Web Fonts Helper 工具在帮助咱们离线某些网络字体并在本地设置它们时很是有用,所以请了解下该工具。
不管你是将 Web 字体仍是 JavaScript 做为关键资源的一部分,都应使浏览器尽快提供该关键资源。
今天还有一个特别的东西要与你分享。除了资源提示和预加载等功能外,咱们还在开发一种全新的实验性浏览器功能,咱们称之为优先级提示。
图20.优先级提示
这项新功能容许你提示浏览器某资源的重要性。它暴露了一个新属性 - importance - 可取值 low,high 或 auto。
这使咱们能下降不过重要资源的优先级,例如非关键样式,图片或 fetch API 调用,以减小流量抢占。咱们还能够提高更重要事物的优先级,例如咱们的英雄图片。
对于咱们的 Oodle App,这实际上给了咱们一个能够优化的机会。
图21.设置初识可见内容的优先级
在咱们对图像设置懒加载以前,浏览器的作法是,咱们将这个图片轮播与全部涂鸦一块儿使用,而浏览器会在轮播最开始时以高优先级获取全部图像。不幸的是,轮播中间的图像对用户来讲是最重要的。因此咱们的作法是,将背景图像的重要性设置得很是低,前景图像的重要性设置得很是高,这在慢速 3G 时能带来 2 秒的提速,咱们也可以快速地获取和渲染这些图像。这是一个很棒的体验。
咱们但愿在几周内将这个功能带到 Canary,还请密切关注。
排版是良好设计的基础,若是你使用的是网络字体,理想状况下你并不想阻塞文本的渲染,并且你绝对不想显示不可见的文本。
咱们在 Lighthouse 中强调了这一点,在 avoid invisible text while web fonts are loading 审查中能够找到。
图22.在加载网络字体时避免使用不可见文本
若是你使用 font face 代码块加载网络字体,而且该字体须要较长时间才能获取到,你就是在让浏览器决定该作什么。有些浏览器会在退回到系统字体以前等待最多三秒钟,而且一旦字体下载完毕,浏览器最终又会切换到字体。
咱们试图避免这种不可见文本,在这种状况下,若是网络字体加载花了太长时间,咱们将没法看到本周的经典涂鸦。幸亏,经过一个名为 font-display 的新功能,你实际上能够更好地控制这个过程。
Font display 可帮助你根据交换所需的时间来决定网络字体的渲染或退阶方式。
在这种状况下,咱们使用字体显示交换。交换为字体提供零秒块周期和无限交换周期。这意味着若是字体加载须要一段时间,浏览器会当即使用备用字体绘制文本。一旦字体可用,它就会转换它。
对咱们的 App 来讲,这功能很是棒,它容许咱们很早就显示一些有意义的文本,并在网络字体准备好后立刻转换过去。
图23.字体显示结果
通常状况下,若是你碰巧在使用网络字体,且它占据了网络的很大一部分,那么你得有一个很好的网络字体加载策略。
有许多 Web 平台能帮你优化字体的加载体验,你还能够查看 Zach Leatherman 的 Web Font Recipes 仓库,由于它实在太棒了。
Web Font Recipes repo: https://www.zachleat.com/web/recipes/
咱们的应用中还有其余部分能够在下载链中提早推送,以便提早提供一些基本的用户体验。
在 Lighthouse 时间轴上,你能够看到在加载全部资源的前几秒内,用户没法看到任何内容。
图24.减小阻塞式渲染样式表的机会
下载和处理外部样式表阻塞了咱们的渲染过程的进展。
咱们能够尝试经过先提供一些样式来优化咱们的关键渲染路径。
若是咱们提取负责初始渲染的样式并在 HTML 中内联它们,浏览器能够直接渲染它们而无需等待外部样式表。
在咱们的例子中,咱们使用名为 Critical 的 NPM 模块在构建步骤中内联 index.html 中的关键内容。
虽然这个模块为咱们完成了大部分繁重工做,但要让它在不一样路线上顺利运行仍然有点棘手。
若是你不当心或者你的站点结构很是复杂,若你没有从一开始就规划 app shell 架构,那么引入这种模式可能很是困难。
这就是为何在早期就考虑性能因素很是重要的缘由。若是你从一开始就没有设计性能,那么以后再实施就可能遇到问题。
最终咱们的风险获得了回报,咱们设法让它发挥做用,App开始更早地提供内容,显着改善了咱们的第一个有意义的绘画时间。
Lighthouse 表现得分从 23 上升到 91。在速度方面取得了至关不错的进步。全部这些变化都是由咱们不断检查并遵循 Lighthouse 报告推进的。若是你想了解咱们如何在技术上实施全部改进,请随时查看咱们的仓库(http://github.com/google/oodle-demo),尤为是 PR。
咱们相信机器学习在将来的许多领域表明着使人兴奋的机会。咱们但愿未来能引起更多实验的一个想法是,真实数据能够真正指导咱们正在建立的用户体验。
今天,咱们对用户可能想要或须要的内容作出了不少武断的决定,由此判断什么资源该预先提取,预加载或预先缓存。若是咱们猜对了,咱们能够优先考虑少许资源,但很难将其扩展到整个网站。
咱们实际上有数据能够更好地为咱们的优化提供支持。使用 Google Analytics reporting API,咱们能够查看下一个首页以及咱们网站上任意网址的退出百分比,从而得出咱们应优先考虑哪些资源的结论。
若是咱们将其与良好的几率模型相结合,咱们就会经过积极预获取内容来避免浪费用户的数据。咱们能够利用 Google Analytics 数据,并使用机器学习和马尔可夫链或神经网络等模型来实现此类模型。
图25.用于Web应用程序的数据驱动捆绑
为了促进这个实验,咱们很高兴地宣布一个叫作 Guess.js 的新计划。
图26. Guess.js
Guess.js 是一个专一于数据驱动Web用户体验的项目。咱们但愿它能激发人们探索使用数据来改善网络性能并超越它。它是彻底开源的,能够 GitHub 上获取。这是由 Minko Gechev,Gatsby 的 Kyle Matthews,Katie Hempenius 和其余一些人与开源社区合做创建的。
分数和指标有助于提升 Web 的速度,但它们只是手段,而不是目标自己。
咱们都经历过慢网速页面加载,但咱们如今有机会为用户提供更加愉悦的快速加载体验。
性能提高是一个旅程。许多小的变化能够带来巨大的收益。经过使用正确的优化工具并密切关注 Lighthouse 报告,你能够为用户提供更好、更具包容性的体验。
特别感谢:Ward Peeters,Minko Gechev,Kyle Mathews,Katie Hempenius,Dom Farolino,Yoav Weiss,Susie Lu,Yusuke Utsunomiya,Tom Ankers,Lighthouse 和 Google Doodles。
英文原文:
https://developers.google.com/web/updates/2018/08/web-performance-made-easy
“UC国际技术”致力于与你共享高质量的技术文章
欢迎关注咱们的公众号、将文章分享给你的好友