华为Web Push接入实战:从Service Worker到后端API全链路解析
作为从业者聊聊华为Web Push的接入。华为Web Push简单说就是华为消息推送能力在Web浏览器环境下的完整落地。过去给Web站点做消息触达要么自己维护WebSocket长连接要么接入第三方推送前者在移动端浏览器上经常被系统杀掉进程后者在华为设备上又可能因为省电策略出现消息延迟。华为Web Push解决的就是这个问题浏览器在页面关闭状态下依然能收到通知并且借助厂商系统级通道到达率和及时性都比自建通道可靠不少。这篇文章我从账号配置、前端Service Worker接入、后端API调用到问题排查把完整链路串一遍适合正在评估或已经决定接入的开发者参考前后端同学都能找到自己关心的那部分。1. 整体架构与设计思路1.1 Web推送的基本运转逻辑先说清楚Web推送是怎么工作的。标准Web Push协议里有三个角色浏览器、推送服务、应用服务器。浏览器里常驻一个Service Worker脚本它是消息到达时唯一能“醒过来”处理通知的地方。推送服务负责把消息从应用服务器转投给浏览器这个服务由浏览器厂商或系统厂商运营。应用服务器也就是我们的后端通过一段受信认证用标准协议把消息发给推送服务。用户第一次访问站点时前端通过pushManager.subscribe()发起订阅浏览器会返回一个Endpoint地址和一组加密密钥。这个Endpoint可以理解为这台浏览器在该推送服务上的“门牌号”后端保存下来之后发消息就是往这个地址投递。华为Web Push本质上是把标准Web Push协议中的推送服务替换成了华为自家的推送通道同时在前端能力和服务端API上做了适配。好处在于在华为设备上这个通道和系统级推送是一体的省电策略不会随便拦截网页关掉、浏览器退到后台消息依然能到达。1.2 为什么选华为Web Push而不是自建通道我看过不少团队在自建推送和接入厂商通道之间反复横跳。自建WebSocket的思路很直观自己维护一条长连接后端能随时下推。但实际运营下来有几个问题绕不过去移动端浏览器对长期后台连接并不友好页面切到后台后浏览器会冻结定时器、断掉WebSocket消息就断了。一台设备的并发连接要维持心跳量一大服务器带宽和连接数成本明显上涨。用户关掉页面标签后网页级连接直接失效完全收不到消息。华为Web Push把连接维护这件事交给了系统和浏览器底层应用服务器只需要在需要时调用发送接口。用户关掉页面也能收到通知这个体验差异对于资讯、导购、协同工具这类站点非常关键。当然它不是万能药如果你需要的是浏览器内实时双向通信WebSocket仍然有用但单向触达提醒的场景Web推送是更经济可靠的选择。1.3 整体接入链路的三个环节整个接入链路可以分为前端、服务端和平台配置三块平台配置在开发者后台创建Web应用开通推送服务拿到App ID、App Secret和VAPID密钥。前端注册Service Worker请求通知权限订阅推送拿到Token后上报给后端。后端用App ID和App Secret换取Access Token调用发送接口向指定Token投递消息。这三块环环相扣。最容易出问题的不是某一个环节特别难而是信息不对齐——前端用了A密钥后端用B密钥或者前端Token拿不到后端盲目发消息报错。后面的内容就按这个链路顺序展开。2. 接入前的准备工作账号、应用与密钥2.1 创建Web应用并开通推送服务在开发者后台创建应用这一步其实不复杂但很多人在“应用类型选什么”上容易犹豫。Web Push接入时应用类型要选择Web不是Android也不是快应用。创建成功后在“开发服务”菜单下找到“推送服务”点击开通。不同开发者账号的界面布局可能略有差异但入口名一般是“Push Kit”或“推送服务”找开发服务那一栏基本不会错。开通后要关注的是应用信息页里的两个值App ID和App Secret。App ID相当于应用在平台上的身份证App Secret是调用服务端API时的密钥这两个值一走完注册流程就能看到。注意App Secret等同于密码不能出现在前端代码里更不能提交到仓库。如果泄露了后台要支持重置重置后老密钥立刻失效所有依赖它获取Token的服务端逻辑都得同步换。2.2 生成VAPID密钥对VAPID是Web Push标准里的应用服务器身份标识机制核心是一对公钥和私钥。浏览器在订阅时会校验你传进来的公钥后端发消息时用私钥签名推送服务通过公钥验证请求来源。华为Web Push沿用这套机制所以在后台的推送服务配置页里需要生成VAPID密钥对。生成完会得到两个字符串公钥用来放在前端订阅代码里私钥用于后端拼接发送请求时的认证信息。有的开发者文档里也把VAPID公钥叫applicationServerKey理解了这一点看到不同叫法就不会发懵。这里有一个实操中容易踩的坑VAPID密钥对生成之后后台一般允许重新生成但一旦重新生成所有前端已订阅的Token和新的订阅都会按新密钥走旧Token可能全部失效。所以千万不要在生产环境随意重置这个密钥确需重置时要做好批量清洗旧Token的准备。2.3 环境要求与兼容性清单Web Push对运行环境有硬性要求接入前先对照确认项目要求协议站点必须是HTTPS本地开发可用localhost豁免浏览器Chrome、Edge、Firefox等主流浏览器版本需支持Service Worker和PushManager权限用户必须同意通知权限后才能订阅成功前后端通信需要有后端接口接收Token并下发消息兼容性层面苹果Safari的历史版本不支持标准Web Push目前在桌面端较新版本才逐步支持。如果产品的主要用户大量使用Safari需要提前评估这个限制或者给不支持的浏览器做降级处理比如站内提示替代方案。这个问题不只是华为Web Push有所有基于Web Push标准的方案都会遇到。3. 前端接入核心实现3.1 注册Service Worker前端接入的第一步是注册Service Worker脚本这个脚本是整个推送接收的中枢。代码通常放在页面入口if (serviceWorker in navigator PushManager in window) { window.addEventListener(load, function () { navigator.serviceWorker.register(/sw.js) .then(function (registration) { console.log(Service Worker 注册成功); window.registration registration; }) .catch(function (error) { console.error(Service Worker 注册失败, error); }); }); }注册成功后registration对象就是后续订阅推送的入口。注册时机建议放在load事件之后避免影响页面首屏渲染。sw.js的路径需要注意/sw.js代表站点根目录它的控制范围默认为根目录下所有页面。如果站点部署在子路径比如/app/那么路径要相应调整为/app/sw.js否则作用域不够页面无法触发订阅。Service Worker文件本身需要放在服务器上并且响应头不能禁用JS执行这个一般默认没问题但公司内部的CDN或网关偶尔会做静态资源改写遇到订阅报错时可以先查响应头。3.2 订阅流程与Token获取注册完成之后就是向浏览器发起订阅请求。订阅方法接收两个核心参数userVisibleOnly固定为true含义是每条推送消息都必须对用户可见不能做静默推送。applicationServerKey就是VAPID公钥需要转成Uint8Array格式。把VAPID公钥从Base64字符串转换为二进制数组是前端代码里最容易被忽略的小细节。标准Web Push API要求的不是普通字符串而是一个字节数组转换工具函数可以直接复用function urlBase64ToUint8Array(base64String) { const padding .repeat((4 - base64String.length % 4) % 4); const base64 (base64String padding) .replace(/-/g, ) .replace(/_/g, /); const rawData window.atob(base64); const outputArray new Uint8Array(rawData.length); for (let i 0; i rawData.length; i) { outputArray[i] rawData.charCodeAt(i); } return outputArray; }订阅调用如下async function subscribePush() { const registration await navigator.serviceWorker.ready; const subscription await registration.pushManager.subscribe({ userVisibleOnly: true, applicationServerKey: urlBase64ToUint8Array(VAPID_PUBLIC_KEY) }); // 这里拿到的 subscription.endpoint 就是要传给后端的推送Token return subscription.endpoint; }这里有一个我个人的实操体会有些开发者在检查订阅状态时会直接用pushManager.getSubscription()判断是否已订阅。这个API本身没问题但如果用户之前用旧公钥订阅过后面公钥换了getSubscription()会返回一个已经失效的订阅对象。稳妥做法是拿到订阅后校验它的endpoint前缀是否和当前推送服务域名匹配不匹配就走重新订阅流程。3.3 消息接收与通知展示订阅拿到Token后消息到达浏览器时Service Worker会触发push事件。事件处理函数里要做两件事解析消息负载调用showNotification展示通知。self.addEventListener(push, function (event) { let payload {}; try { payload event.data ? event.data.json() : {}; } catch (e) { payload { title: 新消息, body: 请点击查看 }; } const options { body: payload.body || , icon: payload.icon || /logo.png, badge: payload.badge || /badge.png, data: payload.data || {} }; event.waitUntil( self.registration.showNotification(payload.title || 新消息, options) ); });event.waitUntil是Service Worker里必须的用法它告诉浏览器在Promise完成之前不要终止Service Worker。直接同步展示通知虽然有时也能工作但不规范在低端机上可能通知还没弹出来Service Worker就被回收了。通知点击事件同样要处理self.addEventListener(notificationclick, function (event) { event.notification.close(); const targetUrl event.notification.data.url || /; event.waitUntil( clients.matchAll({ type: window, includeUncontrolled: true }).then(function (windowClients) { for (let i 0; i windowClients.length; i) { if (focus in windowClients[i]) { windowClients[i].focus(); return; } } return clients.openWindow(targetUrl); }) ); });这个逻辑不复杂但建议每个接Web Push的团队都配上。用户点击通知后没有反馈是通知到达率之外最影响体验的环节。有多个窗口打开时优先聚焦已有窗口没有才新开这是网页端的通用交互习惯。3.4 通知权限引导的实操细节通知权限是浏览器弹窗请求的用户拒绝后后续再次调用subscribe()会直接失败不会再次弹窗。这个限制让权限引导策略变得很重要。我的做法是不在页面首次访问时请求权限而是在用户完成某个有明确动机的动作后再请求比如订阅资讯、提交订单时。如果用户已经拒绝过页面里要提供一个“通知已关闭请在浏览器设置中开启”的引导按钮而不是反复调接口触发无意义的失败。另外浏览器地址栏旁边会有通知权限的图标状态用户可以通过那里管理权限。前端无法强制重新弹出授权窗口只能引导用户手动修改。这个体验细节产品同学和开发同学最好提前对齐。4. 后端发送消息的实现4.1 获取Access Token后端发送消息前先要拿到一个Access Token。华为Push服务用的是OAuth 2.0的客户端凭证模式也就是用App ID和App Secret去换一个临时凭证。请求示例curl -X POST https://oauth-login.cloud.huawei.com/oauth2/v3/token \ -H Content-Type: application/x-www-form-urlencoded \ -d grant_typeclient_credentials \ -d client_id您的AppID \ -d client_secret您的AppSecret返回结果里包含access_token和expires_in字段。Token通常有一个明确的有效期一般是3600秒左右不要太教条地每次都现取也不要一次缓存永久使用。我一般习惯在服务端用内存或者Redis缓存这个Token在有效期还剩五分钟时主动刷新避免并发高峰时所有请求同时去取Token造成瞬时压力。获取失败时要注意排查是不是App ID和App Secret抄串了尤其是App Secret末尾的字符容易复制漏掉。还有一个场景是账号下的服务没开通接口会返回权限类的错误这时候回到控制台确认推送服务状态。4.2 发送消息的接口结构拿Token后调用发送接口curl -X POST https://push-api.cloud.huawei.com/v1/{appId}/messages:send \ -H Content-Type: application/json \ -H Authorization: Bearer {access_token} \ -d { message: { token: [用户的推送Token], notification: { title: 订单发货提醒, body: 您的商品已出库点击查看物流信息 }, web: { h5_url: https://example.com/order/123 } } }token字段是目标设备的推送Token就是前端订阅后拿到的endpoint可以传一个字符串也可以传数组来实现批量发送。不过批量发送有数量上限超过上限就要拆分多批。notification对象控制通知栏展示的标题和正文。web.h5_url是Web端特有的字段用户点击通知后跳转的落地页地址不填的话点击通知可能没有反应。响应里一般会返回成功或失败的状态码成功码是80000000。看到其他码别慌对照官方错误码表排查大多数是Token无效或参数格式问题。4.3 消息类型与参数选择Web Push场景里消息主要分两类。一类是通知消息发出去后由浏览器直接展示通知Service Worker的push事件也能收到负载但即使不写任何处理逻辑通知也能展示。另一类是数据消息只携带业务数据不会主动弹通知需要Service Worker里自行解析并调用showNotification适合需要先做业务处理再决定要不要展示的场景。实际项目中通知消息能覆盖大多数需求。数据消息用在需要个性化处理的场景比如消息里带订单ID前端先查询本地缓存再决定通知文案。这两类的接口写法差别不大主要是notification这个字段的存在与否以及data字段的封装方式。优先级参数也值得一提。华为Push接口支持通过android和web字段控制消息的展示方式和行为比如Web端可以配置消息的生存时间。给用户发验证码类通知时建议设置较短的超时时间避免消息延迟后还在设备上弹出给用户发运营活动通知时超时时间可以适当放宽让消息在用户下次联网时仍然到达。4.4 Token的存储与生命周期管理前端上报的推送Token后端需要用一张表或一个独立的存储空间维护。字段至少包括用户ID、Token、设备信息、创建时间、最后活跃时间。每次用户登录时前端应该重新拉取一下自己的Token状态后端做一次对账避免用户换设备后旧Token一直占着位置。Token失效的场景主要有用户清除了浏览器站点数据。用户主动撤销了通知权限。浏览器重装或系统重置。服务器端VAPID密钥重新生成。后端发送遇到Token无效的错误时应该把对应的Token记录下来并在一定时间后统一清理。这块如果不做时间一长Token表里可能积累大量僵尸数据批量发送时白白耗费请求量。5. 常见问题与排查技巧实录5.1 高频问题速查表现象可能原因处理方式页面提示浏览器不支持推送浏览器版本过旧或不支持Service Worker升级浏览器或在老浏览器上做降级订阅时报错页面非HTTPS环境部署证书本地开发可用localhost调试Token获取成功但后端发送返回无效TokenVAPID密钥不匹配或Token已被清检查前后端密钥配置重新订阅消息发送返回成功但设备收不到通知权限被用户关闭或消息超时时间过短检查用户权限状态调整消息策略点击通知无反应Service Worker未处理notificationclick在sw.js中补充点击事件处理后端Access Token获取失败App Secret错误或服务未开通核对控制台配置确认推送服务已开通5.2 调试思路与工具链Web Push的调试链路比较长前端、中间通道、后端各管一段问题定位的关键是先确定故障在哪一段。最直接的方法是看Service Worker调试面板。打开开发者工具在Application标签下找到Service Workers这里能看到Service Worker是否激活、有没有报错。Push Notification标签可以手动模拟推送事件用来测试sw.js里的展示逻辑是否正常。这种方式不依赖后端非常适合先验证前端链路。后端调试相对更枯燥。发送接口返回的响应码是重要线索失败时响应里会带具体原因。我的排查顺序是先确认Access Token是否有效再确认Token格式是否正确最后在控制台查一下设备是否有投递记录。华为开发者后台的推送服务页面通常有消息统计可以按时间查到消息的状态到达数和点击数都能看到这个是排查到达率问题的重要数据来源。5.3 到达率与体验优化心得平台能力再好使用方式不当也会把到达率做崩。几个我实际验证过的做法第一避免高频率无差别推送。用户授权通知并不代表愿意接受骚扰如果站点连续发几条无关消息用户大概率会在系统设置里关掉权限。权限一旦关了后续消息全部石沉大海。推送的频次控制应该写在产品逻辑里而不是单纯依赖技术通道。第二消息内容里尽量带上明确的落地页。点击通知后到达一个和通知内容强相关的页面用户才知道这个通知的价值。需要登录才能查看的页面落地页最好带上登录态跳转参数否则用户点击后看到登录页很容易直接流失。第三置空或精简通知图标。Web通知在桌面端和移动端显示尺寸不同图标太大或太小都会导致系统对通知卡片做缩放观感不佳。建议准备一个尺寸适中的Logo图标并在前端订阅时把图标地址配置到通知参数里。第四定时任务发消息时要考虑用户时区。如果是面向全国用户的站点凌晨批量发通知对用户是个打扰即便消息本身有价值体验也大打折扣。我个人的体会是华为Web Push接入本身并不复杂前后端加起来一天左右就能跑通主流程难的是把通知权限当成真正需要经营的用户触点来设计。很多项目做完接入后就不管了结果用户关权限率非常高最后得出“推送没用”的结论。实际上从权限引导、消息文案到落地页体验每一环都在影响最终效果。如果你正在做接入建议把“用户收到消息后的行为”纳入上线后的数据观察持续迭代推送策略这才是这套能力真正的价值所在。

相关新闻

大模型蒸馏的远期回旋镖:能力天花板、偏见继承与维护泥潭

大模型蒸馏的远期回旋镖:能力天花板、偏见继承与维护泥潭

1. 大模型蒸馏到底在蒸什么第一次听到“蒸馏”这个词,很多人会联想到酿酒——把一大锅发酵液加热,让酒精挥发再冷凝收集,最后得到浓度更高的烈酒。大模型蒸馏的逻辑几乎一模一样:把一个大模型(教师模型)里学…

2026/10/13 19:07:16 阅读更多 →
FDE前置部署工程师:AI落地最后一公里的关键角色与实操指南

FDE前置部署工程师:AI落地最后一公里的关键角色与实操指南

1. 为什么“最后一公里”总卡在落地环节1.1 从一个真实场景说起去年我参与过一个内部知识库的智能化改造项目。模型选的是当时口碑不错的一个开源版本,硬件也配了双卡推理服务器,Demo 阶段效果惊艳——问什么答什么,回答流畅、格式工整。团队…

2026/10/13 19:07:16 阅读更多 →
大模型训练数据准备全流程:从采集清洗到配比分词

大模型训练数据准备全流程:从采集清洗到配比分词

1. 大模型训练的第一步为什么是数据而不是模型结构很多人第一次接触大模型训练,脑子里想的都是Transformer架构、注意力机制、参数量怎么堆到千亿级别。但真正进过训练流程的人都知道,模型结构在今天的工程实践中反而相对标准化了,真正决定一…

2026/10/13 19:07:16 阅读更多 →

最新新闻

Java异步编程进阶:CompletableFuture任务编排与线程池实战

Java异步编程进阶:CompletableFuture任务编排与线程池实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 2:43:28 阅读更多 →
单片机毕设项目:基于单片机的智能婴儿摇篮温湿度调节与远程监控系统设计 基于WIFI的婴儿床哭泣尿床监测与环境联动控制装置设计(032707)

单片机毕设项目:基于单片机的智能婴儿摇篮温湿度调节与远程监控系统设计 基于WIFI的婴儿床哭泣尿床监测与环境联动控制装置设计(032707)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/10/14 2:43:28 阅读更多 →
【计算机毕业设计单片机案例】基于STM32的婴儿监护环境调节与摇篮哄睡系统设计 基于物联网的智能婴儿床环境调节与生理状态监测装置设计(032707)

【计算机毕业设计单片机案例】基于STM32的婴儿监护环境调节与摇篮哄睡系统设计 基于物联网的智能婴儿床环境调节与生理状态监测装置设计(032707)

博主介绍:✌️码农一枚 ,专注于大学生项目实战开发、讲解和毕业🚢文撰写修改等。全栈领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于嵌入式单片机,Java、小程序技术领域和毕业项目实战 ✌️…

2026/10/14 2:43:28 阅读更多 →
我们在联想 ThinkStation PGX 上搭了一个“AI 编辑部“

我们在联想 ThinkStation PGX 上搭了一个“AI 编辑部“

在输入框里只敲了五个字:“本周的选题”,随后,屏幕上的工具调用记录一行行滚过:分析账号定位、生成检索条件、搜索近期内容、排出候选题。我们选定一个,它又去补资料、搭大纲、写初稿……这不是一次简单的模型问答&…

2026/10/14 2:43:28 阅读更多 →
基于Python的电影受众群体特征分析与可视化实战

基于Python的电影受众群体特征分析与可视化实战

提到“基于Python的电影受众群体特征研究”这个毕设题目,很多准备开题的同学第一个反应是:这方向是不是太普通了?我自己的看法是,普通题目更容易做出完整度和深度。它把Python数据处理、统计分析、可视化、机器学习串成了一条完整…

2026/10/14 2:43:28 阅读更多 →
人行动5.0结业测试:从模型微调到部署排错的备考指南

人行动5.0结业测试:从模型微调到部署排错的备考指南

简介:适用于讯飞新员工及转正考核准备的结业测试答案文档,内容围绕‘讯飞人行动’5.0版本的结业测评展开,完整覆盖单选题、多选题与判断题三类题型,可帮助考生在短时间内掌握考核要点。资源共1个docx文件、仅23KB,轻量…

2026/10/14 2:42:28 阅读更多 →

日新闻

AnyPS5通用适配层设计:兼容性与性能的工程取舍

AnyPS5通用适配层设计:兼容性与性能的工程取舍

1. 从"AnyPS5"这个名字说起:它到底想解决什么问题第一次看到"AnyPS5"这个标题,我脑子里冒出来的第一个念头是:这大概率是一个围绕"跨平台运行"或者"通用适配"做文章的项目。名字里的"Any"…

2026/10/14 0:00:41 阅读更多 →
Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

Flutter跨平台开发实战:二手交易应用与HarmonyOS适配

1. 项目概述:Flutter跨平台二手交易应用开发作为一名长期从事移动应用开发的工程师,我最近完成了一个基于Flutter框架的二手物品交易平台项目。这个项目最吸引我的地方在于它完美结合了Flutter的跨平台优势与HarmonyOS的生态特性,实现了"…

2026/10/14 0:00:41 阅读更多 →
DFS算法解决飞机降落调度问题实战

DFS算法解决飞机降落调度问题实战

1. 项目背景与核心思路飞机降落问题是一个经典的调度优化场景,也是算法竞赛和面试中的高频题目。这个题目要求我们合理安排多架飞机的降落顺序和时间,确保所有飞机都能安全着陆而不发生冲突。在实际航空管制中,这类问题直接关系到飞行安全和机…

2026/10/14 0:00:41 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/13 2:54:56 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/13 16:22:04 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/13 1:58:08 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 22:01:27 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/13 14:57:39 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/12 18:11:17 阅读更多 →