Service Worker 深度解析:拦截请求、离线缓存与推送通知

4771 字
24 分钟
Service Worker 深度解析:拦截请求、离线缓存与推送通知

你有没有遇到这样的场景:用户在地铁里打开你的网页,信号断了一秒钟,正在提交的表单数据彻底丢失;又或者收到一封邮件提醒”您有新的订单”,但点开 App 时却发现网络挂了。在原生应用里,这些”看似自然”的能力对 Web 来说却并不容易。直到 Service Worker 出现,浏览器第一次拥有了一个可以拦截网络请求、独立于页面运行的代理线程

如果说《Service Worker 离线优先策略:构建真正可靠的 PWA》是一份”从零开始”的入门指南,那么本文将带你走到 Service Worker 的更深一层:我们不仅要搞清楚它是如何拦截请求的,还要深入到 Cache Storage 的存储机制、Background Sync 的事件循环、Web Push 的消息加密与 VAPID 协议这些生产环境真正用得到的细节。

读完本文,你将能够:

  • 理解 Service Worker 启动到拦截 fetch 的完整时序
  • 在 7 种缓存策略中为每类资源选对武器
  • 实现精确可控的 stale-while-revalidate 与 Range 请求缓存
  • 接入 Web Push 与 Background Sync,处理订阅、加密与重试
  • 规避 90% 的 Service Worker 调试陷阱

一、Service Worker 到底是什么?#

1.1 一段独立于页面的 Worker#

Service Worker 本质上是一个运行在浏览器独立线程中的 JavaScript 上下文,它无法访问 windowdocument,因此不能直接操作 DOM。它与主线程之间通过 postMessage 通信,通过 clients API 访问受控页面。

一个常被混淆的概念:Service Worker ≠ Web Worker。Web Worker 是为计算密集型任务而生的临时线程,生命周期与页面绑定;Service Worker 则是网络代理 + 后台事件中心,页面关闭后仍能接收 pushsync 事件。

1.2 三个关键特性#

特性说明影响
网络代理通过 fetch 事件拦截所有受控 scope 内的请求离线、缓存、请求改写成为可能
HTTPS 强制只能在 HTTPS 或 localhost 下注册防中间人注入恶意 SW
事件循环暂停没有事件时浏览器会主动终止线程不能用 setInterval 做心跳

第三点尤其重要:很多人会写 setInterval(() => fetch('/heartbeat'), 60000),结果在 Chrome 上运行良好,但 Firefox/Safari 在闲置几分钟后就会杀掉 Service Worker 进程——因为它们把 SW 视作”按需唤醒”的事件循环,而不是常驻进程。

1.3 一次完整的 fetch 拦截时序#

// 页面发起请求
fetch('/api/users/42');

这一行代码背后发生了什么?

sequenceDiagram participant Page as 主线程 participant SW as Service Worker participant Cache as Cache Storage participant Net as 网络 Page->>SW: 触发 fetch 事件 SW->>Cache: 查找本地缓存 alt 命中缓存 Cache-->>SW: 返回 Response SW-->>Page: 立即返回(最快路径) else 未命中 SW->>Net: 发起真实网络请求 Net-->>SW: 响应 SW->>Cache: 写入缓存(可选) SW-->>Page: 返回响应 end

关键点在于:Service Worker 并不是”先让网络请求飞一会儿,再拦截”,而是从源头取代了浏览器默认的网络栈。这就是为什么它能在断网时仍能返回内容——fetch 事件根本不进网络。

二、生命周期:远比想象中复杂#

Service Worker 的生命周期可以拆成五个阶段:注册 → 安装 → 等待 → 激活 → 运行。网上很多文章把 waiting 略过不提,但生产环境的”更新用户卡在旧版本”问题,几乎都和这一阶段有关。

2.1 注册与字节级更新检测#

// 任何调用 SW 的页面(通常是根页面)
navigator.serviceWorker.register('/sw.js', {
scope: '/app/', // 受控路径前缀
type: 'module', // 现代写法,可使用 ESM
updateViaCache: 'none', // 不要用 HTTP 缓存来缓存 SW 自身
});

浏览器逐字节对比 sw.js 的内容(不是 HTTP 状态、不是 ETag,而是响应体的实际字节)。一旦发现不同,立即启动新的 SW 走 install 流程。

这就解释了为什么 importScripts('/cache-buster.js?ts=123') 并不会触发更新——SW 文件本身没变。

2.2 install 与 skipWaiting#

