pwa介绍
渐进式网络应用程式(英语:Progressive Web Apps,简称:PWA)是一种普通网页或网站架构起来的网络应用程式,但它可以以传统应用程式或原生移动应用程式形式展示给用户。这种应用程式形态视图将目前最为现代化的浏览器提供的功能与行动装置的体验优势相结合。
当你的网站实现了 PWA,那就代表了
- 用户可以添加你的博客到电脑 / 手机的桌面,以原生应用般的方式浏览你的博客
- 用户本地可以自动生成缓存,二次访问速度大大加快
- 用户可以离线浏览你的博客
下面的 PWA 实现方法借助了 Gulp 插件,在站点有内容更新时,可以弹窗提醒用户刷新页面。
背景
本文使用butterfly主题作为演示
虽然官方对pwa做了很好的集成,但是文档中介绍的并不全面,而且还有许多坑需要自己踩一遍,特此记录下
配置
本文使用Gulp 和 WorkBox组合进行配置pwa
- 克隆现成的butterfly demo项目,默认集成了
hexo-offline和gulp
https://github.com/jerryc127/butterfly.js.org.git
- 去除
hexo-offline,安装workbox-build
1 2
| npm uninstall hexo-offline npm install workbox-build --save-dev
|
1 2
| https://juejin.cn/post/6844903543615258632 https://developer.mozilla.org/en-US/docs/Web/Manifest
|
开启主题相关设置
1 2 3 4 5 6 7 8 9 10 11 12
|
pwa: enable: true manifest: /wang-xiaowu/pwa/site.webmanifest theme_color: "#fff" apple_touch_icon: /wang-xiaowu/pwa/apple-touch-icon.png favicon_32_32: /wang-xiaowu/pwa/favicon-32x32.png favicon_16_16: /wang-xiaowu/pwa/favicon-16x16.png mask_icon: /wang-xiaowu/pwa/safari-pinned-tab.svg
|
所需文件,图片等资源可在此网站进行生成。Favicon Generator for perfect icons on all browsers (realfavicongenerator.net)
Favicon Generator for perfect icons on all browsers 图文教程






- 生成 README.md(此步可以不选,如何选了记得不要把它放在博客下面,避免生成html,可能会报错)


项目改造
下载资源包,并放在上面配置的source目录下,我配置的是/wang-xiaowu/pwa/, 这里不会生成512图标,但是pwa建议需要一个,所以可以自己改个512的图标放里面,并配置site.webmanifest

关于manifest,还有两个地方需要配置

start_url和scope的解释见 https://juejin.cn/post/6844903543615258632
改造gulpfile.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
| const workbox = require("workbox-build"); gulp.task('generate-service-worker', () => { return workbox.injectManifest({ swSrc: './sw-template.js', swDest: './public/sw.js', globDirectory: './public', globPatterns: [ "**/*.{html,css,js,json,woff2}" ], modifyURLPrefix: { "": "./" } }); });
gulp.task('default', gulp.parallel( 'compress', 'minify-css', 'minify-html', 'generate-service-worker' ))
|
创建 sw-template.js 文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93
| const workboxVersion = '5.1.3';
importScripts(`https://storage.googleapis.com/workbox-cdn/releases/${workboxVersion}/workbox-sw.js`);
workbox.core.setCacheNameDetails({ prefix: "Serok's Blog" });
workbox.core.skipWaiting();
workbox.core.clientsClaim();
workbox.precaching.precacheAndRoute(self.__WB_MANIFEST,{ directoryIndex: null });
workbox.precaching.cleanupOutdatedCaches();
workbox.routing.registerRoute( /\.(?:png|jpg|jpeg|gif|bmp|webp|svg|ico)$/, new workbox.strategies.CacheFirst({ cacheName: "images", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /\.(?:eot|ttf|woff|woff2)$/, new workbox.strategies.CacheFirst({ cacheName: "fonts", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /^https:\/\/fonts\.googleapis\.com/, new workbox.strategies.StaleWhileRevalidate({ cacheName: "google-fonts-stylesheets" }) ); workbox.routing.registerRoute( /^https:\/\/fonts\.gstatic\.com/, new workbox.strategies.CacheFirst({ cacheName: 'google-fonts-webfonts', plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.routing.registerRoute( /^https:\/\/cdn\.jsdelivr\.net/, new workbox.strategies.CacheFirst({ cacheName: "static-libs", plugins: [ new workbox.expiration.ExpirationPlugin({ maxEntries: 1000, maxAgeSeconds: 60 * 60 * 24 * 30 }), new workbox.cacheableResponse.CacheableResponsePlugin({ statuses: [0, 200] }) ] }) );
workbox.googleAnalytics.initialize();
|
注意:把 prefix 修改为你博客的名字(最好用英文)。
添加 js 进主题
1 2 3 4 5
| inject: head: - '<style type="text/css">.app-refresh{position:fixed;top:-2.2rem;left:0;right:0;z-index:99999;padding:0 1rem;font-size:15px;height:2.2rem;transition:all .3s ease}.app-refresh-wrap{display:flex;color:#fff;height:100%;align-items:center;justify-content:center}.app-refresh-wrap a{color:#fff;text-decoration:underline;cursor:pointer}</style>' bottom: - '<div class="app-refresh" id="app-refresh"> <div class="app-refresh-wrap"> <label>✨ 网站已更新最新版本 👉</label> <a href="javascript:void(0)" onclick="location.reload()">点击刷新</a> </div></div><script>function showNotification(){if(GLOBAL_CONFIG.Snackbar){var t="light"===document.documentElement.getAttribute("data-theme")?GLOBAL_CONFIG.Snackbar.bgLight:GLOBAL_CONFIG.Snackbar.bgDark,e=GLOBAL_CONFIG.Snackbar.position;Snackbar.show({text:"已更新最新版本",backgroundColor:t,duration:5e5,pos:e,actionText:"点击刷新",actionTextColor:"#fff",onActionClick:function(t){location.reload()}})}else{var o=`top: 0; background: ${"light"===document.documentElement.getAttribute("data-theme")?"#49b1f5":"#1f1f1f"};`;document.getElementById("app-refresh").style.cssText=o}}"serviceWorker"in navigator&&(navigator.serviceWorker.controller&&navigator.serviceWorker.addEventListener("controllerchange",function(){showNotification()}),window.addEventListener("load",function(){navigator.serviceWorker.register("/sw.js")}));</script>'
|
运行查看效果
1
| hexo clean && hexo g && gulp && hexo d
|
而且手机或者电脑浏览器浏览栏也会有提示你安装应用

问题
浏览器出现Manifest: found icon with no valid size的警告或者错误
参考: https://sharechiwai.com/post/2020-05-05-manifest-found-icon-with-no-valid-size/
原因: 要求最小的 size 是 192 x 192
解決方法 : 移除manifest.json文件icons列表中 比 192 x 192 小的对象