title: Hexo博客开启PWA功能
tags:
  - Hexo
  - PWA
categories:
  - Hexo
top_img: false
cover: '/upload/cdn0files/20200723090629.jpg'
copyright: false
abbrlink: cecbe2d2
date: 2020-07-23 08:53:22
updated: 2020-07-23 08:53:22

Hexo版本:4.2.1

Butterfly版本:3.0.0 RC1

了解

使用前先了解一下什么是PWA:

PWA(Progressive Web App)是一种理念,使用多种技术来增强 web app 的功能,可以让网站的体验变得更好,能够模拟一些原生功能,比如通知推送。在移动端利用标准化框架,让网页应用呈现和原生应用相似的体验。

PWA 不能包含原生OS相关代码。PWA 仍然是网站,只是在缓存、通知、后台功能等方面表现更好。Electron 程序相当于包裹OS原生启动器(Launcher)的网站,未来,许多Electron 程序可能转化为 PWA。

启用

在 Hexo 主题 Butterfly 3.0.0 RC1 版本中,作者已经集成了 PWA 特性,只是需要手动开启和设置一些参数。

安装插件

npm install hexo-offline --save 或者 yarn add hexo-offline

修改站点配置文件

修改 _config.yml 文件,增加以下内容:

## offline config passed to sw-precache.
offline:
  maximumFileSizeToCacheInBytes: 10485760 ## 缓存的最大文件大小,以字节为单位
  staticFileGlobs:
    - public/**/*.{js,html,css,png,jpg,gif,svg,webp,eot,ttf,woff,woff2}
  ## 静态文件合集,如果你的站点使用了例如webp格式的文件,请将文件类型添加进去。
  stripPrefix: public
  verbose: true
  runtimeCaching:
    ## CDNs - should be cacheFirst, since they should be used specific versions so should not change
    - urlPattern: /* ## 如果你需要加载CDN资源,请配置该选项,如果没有,可以不配置。
      handler: cacheFirst
      options:
        origin: your_websie_url ## 可替换成你的 url

更多内容请查看 hexo-offline 的官方文档。

配置主题设置

修改 butterfly.yml 文件:

pwa:
  enable: true
  manifest: /img/pwa/manifest.json
  theme_color: "#fff"
  apple_touch_icon: /img/pwa/apple-touch-icon.png
  favicon_32_32: /img/pwa/32.png
  favicon_16_16: /img/pwa/16.png
  mask_icon: /img/pwa/safari-pinned-tab.svg

这里的图标文件需要自己手动添加

配置 JSON 文件

source/img/pwa/ 目录中创建 manifest.json 文件:

{
    "name": "string", //应用全称
    "short_name": "Junzhou", //应用简称
    "theme_color": "#49b1f5", //匹配浏览器的地址栏颜色
    "background_color": "#49b1f5",//加载应用时的背景色
    "display": "standalone",//首选显示模式 其他选项有:fullscreen,minimal-ui,browser
    "scope": "/",
    "start_url": "/",
    "icons": [ //该数组指定icons图标参数,用来时适配不同设备(需为png,至少包含一个192px*192px的图标)
        {
          "src": "pwaicons/36.png", //图标文件的目录,这里的根目录是source/img/pwa/
          "sizes": "36x36",			//在source/img/pwa下新建文件夹pwaicons,把下面的这几个图标文件放进去
          "type": "image/png"
        },
        {
            "src": "pwaicons/48.png",
          "sizes": "48x48",
          "type": "image/png"
        },
        {
          "src": "pwaicons/72.png",
          "sizes": "72x72",
          "type": "image/png"
        },
        {
          "src": "pwaicons/96.png",
          "sizes": "96x96",
          "type": "image/png"
        },
        {
          "src": "pwaicons/144.png",
          "sizes": "144x144",
          "type": "image/png"
        },
        {
          "src": "pwaicons/192.png",
          "sizes": "192x192",
          "type": "image/png"
        },
        {
            "src": "pwaicons/512.png",
            "sizes": "512x512",
            "type": "image/png"
          }
      ],
      "splash_pages": null //配置自定义启动动画。
  }

注意,JSON 文件是不建议有注释的可能会报错,建议删除注释

你也可以通过 Web App Manifest 快速创建 manifest.json( Web App Manifest 要求至少包含一个 512*512 像素的图标)

可以通过 Chrome 插件 Lighthouse 检查 PWA 配置是否生效以及配置是否正确:

  • 打开博客页面
  • 启动 Lighthouse 插件 ( Lighthouse 插件要求至少包含一个 512*512 像素的图标)

关于 PWA(渐进式增强 Web 应用)的更多内容请参阅 Google Tools for Web Developers

警告:如果要执行下面的操作,请卸载 hexo-offline 插件,并删除在 _config.yml 中的配置

另一种方法

需要注意的是,manifest.json 文件和 PWA 配置和上面一模一样,只是不需要 offline 配置和插件而已

开启设置和配置 manifest.json

安装插件

npm install workbox-build gulp --save-dev

创建 gulpfile.js 文件

在博客根目录,创建一个 gulpfile.js 文件

const gulp = require("gulp");
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("build", gulp.series("generate-service-worker"));

创建 sw-template.js 文件

在博客的根目录里创建一个 sw-template.js 文件:

const workboxVersion = '5.1.3';

importScripts(`https://storage.googleapis.com/workbox-cdn/releases/${workboxVersion}/workbox-sw.js`);

workbox.core.setCacheNameDetails({
    prefix: "your name"
});

workbox.core.skipWaiting();

workbox.core.clientsClaim();

workbox.precaching.precacheAndRoute(self.__WB_MANIFEST);

workbox.precaching.cleanupOutdatedCaches();

// Images
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]
            })
        ]
    })
);

// Fonts
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]
            })
        ]
    })
);

// Google Fonts
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]
            })
        ]
    })
);

// Static Libraries
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 文件进主题

配置 butterfly.yml ,添加需要的 css 和 js 文件

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>'

运行

在你运行 hexo g 后,记得要运行 gulp build 这样才会生效

如果安装完插件发现 gulp 命令无法使用的话,请尝试全局安装:npm install --global gulp-cli

作者: Jerry
連結: https://demo.jerryc.me/posts/ceeb73f/#PWA
來源: Butterfly
著作權歸作者所有。商業轉載請聯絡作者獲得授權,非商業轉載請註明出處。