@bornkiller
2017-10-13T02:59:50.000000Z
字数 3655
阅读 3378
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 successfulconsole.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 stepsevent.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 responseif (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 scriptsimportScripts('workbox-sw.prod.v2.0.0.js');// Construct Workboxconst swWorkBox = new self.WorkboxSW({cacheId: 'react-pwa-starter',skipWaiting: true,clientsClaim: true});// Pre-cache static filesswWorkBox.precache([]);// Register special strategy// Avoid static file fallback into navigate mode// Notice registerNavigationRoute will not cooperate with prerender defaultswWorkBox.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
