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

推荐订阅源

G
Google Developers Blog
人人都是产品经理
人人都是产品经理
腾讯CDC
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
WordPress大学
WordPress大学
S
SegmentFault 最新的问题
小众软件
小众软件
B
Blog
博客园 - 叶小钗
Microsoft Azure Blog
Microsoft Azure Blog
Apple Machine Learning Research
Apple Machine Learning Research
A
About on SuperTechFans
J
Java Code Geeks
Blog — PlanetScale
Blog — PlanetScale
博客园 - 司徒正美
博客园 - 【当耐特】
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Recent Announcements
Recent Announcements
宝玉的分享
宝玉的分享
Martin Fowler
Martin Fowler
Hugging Face - Blog
Hugging Face - Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Last Week in AI
Last Week in AI
V
V2EX

V2EX

我用 AI 写代码,但终端管理反而成了累赘——于是我做了 codux [调研] 各位在公司都用什么 ide 和 agent 写代码? 老运维 share 一个运维平台 看到有公司考核 token 指标,很好奇大家上个月的 AI 账单是多少 GLM-Coding 调用持续报错: z.ai 的 Lite 套餐几乎无法使用,官方 Pro/Max 是否稳定? 现在还有什么渠道可以稳定安全地使用 Claude 吗? 上海漕河泾内推,本组有 2 个 hc,一个后端,一个前端,预算都是 20k 左右,不打卡,氛围好 如果 V2EX 上有一组不永久保存聊天记录(比如只保存 7 天或者 24 小时)的聊天室,那么会开启哪些有用或者有趣的可能? gemini cli 貌似挂了,一直返回 403 第一次在自媒体上赚到钱 收集了最近在使用的低价 GPT, Gemini,邮箱等 AI 会员的小店合集 讨论个大实话:现在企业还在说 AI 编程提效 20%, 30%的,真的太落后,没用懂 AI。因为包括很多前沿公司,已经狂奔到提效 200%-500%的情况 [招聘][远程][币安] 前端/后端/QA/iOS/Android 至少 3 年以上经验 目前有大量 HC 欢迎投递 Chatgpt Pro 用量用不完的可以开这些设置 面试的时候好像遇到钓鱼了,给各位避个坑 cursor 年续费 22 号到期, 自动续费是否还是老的计次套餐呢 被两件破事毁掉的一下午,琐碎的内耗消磨人的精力 使用 Planet 存储 Codex 的会话或者重要信息 如果业务部门领导不要你开发功能,而是要求你教会它用 claude code 开发功能,你会怎么做? 分享一个 MacOS 接绿联 CM818 USB 转 DP 转接器使用感受 我的 HR 朋友 10 年老 Java ,非全大专,大家帮忙看看简历 开源了一个 AI 口语练习工具,音素级发音评分,完全免费可自部署 V2EX 上有哪些你觉得很有趣、印象深刻的妹纸? 字节为啥不出个国内版 Vercel? 有在大马的朋友吗? 问个运营商问题 你们在有领导的公司大群发过的最大胆的消息是什么 公司裁员,目前没有工作。想试试摆摊,做一个移动鲜啤打酒车 我的硬盘 Memblaze Pblaze 5 Linux 下不识别,给 Linux 内核提交了补丁, AI 说有望被合并 只有我一个人觉得 codex 不好用?
一套能让免费 CloudFlare 建的站部分访问延迟变低(几乎瞬间...
shendaowu · 2026-06-03 · via V2EX

效果

  • 我让 AI 写的简单页面 LCP 100 ms 左右。首个请求完成也就二三十毫秒。

缺陷

  • 首次访问别想了,该卡还是卡。
  • 部分解决 Edge 上的一个导致延迟大的问题比较复杂。解决方法在后面。
  • 只对可预测性比较强的网站效果更好。可预测性不强的网站想要快代价很可能比较大,具体方法在后面。
  • 如果网站更新了,用户刷新两次才能获得最新内容。

代价

  • 比较复杂。
  • 网站有一定概率会彻底崩,而且比较难恢复。
  • 可能会消耗更多的流量。

主要技术

  • Service Worker
  • SWR

方法

简单说就是用 service worker 实现一个 SWR 缓存策略。

真传一句话,下面假传万卷书。

service worker 我理解就是一个运行在浏览器的类似代理的东西,可以任意修改特定网站所有的请求的响应。SWR 我理解就是如果没缓存,就先访问服务器获取内容,然后返回响应;如果有缓存,就直接返回缓存,然后再访问服务器更新缓存。这段我写的时候没有参考任何资料,都是凭记忆写的。在意的话自己搜搜。

虽然看起来挺简单的,但实际实现的时候还是会遇到一些比较恶心的问题。比如如果 service worker 安装过程比较耗时的话,可能导致一些页面无法缓存,最好是在 service worker 的激活事件里显式缓存一下。否则可能导致第三次访问的时候才能快速打开。

缺陷详解

我相信在这种条件下让首次访问也快是不可能的。不过我也没啥根据,基本就是直觉,我倒是希望首次也快。可能不同的网站互相缓存可以部分提升。不过 service worker 是不能跨域的,用 service worker 应该是不行。

在 edge 上就算使用了缓存,五分钟以上不访问之后再次访问还是偶尔会出现一两秒的延迟。解决方法是在所有页面中添加一个隐藏的框架,框架的内容就是网站内的一个页面,比如说 /keepalive ,然后每隔一段时间刷新这个框架的内容。这样如果页面开着的话,基本就不会出现卡顿的情况了。我测试半分钟效果不错。另外在 service worker 中添加一点代码,如果请求的 pathname 部分是 /keepalive ,就直接用代码返回响应,不要经过服务器。我试过了,效果一样。我基本可以确定这种刷新不会导致太多的服务器请求。DNS 请求应该是免不了了,我在 CF 的 DNS 请求页面看了,不访问的时候基本没有 DNS 请求,持续刷新的过程中五分钟十次请求。据说那个十次是假的,不知道真假。如果在 service worker 中直接返回响应的话,理论上应该是不会有 web 服务器流量的。但是由于 cloudflare 好像是没有监控静态页面流量的功能,所以我就不管了。Wireshark 我不太会用,不想学。谁要是能帮我分析一下我谢谢你。我问过微软的客服的,看起来微软没有解决这个问题的意思,微软客服大概说 chrome 用了缓存能瞬间打开是链接复用之类的机制比较激进,edge 没那么激进。我有点怀疑 edge 就是没优先用缓存的 dns 映射(这个词可能不准确),chrome 可能直接用缓存的 dns 记录了。就说是 chrome 可能对 dns 映射用的是 SWR 策略。

我想做的是一个类似解谜游戏的网站,实际不是游戏,具体我不想说。我预测大部分用户都会一关一关访问,所以在首页预载第一关,在第一关预载第二关,以此类推效果应该非常不错。这部分代码里没有。我之前试过,但是没有跟解决 edge 问题的代码一起测试,我估计是没什么问题。如果用户访问行为不好预测的话,并且网站页面不多的话,直接暴力预载所有内容可能也不错。网站内容多的话可能就不行了。预载访问量排前几个的?

如果更新了需要刷新两次才能看到最新内容是 SWR 策略决定的。我试过让 AI 实现检测到更新在页面上显示一个通知,但实际搞下来我感觉太复杂了,放弃了。你要是有勇气可以试试。

代价详解

复杂就不多解释了,前面那么多字应该有点体现了。而且我可能还没有把所有坑都踩遍。

service worker 出问题很危险,可能会导致全站无法访问。而且有一些坑。比如如果 service worker 的脚本浏览器缓存时间比较长的话,用户的浏览器可能无法及时更新。解决方法挺复杂。我建议能跑就尽量别改 service worker 的代码。如果有新的需要改 service worker 的需求,如果价值不大就别实现了,保命要紧。反正我就打算这么干了。

cloudflare 一般情况下好像不会给出完整的响应,而是通过对比一个请求头判断浏览器缓存是否过时了,过时了才发完整的请求。我不知道 SWR 会不会利用这个机制。

弯路

刚开始我是想通过浏览器缓存很长时间的 HTML 加动态加载各个页面对应的 js 文件动态修改页面内容来实现的,后来好像被 AI 提醒 service worker 更好就用了后者。

临时 Demo

https://sdwpub.cc.cd/

过一段时间很可能会失效。

在 edge 上测试方法就是开着首页,再打开 https://sdwpub.cc.cd/test 再关闭,缓存这个页面。然后过一段时间访问 https://sdwpub.cc.cd/test 。我测了很多次都没卡顿。如果不开着首页过五分钟就有可能卡顿。chrome 的话,打开一次首页之后再访问首页就不卡顿了。

代码

index.html:

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <title>Edge 卡顿缓解</title>
</head>
<body>
  <!-- 正常页面内容 -->

  <span>测试。</span>

  <!-- 不可见框架,用于保持页面活跃 -->
  <iframe id="keepAliveFrame" src="keepalive" style="display:none;"></iframe>

  <script>
    // 每半分钟刷新一次不可见框架
    setInterval(function() {
      var frame = document.getElementById('keepAliveFrame');
      if (frame) {
        frame.src = 'keepalive?t=' + Date.now();
      }
    }, 30000);

    // 注册 Service Worker
    if ('serviceWorker' in navigator) {
      window.addEventListener('load', function() {
        navigator.serviceWorker.register('/sw.js').catch(function(err) {
          console.error('SW 注册失败:', err);
        });
      });
    }
  </script>
</body>
</html>

sw.js:

// == sw.js ==
// Service Worker:全站 GET 请求 SWR 缓存 + 预缓存首页

const CACHE_NAME = 'swr-cache-v1';

// 安装时预缓存首页
self.addEventListener('install', event => {
  event.waitUntil(
    caches.open(CACHE_NAME).then(cache => cache.add('/').catch(() => {}))
  );
  self.skipWaiting(); // 立即激活,不等待旧 SW
});

// 激活时接管所有客户端
self.addEventListener('activate', event => {
  event.waitUntil(self.clients.claim());
});

// SWR 策略:拦截所有同源 GET 请求
self.addEventListener('fetch', event => {
  const { request } = event;
  if (request.method !== 'GET') return;

  const url_tmp = new URL(event.request.url);
  
  // 判断 pathname 是否为 /keepalive
  if (url_tmp.pathname === '/keepalive') {
    // 直接返回 "test" 四个字符作为响应
    event.respondWith(
      new Response('test', {
        status: 200,
        headers: { 'Content-Type': 'text/plain' }
      })
    );
    return; // 已处理,不再继续
  }

  // 仅处理同源请求,避免跨域 opaque 响应的复杂性
  const url = new URL(request.url);
  if (url.origin !== self.location.origin) return;

  event.respondWith(
    caches.open(CACHE_NAME).then(cache =>
      cache.match(request).then(cached => {
        // 后台网络请求,用于更新缓存
        const networkFetch = fetch(request).then(response => {
          if (response && response.status === 200) {
            cache.put(request, response.clone());
          }
          return response;
        }).catch(() => {});

        if (cached) {
          // 命中缓存:立即返回,同时后台更新
          event.waitUntil(networkFetch);
          return cached;
        }
        // 未命中:等待网络响应
        return networkFetch;
      })
    )
  );
});