惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Google DeepMind News
Google DeepMind News
MongoDB | Blog
MongoDB | Blog
有赞技术团队
有赞技术团队
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
人人都是产品经理
人人都是产品经理
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
B
Blog RSS Feed
T
Tor Project blog
T
Threat Research - Cisco Blogs
Microsoft Azure Blog
Microsoft Azure Blog
M
MIT News - Artificial intelligence
V
Vulnerabilities – Threatpost
Project Zero
Project Zero
C
CXSECURITY Database RSS Feed - CXSecurity.com
The Register - Security
The Register - Security
Latest news
Latest news
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
The Hacker News
The Hacker News
Google DeepMind News
Google DeepMind News
L
LINUX DO - 最新话题
U
Unit 42
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
博客园 - 司徒正美
T
Tenable Blog
H
Hacker News: Front Page
B
Blog
宝玉的分享
宝玉的分享
C
Check Point Blog
美团技术团队
cs.CV updates on arXiv.org
cs.CV updates on arXiv.org
C
CERT Recently Published Vulnerability Notes
P
Proofpoint News Feed
The GitHub Blog
The GitHub Blog
G
GRAHAM CLULEY
Google Online Security Blog
Google Online Security Blog
Security Archives - TechRepublic
Security Archives - TechRepublic
P
Proofpoint News Feed
GbyAI
GbyAI
酷 壳 – CoolShell
酷 壳 – CoolShell
Hugging Face - Blog
Hugging Face - Blog
Y
Y Combinator Blog
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Hacker News - Newest:
Hacker News - Newest: "LLM"
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
Scott Helme
Scott Helme
L
Lohrmann on Cybersecurity
量子位
A
About on SuperTechFans
V2EX - 技术
V2EX - 技术
T
The Exploit Database - CXSecurity.com

方糖博客

站点搬家记 龙虾养殖记 博客折腾记之三 博客折腾记之二 你好,2026 让博客再快一些? 国庆第一天 为Wordpress升级组件 宝塔面板 Onedrive 插件授权失败的另解之法 汽车年检 博客启用新域名fangtang.net EdgeOne + Cloudflare,拉我一把 买了台Netcup的机子 今天有点背 博客折腾记之一 最近的事@10 又想换域名了 五一劳动节快乐 暂时取消网站缓存 最近的事@9 将说说添加到首页 想给网站提提速 最近的天气 | 方糖博客 修改说说样式 | 方糖博客 博客这两天终于安静下来了 晚上出来溜溜 Cursor让人疯狂的工具 说说也可以图文并茂了 垃圾评论回收站 忙碌的一天 Cursor赋能:制作个人单栏WordPress主题 用Cursor折腾了一个主题 折腾 博客搬家 折腾 Rustdesk远程 折腾 博客功能和样式修改 折腾 博客说说(原笔记)加标题 折腾 博客折腾记 吐槽 哪吒2 折腾 家里的自组NAS 日常-新的一年 折腾 再一次折腾我的NAS 日常 冬至夜 Wordpress外链跳转到中间页 折腾 升级PHP 吐槽 插件 折腾 更换LOGO 没事敲打一下自己的博客 吐槽 评论 吐槽 iphone15信号问题
给博客加一个Service Worker耍耍
Feng · 2025-03-08 · via 方糖博客

先说个题外话,上次抄了沉沦大佬的作业《适配评论头像本地缓存功能》,实现了头像下载并缓存到主机上,加快了头像的加载速度,不过在我的博客上却碰了一些问题:一些没有头像或头像链接失效的,就拖慢了页面有显示,最终交由DeepSeek修正了下,完美解决了我的这个问题,涉及的部分代码如下:

// 定义头像缓存目录
define('AVATAR_CACHE_DIR', WP_CONTENT_DIR . '/avatars');
// 定义头像缓存URL
define('AVATAR_CACHE_URL', WP_CONTENT_URL . '/avatars');
// 定义头像缓存有效期,默认7天
define('AVATAR_CACHE_LIFETIME', apply_filters('avatar_cache_lifetime', 7 * DAY_IN_SECONDS));
// 定义头像最大存储时间,默认30天
define('AVATAR_CLEANUP_AGE', apply_filters('avatar_max_age', 30 * DAY_IN_SECONDS));
// 定义默认头像URL
define('DEFAULT_AVATAR_URL', '/wp-content/uploads/2025/03/default.webp');

/**
 * 缓存用户头像到本地
 *
 * @param string $email 用户邮箱
 * @param array $args 头像参数
 * @return string 头像URL
 */
