HEXO支持的PWA官网插件:hexo-offline

  • Github项目地址
    https://github.com/JLHwung/hexo-offline

  • hexo-offline 是一个可以让Hexo博客拥有 PWA 支持的插件,能够默认的把站点中public内的所有静态资源包括 html、css、js、image 等文件缓存起来,达到离线(无网络环境)可访问的效果,拥有像原生APP一般的丝滑体验。

PWA介绍

  • PWA,即 Progressive Web App, 是提升 Web App 的体验的一种新方法,能给用户原生应用的体验。

  • 理解: PWA不是某一项技术,或者某一个新的产物;而是一系列Web技术与标准的集合与应用。通过应用这些新的技术与标准,可以从安全、性能和体验三个方面,优化我们的Web App。所以,其实PWA本质上依然是一个Web App。

PWA包含的技术:

  • Web App Manifest
  • Service Worker
  • Cache API 缓存
  • Push、Notification 推送与通知
  • Background Sync 后台同步
  • 响应式设计

PWA使用前提

你的博客全站资源必须为HTTPS,PWA支持的前提条件。

PWA配置

連結: https://butterfly.js.org/posts/ceeb73f/#PWA

要为Butterfly配上 PWA 特性, 你需要如下几个步骤:

  1. 打开 hexo 工作目录

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

  3. 在根目录创建 hexo-offline.config.cjs 文件,并增加以下内容。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// offline config passed to workbox-build.
module.exports = {
globPatterns: ['**/*.{js,html,css,png,jpg,gif,svg,webp,eot,ttf,woff,woff2}'],
// 静态文件合集,如果你的站点使用了例如 webp 格式的文件,请将文件类型添加进去。
globDirectory: 'public',
swDest: 'public/service-worker.js',
maximumFileSizeToCacheInBytes: 10485760, // 缓存的最大文件大小,以字节为单位。
skipWaiting: true,
clientsClaim: true,
runtimeCaching: [ // 如果你需要加载 CDN 资源,请配置该选项,如果没有,可以不配置。
// CDNs - should be CacheFirst, since they should be used specific versions so should not change
{
urlPattern: /^https:\/\/cdn\.example\.com\/.*/, // 可替换成你的 URL
handler: 'CacheFirst'
}
]
}

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

  1. 在主题配置文件中开启 pwa 选项。
1
2
3
4
5
6
7
pwa:
enable: true
manifest: /img/pwa/manifest.json
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
  1. 在创建source/目录中创建manifest.json文件。
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
{
"name": "string",
"short_name": "Junzhou",
"theme_color": "#49b1f5",
"background_color": "#49b1f5",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "images/pwaicons/36.png",
"sizes": "36x36",
"type": "image/png"
},
{
"src": "images/pwaicons/48.png",
"sizes": "48x48",
"type": "image/png"
},
{
"src": "images/pwaicons/72.png",
"sizes": "72x72",
"type": "image/png"
},
{
"src": "images/pwaicons/96.png",
"sizes": "96x96",
"type": "image/png"
},
{
"src": "images/pwaicons/144.png",
"sizes": "144x144",
"type": "image/png"
},
{
"src": "images/pwaicons/192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "images/pwaicons/512.png",
"sizes": "512x512",
"type": "image/png"
}
],
"splash_pages": null
}

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

  1. 可以通过Chrome插件Lighthouse检查 PWA 配置是否生效以及配置是否正确。
    • 打开博客页面
  • 启动Lighthouse插件 (Lighthouse插件要求至少包含一个 512*512 像素的图标)

PWA 参考文档

  • 关于 PWA(渐进式增强 Web 应用)的更多内容请参閲
  1. Google Tools for Web Developers
  2. Microsoft Edge Developer Documentation
  3. W3C官方规范

构建PWA时遇到的问题及解决方案