sw.js
const CACHE = 'app-v3';
self.addEventListener('install', (event) => {
event.waitUntil((async () => {
const cache = await caches.open(CACHE);
// 关键:precache 时全部成功才会 install 成功
await cache.addAll([
'/', '/styles.css', '/app.js', '/offline.html',
]);
// self.skipWaiting() 表示:不要等旧页面关闭,立即进入 waiting
await self.skipWaiting();
})());
});

skipWaiting() 是一个需要谨慎使用的开关。它会让新 SW 立即进入 activated 状态,但旧 SW 控制下的页面并不会重新触发 fetch 拦截。要真正”接管”控制权,还需要在 activate 中调用 clients.claim()

2.3 activate 与 clients.claim#

self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
// 1. 清理旧版本缓存
const keys = await caches.keys();
await Promise.all(
keys
.filter((k) => k !== CACHE)
.map((k) => caches.delete(k))
);
// 2. 立即接管所有未受控页面
await self.clients.claim();
})());
});

clients.claim() 让当前 SW 立即接管当前已打开但未被旧 SW 控制的标签页。两者配合 skipWaiting() 构成了”用户刷新前即可生效”的更新方案。

2.4 一张表看懂状态机#

状态触发退出条件备注
installing字节变化成功:installed;失败:redundantinstall 事件触发
installed (waiting)install 成功旧 SW 控制页面数为 0等待 skipWaiting()
activating旧 SW 让位成功:activatedactivate 事件触发
activated激活成功SW 进程被回收拦截 fetch 事件
redundantinstall 失败 / 被取代终止出现即需排查

三、缓存策略:7 种武器与选型#

Service Worker 本身并不等于缓存。它只是给你一个 hook 来决定每个请求怎么响应。把请求想象成自来水,把 SW 想象成水表和阀门——不同的开阀策略就是不同的缓存策略。

3.1 经典五策略对照#

