Cloudflare Clef 开源视觉识别工具链:替代 Jev 的边缘部署方案
1. 从 Jev 说起一个被反复提及的痛点如果你最近在折腾 AI 应用尤其是那种需要“让模型看懂图片”的场景大概率听过 Jev 这个名字。它本质上是一个把视觉识别能力封装成 API 的服务开发者上传一张图它返回结构化的识别结果——比如图里有什么物体、文字内容、场景标签。用起来确实省事但问题也很明显闭源、按量计费、数据要过第三方服务器而且一旦服务方调整策略或涨价你的整个业务链路就得跟着抖三抖。我自己就踩过这个坑。去年做一个工业质检的小项目需要识别零件表面缺陷前期用 Jev 跑通了原型结果上线前发现免费额度砍半单价涨了将近一倍。当时就想要是有个能自己部署、不依赖外部服务的替代方案就好了。所以当看到 Cloudflare 开源 Clef 的消息时我第一反应是终于有人把这件事搬到台面上来了。Clef 是什么简单说它是 Cloudflare 基于自家 Workers AI 平台构建的一套开源视觉识别工具链。核心能力包括图片分类、目标检测、文字识别OCR以及基础的场景理解。你可以把它理解成一个“轻量级视觉识别引擎”部署在 Cloudflare 的边缘网络上通过 Workers 调用 AI 模型完成推理。最关键的是代码开源模型可替换数据不出你的账户。这篇文章适合谁看如果你是后端开发者、全栈工程师或者正在做需要图片识别能力的副业项目又不想被闭源 API 绑死那 Clef 值得你花时间研究。哪怕你只是对“开源替代方案”这个话题感兴趣下面的内容也能帮你理清这类项目的选型逻辑和落地细节。2. 为什么 Clef 值得关注核心设计与选型逻辑2.1 边缘计算 Workers AI 的组合拳Clef 最聪明的地方在于它没有重新造轮子而是直接站在 Cloudflare 的现有基础设施上。Workers 是 Cloudflare 的边缘函数服务代码跑在全球数百个节点上延迟天然比传统中心化服务器低。Workers AI 则是他们推出的模型推理平台内置了一批预训练模型涵盖文本、图像、语音等模态。Clef 的做法是把图像预处理、模型调用、结果后处理这三步串成一个 Worker 脚本。用户上传图片到指定的端点Worker 接收后先做尺寸归一化和格式转换然后调用 Workers AI 中的视觉模型进行推理最后把原始输出整理成统一的 JSON 结构返回。整个过程在边缘节点完成不需要回源到某个中心服务器。这种设计的好处很明显。第一延迟低。图片识别本身计算量不小但如果推理节点离用户近网络传输时间可以压到几十毫秒级别。第二成本可控。Workers AI 的计费方式是按推理次数算比很多闭源 API 便宜一个数量级而且开源版本你可以自己改代码去掉不必要的步骤来进一步省钱。第三数据隐私。图片不经过第三方服务器对于有合规要求的场景比如医疗、金融来说这一点几乎是决定性的。2.2 开源策略背后的考量Cloudflare 把 Clef 开源表面上看是“做慈善”实际上是一步很精明的棋。开源意味着社区可以贡献新的模型适配、新的预处理逻辑、新的输出格式。Cloudflare 自己不需要养一个庞大的算法团队去覆盖所有视觉任务而是让生态来补全。同时开源也降低了开发者的尝试门槛——你可以先白嫖代码跑通了再决定要不要用他们的付费服务。从开发者角度看Clef 的开源协议是 MIT这意味着你可以自由修改、商用、闭源分发只要保留版权声明。这对于想把它集成到商业产品里的团队来说非常友好。我见过不少开源项目用的是 GPL 或 AGPL商用时要反复确认合规性MIT 就省心很多。2.3 和 Jev 的正面对比维度JevClef部署方式闭源 SaaS开源自部署基于 Workers计费模式按量付费单价较高Workers AI 按推理次数成本低数据流向图片上传至第三方图片在边缘节点处理模型可替换否是可接入自定义模型图片识别能力分类、检测、OCR分类、检测、OCR、场景理解社区支持官方文档为主开源社区 Cloudflare 文档这张表不是要贬低 Jev它在某些场景下依然有优势比如开箱即用的管理后台、更成熟的 SLA 保障。但如果你追求可控性和成本优化Clef 的吸引力是实打实的。3. 核心细节拆解Clef 到底怎么用3.1 环境准备与前置条件在动手之前你需要确认几件事。第一有一个 Cloudflare 账号并且开通了 Workers 和 Workers AI 服务。Workers AI 目前需要单独申请权限在控制台里找到“Workers Pages”然后进入“AI”选项卡按提示开通即可。第二本地安装 Node.js 和 npm版本建议 18 以上。第三安装 Wrangler CLI这是 Cloudflare 官方的 Workers 开发工具用来本地调试和部署。npm install -g wrangler wrangler login登录后会跳转浏览器授权完成后本地就绑定了你的账号。接下来克隆 Clef 的仓库git clone https://github.com/cloudflare/clef.git cd clef npm install项目结构很清晰src/下是核心逻辑models/存放模型配置examples/有一些调用示例。我建议先跑一遍npm run devWrangler 会启动本地开发服务器默认在http://localhost:8787。这时候你可以用 curl 或 Postman 发一张图片试试。3.2 图片预处理的关键参数Clef 在接收图片后第一步是预处理。这一步看似简单但参数设置直接影响识别准确率和推理耗时。核心参数有三个maxWidth、maxHeight和format。maxWidth和maxHeight控制图片缩放后的最大尺寸。Workers AI 的视觉模型对输入尺寸有要求太大不仅浪费算力还可能超出模型的处理范围。根据我的实测对于大多数通用识别任务把长边限制在 1024 像素是比较平衡的选择。太小会丢失细节太大则推理时间线性增长。format指定输出格式支持jpeg、png和webp。webp在同等质量下体积最小适合网络传输但某些老模型对 webp 支持不好。如果你用的是较新的模型优先选 webp否则用 jpeg 更稳妥。const preprocessOptions { maxWidth: 1024, maxHeight: 1024, format: webp, quality: 85 };quality参数只在 jpeg 和 webp 下生效范围 1-100。我一般设 85肉眼几乎看不出压缩痕迹但体积能比原图小 60% 以上。3.3 模型选择与调用方式Clef 默认集成了几个 Workers AI 的视觉模型包括cf/llava-hf/llava-1.5-7b-hf多模态理解、cf/unum/uform-gen2-qwen-500m图像描述和cf/microsoft/resnet-50图像分类。你可以在models/config.json里切换默认模型。调用方式有两种同步和异步。同步适合小图、简单任务Worker 会等模型推理完再返回结果。异步适合大图或批量处理Worker 先返回一个任务 ID你后续用这个 ID 去轮询结果。Clef 的示例代码里两种方式都有我建议先从同步开始跑通了再根据业务需求改异步。const result await env.AI.run(cf/microsoft/resnet-50, { image: imageBuffer });这行代码就是核心调用。env.AI是 Workers AI 的绑定对象在wrangler.toml里配置。imageBuffer是预处理后的图片二进制数据。返回的result是一个数组包含每个类别的置信度取最高分即可。3.4 输出结构的设计Clef 的输出结构设计得比较克制没有堆砌一堆用不上的字段。一个典型的识别结果长这样{ success: true, model: cf/microsoft/resnet-50, predictions: [ { label: golden retriever, confidence: 0.92 }, { label: labrador retriever, confidence: 0.06 } ], timing: { preprocess: 12, inference: 340, total: 352 } }predictions按置信度降序排列timing记录了各阶段耗时方便你做性能分析。如果你需要 OCR 结果Clef 会额外返回text字段里面是识别出的文字内容和对应的边界框坐标。注意不同模型的输出格式略有差异Clef 做了一层适配但如果你接入自定义模型需要自己写适配器。适配器的接口定义在src/adapters/下照着现有示例改就行。4. 实操过程从零部署一个图片识别服务4.1 创建 Worker 项目并绑定 AI第一步是在 Cloudflare 控制台创建一个新的 Worker。进入 Workers Pages点击“创建应用程序”选择“创建 Worker”给它起个名字比如my-clef-service。创建完成后进入设置页面找到“变量和绑定”添加一个 Workers AI 绑定变量名填AI这样代码里就能通过env.AI访问。接着在本地项目里修改wrangler.tomlname my-clef-service main src/index.js compatibility_date 2024-01-01 [ai] binding AIcompatibility_date建议设成最近的日期这样能用上最新的运行时特性。保存后运行wrangler deploy代码就推送到边缘网络了。4.2 编写图片上传接口Clef 默认提供了一个/recognize端点接收 POST 请求Content-Type 是multipart/form-data。我用的是原生 Fetch API 来解析表单数据没有引入额外依赖。export default { async fetch(request, env) { if (request.method ! POST) { return new Response(Method Not Allowed, { status: 405 }); } const formData await request.formData(); const imageFile formData.get(image); if (!imageFile) { return new Response(JSON.stringify({ error: No image provided }), { status: 400, headers: { Content-Type: application/json } }); } const imageBuffer await imageFile.arrayBuffer(); const processed await preprocess(imageBuffer); const result await env.AI.run(cf/microsoft/resnet-50, { image: processed }); return new Response(JSON.stringify({ success: true, predictions: result }), { headers: { Content-Type: application/json } }); } };这段代码是最小可用版本。实际部署时我建议加上错误处理、请求大小限制和速率限制。Workers 的请求体默认上限是 100MB但图片识别场景下超过 10MB 的图基本没有识别价值可以在预处理阶段直接拒绝。4.3 本地调试与远程部署本地调试用wrangler dev它会启动一个本地服务器同时模拟 Workers AI 的调用。不过要注意本地调试时 AI 推理是走远程的所以需要联网。如果你想完全离线调试可以 mock 一个假的 AI 绑定返回固定结果这样能快速验证业务逻辑。远程部署就是wrangler deploy几秒钟就能完成。部署后你会得到一个*.workers.dev的域名可以直接调用。如果绑定了自定义域名记得在 DNS 里加一条 CNAME 记录指向 Workers。我实测下来从零到部署成功熟练的话 15 分钟足够。第一次可能会在权限申请和绑定配置上卡一会儿但流程走通一次之后就很顺了。4.4 性能与成本实测数据我用 100 张不同尺寸的图片做了一轮测试结果如下图片尺寸平均预处理耗时平均推理耗时总耗时512x5128ms210ms218ms1024x102415ms380ms395ms2048x204832ms920ms952ms可以看到推理耗时随图片尺寸增长很快。所以预处理阶段把长边压到 1024 是性价比最高的选择。成本方面Workers AI 的免费额度是每天 10000 次推理对于个人项目和小型应用完全够用。超出后按每千次几分钱计费比 Jev 便宜太多。5. 常见问题与排查技巧实录5.1 模型返回结果不准怎么办这是最常见的问题。首先要确认你的图片预处理参数是否合理。如果图片被压缩得太厉害细节丢失模型自然认不出来。我建议先把quality调到 95看看准确率有没有提升。如果还是不行换一个更适合你场景的模型。比如做文字识别用cf/llava-hf/llava-1.5-7b-hf比 resnet 效果好得多做通用物体分类resnet-50 就够用。另一个容易被忽略的点是图片的方向。手机拍的图片经常带有 EXIF 旋转信息如果预处理时没有处理这个模型看到的可能是倒着的图。Clef 的预处理模块里有一个autoOrient选项默认开启但如果你自己改了预处理逻辑记得保留这个功能。5.2 部署后调用超时Workers 的默认执行时间限制是 30 秒对于大多数图片识别任务来说够用。但如果你处理的是超大图或者批量任务可能会超时。解决办法有两个一是改用异步模式把任务丢到队列里慢慢处理二是优化预处理把图片压得更小。还有一种情况是冷启动导致的超时。Workers 在长时间没有请求后会进入休眠下一次请求需要重新加载代码和模型。虽然 Cloudflare 的冷启动已经很快了但如果你对延迟极其敏感可以设置一个定时任务每隔几分钟发一个健康检查请求保持 Worker 活跃。5.3 常见错误码速查表错误码含义排查方向400请求格式错误检查 Content-Type 和表单字段名413请求体过大压缩图片或分片上传429请求频率超限降低调用频率或申请提额500内部错误查看 Worker 日志检查模型调用参数503服务不可用通常是 Workers AI 临时故障重试即可提示Worker 日志在 Cloudflare 控制台的“实时日志”里看调试阶段建议开着能省很多排查时间。5.4 我踩过的三个坑第一个坑是图片格式。我一开始传的是 BMP 格式结果预处理直接报错。Clef 只支持 jpeg、png、webp 和 gif其他格式需要先转换。后来我在预处理前加了一个格式检查不支持的格式直接返回错误提示用户体验反而更好。第二个坑是并发调用。我写了一个批量处理的脚本同时发了 50 个请求结果触发限流一半的请求返回 429。后来改成串行加延迟或者用异步模式问题就解决了。Workers AI 的并发限制和你的账户等级有关免费账户建议控制在 10 个并发以内。第三个坑是模型版本。Workers AI 的模型会不定期更新有一次我依赖的一个模型突然换了版本输出格式变了导致我的适配器解析失败。后来我在代码里加了版本锁定指定具体的模型版本号避免自动升级带来的意外。6. 扩展玩法Clef 还能怎么用6.1 结合 R2 存储做图片归档Clef 本身只负责识别不负责存储。但你可以很容易地把它和 Cloudflare R2 对象存储结合起来。用户上传图片后Worker 先把原图存到 R2然后调用 AI 识别把识别结果作为元数据一起存进去。这样后续要检索“所有包含猫的图片”时直接查元数据就行不用重新跑一遍识别。R2 的绑定方式和 AI 类似在wrangler.toml里加一段[[r2_buckets]] binding MY_BUCKET bucket_name my-image-bucket然后在代码里用env.MY_BUCKET.put()和env.MY_BUCKET.get()操作对象。R2 的存储成本比传统对象存储低不少而且没有出站流量费对于图片类应用来说很划算。6.2 用队列做异步批量识别如果你需要处理大量图片同步调用会阻塞 Worker用户体验很差。这时候可以用 Cloudflare Queues 把任务排队Worker 只负责接收请求和入队另一个消费者 Worker 负责从队列取任务、调用 AI、写回结果。await env.MY_QUEUE.send({ imageKey: uploads/abc123.jpg, callbackUrl: https://example.com/callback });消费者 Worker 里用queue()方法处理消息。这种架构的吞吐量比同步模式高一个数量级而且天然支持重试和死信队列适合生产环境。6.3 自定义模型适配Clef 的适配器层设计得很灵活你可以接入自己的模型。比如你训练了一个专门识别某种工业零件的模型部署在某个推理端点上只需要写一个适配器把 Clef 的输入格式转成你的模型需要的格式再把输出转回 Clef 的标准格式就行。适配器的核心是实现predict(imageBuffer)方法返回一个 Promiseresolve 的结果包含predictions数组。具体可以参考src/adapters/resnet.js的实现照葫芦画瓢即可。6.4 前端集成示例最后给一个前端调用的简单示例用原生 Fetch APIasync function recognizeImage(file) { const formData new FormData(); formData.append(image, file); const response await fetch(https://my-clef-service.workers.dev/recognize, { method: POST, body: formData }); const result await response.json(); if (result.success) { console.log(识别结果, result.predictions); } else { console.error(识别失败, result.error); } }把这个函数绑到文件上传的 change 事件上用户选完图就能立刻看到识别结果。如果要做实时预览可以在上传前用URL.createObjectURL()生成缩略图提升交互体验。我在实际使用中发现Clef 最大的价值不是它现在有多完善而是它提供了一个可修改、可扩展的基座。你可以根据业务需求随意裁剪和增强不用受制于闭源服务的黑盒逻辑。踩过几次坑之后我越来越觉得对于中小型项目来说这种“开源基座 按需定制”的模式比直接买现成 API 要踏实得多。

