0%

HEXO笔记②-Hexo博客添加PWA支持

此示例不借助hexo-service-workerhexo-offlinehexo-pwa等插件实现,自己编写 manifest.webmanifest(menifest.json)和 serviceWorker.js 并引入页面

如果使用上述插件,可以参考此博文

编写 manifest.json

  1. Web App Manifest说明文档参考MDN
  2. manifest.json简易生成器Web App Manifest Generator
  3. 如果不使用主题,把生成的manifest.json放置在项目source文件夹根目录,hexo发布时会放置在发布文件夹根目录(跟index.html同一级目录)
  4. 如果使用主题,比如NexT主题放在./themes/next,则把manifest.json放置在./themes/next/source/目录下

编写一个 Service Worker 管理器 js,用于管理 Service Worker 的安装、更新、卸载以及 Service Worker 的同步、通知、Web 推送等任务

sw.reg.mgr.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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
(function () {
/**
* 注册Service Worker
* @param {*} config 配置项目
*/
const register = (config) => {
//浏览器是否支持
if ("serviceWorker" in navigator) {
let swUrl = `${config.path}/${config.name}?v=${config.ver}`;
// 通过ver的变化来强制进行更新操作,每次更新sw.js时进行ver+1操作
navigator.serviceWorker
.register(swUrl)
.then((swReg) => {
console.log("Service Worker注册成功");
if (config && config.onRegister) {
config.onRegister(swReg);
}

swReg.onupdatefound = (e) => {
let installingWorker = swReg.installing;
if (!installingWorker) {
return;
}
installingWorker.onstatechange = (e) => {
if (installingWorker.state === "installed") {
if (navigator.serviceWorker.controller) {
config.log &&
console.log("Service Worker已安装更新");
if (config && config.onUpdate) {
config.onUpdate(swReg);
}
} else {
config.log &&
console.log("Service Worker已安装");
if (config && config.onSuccess) {
config.onSuccess(swReg);
}
}
}
};
};
})
.catch((err) => {
if (config && config.onError) {
config.onError(err);
}
console.error("Service Worker注册期间发生错误:", err);
});

if (navigator.serviceWorker.controller) {
navigator.serviceWorker.controller.postMessage(
"这是从window环境postMessage()"
);

const channel = new MessageChannel();
channel.port1.onmessage = (e) => {
console.log(
`${new Date().toLocaleString()}, index.html port1收到消息:${
e.data
}`
);
};
// 向port2发送消息
navigator.serviceWorker.controller.postMessage(
"这是index.html向port2 postMessage()",
[channel.port2]
);
}

navigator.serviceWorker.onmessage = (e) => {
console.log(
`${new Date().toLocaleString()}, index.html收到的消息: ${
e.data
}`
);
};
}
};

/**
* 注销Service Worker
*/
const unregister = () => {
if ("serviceWorker" in navigator) {
navigator.serviceWorker.ready.then((swReg) => {
swReg.unregister((result) => {
result && console.log("Service Worker注销成功");
});
});
}
};

const broad = new BroadcastChannel("c1");
broad.onmessage = (e) => {
console.log(
`${new Date().toLocaleString()}, index.html收到的Service Worker广播消息:${
e.data
}`
);
};

//注册sw.js
register({
ver: 1,
path: "",
name: "sw.js",
log: true,
onRegister: () => {},
onSuccess: () => {},
onUpdate: () => {},
onError: (err) => {},
});
})();

编写 Service Worker 文件,这里借助谷歌Workbox帮助编写和管理 Service Worker

Service Worker详细说明参照MDN

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
importScripts("https://g.alicdn.com/kg/workbox/3.3.0/workbox-sw.js");

if (workbox) {
workbox.setConfig({
modulePathPrefix: "https://g.alicdn.com/kg/workbox/3.3.0/",
});

// 修改默认配置
workbox.core.setCacheNameDetails({
prefix: "TenBlog",
suffix: "v1",
precache: "precache",
runtime: "runtime",
});

// 预先缓存资源
workbox.precaching.precache([
"/",
"/index.html",
"/404.html",
"/offline.html",
]);

// 从缓存中读取资源的同时发送网络请求更新本地缓存
workbox.routing.registerRoute(
new RegExp("^https?://tanwucheng.github.io/?$"),
workbox.strategies.staleWhileRevalidate()
);

// 网络优先
workbox.routing.registerRoute(
new RegExp(".*.html"),
workbox.strategies.networkFirst()
);

// 缓存优先
workbox.routing.registerRoute(
new RegExp(".*.(?:js|css|jpg|png|gif)"),
workbox.strategies.cacheFirst()
);

/**
* Handling Offline Page fallback
*/
this.addEventListener("fetch", (event) => {
if (
event.request.mode === "navigate" ||
(event.request.method === "GET" &&
event.request.headers.get("accept").includes("text/html"))
) {
event.respondWith(
fetch(event.request.url).catch((error) => {
console.log("fetch error", error);
// Return the offline page
return caches.match("/offline.html");
})
);
} else {
// Respond with everything else if we can
event.respondWith(
caches.match(event.request).then(function (response) {
return response || fetch(event.request);
})
);
}
});
}

Service Worker 文件(sw.js)和 Service Worker(sw.reg.mgr.jd)管理器放在跟 manifest.json 同一级目录

页面引入 manifest.json 和 Service Worker

  • 如果不使用主题,则在运行hexo generate命令后到./public 文件夹下修改index.html,在<head> 节点结束前引入manifest.json,在 <body> 结束前引入Service Worker管理器代码,应该有更简便的方式,还在查找资料中

  • 如果使用主题,比如NexT主题放在./themes/next,则修改./themes/next/layout/_layout.swig 文件,在 <head> 节点结束前引入manifest.json,在 <body> 结束前引入Service Worker管理器代码

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
<html>
<head>
<!--Other code here-->
<link rel="manifest" href="/manifest.json" />
</head>
<body>
<!--Other code here-->
<script type="module">
(function () {
if (window.addEventListener) {
window.addEventListener("load", () => {
let script = document.createElement("script");
// script.src = `/sw.reg.mgr.js?t=${new Date()}`; // 无缓存引用
script.src = `/sw.reg.mgr.js`; // 无缓存引用
script.async = true;
script.type = "text/javascript";
script.crossOrigin = "anonymous";
document.head.insertBefore(
script,
document.head.firstChild
);
});
}
})();
</script>
</body>
</html>
-------------本文结束感谢您的阅读-------------