Trivial Musings: 2025-07-05

Published 2025-07-05 15:45 Updated 2025-07-05 17:16 1116 words 6 min read

碎碎念: 期末 · C碎碎念: 期末 · ARandom Notes: Summary of Several Recent Technical AdvancementsChitchat: Today is my birthday.Random Thoughts: Senior Year Coming-of-Age CeremonyRambling Thoughts: February 25, 2026Rambling Thoughts: February 26, 2022Random Thoughts: 26-02-20Rambling: 26-02-18-2Rambling: 26-02-18碎碎念: 26-02-17Rambling: 26-02-15Rambling Thoughts: 26-02-14Chatter: 26-01-16 Friends, Interpersonal RelationshipsRandom Thoughts: 26-01-01 2025, Annual ReviewRamblings: 2025-10-19Rambling: October 17, 2025 (Supplement)碎碎念: 2025-10-13 (补、密码保护)Rambling: Please forgive my introverted self.Rambling Thoughts: 2025-09-23Ramblings: Perfectly Transplanted PureSuck ThemeRambling Diary: 2025-07-16Trivial Musings: 2025-07-05Murmurings: 2025-06-13Random Thoughts: 2025-06-09Rambling Thoughts: 2025-05-18 A photo taken in the evening.Trivial Thoughts: 2025-05-05Rambling: 2025-04-30Chatty: 2025-04-20Chop Chop Nian: 2025-04-19Rambling: April 13, 2025Ramble: 2025-03-09-2Chatter: 2025-03-09Rambling: I'm too lazy to even write an annual summary.Random Thoughts: November 17, 2024Daily Musings: October 8, 2024, to October 18, 2024Ramblings: 2024-09-29Trivial Musings: September 24, 2024Random Thoughts: 2024-09-23Daily Ramblings: 2024-10-05Random Thoughts: 2024-10-03 Drank a taro boba milk teaRandom Thoughts: 2024-10-03 May the world be forever free from war.Rambling: 2024-09-15碎碎念: 2024-09-01Ramblings: 2024-08-29 Who changed my playlist?碎碎念: 2024-08-29 我抑郁症?Ramblings: 2024-08-29Chatter: 2024-08-26Chatter: 2024-08-22Random Thoughts: 2024-08-18Chatter: 2024-08-11Trivial Ramblings: 2024-08-08碎碎念: 2024-08-06 梦Chatter: 2024-08-06Chatter: 2024-08-04Murmurings: 2024-07-21Trivial Musings: July 13, 2024Ramblings: 2024-07-08Trivial Thoughts: 2024-07-03Murmurings: 2024-07-02Stream of Consciousness: July 1, 2024Rambling Thoughts: 2024-06-30Rambling Thoughts: June 28, 2024Chatter: 2024-06-27碎碎念: 2024-06-26Trivial Musings: 2024-06-22碎碎念: 2024-06-20Rambling: 2024-06-18Trivial Murmurs: 2024-06-17Random Thoughts: 2024-06-15Random Thoughts: June 14, 2024Rambling: 2024 High School Senior Rooftop Shouting碎碎念: 2024-06-06Chatter: 2024-05-30 1Random Thoughts: 2024-05-30 twoRambling Diary: 2024-05-27Rambling Thoughts: 2024-05-26碎碎念: 2024-05-23碎碎念: 2024-05-22Rambling Notes: 2024-05-19碎碎念: 2024-05-17Ramblings: 2024-05-14Rambling Thoughts: 2024-05-13Ramble: 2024-05-12碎碎念: 2024-05-10碎碎念: 2024-05-08碎碎念: 2024-05-06碎碎念: 2024-05-05 M:8 三碎碎念: 2024-05-05碎碎念: 2024-05-05 M:7 二Rambling Thoughts: 2024-05-05 M:7 OneRambling: May 2, 2024 Thursday M:3 light rain碎碎念: 生日Rambling: 2024-04-29 M:7Random Thoughts: 2024-04-27 M:8
I've spent half a day finally getting the website's Service Worker cache configuration set up. Previously, I always thought the website's speed wasn't fast enough, often having to wait several seconds for it to load. Background images would always take a delay before loading properly. I've already done my best to maximize the network loading speed, and the first load has been optimized to the fastest possible speed. I've also added cache-control response headers to all resources I can control. However, when opening the site again, some images and JS files still aren't cached, causing them to briefly appear white before loading. Today I went through the hassle of configuring the Service Worker...

