@bornkiller
2017-10-13T10:59:50.000000Z
字数 3655
阅读 3084
React
步入移动时代,web app
的发展却并不如意,网络速度,网络稳定些,用户体验等问题在移动端更为突出。PWA
的提出意在提升 web app
体验,降低部分场景开发原生应用的必要性。由于 PWA
关联概念较多,本文只讨论具体实施。
PWA
应用需要具备快速启动,离线可用特性,实现这两者,需要 service worker
支持,其本质可理解为运行在浏览器中的代理服务器。离线可用,需要实现 app shell
架构。所谓 app shell
定义,可以简单理解为:完全分割应用的静态资源与动态数据。对于一般应用,使用 SW
完全缓存静态资源,对于动态数据自定义离线策略.
首先在页面中注册来启动安装:
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ', registration.scope);
}).catch(function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
});
}
安装过程中,存储 app shell
关联资源:
const CACHE_NAME = 'react-pwa-starter';
const urlsToCache = [
'/',
'/styles/main.css',
'/script/main.js'
];
self.addEventListener('install', function(event) {
// Perform install steps
event.waitUntil(
caches.open(CACHE_NAME)
.then(function(cache) {
return cache.addAll(urlsToCache);
})
);
});
安装成功后,便是最重要的网络代理实现:
self.addEventListener('fetch', function(event) {
event.respondWith(
caches.match(event.request)
.then(function(response) {
// Cache hit - return response
if (response) {
return response;
}
return fetch(event.request);
}
)
);
});
此处是所有步骤中难度最大的环节,代码实现较为复杂,且代理策略需要细细斟酌。本文中采用非常简单的代理策略:所有静态资源使用 cacheFirst
,并使用 navigate proxy
提升入口 html
文件加载速度。
实际开发中,一般不建议手动处理 service worker
等文件,笔者采用 workbox
库,此库也是功能强大,细节很多,文档不全的典型,静态代理模式如下:
const InjectServiceWorkerPlugin = require('webpack-plugin-inject-service-worker');
const CopyPlugin = require('copy-webpack-plugin');
const WorkboxPlugin = require('workbox-webpack-plugin');
module.exports = {
plugins: [
Reflect.construct(InjectServiceWorkerPlugin, []),
Reflect.construct(CopyPlugin, [[{
from: 'node_modules/workbox-sw/build/importScripts/workbox-sw.prod.*',
to: '[name].[ext]'
}]]),
Reflect.construct(WorkboxPlugin, [
{
globDirectory: './dist/client',
globPatterns: ['**/*.{html,js,css,png,jpg}'],
swSrc: './public/service-worker.js',
swDest: './dist/client/service-worker.js'
}
])
]
};
// Import workbox scripts
importScripts('workbox-sw.prod.v2.0.0.js');
// Construct Workbox
const swWorkBox = new self.WorkboxSW({
cacheId: 'react-pwa-starter',
skipWaiting: true,
clientsClaim: true
});
// Pre-cache static files
swWorkBox.precache([]);
// Register special strategy
// Avoid static file fallback into navigate mode
// Notice registerNavigationRoute will not cooperate with prerender default
swWorkBox.router.registerNavigationRoute('/index.html', {
blacklist: [/\.(js|css|jpe?g|png)$/i]
});
也可单独使用 workbox-cli
处理,特别注意,如果配合 pre-render
策略,务必配置 templatedUrls
配置项:
/**
* @description - Workbox configuration
* @author - huang.jian <hjj491229492@hotmail.com>
*/
module.exports = {
globDirectory: './dist/client',
globPatterns: ['**/*.{html,js,css,png,jpg}'],
swSrc: './public/service-worker.js',
swDest: './dist/client/service-worker.js',
templatedUrls: {
'/search': ['./search/index.html'],
'/review': ['./review/index.html'],
'/gallery': ['./gallery/index.html']
}
};
workbox inject:manifest --config-file workbox.config.js
利用 manifest.json
控制在用户想要看到应用的区域中如何向用户显示网络应用或网站,指示用户可以启动哪些功能,以及定义其在启动时的外观。https://developers.google.com/web/fundamentals/engage-and-retain/web-app-manifest/。
同样利用 webpack plugin
自动生成关键文件。
const WebpackPwaManifest = require('webpack-pwa-manifest');
module.exports = {
plugins: [
Reflect.construct(WebpackPwaManifest, [
{
short_name: 'Baby',
name: 'Blog promise for Carey baby',
display: 'standalone',
background_color: '#2196F3',
theme_color: '#2196F3',
start_url: '/index.html',
ios: true,
icons: [
{
src: path.resolve('public/android-icon.png'),
sizes: [144, 196, 256, 512],
destination: 'android'
},
{
src: path.resolve('public/ios-icon.png'),
sizes: [144, 196, 256, 512],
ios: true,
destination: 'ios'
}
]
}
])
]
};
线上地址:https://www.reverseflower.com。
目前只是基本做到离线可用,首屏性能基本上是相当糟糕,作为后续优化点,后续进一步深入,使用更灵活的代理策略与预渲染提升首屏性能。
Email: hjj491229492@hotmail.com