[{"data":1,"prerenderedAt":637},["ShallowReactive",2],{"\u002F2026\u002F04\u002Fnuxtserviceworker":3,"index_posts":122,"surround-\u002F2026\u002F04\u002Fnuxtserviceworker":634},{"id":4,"title":5,"body":6,"categories":97,"date":99,"description":100,"draft":101,"extension":102,"image":103,"meta":104,"navigation":106,"path":107,"permalink":103,"published":103,"readingTime":108,"recommend":103,"references":103,"seo":113,"sitemap":114,"stem":115,"subtitle":103,"tags":116,"type":119,"updated":120,"__hash__":121},"content\u002Fposts\u002F2026\u002F04\u002FnuxtServiceWorker.md","用元宝写一个ServiceWorker",{"type":7,"value":8,"toc":83},"minimark",[9,13,17,30,34,39,49,53,60,63,66,73,76],[10,11,12],"h2",{"id":12},"前言",[14,15,16],"p",{},"在前几个月的时候，原本打算使用ServiceWorker来对字体、图片以及部分耗时长的文件来进行缓存，解决在非缓存下的等待时间长问题。因为一些原因导致了没去使用，在前几天我通过元宝来解决这个问题，顺便写了一个要求清单来给大家思考",[18,19,26],"pre",{"className":20,"code":22,"filename":23,"language":24,"meta":25},[21],"language-text","清除之前的思考并重新开发，遵循以下全部要求进行开发一个基于Nuxt SSG模式 + ServiceWorker的TS模块\n1.缓存整站的图片、字体后，当自身没有任何新东西的时候，不在继续缓存\n2.在更新时，自动清除客户端中全部缓存并通过悬浮提示框提示，需要在提示末尾处添加一个手动清除按钮，以免无法自动清除。同时自动清除通过强制刷新策略来进行清除，无论用户是否需要自主刷新。同时悬浮提示框需要使用文字提示\n3.全局版本号依靠构建时间（runtimeConfig.public.buildTime）来作为版本号（V年.月.日.时.分），同时在构建出版本号后需要解析到runtimeConfig.public.swBuildTime，每当客户端版本号出现变动时启用要求二\n4.需要在app.config.ts中添加配置项，包含是否永久缓存（若不需要请额外增加一个缓存时间）、逃生门（需要使用[\"\", \"\"]作为配置项，用于配置特定请求忽略）、关闭ServiceWorker选项、额外缓存文件类型\n5.不许在Ctrl + R时出现重新加载的提示\n文字提示：本站已更新至多少版本号（将已经生成好的版本号放在此处），请重新加载以免出现不可预料的问题，刷新缓存（缓存刷新功能放在此处）\n","思考","text","lang=\"text\"",[27,28,22],"code",{"__ignoreMap":29},"",[10,31,33],{"id":32},"配置逻辑","配置&逻辑",[35,36,38],"h3",{"id":37},"nuxt配置","Nuxt配置",[18,40,47],{"className":41,"code":43,"filename":44,"language":45,"meta":46},[42],"language-ts","\u002F\u002F 基础模块导入\nimport fsp from 'fs\u002Fpromises' \u002F\u002F 用于文件读写\nimport fs from 'fs' \u002F\u002F 用于即时读写\n\n\u002F\u002F 1. 基于构建时间生成纯净版本号\nfunction generateBuildTimeVersion(): string {\n  const now = new Date()\n  const pad = (num: number) => num.toString().padStart(2, '0')\n  const year = now.getFullYear()\n  const month = pad(now.getMonth() + 1)\n  const day = pad(now.getDate())\n  const hours = pad(now.getHours())\n  const minutes = pad(now.getMinutes())\n  const seconds = pad(now.getSeconds())\n  \u002F\u002F 确保这里只返回纯粹的版本号，不带任何多余字符\n  return `V${year}.${month}.${day}.${hours}.${minutes}` \n}\n\n\u002F\u002F 基础数据导入与构建Log设置（以防版本号未析出）\nconst masterVersion = generateBuildTimeVersion()\nconsole.log(`[Build] Master Version Generated: ${masterVersion}`)\n\n\u002F\u002F 2. 强化版 SW 版本注入插件\nfunction createSwBuildPlugin(swEntry: string, buildVersion: string) {\n  return {\n    name: 'vite-plugin-sw-version-injector',\n    enforce: 'pre' as const,\n    resolveId(id: string) {\n      if (id === 'virtual:sw-version') return id\n      return null\n    },\n    async load(id: string) {\n      if (id === 'virtual:sw-version') {\n        let versionBase = Math.floor(Math.random() * 1000000);\n        console.log(`[SW Build] Generated base: ${versionBase}. Pausing 1s...`);\n        await new Promise(resolve => setTimeout(resolve, 1000));\n        const finalVersion = `${versionBase}-${Date.now()}`;\n        console.log(`[SW Build] Injected version: ${finalVersion}`);\n        return `export const BUILD_VERSION = '${finalVersion}';`;\n      }\n      return null\n    },\n\n    transform(code: string, id: string) {\n      if (id === swEntry) {\n        const cleanVersion = buildVersion.trim(); \u002F\u002F 清理首尾空格\n        \n        \u002F\u002F 步骤A：直接替换占位符，无视外面套的几层引号\n        let updatedCode = code.replace(\u002F__INJECTED_VERSION__\u002Fg, cleanVersion);\n        \n        \u002F\u002F 步骤B：清除可能不小心拼接到一起的旧前缀 (如 'app-assets-V2026...' 变回 'V2026...')\n        updatedCode = updatedCode.replace(\u002Fapp-assets-+\u002Fg, ''); \n        \n        \u002F\u002F 步骤C：兜底安全替换，防止出现双引号版本\n        updatedCode = updatedCode.replace(\u002F\"__INJECTED_VERSION__\"\u002Fg, `'${cleanVersion}'`);\n\n        return updatedCode;\n      }\n      return code\n    }\n  }\n}\n\n\u002F\u002F 3.独立构建 Service Worker 的函数\nasync function buildServiceWorker(\n  outputDir: string, \n  isDev: boolean, \n  buildVersion?: string \u002F\u002F 🔽 核心修复 3：将参数设为可选，兼容新旧调用方式\n) {\n  const { build } = await import('vite')\n  const swEntry = resolve(process.cwd(), '.\u002Fapp\u002Futils\u002Fsw.ts') \n  const swOutput = resolve(outputDir, 'sw.js')\n\n console.log(`[SW] ${isDev ? 'Dev' : 'Prod'} mode: Building Service Worker...`)\n\n  await build({\n    configFile: false,\n    build: {\n      ssr: false,\n      outDir: outputDir,\n      emptyOutDir: false, \n      minify: isDev ? false : true, \n      lib: {\n        entry: swEntry,\n        name: 'sw',\n        formats: ['iife'],\n        fileName: () => 'sw.js'\n      },\n      rollupOptions: {\n        external: ['virtual:sw-version'], \n        output: {\n          entryFileNames: 'sw.js',\n          inlineDynamicImports: true\n        }\n      }\n    },\n    plugins: buildVersion ? [createSwBuildPlugin(swEntry, buildVersion)] : [] \n  })\n\n  await fsp.access(swOutput).catch(() => {\n    throw new Error(`[SW] Build failed: ${swOutput} not found`)\n  })\n  console.log(`[SW] Build complete -> ${swOutput}`)\n}\n\nexport default defineNuxtConfig({\n  \u002F\u002F 已有的配置不动\n  runtimeConfig: {\n    public: {\n      \u002F\u002F 已有的配置不动\n      swBuildTime: masterVersion\n\t\t},\n  }\n  hooks: {\n    \u002F\u002F 已有的配置不动\n    'nitro:init': (nitro) => {\n      \u002F\u002F =========================================================\n      \u002F\u002F 核心修复：在 Nitro 初始化时，强行将版本号打入运行时配置的最底层\n      \u002F\u002F 这会穿透并覆盖所有的 .env 文件和环境变量默认值\n      \u002F\u002F =========================================================\n      nitro.options.runtimeConfig.public.swBuildTime = masterVersion;\n      \n      console.log(`[Nitro] RuntimeConfig public.swBuildTime forcibly set to: ${masterVersion}`);\n\n      \u002F\u002F 继续处理 app.config.ts 的物理文件补丁\n      try {\n        const appConfigPath = resolve(process.cwd(), 'app.config.ts')\n        let content = fs.readFileSync(appConfigPath, 'utf-8')\n        \n        const searchRegex = \u002FserviceWorkerVersion:\\s*['\"][^'\"]*['\"]\u002F\n        const replaceValue = `serviceWorkerVersion: '${masterVersion}'`\n        \n        if (content.match(searchRegex)) {\n          content = content.replace(searchRegex, replaceValue)\n          fs.writeFileSync(appConfigPath, content, 'utf-8')\n          console.log(`[AppConfig] Patched to: ${masterVersion}`)\n        } else {\n          console.warn('[AppConfig] Warning: Pattern not found')\n        }\n      } catch (e) {\n        console.error('[AppConfig] File patch failed:', e)\n      }\n\n      \u002F\u002F 构建 Service Worker\n      nitro.hooks.hook('compiled', async () => {\n        await buildServiceWorker(nitro.options.output.publicDir, nitro.options.dev, masterVersion)\n      })\n    }\n  }\n})\n","nuxt.config.ts","ts","lang=\"ts\"",[27,48,43],{"__ignoreMap":29},[35,50,52],{"id":51},"app配置","APP配置",[18,54,58],{"className":55,"code":56,"filename":57,"language":45,"meta":46},[42],"  serviceWorker: {\n    \u002F\u002F 1. 主开关：设为 false 时，客户端将注销并停止所有 Service Worker\n    enabled: true, \n    \n    \u002F\u002F 2. 缓存规则\n    cacheRules: {\n      \u002F\u002F 需要拦截缓存的文件后缀\n      extensions: ['png', 'jpg', 'jpeg', 'gif', 'webp', 'svg', 'woff2', 'woff', 'ttf'],\n      \u002F\u002F 额外需要缓存的 MIME 类型\n      mimeTypes: ['image', 'font'],\n      escapeDoors: ['\u002Fapi\u002Frecent-comments', '\u002Fapi\u002Fstats']\n    },\n    \n    \u002F\u002F 3. 永久缓存开关与过期时间（单位：秒）\n    \u002F\u002F 若 permanent 为 true，则无视 maxAge；若为 false，超过 maxAge 的资源会被重新拉取\n    permanent: true, \n    maxAge: 3600 * 24 * 30, \u002F\u002F 30天\n    \n    \u002F\u002F 4. 逃生门 (Escape Door)\n    \u002F\u002F 当请求 URL 包含此前缀时，Service Worker 将彻底放行，不做任何拦截\n    \u002F\u002F 可用于动态大文件下载或需要实时获取的后端接口\n    escapeDoor: ['\u002Fapi\u002Frecent-comments'], \n    \n    \u002F\u002F 5. 版本号 (由构建时自动注入，此处仅为类型提示)\n    \u002F\u002F 在 sw.ts 中我们会通过模板字符串自动生成时间戳\n  },\n","app.Config.ts",[27,59,56],{"__ignoreMap":29},[10,61,62],{"id":62},"核心代码",[35,64,65],{"id":65},"客户端插件",[18,67,71],{"className":68,"code":69,"filename":70,"language":45,"meta":46},[42],"\u002F\u002F plugins\u002Fsw.client.ts\nimport { toRaw } from 'vue';\nimport { h, render, ref } from 'vue'; \u002F\u002F 🔼 引入 Vue 的渲染 API\n\nexport default defineNuxtPlugin((nuxtApp) => {\n  const config = useAppConfig().serviceWorker;\n  const runtimeConfig = useRuntimeConfig();\n\n  if (!config.enabled || !('serviceWorker' in navigator)) return;\n\n  \u002F\u002F ========== 1. 修复：监听控制器变化，解决 Ctrl+R 状态错乱 ==========\n  navigator.serviceWorker.addEventListener('controllerchange', () => {\n    console.log('[SW] Controller changed, reloading page to apply new version...');\n    \u002F\u002F 当有新的 Service Worker 接管页面时，强制刷新以清理旧状态\n    window.location.reload(); \n  });\n\n  const sendConfigToSw = (registration: ServiceWorkerRegistration) => {\n    const sw = registration.active || registration.waiting || registration.installing;\n    if (sw) {\n      const rawRules = toRaw(config.cacheRules); \n      const payload = {\n        permanent: config.permanent,\n        maxAge: config.maxAge,\n        escapeDoors: rawRules.escapeDoors.map(String), \n        extensions: rawRules.extensions.map(String)\n      };\n      \n      sw.postMessage({\n        type: 'INIT_CONFIG',\n        payload\n      });\n    }\n  };\n\n  \u002F\u002F ========== 2. 优化：基于 Vue h() 的现代化小弹窗 ==========\n  const showUpdatePrompt = (newVersion: string) => {\n    \u002F\u002F 避免重复创建\n    if (document.getElementById('sw-update-container')) return;\n\n    const container = document.createElement('div');\n    container.id = 'sw-update-container';\n    document.body.appendChild(container);\n\n    \u002F\u002F 使用 ref 来控制 Vue 组件内部的响应式状态（如果需要的话）\n    const visible = ref(true);\n\n    const vnode = h('div', { \n      class: 'fixed bottom-4 right-4 z-[9999] transition-all duration-300 ease-out',\n      style: { opacity: visible.value ? '1' : '0', transform: visible.value ? 'translateY(0)' : 'translateY(20px)' }\n    }, [\n      h('div', { class: 'bg-white dark:bg-zinc-900 shadow-2xl rounded-xl p-4 w-80 border border-zinc-200 dark:border-zinc-800' }, [\n        \u002F\u002F 标题\n        h('h3', { class: 'font-bold text-zinc-800 dark:text-zinc-100 mb-2 flex items-center gap-2' }, [\n          h('span', { class: 'w-2 h-2 bg-green-500 rounded-full animate-pulse' }),\n          '站点更新可用'\n        ]),\n        \u002F\u002F 版本号信息\n        h('p', { class: 'text-sm text-zinc-600 dark:text-zinc-400 mb-1' }, [\n          '最新版本：',\n          h('code', { class: 'text-xs bg-zinc-100 dark:bg-zinc-800 px-1.5 py-0.5 rounded-md font-mono' }, newVersion)\n        ]),\n        \u002F\u002F 提示文字\n        h('p', { class: 'text-xs text-zinc-500 dark:text-zinc-500 mb-4' }, \n          '请重新加载以免出现不可预料的问题。'\n        ),\n        \u002F\u002F 按钮组\n        h('div', { class: 'flex gap-2 justify-end' }, [\n          h('button', {\n            class: 'px-4 py-1.5 text-sm rounded-lg bg-zinc-100 hover:bg-zinc-200 dark:bg-zinc-800 dark:hover:bg-zinc-700 text-zinc-700 dark:text-zinc-300 transition-colors',\n            onClick: () => {\n              visible.value = false;\n              setTimeout(() => {\n                render(null, container);\n                container.remove();\n              }, 300); \u002F\u002F 等待过渡动画结束\n            }\n          }, '稍后再说'),\n          h('button', {\n            class: 'px-4 py-1.5 text-sm rounded-lg bg-blue-500 hover:bg-blue-600 text-white font-medium transition-colors shadow-sm',\n            onClick: () => {\n              window.location.reload();\n            }\n          }, '立即刷新'),\n          h('button', {\n            class: 'px-4 py-1.5 text-sm rounded-lg bg-red-500 hover:bg-red-600 text-white font-medium transition-colors shadow-sm',\n            onClick: () => {\n              const sw = navigator.serviceWorker.controller;\n              if (sw) {\n                sw.postMessage({ type: 'CLEAR_CACHE' });\n                sw.addEventListener('statechange', () => {\n                  window.location.reload();\n                });\n              } else {\n                window.location.reload();\n              }\n            }\n          }, '清除缓存')\n        ])\n      ])\n    ]);\n\n    render(vnode, container);\n  };\n\n  nuxtApp.hook('app:mounted', async () => {\n    const registration = await navigator.serviceWorker.register('\u002Fsw.js').catch(err => {\n      console.error('[SW] 注册失败:', err);\n      return null;\n    });\n\n    if (registration) {\n      if (registration.active) {\n        sendConfigToSw(registration);\n      } else {\n        registration.addEventListener('activate', () => {\n          sendConfigToSw(registration);\n        });\n      }\n\n      registration.addEventListener('updatefound', () => {\n        const newWorker = registration.installing;\n        if (newWorker) {\n          newWorker.addEventListener('statechange', () => {\n            if (newWorker.state === 'installed' && navigator.serviceWorker.controller) {\n              const newVersion = runtimeConfig.public.swVersion;\n              showUpdatePrompt(`${newVersion}`);\n            }\n          });\n        }\n      });\n    }\n  });\n});\n","sw.client.ts",[27,72,69],{"__ignoreMap":29},[35,74,75],{"id":75},"服务引擎",[18,77,81],{"className":78,"code":79,"filename":80,"language":45,"meta":46},[42],"\u002F\u002F src\u002Fsw.ts\ndeclare const self: ServiceWorkerGlobalScope;\n\u002F\u002F ========== 1. 类型定义 ==========\nexport interface SwConfig {\n  permanent: boolean;\n  maxAge: number; \n  escapeDoors: string[]; \n  extensions: string[];\n}\n\n\u002F\u002F ========== 2. 版本号 (由构建插件注入) ==========\nlet BUILD_VERSION = '__INJECTED_VERSION__';\nif (typeof BUILD_VERSION === 'undefined') {\n  BUILD_VERSION = `${Date.now()}-fallback`;\n}\n\n\u002F\u002F 直接使用完整版本号作为缓存名，清晰明了\nconst CACHE_NAME = `app-assets-${BUILD_VERSION}`;\n\n\u002F\u002F ... 文件其余部分完全不变 ...\n\n\u002F\u002F ========== 3. 运行时配置存储 ==========\nlet runtimeConfig: SwConfig | null = null;\n\n\u002F\u002F ========== 4. 工具函数 ==========\nfunction isCacheableRequest(url: string): boolean {\n  if (!runtimeConfig) return false;\n  \n  try {\n    const { pathname, href } = new URL(url, self.location.origin);\n    \n    if (runtimeConfig.escapeDoors.some(door => door && href.includes(door))) {\n      return false;\n    }\n    \n    const ext = pathname.split('.').pop()?.toLowerCase();\n    if (ext && runtimeConfig.extensions.includes(ext)) return true;\n    \n    return false;\n  } catch {\n    return false;\n  }\n}\n\n\u002F\u002F ========== 5. 生命周期：安装 ==========\nself.addEventListener('install', (event: ExtendableEvent) => {\n  self.skipWaiting(); \n});\n\n\u002F\u002F ========== 6. 生命周期：激活 ==========\nself.addEventListener('activate', (event: ExtendableEvent) => {\n  event.waitUntil(\n    caches.keys().then((cacheNames) => {\n      return Promise.all(\n        cacheNames.map((name) => {\n          if (name !== CACHE_NAME) {\n            console.log(`[SW] 清理旧版本缓存: ${name}`);\n            return caches.delete(name);\n          }\n          return undefined;\n        })\n      );\n    }).then(() => {\n      return self.clients.claim();\n    })\n  );\n});\n\n\u002F\u002F ========== 7. 拦截网络请求 (Fetch) ==========\nself.addEventListener('fetch', (event: FetchEvent) => {\n  const { request } = event;\n  \n  if (request.method !== 'GET' || !isCacheableRequest(request.url)) return;\n\n  const fetchAndCache = async () => {\n    const cache = await caches.open(CACHE_NAME);\n    try {\n      const networkResponse = await fetch(request);\n      if (networkResponse.ok) {\n        const responseClone = networkResponse.clone();\n        const headers = new Headers(responseClone.headers);\n        headers.append('sw-cache-timestamp', Date.now().toString());\n        \n        const modifiedResponse = new Response(responseClone.body, {\n          status: responseClone.status,\n          statusText: responseClone.statusText,\n          headers\n        });\n        await cache.put(request, modifiedResponse);\n      }\n      return networkResponse;\n    } catch {\n      const cached = await cache.match(request);\n      return cached || Response.error();\n    }\n  };\n\n  event.respondWith(\n    caches.open(CACHE_NAME).then(async (cache) => {\n      const cachedResponse = await cache.match(request);\n\n      if (cachedResponse) {\n        if (runtimeConfig && !runtimeConfig.permanent) {\n          const cachedTime = cachedResponse.headers.get('sw-cache-timestamp');\n          if (cachedTime && (Date.now() - parseInt(cachedTime, 10) > runtimeConfig.maxAge * 1000)) {\n            event.waitUntil(fetchAndCache());\n            return cachedResponse; \n          }\n        }\n        event.waitUntil(fetchAndCache());\n        return cachedResponse;\n      }\n\n      return await fetchAndCache();\n    })\n  );\n});\n\n\u002F\u002F ========== 8. 通信：接收主线程配置与指令 ==========\nself.addEventListener('message', (event: ExtendableMessageEvent) => {\n  const { data } = event;\n  \n  if (data.type === 'INIT_CONFIG' && data.payload) {\n    runtimeConfig = data.payload;\n    console.log('[SW] 配置初始化成功', runtimeConfig);\n  }\n\n  if (data.type === 'CLEAR_CACHE') {\n    event.waitUntil(\n      caches.delete(CACHE_NAME).then(() => {\n        console.log('[SW] 手动清除缓存完成');\n      })\n    );\n  }\n});\n","sw.ts",[27,82,79],{"__ignoreMap":29},{"title":29,"searchDepth":84,"depth":84,"links":85},4,[86,88,93],{"id":12,"depth":87,"text":12},2,{"id":32,"depth":87,"text":33,"children":89},[90,92],{"id":37,"depth":91,"text":38},3,{"id":51,"depth":91,"text":52},{"id":62,"depth":87,"text":62,"children":94},[95,96],{"id":65,"depth":91,"text":65},{"id":75,"depth":91,"text":75},[98],"站点魔改","2026-04-21 14:00:00","该文章主要写了对于低价机器的试水，并提醒是超开类型的机器。在测试的过程中发现机器性能较高，且展示出机器的具体价格，并单独列出只有精简版未采用完整版测试。",false,"md",null,{"slots":105},{},true,"\u002F2026\u002F04\u002Fnuxtserviceworker",{"text":109,"minutes":110,"time":111,"words":112},"12 min read",11.735,704100,2347,{"title":5,"description":100},{"loc":107},"posts\u002F2026\u002F04\u002FnuxtServiceWorker",[117,118],"Nuxt","页面","tech","2026-04-21 22:00:00","0uiW6jevB8BXpWz8V_BNhiwgnfx1QSF3tAzWnOswpbQ",[123,140,155,169,183,196,209,221,235,247,258,272,287,302,315,332,346,361,376,390,405,421,438,454,466,480,493,505,519,531,541,553,565,577,588,599,603,616,625],{"categories":124,"date":126,"description":127,"image":103,"path":128,"readingTime":129,"recommend":103,"tags":134,"title":138,"type":119,"updated":139},[125],"技术探索","2023-12-06 12:45:00","本篇转载三种不同方案（已经放好原文章链接）","\u002F2023\u002F03\u002Fessaythree",{"text":130,"minutes":131,"time":132,"words":133},"48 min read",47.34,2840400,9468,[135,136,137],"hexo","butterfly","美化","即刻短文的三种部署方案","2023-12-07 14:09:00",{"categories":141,"date":142,"description":143,"image":144,"path":145,"readingTime":146,"recommend":151,"tags":152,"title":153,"type":119,"updated":154},[98],"2024-04-19 10:00:00","友链页面美化以及添加功能","\u002Fimage\u002FPostCover\u002FflinkPage.avif","\u002F2024\u002F04\u002Fflinkpagemeihua",{"text":147,"minutes":148,"time":149,"words":150},"11 min read",10.8,648000,2160,1,[135,136],"友链魔改","2025-04-19 12:09:00",{"categories":156,"date":157,"description":158,"image":159,"path":160,"readingTime":161,"recommend":151,"tags":166,"title":167,"type":119,"updated":168},[98],"2025-02-05 09:00:00","这篇文章讲述在博客中对导航栏进行修改，并添加多个产品页面地址，且采用安知鱼的导航栏CSS样式表。","\u002Fimage\u002FPostCover\u002FnavMuogai.avif","\u002F2025\u002F02\u002Fbutterflynavadd",{"text":162,"minutes":163,"time":164,"words":165},"9 min read",8.585,515100,1717,[135,136],"butterfly导航栏修改方案","2025-03-05 10:00:00",{"categories":170,"date":171,"description":172,"image":173,"path":174,"readingTime":175,"recommend":151,"tags":180,"title":181,"type":119,"updated":182},[98],"2025-02-27 10:10:55","本篇文章讲述了如何在博客的底部模块进行魔改添加图标、来源图片、返回顶部以及建站时间，对建站时间进行js内联到模块中。","\u002Fimage\u002FPostCover\u002FfooterMeihua.avif","\u002F2025\u002F02\u002Ffooterqcqxstyle",{"text":176,"minutes":177,"time":178,"words":179},"4 min read",3.31,198600,662,[135,136,137],"轻笑底部美化","2025-02-28 10:00:00",{"categories":184,"date":185,"description":186,"image":187,"path":188,"readingTime":189,"recommend":103,"tags":193,"title":194,"type":119,"updated":195},[98],"2025-03-24 08:00:09","这篇简述如何给自己博客中的归档、分类、标签页三个页面的文章卡片加上所属分类和标签，并说明了具体的实践环境，以最大限度的方式进行CSS美化。但也请注意要经常备份以免出现大量错误。","\u002Fimage\u002FPostCover\u002FarchiveMougai.avif","\u002F2025\u002F03\u002Farchivemougai",{"text":176,"minutes":190,"time":191,"words":192},3.08,184800,616,[135,136],"给页面加上所属分类和标签以及美化","2025-03-24 10:56:09",{"categories":197,"date":198,"description":199,"image":200,"path":201,"readingTime":202,"recommend":103,"tags":206,"title":207,"type":119,"updated":208},[98],"2025-03-05 08:00:00","这篇文章讲述如何给自己博客中的归档、分类、标签页三个页面的文章卡片添加数字来进行编排，以及对添加的数字进行CSS美化，但也请注意要经常备份以免出现错误。","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fimg\u002Fdefault_cover_29.avif?v=20260104","\u002F2025\u002F03\u002Fartice-sort",{"text":176,"minutes":203,"time":204,"words":205},3.48,208800,696,[135,136],"美化文章卡片显示数字","2025-03-06 18:00:00",{"categories":210,"date":198,"description":211,"image":212,"path":213,"readingTime":214,"recommend":103,"tags":219,"title":220,"type":119,"updated":208},[98],"这篇文章讲述如何在页面中添加模块以及使用CSS添加颜色以及背景图片。","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fimg\u002Fdefault_cover_21.avif","\u002F2025\u002F03\u002Fbackgroud-qcqx",{"text":215,"minutes":216,"time":217,"words":218},"1 min read",0.71,42600,142,[135,136],"轻笑风格背景",{"categories":222,"date":223,"description":224,"image":225,"path":226,"readingTime":227,"recommend":103,"tags":232,"title":233,"type":119,"updated":234},[98],"2025-03-24 09:00:00","本文章以安知鱼的页面文件为模版进行大幅度修改，仿轻笑的关于页面的模块摆放以及调整CSS样式来进行细致魔改。","\u002Fimage\u002FPostCover\u002FbtfAboutPage.avif","\u002F2025\u002F03\u002Fbtfaboutpage",{"text":228,"minutes":229,"time":230,"words":231},"24 min read",23.275,1396500,4655,[135,136],"关于页面(butterfly)","2025-03-25 11:20:00",{"categories":236,"date":198,"description":237,"image":238,"path":239,"readingTime":240,"recommend":103,"tags":245,"title":246,"type":119,"updated":208},[98],"这篇文章讲述了如何美化博客侧边栏，并调整各个卡片内容的样式表。","\u002Fimage\u002FPostCover\u002FcardMeihua.avif","\u002F2025\u002F03\u002Fcardmeihua",{"text":241,"minutes":242,"time":243,"words":244},"5 min read",4.77,286200,954,[135,136],"卡片美化",{"categories":248,"date":198,"description":249,"image":250,"path":251,"readingTime":252,"recommend":103,"tags":256,"title":257,"type":119,"updated":208},[98],"从零开始魔改butterfly","\u002Fimage\u002FPostCover\u002FfunctionMeihua.avif","\u002F2025\u002F03\u002Ffunctionmeihua",{"text":215,"minutes":253,"time":254,"words":255},0.345,20700,69,[135,136],"功能美化",{"categories":259,"date":260,"description":261,"image":262,"path":263,"readingTime":264,"recommend":103,"tags":269,"title":270,"type":119,"updated":271},[98],"2025-03-01 10:00:00","本文依照轻笑的部分内容，参考首页banner写法以及对首页banner添加鼠标滚动以及移动端滑动，通过CSS样式进行美化整个首页banner，此篇文章重点讲述了如何解决轻笑给的js内容在首页会出现不断加载的问题。","\u002Fimage\u002FPostCover\u002FrandomPost.avif","\u002F2025\u002F03\u002Frandompost",{"text":265,"minutes":266,"time":267,"words":268},"6 min read",5.595,335700,1119,[135,136,137],"Banner随机文章展示(轻笑同款)","2025-03-05 12:09:00",{"categories":273,"date":274,"description":275,"image":276,"path":277,"readingTime":278,"recommend":282,"tags":283,"title":285,"type":119,"updated":286},[98],"2025-08-11 10:00:00","本篇文章讲述了添加模块代码并在博客的友情链接中添加模块，且使用css美化","\u002Fimage\u002FPostCover\u002FlinkTop.avif","\u002F2025\u002F08\u002Flinktop",{"text":162,"minutes":279,"time":280,"words":281},8.88,532800,1776,11,[117,284,137],"魔改","在友链页面添加滚动头像banner","2025-08-11 20:49:00",{"categories":288,"date":289,"description":290,"image":291,"path":292,"readingTime":293,"recommend":298,"tags":299,"title":300,"type":119,"updated":301},[98],"2025-08-22 10:00:00","近期将博客迁移至Nuxt框架时，重新搭建了「关于页面」。过程中添加了技能展示组件（skillinfo.vue），实现标签分组渲染；新建about.vue页面，整合个人信息、技能、偏好等多模块内容，并通过ts定义数据接口（about.ts\u002Fcreativity.ts）。","\u002Fimage\u002FPostCover\u002FnuxtAboutPage.avif","\u002F2025\u002F08\u002Fnuxtaboutpage",{"text":294,"minutes":295,"time":296,"words":297},"16 min read",15.92,955200,3184,10,[117,284,137],"关于页面(Nuxt)","2025-09-01 20:49:00",{"categories":303,"date":304,"description":305,"image":306,"path":307,"readingTime":308,"recommend":312,"tags":313,"title":314,"type":119,"updated":301},[98],"2025-09-02 10:00:00","本篇文章主要简述了如何给Nuxt框架添加站点详情","https:\u002F\u002Fwww.yjluo.top\u002Fimage\u002FPostCover\u002FsiteInfo.avif","\u002F2025\u002F09\u002Fsitelinkpageadd",{"text":241,"minutes":309,"time":310,"words":311},4.38,262800,876,9,[117,284,137],"添加站点详情页面",{"categories":316,"date":318,"description":319,"image":320,"path":321,"readingTime":322,"recommend":103,"tags":326,"title":329,"type":330,"updated":331},[317],"日志记录","2025-12-28 10:00:00","该文章详细记录了2025年中的上下半年所做的事情，并且还透露出后续计划中会干什么。而在前言中也写到此文为水字数而出，上下半年均写到更换框架与服务有关于的内容。对于未来计划中提到了要对一些网站进行恢复、以及明年将在文章中加入与自创小说有关的内容","\u002Fimage\u002FPostCover\u002FannualSummary.avif","\u002F2025\u002F12\u002Fannualsummary",{"text":176,"minutes":323,"time":324,"words":325},3.855,231300,771,[327,328],"总结",2025,"年度总结：旧去新","story","2025-12-28 20:49:00",{"categories":333,"date":334,"description":335,"image":336,"path":337,"readingTime":338,"recommend":312,"tags":343,"title":344,"type":119,"updated":345},[98],"2025-12-01 10:00:00","该文章详细介绍了基于Vue3+TypeScript开发的豆瓣追更记录系统，采用组件化架构实现两栏筛选菜单、动态加载动画和卡片式作品展示，通过Pinia状态管理+Vite构建工具实现数据流管理，集成防抖加载、虚拟滚动等性能优化方案。","\u002Fimage\u002FPostCover\u002FbanguimPage.avif","\u002F2025\u002F12\u002Fbanguimpageadd",{"text":339,"minutes":340,"time":341,"words":342},"23 min read",22.175,1330500,4435,[117,284,137],"添加追更历史","2025-12-01 20:49:00",{"categories":347,"date":348,"description":349,"image":350,"path":351,"readingTime":352,"recommend":357,"tags":358,"title":359,"type":119,"updated":360},[98],"2025-12-23 10:00:00","该文章介绍Nuxt博客适配中评论模块的Vue组件实现与Artalk评论系统单例管理逻辑，并提供具体的适配评论表情包的Json信息，评论功能有着KaTeX数学公式渲染、图片灯箱、动态监听以及管理逻辑的初始化、计数、暗黑模式切换等方式。","\u002Fimage\u002FPostCover\u002FcommentMeihua.avif","\u002F2025\u002F12\u002Fcommentadd",{"text":353,"minutes":354,"time":355,"words":356},"32 min read",31.72,1903200,6344,8,[117,284,137],"评论优化","2026-03-01 20:49:00",{"categories":362,"date":363,"description":364,"image":365,"path":366,"readingTime":367,"recommend":372,"tags":373,"title":374,"type":119,"updated":375},[98],"2025-12-03 10:05:09","该博客持续维护Nuxt框架博客系统，新增装备页面实现硬件\u002F外设分类展示，支持动态过滤、标签筛选与规格参数渲染，通过TypeScript定义数据接口，结合Vue3响应式布局与SCSS响应式设计，完成设备卡片动态加载、悬停动效及跨端适配优化，集成评论跳转与购买信息展示功能。","\u002Fimage\u002FPostCover\u002FequipmentPage.avif","\u002F2025\u002F12\u002Fequipmentpageadd",{"text":368,"minutes":369,"time":370,"words":371},"8 min read",7.28,436800,1456,7,[117,284,137],"添加装备页面","2025-12-03 20:49:09",{"categories":377,"date":378,"description":379,"image":380,"path":381,"readingTime":382,"recommend":386,"tags":387,"title":388,"type":119,"updated":389},[98],"2025-12-09 10:00:00","该文章记录了项目版本迭代中的UI优化与功能调整，包括增加可后期换配置且内置移动到赞赏总览触发效果的打赏弹窗、优化头部封面移动端预览、增加版权图标虚化及打赏入口、将本地desc预览改ai摘要样式、增加版权卡片及更换头部信息样式。","\u002Fimage\u002FPostCover\u002FpostMeihua.avif","\u002F2025\u002F12\u002Fpostpagexiugai",{"text":109,"minutes":383,"time":384,"words":385},11.165,669900,2233,6,[117,284,137],"文章美化","2025-12-09 20:49:00",{"categories":391,"date":392,"description":393,"image":394,"path":395,"readingTime":396,"recommend":401,"tags":402,"title":403,"type":119,"updated":404},[98],"2025-12-04 10:00:00","该文章记录了项目版本迭代中的UI优化与功能调整，包括站点详情卡片组件化改造（采用Badge组件优化布局）、分类卡片新增文章数量统计功能（重新严重问题）、标签卡片新增文章标签统计功能、博主信息模块的拆分与重构，同时删除了冗余的左侧图片和完整博主卡片，最终形成模块化组件结构（涉及5个核心组件及数据调用逻辑调整）。","\u002Fimage\u002FPostCover\u002FsmallCard.avif","\u002F2025\u002F12\u002Fsmallcardadd",{"text":397,"minutes":398,"time":399,"words":400},"10 min read",9.745,584700,1949,5,[117,284,137],"侧边组件美化","2025-12-04 20:49:00",{"categories":406,"date":407,"description":408,"image":409,"path":410,"readingTime":411,"recommend":103,"tags":416,"title":419,"type":119,"updated":420},[98],"2026-01-27 10:00:00","自定义全局的样式颜色，具有对特定CSS中所具有的自定义样式颜色来进行整合，并且作者还顺便水了一篇文章","\u002Fimage\u002FPostCover\u002FcolorStyleAll.avif","\u002F2026\u002F01\u002Fcolorstyleall",{"text":412,"minutes":413,"time":414,"words":415},"14 min read",13.155,789300,2631,[417,418],"全局颜色","自定义","自定义全局颜色","2026-01-27 20:49:00",{"categories":422,"date":423,"description":424,"image":425,"path":426,"readingTime":427,"recommend":103,"tags":432,"title":436,"type":119,"updated":437},[125],"2026-01-11 10:00:00","这篇文章是一篇实战经验分享，主要讲解了如何利用腾讯云的EdgeOne边缘安全加速平台，为网站字体等静态资源搭建一个自定义的、高性能的镜像加速服务。","\u002Fimage\u002FPostCover\u002FjsdmEdge.avif","\u002F2026\u002F01\u002Fjsdmedge",{"text":428,"minutes":429,"time":430,"words":431},"3 min read",2.72,163200,544,[433,434,435],"镜像","EdgeOne","jsdmirror","使用EdgeOne CDN搭建自用Jsd镜像","2026-01-11 20:49:00",{"categories":439,"date":440,"description":441,"image":442,"path":443,"readingTime":444,"recommend":103,"tags":449,"title":452,"type":119,"updated":453},[125],"2026-01-28 19:00:00","本文是一篇关于 Komari Monitor（一款服务器监控系统）的详细介绍与实用指南。文章以作者个人经验为引，对比了哪吒监控（V0\u002FV1版本）与 Komari 在各方面的差异，并逐步演示了如何部署、配置和使用 Komari。","\u002Fimage\u002FPostCover\u002FkomariMonitor.avif","\u002F2026\u002F01\u002Fkomarimonitor",{"text":445,"minutes":446,"time":447,"words":448},"7 min read",6.86,411600,1372,[450,451],"探针","监控","komari：全新的探针站点","2026-01-29 13:00:00",{"categories":455,"date":456,"description":393,"image":457,"path":458,"readingTime":459,"recommend":84,"tags":463,"title":464,"type":119,"updated":465},[98],"2026-01-01 10:00:00","\u002Fimage\u002FPostCover\u002FNewYear.avif","\u002F2026\u002F01\u002Fnewyear",{"text":428,"minutes":460,"time":461,"words":462},2.15,129000,430,[117,284,137],"踏入2026：目标新方向","2026-01-01 20:49:00",{"categories":467,"date":468,"description":469,"image":470,"path":471,"readingTime":472,"recommend":103,"tags":477,"title":478,"type":330,"updated":479},[98],"2026-01-05 10:00:00","该文章记录了项目中对于字体、图片以及构建产物等静态资源的优化与处理，并且表示自身对于图片的存放位置进行优化。","\u002Fimage\u002FPostCover\u002FsiteAssets.avif","\u002F2026\u002F01\u002Fsiteassets",{"text":473,"minutes":474,"time":475,"words":476},"2 min read",1.82,109200,364,[117,284,137],"站点资源优化","2026-01-07 20:49:00",{"categories":481,"date":483,"description":100,"image":484,"path":485,"readingTime":486,"recommend":103,"tags":490,"title":491,"type":119,"updated":492},[482],"日常随笔","2026-01-12 10:00:00","\u002Fimage\u002FPostCover\u002FvpsTalk.avif","\u002F2026\u002F01\u002Fvpstalk",{"text":294,"minutes":487,"time":488,"words":489},15.545,932700,3109,[117,284,137],"随笔：低价主机试水","2026-01-12 20:49:00",{"categories":494,"date":495,"description":496,"image":484,"path":497,"readingTime":498,"recommend":103,"tags":502,"title":503,"type":119,"updated":504},[482],"2026-01-30 10:00:00","该文章主要写了对于大容量硬盘主机的试水。在测试的过程中发现机器性能较高，且展示出机器的具体价格，并单独列出只有精简版未采用完整版测试。","\u002F2026\u002F01\u002Fvpstalk-2",{"text":412,"minutes":499,"time":500,"words":501},13.965,837900,2793,[117,284,137],"随笔：大容量主机测试","2026-01-30 20:49:00",{"categories":506,"date":507,"description":508,"image":509,"path":510,"readingTime":511,"recommend":151,"tags":516,"title":517,"type":119,"updated":518},[98],"2026-02-20 10:00:00","该文章展示多个以鸣潮为主题的档案组件，包含具体代码、属性表格对应、预览整体组件、写法展示四种类型，并在文章末尾附加更新报告。","\u002Fimage\u002FPostCover\u002FWutheringWavesPostWidget.avif","\u002F2026\u002F02\u002Fwutheringwavespostwidget",{"text":512,"minutes":513,"time":514,"words":515},"100 min read",99.23,5953800,19846,[117,284,137],"【鸣潮】档案文章组件","2026-02-26 10:00:00",{"categories":520,"date":521,"description":100,"image":522,"path":523,"readingTime":524,"recommend":103,"tags":528,"title":529,"type":119,"updated":530},[125],"2026-02-03 10:00:00","\u002Fimage\u002FPostCover\u002FfnosInstall.avif","\u002F2026\u002F02\u002Ffnosinstall",{"text":162,"minutes":525,"time":526,"words":527},8.91,534600,1782,[117,284,137],"给老MAC升级为飞牛OS","2026-02-05 20:49:00",{"categories":532,"date":521,"description":100,"image":533,"path":534,"readingTime":535,"recommend":103,"tags":539,"title":540,"type":119,"updated":530},[98],"\u002Fimage\u002FPostCover\u002FmacBookPerformancTest.avif","\u002F2026\u002F02\u002Fmacbookperformanctest",{"text":428,"minutes":536,"time":537,"words":538},2.47,148200,494,[117,284,137],"【精简】测试老MAC性能",{"categories":542,"date":543,"description":100,"image":544,"path":545,"readingTime":546,"recommend":103,"tags":550,"title":551,"type":119,"updated":552},[98],"2026-02-04 10:00:00","\u002Fimage\u002FPostCover\u002FmusicInstall.avif","\u002F2026\u002F02\u002Fmusicinstall",{"text":162,"minutes":547,"time":548,"words":549},8.19,491400,1638,[117,284,137],"【本地+云端】搭建道理鱼音乐","2026-02-06 20:49:00",{"categories":554,"date":555,"description":100,"image":556,"path":557,"readingTime":558,"recommend":91,"tags":562,"title":563,"type":119,"updated":564},[98],"2026-03-03 10:00:00","\u002Fimage\u002FPostCover\u002FfooterNuxtMeihua.avif","\u002F2026\u002F03\u002Ffooternuxtmeihua",{"text":176,"minutes":559,"time":560,"words":561},3.235,194100,647,[117,284,137],"页脚魔改(Nuxt版本)","2026-03-06 10:00:00",{"categories":566,"date":567,"description":100,"image":556,"path":568,"readingTime":569,"recommend":87,"tags":574,"title":575,"type":119,"updated":576},[98],"2026-03-21 14:00:00","\u002F2026\u002F03\u002Fsteamgamepage",{"text":570,"minutes":571,"time":572,"words":573},"18 min read",17.265,1035900,3453,[117,118],"游戏展示页面","2026-03-22 10:00:00",{"categories":578,"date":579,"description":100,"image":556,"path":580,"readingTime":581,"recommend":151,"tags":585,"title":586,"type":119,"updated":587},[98],"2026-04-11 14:00:00","\u002F2026\u002F04\u002Fessaynuxtpage",{"text":265,"minutes":582,"time":583,"words":584},5.93,355800,1186,[117,118],"说说页面（Nuxt版本）","2026-04-11 22:00:00",{"categories":589,"date":590,"description":100,"image":103,"path":591,"readingTime":592,"recommend":103,"tags":596,"title":597,"type":119,"updated":598},[125],"2026-04-13 14:00:00","\u002F2026\u002F04\u002Fnuxthotpage",{"text":162,"minutes":593,"time":594,"words":595},8.985,539100,1797,[117,118],"用Codex写一个热搜页面","2026-04-13 22:00:00",{"categories":600,"date":99,"description":100,"image":103,"path":107,"readingTime":601,"recommend":103,"tags":602,"title":5,"type":119,"updated":120},[98],{"text":109,"minutes":110,"time":111,"words":112},[117,118],{"categories":604,"date":606,"description":319,"image":607,"path":608,"readingTime":609,"recommend":103,"tags":613,"title":614,"type":330,"updated":615},[605],"自设记录","2025-12-29 10:00:00","https:\u002F\u002Fsourceimage.s3.bitiful.net\u002Fpost\u002Fimg\u002FannualSummary\u002Fcover.webp","\u002Fnovel\u002Fworld\u002Fharmworld",{"text":473,"minutes":610,"time":611,"words":612},1.31,78600,262,[327,328],"世界志：鸿蒙界","2025-12-29 20:49:00",{"categories":617,"date":606,"description":319,"image":607,"path":618,"readingTime":619,"recommend":103,"tags":623,"title":624,"type":330,"updated":615},[605],"\u002Fnovel\u002Fworld\u002Flmmortalgod",{"text":445,"minutes":620,"time":621,"words":622},6.295,377700,1259,[327,328],"世界志：仙神界",{"categories":626,"date":606,"description":319,"image":607,"path":627,"readingTime":628,"recommend":103,"tags":632,"title":633,"type":330,"updated":615},[605],"\u002Fnovel\u002Fworld\u002Fdh",{"text":473,"minutes":629,"time":630,"words":631},1.65,99000,330,[327,328],"世界志：大荒",[635,103],{"title":597,"path":591,"stem":636,"date":590,"type":119,"children":-1},"posts\u002F2026\u002F04\u002FnuxtHotPage",1784195346194]