Translated by AI model Qwen/Qwen3-8B.

Source Language: Simplified Chinese, Target Language: english, Translation Time: 2026-05-01 03:11

.

AI translation is for reference only. Accuracy is not guaranteed, please refer to the original text.

忙活了半天终于把网站的 Service Worker 缓存配置给弄好了。(暑假放假的第一天就折腾这些,下个月我就是高三学生了)

之前(一直)觉得网站的速度不够快,总要等好几秒才加载完,背景图总是卡下才加载好。我已经尽自己能力把网络加载速度弄到最大了(在不换主题不删功能的条件下),首次加载已经做到最快加载了。也把我能控制地资源全加上了 cache-control 响应头,但第二次打开总有些图片啊、js 啊没缓存,比如图片的白一下才显示出来。

今天就去折腾 Service Worker 了,使得博客的访问速度德到大大的提升,虽然不知为何有时图片等突然变得加载很慢,但总的来说比之前快了不少。

Service Worker 的代码我贴在这了,可以用作参考。

const swconfig = {
    CACHE_VERSION: "v2.0",
    runtimeCaching: [
        // {
        //     urlPattern: /^https:\/\/cdn\.example\.com\/.*/,
        //     handler: "CacheFirst",
        //     maxAgeSeconds: 60 * 60 * 24 * 365
        // },
        // {
        //     urlPattern: /https:\/\/blog.ksable.top\//gi,
        //     handler: "NetworkFirst",
        //     maxAgeSeconds: 60 * 60 * 24 * 7
        // },
        {
            urlPattern: RegExp('^https://www.favicon.vip/get.php'),
            handler: "CacheFirst",
            maxAgeSeconds: 60 * 60 * 24 * 365
        },
        {
            urlPattern: RegExp('^https://image.thum.io/'),
            handler: "CacheFirst",
            maxAgeSeconds: 60 * 60 * 24 * 30
        },
        {
            urlPattern: /https?:\/\/[^\/]+\/.*\.(png|jpg|jpeg|gif|svg|ico|woff2|ttf|js|css?)(\?.*)?/,
            handler: "CacheFirst",
            maxAgeSeconds: 60 * 60 * 24 * 365
        },
        {
            urlPattern: /^https:\/\/ik.imagekit.io\//,
            handler: "CacheFirst",
            maxAgeSeconds: 0
        },
        {
            urlPattern: /^https:\/\/weavatar.com\/avatar\//,
            handler: "CacheFirst",
            maxAgeSeconds: 60 * 60 * 24 * 30
        },
        {
            urlPattern: /^https:\/\/assets.ksable.top\/js\/my-js.js/,
            handler: "CacheFirst",
            maxAgeSeconds: 60 * 60 * 24 * 1
        }
    ],
    exclude: [
        /^https:\/\/blog.ksable.top\/sw.js/gi
    ],
    precacheUrls: [
        '/content.json',   // 预缓存的内容JSON文件
        '/offline.html'    // 离线页面
    ]
};

const CACHE_NAME = `${swconfig.CACHE_VERSION}-cache`;
const CACHE_META_KEY = 'cache-meta';
const OFFLINE_URL = '/offline.html';

// 匹配请求对应的规则
function matchRule(request) {
    const url = request.url;

    // 检查排除规则
    for (const pattern of swconfig.exclude) {
        if (pattern.test(url)) return null;
    }

    // 反向遍历确保后面的规则优先级更高
    for (let i = swconfig.runtimeCaching.length - 1; i >= 0; i--) {
        const rule = swconfig.runtimeCaching[i];
        if (rule.urlPattern.test(url)) {
            return rule;
        }
    }

    return null;
}

// 获取缓存元数据
async function getCacheMeta() {
    const cache = await caches.open(CACHE_NAME);
    const response = await cache.match(CACHE_META_KEY);
    return response ? await response.json() : {};
}

// 更新缓存元数据
async function updateCacheMeta(url, timestamp) {
    const cache = await caches.open(CACHE_NAME);
    const meta = await getCacheMeta();

    meta[url] = {
        timestamp,
        cachedAt: Date.now()
    };

    await cache.put(
        CACHE_META_KEY,
        new Response(JSON.stringify(meta), {
            headers: { 'Content-Type': 'application/json' }
        })
    );
}