function cache_avatar_locally($email, $args = array()) {
    // 检查WP_Filesystem函数是否存在,若不存在则加载文件操作类
    if (!function_exists('WP_Filesystem')) {
        require_once ABSPATH . 'wp-admin/includes/file.php';
    }
    WP_Filesystem();
    global $wp_filesystem;
    // 默认头像参数
    $default_args = array(
        'default' => DEFAULT_AVATAR_URL,
        'size'    => 96,
        'rating'  => 'g',
        'scheme'  => null
    );
    $args = wp_parse_args($args, $default_args);
    $args = apply_filters('avatar_cache_params', $args, $email);
    // 对参数进行排序,以确保生成一致的hash值
    $sorted_args = ksort_deep($args);
    $param_string = http_build_query($sorted_args);
    // 根据用户邮箱和参数生成唯一的hash值
    $hash = md5(strtolower(trim($email)) . '|' . $param_string);
    $base_path = AVATAR_CACHE_DIR . '/' . $hash;
    // 查找是否已有缓存的头像文件
    $avatar_file = find_existing_avatar($base_path);
    if ($avatar_file) {
        // 如果头像文件存在且需要刷新,则安排刷新操作
        if (should_refresh_avatar($avatar_file)) {
            schedule_avatar_refresh($email, $args);
        }
        // 返回头像的URL
        return str_replace(AVATAR_CACHE_DIR, AVATAR_CACHE_URL, $avatar_file);
    }
    // 获取Gravatar头像URL
    $gravatar_url = get_avatar_url($email, $args);
    // 获取头像内容
    $response = wp_remote_get($gravatar_url, array('timeout' => 10));
    // 如果请求失败,则返回默认头像URL
    if (is_wp_error($response) || 200 !== wp_remote_retrieve_response_code($response)) {
        return $args['default'];
    }
    // 获取图片类型
    $content_type = wp_remote_retrieve_header($response, 'content-type');
    // 获取文件扩展名
    $ext = get_extension_from_mime($content_type);
    // 设置缓存头像的路径
    $avatar_path = "{$base_path}.{$ext}";
    $avatar_dir = dirname($avatar_path);
    // 如果缓存目录不存在,则创建目录
    if (!file_exists($avatar_dir)) {
        wp_mkdir_p($avatar_dir);
    }
    // 将头像内容保存到本地
    if ($wp_filesystem->put_contents($avatar_path, wp_remote_retrieve_body($response), 0644)) {
        return AVATAR_CACHE_URL . "/{$hash}.{$ext}";
    }
    // 如果保存失败,则返回默认头像URL
    return $args['default'];
}

/**
 * 深度排序数组
 *
 * @param array $array 输入数组
 * @return array 排序后的数组
 */
function ksort_deep($array) {
    if (is_array($array)) {
        ksort($array);
        foreach ($array as $key => $value) {
            $array[$key] = ksort_deep($value);
        }
    }
    return $array;
}

/**
 * 查找已存在的头像文件
 *
 * @param string $base_path 头像缓存基础路径
 * @return string|null 头像文件路径,若没有则返回null
 */
function find_existing_avatar($base_path) {
    $files = glob("{$base_path}.*");
    foreach ($files as $file) {
        if (is_file($file) && in_array(pathinfo($file, PATHINFO_EXTENSION), ['jpg', 'jpeg', 'png', 'webp'])) {
            return $file;
        }
    }
    return null;
}


好了,言归正传,开始今天的唠叨话题吧——给博客加个Service Workers,网上类似的代码有很多,有些甚至还要收费(小小鄙视一下),好在现在各种AI工具加持,终于有了适合自己的代码,大概的步骤如下:

  • 在网站根目录下创建sw.js文件,这个在移动终端上也适应了(之前出现了电脑端访问时头像显示正常,在移动端时无法显示),大致的内容如下:
// 配置中心
const CACHE_NAME = 'wp-sw-desktop-v2';
const OFFLINE_URL = '/offline.html';
const VALID_SCHEMES = ['http:', 'https:'];
const STATIC_EXTS = ['css', 'js', 'png', 'jpg', 'webp', 'svg'];
const UPLOAD_PATHS = ['/wp-content/uploads/', '/wp-content/avatars/'];
// 预缓存清单(桌面端专用)
const PRECACHE_RESOURCES = [
  '/',
  '/index.php',
  '/wp-content/themes/主题/style.css',
  '/wp-content/themes/主题/static/helper.js',
  '/wp-content/themes/主题/static/lang.js',
  '/wp-content/themes/主题/static/script.js',
  '/wp-content/themes/主题/static/modules.js',
  '/wp-content/themes/主题/static/package.js',
  '/favicon.ico',
  OFFLINE_URL
];
// 安装阶段:增强型预缓存
self.addEventListener('install', (event) => {
  event.waitUntil(
    caches.open(CACHE_NAME)
      .then(cache => {
        return Promise.all(
          PRECACHE_RESOURCES.map(url => 
            cache.add(url).catch(err => 
              console.error(`预缓存失败: ${url}`, err)
            )
          )
        );
      })
      .then(() => self.skipWaiting())
      .catch(err => {
        console.error('安装失败:', err);
        self.skipWaiting();
      })
  );
});
// 激活阶段:清理旧缓存
self.addEventListener('activate', (event) => {
  event.waitUntil(
    caches.keys().then(keys => 
      Promise.all(keys.map(key => 
        key !== CACHE_NAME && caches.delete(key)
      ))
    ).then(() => self.clients.claim())
  );
});
// 请求处理:智能缓存策略
self.addEventListener('fetch', event => {
  const { request } = event;
  const url = new URL(request.url);
  // 安全过滤
  if (!VALID_SCHEMES.includes(url.protocol)) return;
  // 缓存策略路由
  event.respondWith(
    cacheFirstWithUpdate(request)
  );
});
// 核心缓存策略
async function cacheFirstWithUpdate(request) {
  try {
    // 优先返回缓存
    const cached = await caches.match(request);
    if (cached) return cached;
    // 网络请求
    const netRes = await fetch(request);
    // 动态缓存
    if (shouldCache(request, netRes)) {
      cachePut(request, netRes.clone());
    }
    return netRes;
  } catch (err) {
    // 离线回退
    return handleOffline(request);
  }
}
// 缓存条件判断
function shouldCache(req, res) {
  const url = new URL(req.url);
  return res.status === 200 &&
    (STATIC_EXTS.some(ext => url.pathname.endsWith(`.${ext}`)) ||
     UPLOAD_PATHS.some(path => url.pathname.startsWith(path)));
}
// 安全存储
async function cachePut(req, res) {
  try {
    const cache = await caches.open(CACHE_NAME);
    await cache.put(req, res);
  } catch (err) {
    console.error('缓存存储失败:', req.url, err);
  }
}
// 离线处理
async function handleOffline(req) {
  if (req.destination === 'document') {
    return (await caches.match(OFFLINE_URL)) || Response.redirect(OFFLINE_URL);
  }
  return new Response('', { 
    status: 503,
    headers: { 'Content-Type': 'text/html' } 
  });
}
  • 在网站根目录下创建 offline.html,具体的代码如下:
<!-- 创建/offline.html -->
<!DOCTYPE html>
<html>
<head>
  <title>离线模式</title>
  <meta charset="utf-8">
</head>
<body>
  <h1>您处于离线状态,部分内容可能无法显示</h1>
</body>
</html>
  • 在主题的header.php文件加入以下代码:
<script>
// 移动端检测函数(综合 UA 和屏幕尺寸)
function isMobile() {
  const ua = navigator.userAgent;
  const isMobileUA = /Android|iPhone|iPad|iPod|Windows Phone/i.test(ua);
  const isMobileScreen = window.matchMedia('(max-width: 768px)').matches;
  return isMobileUA || isMobileScreen;
}
// Service Worker 注册控制
if ('serviceWorker' in navigator && !isMobile()) {
  navigator.serviceWorker.register('/sw.js')
    .then(reg => console.log('SW 已注册:', reg))
    .catch(err => console.error('SW 注册失败:', err));
} else if (isMobile()) {
  // 强制注销移动端可能存在的旧 SW
  navigator.serviceWorker.getRegistrations().then(registrations => {
    registrations.forEach(reg => reg.unregister());
    console.log('已禁用移动端 Service Worker');
  });
}
</script>

一切添加完毕后,打开首页(建议清除一下缓存),按F12键--Application--Service Workers,如果没有报错,Status显示小绿圆的话,说明就OK了,当然以上代码我在目前用的主题是可以的,不代表其它主题就没有问题,另外加了Service Worekers能不能明显提升加载速度,这个还得再使用一段时间看看,个人感觉不是很明显(这个主题用起来本身也不是很慢,VPS线路好的话更加没的说),所以是骡子是马,还得拉出来溜溜不是嘛。