Service Worker‌ 是一种运行在浏览器背景的脚本,它可以在页面关闭后继续运行,主要用于处理网络请求、缓存资源、推送消息等功能。

认识

Service Worker 也是一个后台运行的脚本,充当一个代理服务器,拦截用户发出的网络请求,比如加载脚本和图片。Service Worker 可以修改用户的请求,或者直接向用户发出回应,不用联系服务器,这使得用户可以在离线情况下使用网络应用。它还可以在本地缓存资源文件,直接从缓存加载文件,因此可以加快访问速度。

条件

要使用 Service Worker‌ 确保网站支持 HTTPS 协议,这是必要条件。

注册

Typecho 使用的话,需要在主题的 header.php 或者 footer.php 里面添加如下代码,以完成 Service Worker 注册。

<script>
    if ('serviceWorker' in navigator) {
        navigator.serviceWorker.register('/serviceworker.js')
            .then(registration => {
                console.log('Service Worker 注册成功:', registration);
            })
            .catch(error => {
                console.log('Service Worker 注册失败:', error);
            });
    }
</script>

新建

在网站的根目录新建 serviceworker.jsoffline.html 两个独立文件,其中js为引入的必需文件,offline为非必需离线文件。

const CACHE_NAME = 'typecho-cache-v2';
const OFFLINE_URL = '/offline.html';

const urlsToCache = [
    '/',
    '/index.php',
    '/usr/themes/你的主题名字/css/style.min.css', /**css文件
    '/usr/themes/你的主题名字/js/script.min.js', /**js文件
    '/usr/uploads/ /**存储在服务器的图片或文件
    '/favicon.ico',
    OFFLINE_URL,
    'https://artalk.bosir.cn/dist/Artalk.js' /**第三方缓存文件
];

self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME)
            .then(cache => cache.addAll(urlsToCache))
            .then(() => self.skipWaiting())
    );
});

self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(name => {
                    if (name !== CACHE_NAME) {
                        return caches.delete(name);
                    }
                })
            );
        }).then(() => self.clients.claim())
    );
});

self.addEventListener('fetch', event => {
    if (event.request.method !== 'GET') return;

    event.respondWith(
        caches.match(event.request).then(response => {
            if (response) return response;

            return fetch(event.request).then(response => {
                if (!response || response.status !== 200 || response.type !== 'basic') {
                    return response;
                }
                const responseToCache = response.clone();
                caches.open(CACHE_NAME).then(cache => cache.put(event.request, responseToCache));
                return response;
            }).catch(() => {
                return caches.match(OFFLINE_URL);
            });
        })
    );
});

offline模式,即离线模式,f12 打开开发者工具后找到 online选项,点击后会出现offine 字样,点击打开离线模式,刷新页面会发现首页依然可以访问,而没有缓存的页面就显示下面的离线页面。

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>水清无鱼</title>
</head>
<body>
    <h1>离线模式</h1>
</body>
</html>

缓存

为了防止在浏览器需要请求新版本的 serviceworker.js 文件时,而文件自身被缓存,所以需要给 serviceworker.js 文件单独指定缓存头 Cache-control: no-storeno-cache 。然后在 Nainx 的 server { 区域内添加:

# serviceworker.js 不设置缓存
location ~* /(.*)/serviceworker\.js {
    add_header Cache-Control no-cache;
    add_header Pragma no-cache;
    add_header Expires 0;
}

添加完毕后,重启Nginx服务器。

更新

在更新 Service Worker 时,需要修改 CACHE_NAME 的版本号以触发缓存更新

const CACHE_NAME = 'typecho-cache-v1';
或
const CACHE_NAME = 'typecho-cache-v2';

总结

整体来说,只要不涉及复杂的主题和js,Typecho 已经足够快,且拥有良好的打开速度。可如果你像我一样比较追求极致性能,喜欢折腾,也可以尝试一下。我的目标是把博客优化到和静态博客一样丝滑,目前缓存过后进入首页只需要100ms以内,en...

题外话

目前 typecho 已经出现了 1.3.0测试版本,没错,就是目前我现在使用的这个版本。不过我并不建议大家进行升级,毕竟BUG有点多。我目前是全站 0 插件,测试版本的兼容性有待考量。且如果php像我一样为最新版,出现的问题可能更多...

参考资料

1.// cloud.tencent.com/developer/article/2005909

2.// www.luolt.cn/archives/1848.html

3.// www.bookstack.cn/read/webapi-tutorial/docs-service-worker.md