// 后台更新缓存
async function backgroundUpdate(request, rule) {
    try {
        const cache = await caches.open(CACHE_NAME);
        const cachedResponse = await cache.match(request);

        if (!cachedResponse) return;

        // 准备验证头
        const headers = new Headers();
        const etag = cachedResponse.headers.get('ETag');
        const lastModified = cachedResponse.headers.get('Last-Modified');

        if (etag) headers.set('If-None-Match', etag);
        if (lastModified) headers.set('If-Modified-Since', lastModified);

        // 发送验证请求
        const networkResponse = await fetch(request, {
            headers,
            cache: 'no-cache'
        });

        if (networkResponse.status === 304) {  // 未修改
            await updateCacheMeta(request.url, Date.now());
        } else if (networkResponse.ok) {  // 资源已更新
            const responseClone = networkResponse.clone();
            await cache.put(request, responseClone);
            await updateCacheMeta(request.url, Date.now());
        }
    } catch (error) {
        console.error('Background update failed:', error);
    }
}

// 安装阶段 - 初始化缓存
self.addEventListener('install', event => {
    event.waitUntil(
        caches.open(CACHE_NAME).then(cache => {
            // 初始化缓存元数据
            return cache.put(
                CACHE_META_KEY,
                new Response('{}', {
                    headers: { 'Content-Type': 'application/json' }
                })
            ).then(() => {
                // 预缓存关键资源
                return cache.addAll(swconfig.precacheUrls);
            });
        }).then(() => self.skipWaiting())
    );
});

// 激活阶段 - 清理旧缓存
self.addEventListener('activate', event => {
    event.waitUntil(
        caches.keys().then(cacheNames => {
            return Promise.all(
                cacheNames.map(name => {
                    if (name !== CACHE_NAME && name.startsWith('v')) {
                        return caches.delete(name);
                    }
                })
            );
        }).then(() => self.clients.claim())
    );
});

// 请求处理
self.addEventListener('fetch', event => {
    const request = event.request;

    // 只处理GET请求
    if (request.method !== 'GET') return;

    // 特殊处理导航请求的离线回退
    if (request.mode === 'navigate') {
        event.respondWith(
            (async () => {
                try {
                    // 首先尝试网络请求
                    const networkResponse = await fetch(request);
                    return networkResponse;
                } catch (error) {
                    // 网络失败时返回离线页面
                    const offlineResponse = await caches.match(OFFLINE_URL);
                    if (offlineResponse) {
                        return offlineResponse;
                    }
                    // 如果离线页面也未缓存,返回简单错误响应
                    return new Response('Offline', {
                        status: 503,
                        statusText: 'Service Unavailable'
                    });
                }
            })()
        );
        return;
    }
    // 匹配缓存规则
    const rule = matchRule(request);
    if (!rule) return;

    // 处理不同缓存策略
    switch (rule.handler) {
        case 'NetworkOnly':
            event.respondWith(fetch(request));
            break;

        case 'CacheOnly':
            event.respondWith(
                caches.match(request).then(response => response || Response.error())
            );
            break;

        case 'NetworkFirst':
            event.respondWith(
                fetch(request).catch(() => caches.match(request))
            );
            break;

        case 'CacheFirst':
        default:
            event.respondWith((async () => {
                const cache = await caches.open(CACHE_NAME);
                const cachedResponse = await cache.match(request);
                const meta = await getCacheMeta();
                const url = request.url;
                const metaEntry = meta[url];

                // 如果找到缓存
                if (cachedResponse) {
                    // 检查是否需要后台更新
                    if (rule.maxAgeSeconds > 0 && metaEntry) {
                        const age = (Date.now() - metaEntry.cachedAt) / 1000;
                        if (age > rule.maxAgeSeconds) {
                            // 后台更新不影响当前响应
                            event.waitUntil(backgroundUpdate(request, rule));
                        }
                    }
                    return cachedResponse;
                }

                // 没有缓存则请求网络
                try {
                    const networkResponse = await fetch(request);
                    if (networkResponse.ok) {
                        const responseClone = networkResponse.clone();
                        await cache.put(request, responseClone);
                        await updateCacheMeta(url, Date.now());
                    }
                    return networkResponse;
                } catch (error) {
                    return Response.error();
                }
            })());
            break;
    }
});

If you enjoyed this, leave a comment~