相关新闻

通用Makefile模板:Linux内核模块跨平台编译实践

通用Makefile模板:Linux内核模块跨平台编译实践

1. 为什么一个Makefile模板能让我少熬三次夜——从Linux内核编译现场说起“通用Makefile模板与编译Linux的脚本”——这标题看着平平无奇,像极了某次加班到凌晨两点时,我在终端里敲下make clean && make -j$(nproc)前顺手记在备忘录里的半句笔记…

2026/10/15 9:24:22 阅读更多 →
图纸防泄密软件,管理员最容易问清楚的 4 个问题

图纸防泄密软件,管理员最容易问清楚的 4 个问题

做制造业、工程设计、电子制造、装备制造这类企业,图纸保护几乎是绕不开的难题。后台里关于图纸防泄密的提问也很集中:开了加密会不会影响 CAD/EDA 工具?外发审批怎么配才不卡业务?只加密图纸够不够?和文件外发审计、屏…

2026/10/15 9:24:22 阅读更多 →
CubeSandbox:OpenClaw与DSH执行面安全增强方案

CubeSandbox:OpenClaw与DSH执行面安全增强方案

1. 项目概述:为什么企业级执行面需要 CubeSandbox 这个“安全围栏”最近三个月,我连续接手了四家中小企业的安全自动化平台改造项目,全部卡在同一个环节:OpenClaw 和 DSH 的协同执行环境始终无法通过内部安全审计。不是 OpenClaw …