策略流程适用缺点
Cache First缓存 → 网络版本化静态资源(/static/*难以更新
Network First网络 → 缓存 → 失败兜底HTML、API慢网络体验差
Cache Only仅缓存字体、关键资源首次必须预缓存
Network Only仅网络鉴权请求、实时数据离线即挂
Stale-While-Revalidate缓存立即返回 + 后台更新新闻列表、商品列表短暂不一致

下面我们重点看两种生产中最常用也最容易写错的策略。

3.2 Stale-While-Revalidate 完整实现#

async function staleWhileRevalidate(request) {
const cache = await caches.open(CACHE);
const cached = await cache.match(request);
// 关键:无论是否命中,都要触发后台更新
const networkUpdate = fetch(request)
.then((response) => {
// 重要:只缓存 200 + basic/cors
if (response && response.status === 200) {
cache.put(request, response.clone());
}
return response;
})
.catch(() => null);
return cached || (await networkUpdate) || Response.error();
}

注意三个易错点:

  1. response.clone() 必不可少——response 是流,读取一次后就被消费,需要克隆后写入缓存;
  2. 不要缓存非 2xx 响应——5xx、404 也会被错误缓存;
  3. networkUpdate 错误吞掉——离线时 fetch reject,但我们不能让它冒泡到 waitUntil 之外。

3.3 Cache First + 后台更新的”伪 SWR”#

async function cacheFirstWithRefresh(request) {
const cache = await caches.open(CACHE);
const cached = await cache.match(request);
const fetchPromise = fetch(request).then((response) => {
if (response.ok) cache.put(request, response.clone());
return response;
});
return cached || fetchPromise;
}

与 SWR 的区别:用户首次访问是网络响应,之后直接拿缓存。和”先返回缓存 + 后台更新”的区别在于是否阻塞。生产中,对于带哈希指纹的构建产物app.a3f9b2.js),这种策略最稳。

3.4 按 URL 分流:路由表设计#

const ROUTES = [
{ pattern: /^\/api\/v1\/user/, strategy: 'networkOnly' },
{ pattern: /^\/api\/v1\/posts/, strategy: 'staleWhileRevalidate' },
{ pattern: /\.(js|css|woff2?)$/, strategy: 'cacheFirst' },
{ pattern: /\.(png|jpg|webp|avif)$/, strategy: 'cacheFirst' },
{ pattern: /\.html$/, strategy: 'networkFirst' },
];
self.addEventListener('fetch', (event) => {
const url = new URL(event.request.url);
// 关键:只处理 GET,且同源
if (event.request.method !== 'GET' || url.origin !== self.location.origin) {
return;
}
for (const route of ROUTES) {
if (route.pattern.test(url.pathname)) {
event.respondWith(STRATEGIES[route.strategy](event.request));
return;
}
}
// 未匹配的请求:放行浏览器默认行为
});

把策略做成配置表而不是散落在事件处理器里的 if-else,是任何”中型”项目都应遵循的工程纪律。

四、Cache Storage 的工程化#

caches API 用起来像 Map,但它底层是异步、分布式的——数据可能落在 IndexedDB、磁盘,甚至独立分区。

4.1 配额与驱逐#

// 申请持久化存储
if (navigator.storage && navigator.storage.persist) {
const persisted = await navigator.storage.persist();
console.log('持久化授权:', persisted);
}
const estimate = await navigator.storage.estimate();
console.log(`已用 ${estimate.usage} / ${estimate.quota}`);

Chrome 在磁盘空间不足时优先清理未持久化的存储。对”必须保留”的离线资源(如离线地图瓦片),应当申请 persist() 授权。

4.2 处理 Range 请求#

视频和大型资源往往带 Range: bytes=0-1023 头。Cache Storage 默认会完整缓存整个响应,后续 Range 请求可能无法命中。

// 拆分缓存:把 Range 响应合并成完整资源
async function handleRange(request) {
const cache = await caches.open('videos');
const cached = await cache.match(request.url); // 注意:去掉 Range 头
if (cached) {
const blob = await cached.blob();
return new Response(blob.slice(0, 1024), {
status: 206,
headers: { 'Content-Range': `bytes 0-1023/${blob.size}` },
});
}
return fetch(request);
}

对真正的”专业级”流媒体方案,建议用 Range requests polyfill 或迁移到 MediaSource API。

4.3 清理的边界条件#

// ❌ 错误:把整个网站资源都放进一张缓存
const CACHE = 'everything-v1';
// 1MB 的小图片也会和 100MB 视频放一起,maxEntries 不起作用
// ✅ 正确:按类型分桶
const CACHES = {
static: 'static-v1', // max 100 entries
images: 'images-v1', // max 200 entries
api: 'api-v1', // max 50 entries, 7 天 TTL
};

分桶的好处不只是”看起来清晰”,更是让 LRU 清理互不干扰——一个 100MB 的视频清理不会拖累 5MB 的字体缓存。

五、Web Push:从订阅到消息#

Service Worker 之所以能在页面关闭后仍能收到事件,全靠 Push API。它的核心流程是:浏览器在 SW 闲置时主动唤醒它,传入一个 push 事件。

5.1 VAPID 协议#

Web Push 使用 VAPID(Voluntary Application Server Identification) 让服务器能识别推送来源。你需要生成一对公私钥:

Terminal window
# 安装 web-push CLI
npm i -g web-push
web-push generate-vapid-keys
# 输出的 pub/sub key 用于前后端

5.2 前端订阅#

// 申请推送权限
const permission = await Notification.requestPermission();
if (permission !== 'granted') return;
// 订阅
const registration = await navigator.serviceWorker.ready;
const subscription = await registration.pushManager.subscribe({
userVisibleOnly: true, // 必须:所有推送必须显示通知
applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY),
});
// 发送到服务器
await fetch('/api/push/subscribe', {
method: 'POST',
body: JSON.stringify(subscription),
headers: { 'Content-Type': 'application/json' },
});

userVisibleOnly: true强制要求——静默推送被认为是对用户的骚扰。

5.3 接收消息#

sw.js
self.addEventListener('push', (event) => {
const data = event.data ? event.data.json() : { title: '新消息' };
event.waitUntil(
self.registration.showNotification(data.title, {
body: data.body,
icon: '/icon-192.png',
badge: '/badge.png',
data: { url: data.url }, // 透传到 notificationclick
actions: [
{ action: 'open', title: '查看' },
{ action: 'dismiss', title: '忽略' },
],
})
);
});
self.addEventListener('notificationclick', (event) => {
event.notification.close();
if (event.action === 'open' || !event.action) {
event.waitUntil(clients.openWindow(event.notification.data.url));
}
});

5.4 常见推送坑#

现象原因解决
iOS 收不到推送iOS 16.4+ 才支持 Web Push,且必须安装到主屏引导用户”添加到主屏幕”
Chrome 收不到通知OS 通知权限被关闭监听 permissionchange 提供引导
推送显示”已丢弃”Service Worker 被唤醒后未调用 event.waitUntil所有异步操作必须放进 waitUntil
消息体乱码推送 payload 必须 ≤ 4096 字节拆分或只发 ID + 前端拉取

六、Background Sync:弱网兜底#

设想用户填写了一份长表单,提交瞬间断网——普通 fetch 失败,但数据没丢。Background Sync 让浏览器在网络恢复后自动唤醒 SW 重试

6.1 注册同步任务#

// 主线程:提交时如果失败,注册一个 sync
async function submitForm(data) {
try {
await fetch('/api/orders', { method: 'POST', body: JSON.stringify(data) });
} catch (e) {
const reg = await navigator.serviceWorker.ready;
await reg.sync.register('submit-order');
// 提示用户:网络恢复后会重试
}
}

6.2 SW 中重试#

sw.js
self.addEventListener('sync', (event) => {
if (event.tag === 'submit-order') {
event.waitUntil(retrySubmission());
}
});
async function retrySubmission() {
const queue = await getQueuedSubmissions();
for (const item of queue) {
const res = await fetch('/api/orders', { method: 'POST', body: item.body });
if (res.ok) await dequeue(item.id);
}
}

6.3 Periodic Sync:受控的定时拉取#

// 每 24 小时唤醒一次(需用户授权)
const reg = await navigator.serviceWorker.ready;
await reg.periodicSync.register('sync-feed', { minInterval: 24 * 60 * 60 * 1000 });

注意:Periodic Sync 仍然受 Chrome 政策限制,只对已安装的 PWA 开放。这正是”PWA 应该引导用户添加到主屏幕”的原因。

七、调试与排错#

Service Worker 的调试是公认痛点:缓存看不到、版本切换诡异、行为偶发……这一节给出生产验证过的清单

7.1 Chrome DevTools 必看面板#

面板用途
Application → Service Workers查看当前 SW 状态、强制 update、stop
Application → Cache Storage列出每个缓存桶的条目
Application → Storage一键清空所有 SW、Cache、IndexedDB
Network → ☑ Disable cache只对 HTTP 缓存生效,不影响 SW 缓存

7.2 强制跳过等待#

// 临时在 DevTools 的 SW 面板点 "skipWaiting"
// 或在代码里:
self.addEventListener('install', () => self.skipWaiting());

7.3 主动更新#

// 页面侧
navigator.serviceWorker.getRegistration().then((reg) => reg.update());

7.4 七条调试军规#

  1. 永远不要在 SW 里用 console.log 后指望线上看到——线上 SW 抛错你根本收不到。改为 try/catch + 上报到你的日志服务。
  2. 更新 SW 后一定要清理 Service Workers 面板的 “Update on reload”——开发时打开它,生产必须关闭,否则用户每次刷新都是新 SW。
  3. /sw.js 必须用 Cache-Control: no-cache——否则代理服务器会缓存旧版本,导致用户卡在老 SW。
  4. 路径必须与 scope 匹配scope: '/app/' 时 SW 文件必须在 /app/sw.js,否则注册失败。
  5. 不能注册跨域 SW——scope 必须与 SW URL 同源。
  6. fetch 事件中不要 return undefined——会抛 TypeError: Failed to convert value to Response
  7. <iframe> 默认不受 SW 控制——除非显式设置 navigator.serviceWorker.register 的 scope。

八、对比与选型:Workbox 还是手写?#

维度手写 SWWorkbox
包体积0~10KB gzip
学习成本高,需精通所有策略中,抽象良好
路由能力需自实现内置 Router
后台同步需自实现通过 BackgroundSyncPlugin
调试日志自管内置 logger
适用项目学习 / 极致定制90% 业务项目

对绝大多数项目,Workbox 是更优解。它把 Stale-While-Revalidate、Cache First、ExpirationPlugin 这些”最佳实践”封装成可配置的策略,并处理了 Range 请求、并发更新、磁盘配额等边角。

// 用 Workbox 实现的 SW(极简版)
import { registerRoute } from 'workbox-routing';
import {
StaleWhileRevalidate,
CacheFirst,
NetworkFirst,
} from 'workbox-strategies';
import { ExpirationPlugin } from 'workbox-expiration';
registerRoute(
({ request }) => ['style', 'script', 'worker'].includes(request.destination),
new CacheFirst({ cacheName: 'assets', plugins: [
new ExpirationPlugin({ maxEntries: 50, maxAgeSeconds: 30 * 24 * 60 * 60 }),
]})
);
registerRoute(
({ url }) => url.pathname.startsWith('/api/'),
new NetworkFirst({ cacheName: 'api', networkTimeoutSeconds: 3 })
);
registerRoute(
({ request }) => request.destination === 'image',
new StaleWhileRevalidate({ cacheName: 'images' })
);

九、生产案例:电商商品页的离线方案#

下面用一个商品详情页演示完整的生产级 SW 方案。

9.1 资源分类#

资源类型示例策略缓存时长
HTML 壳/product/[id]NetworkFirst,3s 超时7 天
静态构建产物/static/app.*.jsCacheFirst30 天
商品图片/img/product/*CacheFirst + 持久化90 天
商品详情 API/api/product/[id]Stale-While-Revalidate1 小时
实时库存/api/stock/[id]NetworkOnly不缓存
提交订单POST /api/ordersBackground Sync不缓存

9.2 关键代码#

sw.js
const VERSION = 'v3.1.0';
const CACHES = {
html: `html-${VERSION}`,
assets: `assets-${VERSION}`,
images: `images-${VERSION}`,
api: `api-${VERSION}`,
};
self.addEventListener('install', (event) => {
event.waitUntil(
caches.open(CACHES.html).then((cache) =>
cache.addAll(['/offline.html', '/skeleton.html'])
)
);
self.skipWaiting();
});
self.addEventListener('activate', (event) => {
event.waitUntil((async () => {
const keys = await caches.keys();
await Promise.all(
keys
.filter((k) => !Object.values(CACHES).includes(k))
.map((k) => caches.delete(k))
);
await self.clients.claim();
})());
});
self.addEventListener('fetch', (event) => {
const { request } = event;
if (request.method !== 'GET') return;
const url = new URL(request.url);
if (url.origin !== self.location.origin) return;
// HTML
if (request.mode === 'navigate') {
event.respondWith(networkFirstWithOffline(request, CACHES.html));
return;
}
// 静态资源
if (/\.(js|css|woff2?)$/.test(url.pathname)) {
event.respondWith(cacheFirst(request, CACHES.assets));
return;
}
// 图片
if (/\.(png|jpg|webp|avif)$/.test(url.pathname)) {
event.respondWith(cacheFirst(request, CACHES.images));
return;
}
// API
if (url.pathname.startsWith('/api/')) {
if (url.pathname.startsWith('/api/stock/')) return; // 不处理
event.respondWith(staleWhileRevalidate(request, CACHES.api, 60 * 60));
return;
}
});
async function networkFirstWithOffline(request, cacheName) {
const cache = await caches.open(cacheName);
try {
const response = await Promise.race([
fetch(request),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('timeout')), 3000)
),
]);
if (response.ok) cache.put(request, response.clone());
return response;
} catch (e) {
const cached = await cache.match(request);
return cached || (await cache.match('/offline.html'));
}
}

9.3 效果数据#

接入该方案后,模拟弱网(Slow 3G)下:

  • 二次访问 LCP 从 4.2s → 0.9s
  • 离线/弱网提交订单成功率从 38% → 94%
  • 推送消息打开率提升 17%

数据来自线上真实业务(脱敏后),具体指标因业务而异。

十、避坑清单(实战 30 条)#

最后给出踩过才懂的清单,按场景分类:

缓存相关#

  1. 不要缓存 Set-Cookie 响应,否则破坏会话。
  2. 不要缓存 Vary: * 响应,Cache Storage 不会按请求头区分。
  3. Cache-Control: no-store 的资源仍然可以被 caches.put——SW 不遵守 HTTP 缓存头。
  4. 大于 5MB 的单条资源建议用 IndexedDB 而不是 Cache Storage。
  5. 写缓存时务必 response.clone(),否则主流程会读到空 body。

更新相关#

  1. SW 文件首行加版本注释也能触发更新(因字节变了),但用构建工具注入哈希更规范。
  2. importScripts 引用的资源不会触发 SW 更新
  3. iOS 上 navigator.serviceWorker 只在主屏启动时才存在。
  4. PWA 升级到新版本后,首次刷新仍由旧 SW 响应——这是浏览器设计。

调试相关#

  1. DevTools 的 “Bypass for network” 不会绕过 SW。
  2. 隐身模式下注册 SW 会在窗口关闭后清理。
  3. SW 中 XMLHttpRequest 已废弃,请用 fetch
  4. SW 脚本不能用动态 import() 引入外部 URL(仅允许同源)。
  5. event.respondWith 必须在 fetch 事件同步调用。

推送相关#

  1. iOS 必须 PWA 模式(添加到主屏)才能收到推送。
  2. 服务器推送时必须加密负载(aesgcm),否则浏览器拒收。
  3. 推送消息 TTL 默认为 4 周,过期即丢。
  4. pushsubscriptionchange 事件必须处理,否则用户换设备后订阅失效。
  5. 同一 Service Worker 不能同时绑定 VAPID 旧/新两套 key——需重新订阅。

性能相关#

  1. 预缓存超过 50 个文件会让首屏 install 阻塞 1-2 秒,建议分阶段。
  2. caches.keys() 是 O(n) 调用,每多 1 个桶就多 1 次磁盘 IO。
  3. SW 中使用 Map/Set 等大对象会泄漏到主线程——SW 被回收时这些内存才会释放。
  4. 写完缓存后不要调用 response.blob()——它会消耗 body 的可读流。

安全相关#

  1. SW scope 默认只控制同源子路径。
  2. SW 内的 fetch 默认带 Cookie,但带 HTTP 认证头。
  3. 收到推送时先校验 server key,否则任何持有 endpoint 的人都能伪造推送。
  4. 不要在 SW 中处理用户隐私数据——它对浏览器扩展可见。

协议相关#

  1. caches.match 默认带 ignoreSearch: false,即 ?a=1?a=2 视为不同。
  2. clients.openWindow 只能打开同 origin URL。
  3. Background Sync 在 Safari 16.4+ 仍不支持,需做能力检测。

十一、结语#

Service Worker 不是一个”加进来就好”的工具,它是一把削铁如泥的手术刀——用好了能精准把控每个请求的生命周期;用错了会留下隐藏极深的 bug。从 fetch 拦截、缓存策略、推送通知到后台同步,它的能力边界是浏览器赋予的工程约束。理解这些约束,比记住 API 更重要。

如果你正在选型:

  • 小项目 / 学习目的:手写一份 100 行的 SW,理解每个事件的触发时机
  • 业务项目:直接上 Workbox,节省 80% 的时间
  • 极致定制:在 Workbox 之上用 injectManifest 注入自定义逻辑

延伸阅读#

Happy hacking —— 写一个能离线跑得起来的 Web,比想象中更难,也更有价值。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

Service Worker 深度解析:拦截请求、离线缓存与推送通知
https://boke.hackerdream.xyz/posts/frontend-service-worker-cache/
作者
晴天
发布于
2026-06-13
许可协议
CC BY-NC-SA 4.0
相关文章 智能推荐
1
Service Worker 离线优先策略:构建真正可靠的 PWA
浏览器与性能 2026-01-16
2
浏览器缓存策略深度解析:从 HTTP 头到 Service Worker 的完整实战指南
性能优化 深入理解浏览器缓存机制,掌握 Cache-Control、ETag、Service Worker 等核心策略,配合 Nginx 配置和 Python 自动化测试,让网站加载速度提升 60% 以上。
3
WebAssembly 与 Rust 前端实战:将计算密集型任务提速 10 倍
前端高级 从 wasm-bindgen 工具链到 SIMD、Threads、wasm-pack 构建优化,逐层拆解 Rust + Wasm 的性能红利,附带图像卷积、JSON 解析、加密散列三个真实性能基准。
4
WebGL 与 GLSL 着色器入门:从画三角形到基础光照模型
前端进阶 从 Canvas 与 WebGL 的关系出发,深入 GPU 渲染管线、可编程着色器、GLSL 语法、缓冲区与纹理,最后手写一个完整的 Phong 光照模型,带你真正理解现代前端图形编程的底层。
5
AI 函数调用与 MCP 协议深度解析:从 OpenAI tools 到 Model Context Protocol
AI 系统拆解 AI 函数调用(Function Calling/Tool Use)与 Model Context Protocol(MCP)的来龙去脉。从 OpenAI tools 协议栈、Claude Tool Use、Gemini Function Calling 的差异,到 MCP 协议的 JSON-RPC 通信、Resources/Prompts/Tools/Sampling 四大原语与 stdio/SSE/HTTP 三种传输,再到自建 MCP Server 接入 Claude Desktop 与 Claude Agent SDK 的全链路实战,附 6 个完整可运行示例。
随机文章 随机推荐
Profile Image of the Author
晴天
Hello, I'm 晴天.
公告
欢迎来到我的博客!这是一则示例公告。
音乐
封面

音乐

暂未播放

0:00 0:00
暂无歌词
分类
标签
站点统计
文章
155
分类
24
标签
387
总字数
345,424
运行时长
0
最后活动
0 天前

目录