碎碎念: 2025-07-05

发表于 2025-07-05 15:45 更新于 2025-07-05 17:16 1082 字 6 min read

碎碎念: 26-02-22碎碎念: 26-02-20碎碎念: 26-02-18-2碎碎念: 26-02-18碎碎念: 26-02-17碎碎念: 26-02-15碎碎念: 26-02-14碎碎念: 26-01-16 朋友、人际交往碎碎念: 26-01-01 2025年,年度回顾碎碎念: 2025-10-19碎碎念: 2025-10-17 (补)碎碎念: 2025-10-13 (补、密码保护)碎碎念: 请原谅内向的我碎碎念: 2025-09-23碎碎念: 完美移植 PureSuck 主题碎碎念: 2025-07-16碎碎念: 2025-07-05碎碎念: 2025-06-13碎碎念: 2025-06-09碎碎念: 2025-05-18 一张傍晚的照片碎碎念: 2025-05-05碎碎念: 2025-04-30碎碎念: 2025-04-20碎碎念: 2025-04-19碎碎念: 2025-04-13碎碎念: 2025-03-09-2碎碎念: 2025-03-09碎碎念: 我太懒了,连个年终总结都没写碎碎念: 2024-11-17碎碎念: 2024-10-8 至 2024-10-18碎碎念: 2024-09-29碎碎念: 2024-09-24碎碎念: 2024-09-23碎碎念: 2024-10-05碎碎念: 2024-10-03 喝上了芋泥啵啵奶绿碎碎念: 2024-10-03 愿世界永无战事碎碎念: 2024-09-15碎碎念: 2024-09-01碎碎念: 2024-08-29 谁动我歌单?碎碎念: 2024-08-29 我抑郁症?碎碎念: 2024-08-29碎碎念: 2024-08-26碎碎念: 2024-08-22碎碎念: 2024-08-18碎碎念: 2024-08-11碎碎念: 2024-08-08碎碎念: 2024-08-06 梦碎碎念: 2024-08-06碎碎念: 2024-08-04碎碎念: 2024-07-21碎碎念: 2024-07-13碎碎念: 2024-07-08碎碎念: 2024-07-03碎碎念: 2024-07-02碎碎念: 2024-07-01碎碎念: 2024-06-30碎碎念: 2024-06-28碎碎念: 2024-06-27碎碎念: 2024-06-26碎碎念: 2024-06-22碎碎念: 2024-06-20碎碎念: 2024-06-18碎碎念: 2024-06-17碎碎念: 2024-06-15碎碎念: 2024-06-14碎碎念: 2024 年的高三喊楼碎碎念: 2024-06-06碎碎念: 2024-05-30 一碎碎念: 2024-05-30 二碎碎念: 2024-05-27碎碎念: 2024-05-26碎碎念: 2024-05-23碎碎念: 2024-05-22碎碎念: 2024-05-19碎碎念: 2024-05-17碎碎念: 2024-05-14碎碎念: 2024-05-13碎碎念: 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 二碎碎念: 2024-05-05 M:7 一碎碎念: 2024-05-02 星期四 M:3 小雨碎碎念: 2024-04-29 M:7碎碎念: 2024-04-27 M:8
この投稿は「日本語」では表示できません。元の投稿を表示しています。
忙活了半天终于把网站的 Service Worker 缓存配置给弄好了。 之前(一直)觉得网站的速度不够快,总要等好几秒才加载完,背景图总是卡下才加载好。我已经尽自己能力把网络加载速度弄到最大了,首次加载已经做到最快加载了。也把我能控制地资源全加上了 cache-control 响应头,但第二次打开总有些图片啊、js啊没缓存,比如图片的白一下才显示出来。 今天就去折腾 Service Wor...

忙活了半天终于把网站的 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;
    }
});

喜欢的话,留下你的评论吧~

© 2024 - 2026 kissablecho
Powered by theme astro-koharu · Inspired by Shoka