为什么 Pinterest 会选择用 PWA?简单回顾下相关的历史
在最开始的时候,因为专注于国际市场的增长,Pinterest 关注了移动端网页的开发,也由此有了 Pinterest PWA.
在分析了未经验证的移动端网页用户的相关数据后,Pinterest 发现他们原来旧而慢的网络体验仅能将 1% 的用户转化为注册,登录或下载 app 作为本地应用使用的用户.如果能够提升这一转化率的话,无疑是一个巨大的机会,所以他们开始了对 PWA 的投资.
在一个季度内建立和推出 PWA
用时超过 3 个月,Pinterest 通过使用 React,Redux 和 webpack 重构了他们移动端网页的体验.移动端网页的重写也提高了他们几项核心业务指标.
与旧移动端网页的体验相比,新移动端网页用户的使用时间增加了 40%,用户生成的广告收益增加了 44%,并且核心业务增长了 60%.
与此同时,移动端网页的重写也改善了 Pinterest 网页的一些性能.
Pinterest PWA 在 3G 普通移动硬件上的加载速度很快
Pinterest 旧的移动端网页含有大量的需要占用很多 CPU 的 JavaScript 包,延长了 Pin 网页加载和取得互动 所需的时间 .
在可以进行任何互动之前,用户经常需要等 23 秒:
(Pinterest 原有的移动端网站需要花费 23s 取得互动.这一过程中,他们会发送 2.5MB 以上的 JavaScript,其中约有 1.5MB 用于主包,1MB 用于懒加载.在主线程最终能够实现交互之前,需要花费几秒钟的时间来解析和编译)
他们新移动端网页的体验有了极大的提高.
不仅是因为他们分散和减少了数百 KB 的 JavaScript,将核心包体的大小从 650KB 降到了 150KB,也是因为他们提高了网页的一些关键性能指标. 首次有效绘制 时间由 4.2s 降低到了 1.8s,并且 可交互时间 由 23s 降低到了 5.6s.
以上的测试结果是在连接了缓慢 3G 网络的普通 Android 硬件上得到的.在重复访问的情况下,结果甚至更好.
得益于 服务工作线程缓存了主要的 JavaScript,CSS 和静态 UI 资源,重复访问的时间被缩短到了 3.9s:
尽管 Pinterest 有 iOS 和 Android 应用,但是只需在开始时下载约为 150KB 优化压缩(minified & gzipped)过的代码,就能够在网页应用上实现与本地应用相同的主页推送体验.对比于 Android 版应用的 9.6MB 和 iOS 版应用的 56MB:
然而值得注意的是,与本地应用相比 Pinterest PWA 的优点并不局限于前期主页推送体验.PWA 还会按新路由的需要来加载代码,而且额外代码的成本会被分摊到使用网页应用的整个过程中.随后的导航仍然不会像下载应用那样消耗大量的数据.
(Pinterest 的 PWA 分别在移动端的 Firefox,Edge 和 Safari 上的显示)
基于路由的 JavaScript 分块(chunking)
在前期仅加载用户需要的代码降低了网络传输和解析 / 编译 JavaScript 的时间,从而提高了网页的加载速度和缩短了实现交互的时间.随后非关键资源可以根据需要进行懒加载.
Pinterest 开始将原有的高达几个 MB 的 JavaScript 包拆分成 3 种不同类型的 webpack 模块,效果还挺不错:
一类是包含外部依赖性的 vendor 模块(react,redux,react-router 等),大约 73KB
一类是包含渲染应用所需要的大部分代码的入口模块(entry chunk)(即常见的库,主要的页面外壳,我们的 redux store),大约 72KB
一类是包含关于单个路由的代码的异步路由模块(async route chunk),大约 13 到 18KB
以下 Network 的瀑布记录,突出显示了渐进式地按需传送代码如何避免了整体(monolithic)传送包体的需求:
(对于长期缓存,Pinterest 也在每个文件名中包含了一个模块相关 (chunk-specific) 的哈希,通过 chunkhash 替换)
Pinterest 用了 webpack 的 CommonsChunkPlugin 插件来将他们的 vendor 包体拆分到可缓存的模块内:
(原代码见 sample-webpack.js hosted with ❤ by GitHub )
const 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);
}
})
];
在分块的过程中,他们也用了 React Router 来实现 代码拆分 :
(原代码见 sample-codesplitting.js hosted with ❤ by GitHub
// Create a loader
const Closeup = () => import(/* webpackChunkName: "CloseupPage" */ 'app/mobile/routes/CloseupPage');
// Register it to the route
route('/pin/:pinId', routes.Closeup, { name: 'Closeup' }),
// Render a react-router-v4 Route with the route bundle loader
<Route exact key="matched-route" path={path} render={matchProps =>
<PageRoute
bundleLoader={loader}
routeName={name}
{...matchProps}
{...props}
/>}
/>
// Async load the route bundle
class PageRoute extends PureComponent {
render() {
const { bundleLoader, ...props } = this.props;
return <Loader loader={bundleLoader} {...props} />;
}
}
// Load it and render
class Loader extends PureComponent {
componentWillMount() {
this.props.loader().then(module => {
this.setState({ LoadedComponent: module.default });
});
}
}
)
用 babel-preset-env 来只编译(transpile)目标浏览器所需的内容
Pinterest 用了 Babel 的 babel-preset-env 来仅编译(transpile)不受目标浏览器支持的 ES2015 + 功能.Pinterest 针对的是现代浏览器最新的两个版本,他们的. babelrc 设置类似于:
(原代码见: .babelrc hosted with ❤ by GitHub
{
"presets": [
["env", {
"targets": {
"browsers": ["last 2 versions"]
}
}]
]
}
)
其实 Pinterest 也可以对此作进一步的优化,按照实际需要有条件地提供 polyfills(比如:Safari 国际化的 API ).但是目前这还是这一优化仍在计划中.
使用 Webpack Bundle Analyzer 来分析改进空间
Webpack Bundle Analyzer 是一个很好的工具,可以帮助人切实地理解传送给客户的 JavaScript 包之间的依赖关系.
如下图所示,在早期的 Pinterest 版本的输出中,有很多的紫色,粉色和蓝色的区域.这些都是被懒加载的路由异步模块.Webpack Bundle Analyzer 可以帮助 Pinterest 将大多数的含有重复代码的模块可视化:
Webpack Bundle Analyzer 可以将重复代码在不同模块之间的大小比例视觉化.
在有了所有模块中有重复代码的信息之后,Pinterest 就可以做出调用.他们把异步模块中的重复代码移到了主要模块中.虽然这一改动增加了 20% 入口模块的大小,但是却将所有懒加载模块的大小减小了 90%!
图像优化
大部分 Pinterest PWA 中内容的懒加载都是通过无限网格瀑布流插件 Masonry 来处理的.它内置了对虚拟化的支持,并且仅装载(mounting)视口内的子项.
Pinterest 也在他们的 PWA 中使用了渐进式加载图片的技术.有主导颜色的占位符在最开始会被用于每一个 Pin.而 Pin 的图像会以 Progressive JPEGs 来提供,其质量会随着扫描次数的增加而增加:
React 性能的痛点
在 Pinterest 使用网格瀑布流 Masonry 插件的同时,他们也面临着 React 带来的一些渲染性能的问题.装载和卸载大的组件树(像 Pin)可能会很慢.一个 Pin 里面有很多的东西:
尽管当时他们写 Pinterest 的时候用的是 React 15.5.4, 但是他们寄希望于 React 16 (Fiber) 将会大大减少卸载所用的时间.与此同时,虚拟化的网格也会显著地减少组件卸载的时间.
Pinterest 还会限制 Pin 的插入,以便更快地测量 / 渲染第一个 Pin,但是这也意味着设备 CPU 的工作量更大了.
导航转换
为了提高感知性能,Pinterest 也更新了导航栏图标的选定状态,将其独立于路由之外.这就确保了当导航从一个路由转到另一个路由的时候,用户并不会因为网络的阻塞而感到缓慢.用户在等待数据到达时可以快速地获得可视化界面.
使用 Redux 的体验
Pinterest 在他们所有的 API 数据中均使用了 normalizr (normalizr 会根据一种模式来规范化嵌套的 JSON).从 Redux DevTools 就可以看出:
这样做的缺点是逆规范化 (denormalization) 会变得很慢,在渲染的阶段最终他们很大程度上是依赖于 reselect 的 selector 模式来记忆(memoizing)逆规范化.他们也尽可能的在最低程度上进行逆规范处理,以确保单个的更新不会导致大规模的重新渲染.
举个例子来说,他们的网格项目列表只是由 Pin ID 与逆规范化自身的 Pin 组件组成的.如果任何给定的 Pin 有了改变,则完整的网格不必重新渲染.但是有得就有失,这样 Pinterest PWA 就有了很多 Redux 用户,虽然这一点尚未对性能产生显著的影响.
用 Service Worker 来缓存资源
Pinterest 用了 Workbox 库来生成和管理他们的 Service worker:
(原代码见: sample-sw-caching.js hosted with ❤ by GitHub) )
/* 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 使用缓存优先策略(cache-first strategy)来缓存任何 JavaScript 或者 CSS 的包,并且也会缓存其用户的界面(应用程序的外壳).
(在缓存资源优先的设置中,如果请求与缓存条目相匹配,则以缓存的资源为准.否则,则尝试从网络获取资源.如果网络请求成功,则对缓存进行更新.要了解更多有关使用 Service Worker 的缓存策略,请阅读 Jake Archibald 的 Offline Cookbook .)
他们也为应用程序外壳(webpack 运行时,vendor 和 entry 模块)加载的初始包定义了预缓存.
因为 Pinterest 是一个具有全球影响力的网站,能够支持多种语言,所以他们还会生成适用于每个语言区域的 Service Worker 配置,以便其预缓存不同语言区域的软件包.Pinterest 也使用了 webpack 的命名模块来预缓存顶级(top-level)异步路由包.
这项工作是在几个较小的迭代中逐步推出完成的.
第一步:Pinterest 的 Service Worker 仅缓存运行时需要懒加载的脚本.充分利用 V8 的代码缓存 ,跳过了一些在重复视图解析 / 编译所需的成本,使得加载能够快速的进行.从有 Service Worker 存在的 Cache Storage 获得的脚本能够很快地进行代码缓存,因为浏览器很可能知道当重复访问时用户最终会重复使用这些资源.
在这之后,Pinterest 推进到预缓存其 vendor 和入口模块.
接下来,Pinterest 开始预缓存一些使用最多的路由(比如主页,锁定收藏的网页,搜索页等)
最后,他们开始为每个地域生成一个 Service Worker,这样的话就能够缓存不同地域的语言包.这不仅是为了保证重复加载的性能,也是为了保证绝大多数的用户可以享受基本的离线渲染功能.
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
/* 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
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
,
'entryChunk-mobile-.*\\.js'
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
,
'entryChunk-webpack-.*\\.js'
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
在最开始的时候,因为专注于国际市场的增长,Pinterest关注了移动端网页的开发,也由此有了Pinterest PWA.
,
`locale-${locale}-mobile.*js
()
在分析了未经验证的移动端网页用户的相关数据后,Pinterest发现他们原来旧而慢的网络体验仅能将1%的用户转化为注册,登录或下载app作为本地应用使用的用户.如果能够提升这一转化率的话,无疑是一个巨大的机会,所以他们开始了对PWA的投资.
用时超过,Pinterest通过使用React,Redux和webpack重构了他们移动端网页的体验.移动端网页的重写也提高了他们几项核心业务指标.
与旧移动端网页的体验相比,新移动端网页用户的使用时间增加了,用户生成的广告收益增加了,并且核心业务增长了.
与此同时,移动端网页的重写也改善了Pinterest网页的一些性能.
Pinterest旧的移动端网页含有大量的需要占用很多CPU的JavaScript包,延长了Pin网页加载和取得互动.
在可以进行任何互动之前,用户经常需要等:
()
他们新移动端网页的体验有了极大的提高.不仅是因为他们分散和减少了数百KB的JavaScript,将核心包体的大小从650KB降到了150KB,也是因为他们提高了网页的一些关键性能指标.时间由4.2s降低到了1.8s,并且由23s降低到了5.6s.
以上的测试结果是在连接了缓慢3G网络的普通Android硬件上得到的.在重复访问的情况下,结果甚至更好.
得益于 服务工作线程缓存了主要的JavaScript,CSS和静态UI资源,重复访问的时间被缩短到了3.9s:
https://unpkg.com/workbox-sw@1.1.0/build/importScripts/workbox-sw.prod.v1.1.0.js
尽管Pinterest有iOS和Android应用,但是只需在开始时下载约为150KB优化压缩(minified & gzipped)过的代码,就能够在网页应用上实现与本地应用相同的主页推送体验.对比于Android版应用的9.6MB和iOS版应用的56MB:
'app/mobile/polyfills.js'
然而值得注意的是,与本地应用相比Pinterest PWA的优点并不局限于前期主页推送体验.PWA还会按新路由的需要来加载代码,而且额外代码的成本会被分摊到使用网页应用的整个过程中.随后的导航仍然不会像下载应用那样消耗大量的数据.
在前期'react'降低了'redux'的时间,从而提高了网页的加载速度和缩短了实现交互的时间.随后非关键资源可以根据需要进行懒加载.
'normalizr'
('react-redux')
'react-router-dom'
Pinterest开始将原有的高达几个MB的JavaScript包拆分成3种不同类型的webpack模块,效果还挺不错:
'entryChunk-webpack''entryChunk-mobile'
以下Network的瀑布记录,突出显示了渐进式地按需传送代码如何避免了整体(monolithic)传送包体的需求:
Pinterest用了webpack的'Closeup'插件来将他们的vendor包体拆分到可缓存的模块内:
"CloseupPage"
('/pin/:pinId')
在分块的过程中,他们也用了"last 2 versions"来实现'https://unpkg.com/workbox-sw@1.1.0/build/importScripts/workbox-sw.prod.v1.1.0.js':
"matched-route"
("env" )
Pinterest用了Babel的"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."来仅编译(transpile)不受目标浏览器支持的ES2015+功能.Pinterest针对的是现代浏览器最新的两个版本,他们的.babelrc设置类似于:
'sw-shell.html'
('swTemplates/mobileBase.js'
)
'appVersion'
其实Pinterest也可以对此作进一步的优化,按照实际需要有条件地提供polyfills(比如:Safari"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响.").但是目前这还是这一优化仍在计划中.
'appVersion'
('appVersion'
)
'appVersion'
"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."是一个很好的工具,可以帮助人切实地理解传送给客户的JavaScript包之间的依赖关系.
如下图所示,在早期的Pinterest版本的输出中,有很多的紫色,粉色和蓝色的区域.这些都是被懒加载的路由'appVersion'模块.Webpack Bundle Analyzer可以帮助Pinterest将大多数的含有"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."的模块可视化:
'appVersion'
Webpack Bundle Analyzer可以将重复代码在不同模块之间的大小比例视觉化.
在有了所有模块中有重复代码的信息之后,Pinterest就可以做出调用.// Register it to the route
// Render a react-router-v4 Route with the route bundle loader// Load it and render
大部分Pinterest PWA中内容的懒加载都是通过无限网格瀑布流插件// Add app shell to the webpack-generated precache list来处理的.它内置了对虚拟化的支持,并且仅装载(mounting)视口内的子项.
// Register precache list with Workbox
Pinterest也在他们的PWA中使用了渐进式加载图片的技术.有主导颜色的占位符在最开始会被用于每一个Pin.而Pin的图像会以// Prefer app-shell for full-page loads来提供,其质量会随着扫描次数的增加而增加:
// bunch of non-app routes/* webpackChunkName: "CloseupPage" */
在Pinterest使用网格瀑布流/* global $VERSION, $Cache, importScripts, WorkboxSW */插件的同时,他们也面临着React带来的一些渲染性能的问题.装载和卸载大的组件树(像Pin)可能会很慢.一个Pin里面有很多的东西:
/* Create a service worker for every locale to precache the locale bundle */
尽管当时他们写Pinterest的时候用的是React 15.5.4, 但是他们寄希望于(Fiber)将会大大减少卸载所用的时间.与此同时,也会显著地减少组件卸载的时间.
Pinterest还会限制Pin的插入,以便更快地测量/渲染第一个Pin,但是这也意味着设备CPU的工作量更大了.
为了提高感知性能,Pinterest也更新了导航栏图标的选定状态,将其独立于路由之外.这就确保了当导航从一个路由转到另一个路由的时候,用户并不会因为网络的阻塞而感到缓慢.用户在等待数据到达时可以快速地获得可视化界面.
Pinterest在他们所有的API数据中均使用了(normalizr会根据一种模式来规范化嵌套的JSON).从Redux DevTools就可以看出:
这样做的缺点是逆规范化(denormalization)会变得很慢,在渲染的阶段最终他们很大程度上是依赖于的selector模式来记忆(memoizing)逆规范化.他们也尽可能的在最低程度上进行逆规范处理,以确保单个的更新不会导致大规模的重新渲染.
举个例子来说,他们的网格项目列表只是由Pin ID与逆规范化自身的Pin组件组成的.如果任何给定的Pin有了改变,则完整的网格不必重新渲染.但是有得就有失,这样Pinterest PWA就有了很多Redux用户,虽然这一点尚未对性能产生显著的影响.
Pinterest用了Workbox库来生成和管理他们的Service worker:
()
如今,Pinterest使用缓存优先策略(cache-first strategy)来缓存任何JavaScript或者CSS的包,并且也会缓存其用户的界面(应用程序的外壳).()
他们也为应用程序外壳(webpack运行时,vendor和entry模块)加载的初始包定义了预缓存.因为Pinterest是一个具有全球影响力的网站,能够支持多种语言,所以他们还会生成,以便其预缓存不同语言区域的软件包.Pinterest也使用了webpack的命名模块来预缓存顶级(top-level)异步路由包.
这项工作是在几个较小的迭代中逐步推出完成的.
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
,
'pjs-HomePage.*\\.js'
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
,
'pjs-SearchPage.*\\.js'
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
(原代码见:sample-sw-generation.js hosted with ❤ by GitHub
,
'pjs-CloseupPage.*\\.js'
)
应用外壳的挑战
Pinterest发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用Lighthouse进行审查
Pinterest用了Lighthouse对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用Lighthouse作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest刚刚部署了对web推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于<link rel = preload>的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺Pinterest的Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman和Ben Finkel推出了他们的Progressive Web App ,也感谢他们对于本文的贡献.也感谢Jeffrey Posnick和Zouhir对本文的审读.
原文链接:A Pinterest Progressive Web App Performance Case Study
感谢徐川对本文的审校.
评价本文
专业度
风格
编辑观点
主编观点
此内容所在的主题为语言 & 开发
相关主题:
语言 & 开发
架构 & 设计
Performance
性能调优
移动应用
移动开发
告诉我们您的想法
允许的HTML标签: a,b,br,blockquote,i,li,pre,u,ul,p
当有人回复此评论时请E-mail通知我
社区评论
讨论
(原代码见: sample-sw-generation.js hosted with ❤ by GitHub
]
}
})
});
},
{});
// Add to webpack
plugins: [new ServiceWorkerPlugin(BaseConfig, ServiceWorkerConfigs);]
)
应用外壳的挑战
Pinterest 发现实施他们应用的外壳有些难.因为桌面时代(desktop-era)会假定多少数据能够通过有线连接发送出去,而其应用外壳的初始有效负载量很大包含有很多无关紧要的信息,比如用户的测试组,用户信息,上下文信息等.
他们不得不问自己:"我们是否应该把这些内容缓存在应用程序的外壳中?或者选择在渲染任何内容之前忍受阻塞网络请求对性能的影响."
最终,他们选择这些内容缓存到应用外壳中,这就需要对什么时候应该让应用外壳失效(注销,从设置更新用户信息等)进行一定的管理.每一个请求的响应有一个'appVersion',如果应用程序的版本发生了变化,他们会先取消注册 Service Worker,转而注册新的请求,然后在下一次路由更改时重新加载整个页面.
用 Lighthouse 进行审查
Pinterest 用了 Lighthouse 对其性能的提升进行一次性的验证,以确保相关性能改进的方向是正确的.观察类似于持续互动时间这类的指标是很有用的.
下一年,他们希望用 Lighthouse 作为回归机制(regression mechanism)来验证页面的加载速度是否仍然快速.
未来
Pinterest 刚刚部署了对 web 推送通知的支持,并且也在致力于提高未经身份验证(注销)时的用户体验.
他们有兴趣探索对于 < link rel = preload > 的支持,用其来预加载关键包和减少在首次加载时传送给用户的无用 JavaScript.请继续期待他们未来更好的用户体验!
在此祝贺 Pinterest 的 Zack Argyle,YenWei Liu, Luna Ruan,Victoria Kwong, Imad Elyafi, Langtian Lang,Becky Stoneman 和 Ben Finkel 推出了他们的 Progressive Web App ,也感谢他们对于本文的贡献.也感谢 Jeffrey Posnick 和 Zouhir 对本文的审读.
来源: http://www.infoq.com/cn/articles/pinterest-progressive-web-app-performance-case-study