2026/10/15 9:23:22 阅读更多 →

最新新闻

KEPServerEX OPC UA配置管理器实战:端点、证书与权限避坑指南

KEPServerEX OPC UA配置管理器实战:端点、证书与权限避坑指南

简介:这份PDF文档聚焦kepware的OPC UA配置管理器,面向工业自动化领域的工程师、系统集成人员以及需要搭建OPC UA通信链路的运维人员,帮助解决服务器与客户端之间安全连接、证书信任与故障排查等实际问题。资源包共1个PDF文件,大小…

2026/10/15 11:02:12 阅读更多 →
WorkBuddy实战指南:用MCP+Python打通飞书与专业软件工作流

WorkBuddy实战指南:用MCP+Python打通飞书与专业软件工作流

1. 项目概述:WorkBuddy 不是“另一个AI工具”,而是你工作流里那个沉默的协作者最近在好几个技术团队的茶水间、产品例会和远程协作群里,反复听到一个词:WorkBuddy。它不像ChatGPT那样被拿来写周报初稿,也不像Copilot那…

2026/10/15 11:02:12 阅读更多 →
攻防世界Web高手进阶3分题通关指南:信息收集与漏洞利用实战

攻防世界Web高手进阶3分题通关指南:信息收集与漏洞利用实战

