• 原文地址:A Tinder Progressive Web App Performance Case Study
  • 原文作者:Addy Osmani
  • 译文出自:掘金翻译计划
  • 本文永久链接:github.com/xitu/gold-m…
  • 译者:pot-code
  • 校对者:zwwill

PWA 实战:Tinder 的性能优化之道

最近 Tinder 在 web 端发力,公布了 PWA  应用 Tinder Online ,现已全平台支持。在技术实现上,为了应对 JavaScript 性能优化问题 采用了最前沿的技术,例如,使用 Service Workers 来应对网络弹性问题、使用 消息推送(Push Notifications) 来支撑聊天业务。本文将向各位讲解大佬们是如何处理开发中的性能优化问题的。

开发历程

Tinder Online 肩负着打开新市场的使命,它背后的团队希望把它打造成一个全平台无缝体验的在线聊天平台。

产品的 MVP 开发花了 3 个月,UI 库用了 React,状态管理用的是 Redux。最后的成果还是显著的,在不影响功能体验的前提下,数据传输量减少到了原来的十分之一:

上图是 Tinder Online 和手机 app 在安装过程中所需数据量大小的比较,虽然这两个类型不同,但是还是具有参考意义的。 相对手机 app 来说,PWA 只有在需要时才加载新的代码。用户边使用边下载,因为下载过程分散在整个使用过程中,所以用户并不会察觉到。即使用户在使用中访问了应用的其他部分,综合下载量也还是少于直接下载整个 app 所需的数据量。

上线后的前期表现还是不错的,用户划一划频率、发信频率以及在线时长均优于在手机 app 上的表现。总之,使用 PWA 之后(针对 PWA 和原生的比较):

  • 用户划一划的频率变高了
  • 用户的发信更频繁了
  • 不影响用户买买买
  • 用户更加卖力的经营自己的账户
  • 用户在线时间更长了

性能表现

数据显示,手机端用户主要使用的设备包括但不限于:

  • Apple iPhone & iPad
  • Samsung Galaxy S8
  • Samsung Galaxy S7
  • Motorola Moto G4

再使用 Chrome User Experience report (简称 CrUX)进行分析,得知用户主要使用 4G 网络进行访问:

注:可以参考 Rick Viscomi 在 PerfPlanet 上对 CrUX 的介绍,Inian Parameshwaran 介绍的 rUXt 在网络可视化分析这块针对大流量网站更具优势。

在常用的 web 应用测试网站(WebPageTest 和 Lighthouse)上进行测试,使用 4G 网络的 Galaxy S7 可以在 5秒内 完全加载应用:

相比高端机型,配置 相对一般 的机型(例如 Moto G4)的性能表现还有很大的提升空间,这类机型的 CPU 资源比较吃紧:

Tinder 现在也确实在针对这方面做优化,期待他们以后的表现。

性能优化

Tinder 为了加快页面的加载使用了很多技术,例如基于路由的代码分割、性能预算(performance budgets)以及静态资源持久缓存。

基于路由的代码分割

起初打包的文件非常巨大,严重拖慢了交互就绪的速度,对用户体验影响很大。因为打包的文件里包含了除核心交互以外的代码,这就需要使用 代码分割(code-splitting) 抽离出暂时不需要的代码,只保留核心功能。

针对这个问题,Tinder 引入了 React Router 和 React Loadable。得益于前期架构的优势,整个应用非常适合使用基于配置的方式处理路由和渲染,因此,代码分割也能很顺利的在顶层上实现。

小结:

React Loadable 是 James Kyle 开发的一个轻型库,简化了 React 中基于组件的代码分割操作,它提供的 Loadable 函数是一个高阶(higher-order)组件(创建组件的函数),专门用于处理代码分割。

下面举例说明。假如有两个组件 “A” 和 “B”,分割前,它们和入口文件一并打包,因为这两个模块目前并不需要,所以这样全部打包在一起是很低效的:

这里要求使用代码分割之后,组件 A 和 B 只是在需要时才加载。为此,Tinder 引入了 React Loadable、动态导入函数 import() 以及 webpack 神奇的注释语法 (用来为动态导入的模块命名):

在公共库(即 vendor)的处理上,Tinder 使用了 webpack 官方提供的 CommonsChunkPlugin 插件,把路由间公用的库单独打包到一个文件中,利用浏览器的缓存机制提升性能:

接着使用 React Loadable 提供的预加载功能 针对那些/在接下来的交互中/存在潜在加载需求/的资源/做预加载处理(译者注:注意断句):

Tinder 还用 Service Workers 对所有路由做了预缓存处理,其中就包括用户经常访问而没有做代码分割的路由。最后使用 UglifyJS 对代码进行压缩:

new webpack.optimize.UglifyJsPlugin({parallel: true,compress: {warnings: false,screw_ie8: true},sourceMap: SHOULD_SOURCEMAP}),
复制代码

成果

使用代码分割后,打包文件大小从 166KB 减到 101KB,DOM 内容加载时间(DCL)也从 5.46s 降低到 4.69s:

静态资源持久缓存

在 webpack 的输出中配置 [chunkhash],一方面可以用来规避开发过程中因浏览器缓存而引发的资源不更新问题,二来也可以确保缓存有效。

由于 Tinder 使用了大量的第三方开源库,如果其中的一个依赖发生变化都会导致 [chunkhash] 的重新计算,从而导致之前的缓存失效。为了解决这个问题,Tinder 制定了一个 外部依赖白名单,另外还将 manifest 从主干中抽出,进一步改进缓存。最后,主干代码和 manifest 的打包大小都只有 160KB。

预加载潜在需求资源

这里用到了 <link rel=preload>,它告诉浏览器这是一个关键资源,马上要用到,需要尽早加载。在单页应用(SPA)中,这些资源可以是打包后的 JavaScript 文件。

Tinder 将核心体验相关的资源文件进行了预加载处理,最终加载时间减少了 1s,首次绘制时间也从 1000ms 减少到 500ms。

性能预算

为了达到移动端的性能期望,Tinder 引入了 性能预算。Alex Russell 在他发表过的一篇文章(Can you afford it?: real-world performance budgets)中指出:难就难在要在网络环境不好、配置也一般的移动设备上提供良好的用户体验,因为提升空间非常有限。

为了保证应用能快速就绪,Tinder 规定公共依赖和主干代码的大小要维持在 155KB 左右,分配给异步加载(懒加载)的数据量大约 55KB 左右、其他部分代码 35KB 左右,CSS 则限制在 20KB 左右。 这个规划保证了最坏情况下的性能表现。

Webpack 打包分析

开源工具 Webpack Bundle Analyzer 可以对依赖进行可视化分析,暴露出那些明显的可优化问题。

Tinder 主要用它来分析以下几类优化问题:

  • Polyfills 代码占比: 因为 Tinder 的目标平台包括了 IE11 和 Android 4.4,免不了使用一些 Polyfills,但是又不希望这些代码占据太多数据量,所以直接用了 babel-preset-envcore-js
  • 是否引入了不必要的第三方库: 最后移除了 localForage ,使用 IndexedDB 作为替代。
  • 分割方案是否最优: 将首次绘制/交互中用不到的组件从主干代码中剔除。
  • 是否可提炼复用代码: 将子模块中使用超过三次的公共代码抽出成异步加载的代码块。
  • CSS: 把 critical CSS 从核心包中移除(转而使用服务器端渲染的方式)。

这个工具可以搭配 Webpack 的 Lodash Module Replacement 插件 使用。这个插件将模块中使用到的一些特定方法替换成实现上更简单、功能上等效的代码,从而减小打包文件大小:

Webpack Bundle Analyzer 可以集成到 Webpack 的配置中。来自 Tinder 的配置参考:

