多应用+插件架构,代码干净,二开方便,首家独创一键云编译技术,文档视频完善,免费商用码云13.8K 广告
[TOC] PWA(Progressive Web App)是对Web页面的一种增加技术,当前若浏览器支持则可以表现增强后的特性,如果不支持也不影响Web页面的表现。目前,我们主要使用的是它的前端缓存能力。虽然PWA主要针对移动端,实际PC端页面也可以以同样模式来操作 配置有如下几个步骤: 1. 页面视口 ``` <!-- PC端 --> <meta name="viewport" content="width=device-width,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no"> ``` PC端设置禁止缩放页面,显示比例100%,移动端不需要配置`viewport`,因为,我们使用的移动端响应式JS会动态配置。 2. 在网站根目录创建文件`manifest.json`: ``` { "name": "这里写网站的标题名称", "short_name": "这里写网站标题的简称", "start_url": "这里写网站起始URL一般使用/", "display": "这里写网站在浏览器中的显示模式一般使用standalone", "background_color": "网站的背景颜色16进制颜色值", "description": "这里写对于网站的描述", "orientation": "网站显示的方向portrait-primary", "theme_color": "网站的主题颜色16进制颜色值", "icons": [ { //网站放置在桌面使用的图标,一般只需要192和512两个尺寸即可 "src": "/img/192.png", "sizes": "192x192", "type": "image/png" }, { "src": "/img/512.png", "sizes": "512x512", "type": "image/png" }] } ``` 3. 在页面头部添加 ``` <link rel="manifest" href="/manifest.json"> <meta name="theme-color" content="这里写网站UI专题色16进制颜色值"> ``` 4. 添加`serviceworker.js`文件 ``` 'use strict'; //缓存版本号,随着页面的更改,如果要更新缓存,请修改这里的版本号 const version = 'v20190212'; const __DEVELOPMENT__ = false; const __DEBUG__ = false; //配置两个离线用资源 const offlineResources = ['/', '/offline.html', '/offline.svg']; const ignoreFetch = [ //忽略抓取的URL或目录,请酌情修改 /https?:\/\/xiongzhang.baidu.com\//, /https?:\/\/ae.bdstatic.com\//, /https?:\/\/msite.baidu.com\//, /https?:\/\/s.bdstatic.com\//, /https?:\/\/timg01.bdimg.com\//, /https?:\/\/zz.bdstatic.com\//, /https?:\/\/hm.baidu.com\//, /https?:\/\/jspassport.ssl.qhimg.com\//, /https?:\/\/s.ssl.qhres.com\//, /https?:\/\/changyan.itc.cn\//, /https?:\/\/changyan.sohu.com\//, /.php$/, /more/, ]; function onInstall(event) { log('install event in progress.'); event.waitUntil(updateStaticCache()); } function updateStaticCache() { return caches.open(cacheKey('offline')).then((cache) => { return cache.addAll(offlineResources); }).then(() => { log('installation complete!'); }); } function onFetch(event) { const request = event.request; if (shouldAlwaysFetch(request)) { event.respondWith(networkedOrOffline(request)); return; } if (shouldFetchAndCache(request)) { event.respondWith(networkedOrCached(request)); return; } event.respondWith(cachedOrNetworked(request)); } function networkedOrCached(request) { return networkedAndCache(request).catch(() => { return cachedOrOffline(request) }); } function networkedAndCache(request) { return fetch(request).then((response) => { var copy = response.clone(); caches.open(cacheKey('resources')).then((cache) => { cache.put(request, copy); }); log("(network: cache write)", request.method, request.url); return response; }); } function cachedOrNetworked(request) { return caches.match(request).then((response) => { log(response ? '(cached)' : '(network: cache miss)', request.method, request.url); return response || networkedAndCache(request).catch(() => { return offlineResponse(request) }); }); } function networkedOrOffline(request) { return fetch(request).then((response) => { log('(network)', request.method, request.url); return response; }).catch(() => { return offlineResponse(request); }); } function cachedOrOffline(request) { return caches.match(request).then((response) => { return response || offlineResponse(request); }); } function offlineResponse(request) { log('(offline)', request.method, request.url); if (request.url.match(/\.(jpg|png|gif|svg|jpeg)(\?.*)?$/)) { return caches.match('/offline.svg'); } else { return caches.match('/offline.html'); } } function onActivate(event) { log('activate event in progress.'); event.waitUntil(removeOldCache()); } function removeOldCache() { return caches.keys().then((keys) => { return Promise.all(keys.filter((key) => { return !key.startsWith(version); }).map((key) => { return caches.delete(key); })); }).then(() => { log('removeOldCache completed.'); }); } function cacheKey() { return [version, ...arguments].join(':'); } function log() { if (developmentMode()) { console.log("SW:", ...arguments); } } function shouldAlwaysFetch(request) { return __DEVELOPMENT__ || request.method !== 'GET' || ignoreFetch.some(regex => request.url.match(regex)); } function shouldFetchAndCache(request) { return ~request.headers.get('Accept').indexOf('text/html'); } function developmentMode() { return __DEVELOPMENT__ || __DEBUG__; } log("Hello from ServiceWorker land!", version); self.addEventListener('install', onInstall); self.addEventListener('fetch', onFetch); self.addEventListener("activate", onActivate); ``` 以上文件命名为`serviceworker.js`并保持在网站根目录 5.配置`serviceworker.js`加载,将serviceworker的加载配置内容添加到网站公共JS内: ``` // 延迟注册serviceWorker window.addEventListener('load', function() { if('serviceWorker' in navigator){ navigator.serviceWorker.register('/serviceworker.js').then(function (registration) { console.log('Service Worker Registered,register script: serviceworker.js.'); }).catch(function (error) { // registration failed console.log('Registration failed with ' + error); }); } }); ``` 一旦以上的内容全部搞点,可以通过google的`lighthouse`检查一下,如果serviceworker发生作用,则可以在浏览器查看: ![](https://box.kancloud.cn/dee76f6d571704de111c04eb2d93dc0e_851x460.jpg) 6. 善后优化工作 为了让`serviceworker.js`能保持最新,需要在服务器配置(nginx)禁止浏览器缓存这个文件: ``` location ~* (serviceworker.js)$ { add_header Last-Modified $date_gmt; add_header Cache-Control 'no-store, no-cache, must-revalidate, proxy-revalidate, max-age=0'; if_modified_since off; expires off; etag off; } ``` 另外,结合nginx的push能力,nginx应当主动push俩文件: ``` http2_push /serviceworker.js; http2_push /manifest.json; ```