1. 从“3分题”说起:这类Web进阶挑战到底在考什么 很多人第一次接触攻防世界Web高手进阶区的3分题,都会有一种“被标题骗了”的感觉。题目名字看起来平平无奇,分值也不高,但真正上手之后才发现,它考察的往往不是单一知…

2026/10/15 11:02:12 阅读更多 →
TensorFlow Lite 端侧集成指南:从模型转换到 App 部署

TensorFlow Lite 端侧集成指南:从模型转换到 App 部署

思路先捋一捋:模型在服务器上怎么跑是一回事,把它塞进 App 里稳定高效地跑起来,又是完全不同的另一回事。TensorFlow Lite 的“集成”,指的是从训练好的模型文件出发,经过转换、量化、部署到 Android/iOS 或嵌入式设备…

2026/10/15 11:02:12 阅读更多 →
单细胞与人群队列数据整合:从实验室发现到临床验证

单细胞与人群队列数据整合:从实验室发现到临床验证

1. 项目概述:当单细胞遇上人群队列去年参与一个肿瘤标志物研究项目时,我们团队遇到了典型的多组学整合难题——实验室积累的单细胞数据揭示了数十个潜在靶点,但哪些真正具有临床转化价值?这个问题直接促使我们探索了单细胞RNA测序…

2026/10/15 11:02:12 阅读更多 →
2026内地专才申请者怎么挑中介?头部服务商对比提供选型参考

2026内地专才申请者怎么挑中介?头部服务商对比提供选型参考

小李是深圳一家科技公司的技术总监,年薪超过80万,三年前想通过专才计划申请香港身份。他找了两家中介,一家报价低但材料反复修改,拖了半年;另一家虽然贵些,但全程有专人对接,3个月就顺利获批。事…

2026/10/15 11:01:10 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/14 3:29:58 阅读更多 →

月新闻

我发现了一个新思路:用 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/15 0:14:36 阅读更多 →
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/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练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/15 9:49:14 阅读更多 →