大概这是之前看 The Cost Of JavaScript In 2018 中提到的以参考索引,其实里面很多点挺重要的,当然推荐大家阅读下全文 A Pinterest Progressive Web App Performance Case Study; 后面其实说几点自己印象非常深刻的点;
为什么做这次性能改造
其实很多时候做性能改造,都会面临这样的质疑,它不是对现有版本的任何功能性的迭代,而是从多个方面去提升一些所谓数据指标。因此很多 产品(🐩) 都会去质疑这一件事情,但是 Pinterest 这次改造给出了更为直接的数据,性能的优化与用户数据方面的正相关:
其中相比老版本,用户的停留时间和核心参与度都有非常大的提升;其中和广告相关的数据,也有非常显著的增加;不难理解,当用户用更短的时间触达到页面,
合理的设置 chunks
如果我们是做基于 vue 项目或者 React 的项目,我们可以参考下 Pinterest 的 JS Bundles;
-
js vendor
包含一些第三方的库比如(React, Redux, React-Router) 等; -
entry chunk
也就是我们存放主要代码逻辑的部分比如 页面的结构或者redux store; -
async router
则是包含一些并不在首页的子页面的 bundle;
webpack 的配置:
onst bundles = {
'vendor-mweb': [
'app/mobile/polyfills.js',
'intl',
'normalizr',
'react-dom',
'react-redux',
'react-router-dom',
'react',
'redux'
],
'entryChunk-webpack': 'app/mobile/runtime.js',
'entryChunk-mobile': 'app/mobile/index.js'
};
const chunkPlugins = [
new webpack.optimize.CommonsChunkPlugin({
name: 'vendor-mweb',
minChunks: Infinity,
chunks: ['entryChunk-mobile']
}),
new webpack.optimize.CommonsChunkPlugin({
name: 'entryChunk-webpack',
minChunks: Infinity,
chunks: ['vendor-mweb']
}),
new webpack.optimize.CommonsChunkPlugin({
children: true,
name: 'entryChunk-mobile',
minChunks: (module, count) => {
return module.resource && (isCommonLib(resource) || count >= 3);
}
})
];
可以参考 code splitting 的技巧: https://github.com/ReactTraining/react-router/blob/master/packages/react-router-dom/docs/guides/code-splitting.md
使用 Webpack Bundle Analyzer
webpack-bundle-analyzer 是一款非常有用的工具,可以很好的帮助我们降低 bundle 的小小,比如下面图中紫色和蓝色的快,它们是会惰性加载的模块,通过分析,Pinterest 将一些重复的模块移动到主 entry ,虽然这增加 entry 20% 的大小,但是却可以明显降低子 chunk 的 90% 的大小;
图片的 lazyload
Pinterest 由于是专注于图片的网站,因此文章分析了自己是如何使用懒加载的图片。
Pinterest 采用了瀑布流布局,大概在移动端,会懒加载下一屏的图片,而且基于 color-placeholder 的加载体验,总之效果还是赞的;
自己之前写过一篇 Color placeholder 的加载,有兴趣可以看下简单实现;
Service Worker
如果你想使用 Service Worker
; 我们还是建议大家使用 workbox, 它可以帮助你非常轻松的管理自己的 Service Worker;
/* global $VERSION, $Cache, importScripts, WorkboxSW */
importScripts('https://unpkg.com/workbox-sw@1.1.0/build/importScripts/workbox-sw.prod.v1.1.0.js');
// Add app shell to the webpack-generated precache list
$Cache.precache.push({ url: 'sw-shell.html', revision: $VERSION });
// Register precache list with Workbox
const workbox = new WorkboxSW({ handleFetch: true, skipWaiting: true, clientClaim: true });
workbox.precache($Cache.precache);
// Runtime cache all js
workbox.router.registerRoute(/webapp\/js\/.*\.js/, workbox.strategies.cacheFirst());
// Prefer app-shell for full-page loads
workbox.router.registerNavigationRoute('sw-shell.html', {
blacklist: [
// bunch of non-app routes
],
});
Pinterest 缓存了所有的 JS 或者 CSS 的 bundle 资源,同时还包括了整个 APP 的架子;
- Pinterest 只会先缓存 runtime 的 或者惰性加载依赖的脚本;这可以提高 V8 的编译和解析的速度;
- Pinterest 接下来会缓存 vendor 和 entry 的脚本资源;
- 随之 Pinterest 会缓存一些经常用的路由资源;
- 最后 会生成 一个
service worker
给每个本地的资源版本
/* Create a service worker for every locale to precache the locale bundle */
const ServiceWorkerConfigs = locales.reduce((configs, locale) => {
return Object.assign(configs, {
[`mobile-${locale}`]: Object.assign({}, BaseConfig, {
template: path.join(__dirname, 'swTemplates/mobileBase.js'),
cache: {
template: path.join(__dirname, 'swTemplates/mobileCache.js'),
precache: [
'vendor-mweb-.*\\.js$',
'entryChunk-mobile-.*\\.js$',
'entryChunk-webpack-.*\\.js$',
`locale-${locale}-mobile.*js$`,
'pjs-HomePage.*\\.js$',
'pjs-SearchPage.*\\.js$',
'pjs-CloseupPage.*\\.js$'
]
}
})
});
}, {});
// Add to webpack
plugins: [
new ServiceWorkerPlugin(BaseConfig, ServiceWorkerConfigs);
]
使用 lighthouse 查看性能测试
lighthouse 推出来很久了,但是在最新的 66 还是 67 版本开始正式集成到了 devtool 上,是非常全面的性能测试工具。这其中包含了对 PWA 的可靠性进行测试;