Uber 高性能 Web App 优化实践
原文 - Building m.uber: ENGINEERING A HIGH-PERFORMANCE WEB APP FOR THE GLOBAL MARKET
Performance matters on mobile.
又是一篇关于性能优化的实践。
m.uber 团队对 m.uber - 即他们的超级轻量 web app 做了一些性能优化的工作。
范围全面,从代码到打包到部署到缓存,都有涉及。
TL;DR
Performance Tools
- Preact over React
- Webpack dynamic bundle splitting & tree-shaking capabilities
- Tiny Libraries & Minimal Dependencies
- source-map-explorer
下面是正文:
Smaller, faster: how we built it
技术栈
代码层面:ES2015+,使用 Babel 编译;
框架:Preact
+ Webpack
;
设计的痛点是,在保证类 Native App 丰富体验的同时,最小化客户端体积。所以 Preact + Webpack 的 dynamic bundle splitting 和 tree-shaking capabilities 功能完美搭配。
Initial server render 首次服务端渲染
因为在所有核心打包的 JS 全部被下载前,客户端或浏览器都不能开始渲染 markup 标记。
为了更快首屏渲染,m.uber 在首次浏览器请求时候会返回服务端渲染好的 Preact,且 state
及 markup
都嵌到行内,全部都是字符串,所以这些内容一旦被客户端下载,就可以立马加载出来。
Serve bundles on demand 按需打包加载
m.uber 中大部分 JS 都是用来做一些辅助功能,这些都是没有必要一次性加载的,所以他们用了 Webpack 的 Code Splitting
工具按需加载代码。
(这个我在 Accounts 项目也实践过,一个巨大的 JS 文件拆分成了三个小的 JS 文件,不过也需要考虑每个 HTTP 请求时间。)
We use a splitPage function that returns the ancillary bundle wrapped in an asynchronous component.
// Example: settings page
const AsyncSettings = splitPage(
{load: () => import('../../screens/settings')}
);
// 当且仅当 `AsyncSettings` 被 Parent Component render() 调用,
// setting bundle js 才会被下载.
Tiny Libraries 更小的库
m.uber 本意上是希望在 2G 网下也能飞快,所以打包后的体积也很重要。
他们 App 核心部分(叫车功能)在 gzipped & minified 后只有 50kB 大小,所以在典型 2G 网(50kB/s, 500ms 延时)下,也能三秒内开始交互。
以下是优化前后的打包和依赖的对比。