Service Worker 路径问题

  • PWA 的 manifest 和 service worker 注册代码都已经正确生成到 HTML 中,但有一个关键问题:Service Worker 注册路径是否正确

  • 查看代码发现:

  • manifest 路径:/img/pwa/manifest.json ✓

  • Service Worker 注册路径:service-worker.js(相对路径)

  • 但 service-worker.js 实际位于 public/service-worker.js,也就是根目录下。

  • 问题在于浏览器访问 http://localhost:4000/ 时,Service Worker 的注册路径是 service-worker.js,这实际上是对的。

manifest.json 路径问题

  • manifest.json 中的图标路径是绝对路径 /web-app-manifest-xxx.png

  • 但这些图标实际在 /img/pwa/ 目录下manifest.json 中的图标路径不正确

  • 当前路径:/web-app-manifest-192x192.png(指向根目录)

  • 实际位置:/img/pwa/web-app-manifest-192x192.png

  • 这会导致浏览器无法找到图标,从而无法正确识别 PWA。

修复方法:

  • 修复前:”src”: “/web-app-manifest-192x192.png”(指向根目录,文件不存在)

  • 修复后:”src”: “/img/pwa/web-app-manifest-192x192.png”(正确路径)

现在 PWA 应该可以正常工作了:

  1. 访问 http://localhost:4000/
  2. 打开 Chrome DevTools → Application → Manifest,应该能看到正确的配置
  3. 在地址栏右侧或浏览器菜单中应该出现”安装”图标

注意:如果浏览器之前已经缓存了错误的 manifest,可能需要:

  • 清除浏览器缓存

  • 或者按 Ctrl+Shift+R 强制刷新

  • 或者在 DevTools 的 Application → Service Workers 中点击 “Unregister” 然后刷新页面

图标格式

1
2
最好不要使用“any maskable”的“purpose”来声明图标。否则,相应图标可能会因内边距过大或过小而无法在某些平台上正确显示。
最好不要使用“any maskable”的“purpose”来声明图标。否则,相应图标可能会因内边距过大或过小而无法在某些平台上正确显示。

图标分离 - 将 purpose 从 “any maskable” 分离为独立的图标:

  • web-app-manifest-192x192.png / web-app-manifest-512x512.png:purpose: “any”
  • maskable-icon-192x192.png / maskable-icon-512x512.png:purpose: “maskable”

屏幕截图问题

  • 控制台报错
1
2
桌面设备不支持信息更丰富的 PWA 安装界面。请至少添加 1 张“form_factor”设置为“wide”的屏幕截图。
移动设备不支持信息更丰富的 PWA 安装界面。请至少添加 1 张如下屏幕截图:“form_factor”未设置或设为“wide”以外的值。

创建 PWA 屏幕截图。根据规范:

  • 桌面端:需要 form_factor: “wide” 的截图(横向,如 1280x720)

  • 移动端:需要 form_factor: “narrow” 或不设置的截图(纵向,如 750x1334)

根据规范,添加路径

1
2
3
4
5
6
7
8
9
10
11
12
13
"screenshots": [
{
"src": "/img/pwa/screenshot-wide.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/img/pwa/screenshot-narrow.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow"
}

manifest.json完整配置参考

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
{
"name": "winwardking's blog",
"short_name": "LwqBlog",
"theme_color": "#ffffff",
"background_color": "#ffffff",
"display": "standalone",
"scope": "/",
"start_url": "/",
"icons": [
{
"src": "/img/pwa/web-app-manifest-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "any"
},
{
"src": "/img/pwa/web-app-manifest-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "any"
},
{
"src": "/img/pwa/maskable-icon-192x192.png",
"sizes": "192x192",
"type": "image/png",
"purpose": "maskable"
},
{
"src": "/img/pwa/maskable-icon-512x512.png",
"sizes": "512x512",
"type": "image/png",
"purpose": "maskable"
}
],
"screenshots": [
{
"src": "/img/pwa/screenshot-wide.png",
"sizes": "1280x720",
"type": "image/png",
"form_factor": "wide"
},
{
"src": "/img/pwa/screenshot-narrow.png",
"sizes": "750x1334",
"type": "image/png",
"form_factor": "narrow"
}
]
}