plugins: [new BundleAnalyzerPlugin({analyzerMode: 'server',analyzerPort: 8888,reportFilename: 'report.html',openAnalyzer: true,generateStatsFile: false,statsFilename: 'stats.json',statsOptions: null})
复制代码

经过一系列优化之后,剩下的主要是主干部分代码。这些代码暂时就不用动了,除非架构发生变化。

CSS 的优化策略

Tinder 使用 Atomic CSS 创建了许多高复用的 CSS 样式,其中一些做了内联处理,在初始化绘制过程中生效,剩下的则分散在外部 CSS 文件中(包括动画或者 base/reset 等样式)。关键样式(Critical styles)使用 gzip 压缩之后大小不超过 20KB,最近的一次构建则压缩到了 11KB 以下。

Tinder 还使用了 CSS stats 和 Google Analytics 跟踪每次发版的变化。在使用 Atomic CSS 前,页面的平均加载时间在 6.75s 左右,使用之后降到 5.75s 左右。

最后用 Autoprefixer 添加浏览器前缀:

new webpack.LoaderOptionsPlugin({options: {context: paths.basePath,output: { path: './' },minimize: true,postcss: [autoprefixer({browsers: ['last 2 versions','not ie < 11','Safari >= 8']})]}
}),
复制代码

运行时性能

使用 requestIdleCallback() 推迟非关键事务

使用 requestIdleCallback() 将非关键事务推迟到空闲期运行,以提高运行时性能。

requestIdleCallback(myNonEssentialWork);
复制代码

什么叫非关键事务?比如做插桩标记(instrumentation beacons)。Tinder 团队为了减少刷屏时的绘制次数,对合成层(composite layers)也做了优化。

在刷屏操作中,是否使用 requestIdleCallback() 的对比:

使用前..

使用后...

依赖优化

Webpack 3 + 作用域提升

在 webpack 3 以前,每个模块在打包后都会被包裹进一个独立的闭包内,但是这些包裹起来的方法(闭包)执行效率很低。对此,Webpack 3 推出了“作用域提升”机制,将多个模块打包进一个闭包中(相当于合并作用域),以提高执行速度。使用这一功能需要引入 Module Concatenation 插件:

new webpack.optimize.ModuleConcatenationPlugin()
复制代码

使用作用域提升机制后,Tinder 第三方依赖初始化解析时间减少了 8%。

React 16

React 16 优化了打包之后的文件大小,新的打包算法(Rollup)会剔除一些暂时用不到的代码。

Tinder 从 React 15 升级到 React 16 后,gzip 压缩后的依赖大小减小了约 7%

最后,react + react-dom 压缩后从之前的 50KB 降到现在的 35KB 左右,效果拔群。这里要特别感谢 Dan Abramov、Dominic Gannaway 和 Nate Hunzaker 的付出,他们在 React 16 中为降低打包文件大小做了不少贡献。

网络弹性和静态资源缓存

Tinder 还用了 Workbox Webpack 插件,用于缓存 Application Shell 和核心静态资源,例如主干代码、第三方库、manifest 和 CSS。使用后,具备了较强的频繁访问抗压能力,同时用户再次使用时,启动速度也大大提高了。

更上一层楼

用 source-map-explorer (又一个包分析工具)再次对包进行深入分析,发现还有继续缩小网络负载的优化空间。在登陆场景中,用户还没登录的情况下,Facebook 图片、通知、私信以及验证码这些组件并不需要加载,将这些组件从关键路径(critical path)移除之后可为主干代码省下 20% 的数据量:

因为上一步移除了 Facebook 的组件,所以其相关依赖 Facebook SDK 也可以直接移除,后面需要用到的时候再进行加载(懒加载),这样就又节省了 200KB,而且初始加载时间也减少了 1 秒。

总结

虽然 Tinder 还在继续迭代他们的产品,但是已经初见成效了,你可以随时访问 Tinder.com 关注最新进展。

感谢并祝贺 Roderick Hsiao、 Jordan Banafsheha 以及 Erik Hellenbrand,恭喜你们成功发布了 Tinder Online,谢谢你们对我的文章提出的指导意见,还有 Cheney Tsai,你的观点给了我很多启发。

相关阅读:

  • A Pinterest PWA performance case study
  • A Treebo React & Preact performance case study
  • Twitter Lite and high-performance PWAs at scale

本文转载自 Performance Planet。 如果你对 React 还不熟悉,可以参考我推荐的教程:React for Beginners,对新手十分友好。


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

[译] PWA 实战:Tinder 的性能优化之道相关推荐

  1. (译)2019年前端性能优化清单 — 上篇

    (译)2019年前端性能优化清单 - 上篇 (译)2019年前端性能优化清单 - 中篇 (译)2019年前端性能优化清单 - 下篇 写在译前:首先介绍一下我自己,一个跨行业的.完全非科班生的文科单身狗 ...

  2. (译)2019年前端性能优化清单 — 中篇

    (译)2019年前端性能优化清单 - 上篇 (译)2019年前端性能优化清单 - 中篇 (译)2019年前端性能优化清单 - 下篇 目录 资源优化 17. 使用 Brotli 或 Zopfli 进行纯 ...

  3. 百万并发下的Nginx性能优化之道,值得看!!!

    Nginx很火,火到无论是创业公司,还是BAT等一线互联网公司,都会使用Nginx.因为它就像一个万能药,在任何存在性能需求的场合总能找见它的身影.它可以轻松在百万并发连接下实现高吞吐量的Web服务, ...

  4. MySQL性能优化之道

    1.in和not in子查询优化 not in 是不能命中索引的,所以以下子查询性能很低. 如果是确定且有限的集合时,可以使用.如 IN (0,1,2). 用 exists或 notexists代替 ...

  5. 【性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

    一.写在前面   相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这是非常火的一门技术. 如果只是用户量很少的传统IT系统,使用Spring Cloud可能还暴露不出 ...

  6. 实战:Redis 性能优化方案

    Redis 是基于单线程模型实现的,也就是 Redis 是使用一个线程来处理所有的客户端请求的,尽管 Redis 使用了非阻塞式 IO,并且对各种命令都做了优化(大部分命令操作时间复杂度都是 O(1) ...

  7. 性能优化之道】每秒上万并发下的Spring Cloud参数优化实战

    本文为转载文章,作者:中华石杉,十余年BAT架构经验,倾囊相授.作者微信公众号:石杉的架构笔记(ID:shishan100) 一.写在前面 相信不少朋友都在自己公司使用Spring Cloud框架来构 ...

  8. 性能优化之道:上万并发下的SpringCloud参数优化实战

    目录 一.写在前面 二.场景引入,问题初现 三.扬汤止沸,饮鸩止渴 四.问题爆发,洪水猛兽 五.追本溯源,治标治本 六.总结全文,回眸再看 一.写在前面 相信不少朋友都在自己公司使用Spring Cl ...

  9. 每秒上万并发下的Spring Cloud性能优化之道

    一.写在前面 相信不少朋友都在自己公司使用Spring Cloud框架来构建微服务架构,毕竟现在这是非常火的一门技术. 如果只是用户量很少的传统IT系统,使用Spring Cloud可能还暴露不出什么 ...

  10. Core Web Vitals和Mobile-First Indexing来袭,中国跨境电商平台的Web性能优化之道

     关注ITValue,看企业级最新鲜.最价值报道! 本文为署名文章 | 作者@Akamai亚太区架构师经理李文涛 | 图片来源@视觉中国 | 进入到六月,国人便被网上的各种促销信息"狂轰滥炸 ...

最新文章

  1. oracle修改机器名后不能启动Console的解决方案
  2. 软件开发模型之优缺点
  3. python3 import导入模块
  4. CSS3 display:flex和display:box有什么区别?
  5. 动点四边形周长最短_初中几何--线段之和最小值 Part 1:通过点关于直线对称点得到两定点之间直线段长度最短。...
  6. String转int,int转String
  7. 地磅称重软件源码_电脑设备器件+塔吊主吊臂+撇渣管、丝杆+地磅称重传感器+极柱触头盒弯板+批式循环谷物干燥机+升降机标准节...
  8. 格兰杰因果关系检验r语言_R语言 t检验t.test
  9. 软件架构师常会用到的几款软件
  10. java如何输入(输出)二进制,八进制,十六进制数?(新手向)
  11. 推荐几个常用常玩的小游戏网址包括4399.com
  12. 9个妙招教你玩转微信
  13. springboot+Rabit实战三:(springboot+rabbit 项目搭建)
  14. 数字联盟可信ID 3.0 正式上线升级 用真实数据助力企业增长
  15. Google Analytics
  16. 与“十“俱进 阿里数据库运维10年演进之路 1
  17. 服务器运维用macos,MacOS和Linux区别_网站服务器运行维护,linux,macos
  18. 湖南省邵阳市谷歌高清卫星地图下载
  19. ICCV2021 Oral 论文及论文实现代码合集
  20. 百度云盘批量转存工具

热门文章

  1. java软件安装教程_r软件安装教程
  2. 使用Requests爬取网页图片并保存
  3. 删除目录以及子目录以下所有目录和文件
  4. Windows取证分析基础知识大全
  5. 风口来了?关于电子信息工程专业的有关介绍
  6. JS实现本地文件选择
  7. Coverity代码静态检测工具介绍
  8. 根据用户IP精确定位推送消息
  9. java根据ip获取定位(实用粗略定位)
  10. LAGON ATOLL