纯前端实现域名DNS查询工具:JS核心逻辑与DoH接口实战解析
做在线域名DNS查询工具核心JS实现看着不难真正跑通一遍才发现里面有不少讲究。这个工具解决的问题很实际不用开终端敲 dig 或 nslookup浏览器里输个域名就能把 A、AAAA、CNAME、MX、TXT、NS、SOA 这些记录一次查明白。尤其是排查域名解析异常、验证 CDN 切换是否生效、确认 MX 和 SPF 配置对不对网页版工具比命令行直观太多给非运维岗位的同学用也完全没有门槛。我这次做的是纯前端方案不搭后端服务整个查询逻辑全部用原生 JavaScript 实现。整体代码量不大三百多行但里面涉及 DNS 协议交互、HTTPS 请求构造、JSON 解析、输入校验、超时兜底这些关键环节每一块都有值得抠的细节。这篇文章把当时的设计思路、接口协议、完整代码和踩过的坑全部整理出来给正在做类似工具或者想了解纯前端怎么对接网络查询接口的同学一份可复现的参考。1. 项目背景与核心需求拆解1.1 网页版DNS查询解决了什么痛点日常工作中查域名解析记录多数人的第一反应是命令行工具。dig 确实强大输出信息也很全但有两个现实问题一是要求使用者具备一定命令行基础很多非技术岗位的同事看到终端就发怵二是不同操作系统的命令差异不小平时用 Linux 的人切到 Windows 上连 nslookup 的交互模式都能绕晕。所以做一个浏览器直接访问的查询工具本质上就是把解析查询这个高频操作从终端搬到 GUI 里。项目要解决的核心需求有三个输入一个域名就能查支持多种常见记录类型结果展示要直观不能把一片 JSON 糊在页面上。第三个需求往往被新手忽略但恰恰是工具能不能真正落地使用的关键。我当时定的展示方案是表格化输出每条记录单独一行类型、TTL、解析值分列展示一眼扫过就能拿到需要的信息。1.2 技术方案对比纯前端、本地代理、后端聚合动手之前我对比了三条技术路线。第一条是纯前端直连公共DNS服务的HTTPS查询接口优点是零部署、代码简单、任何人打开 HTML 文件就能用缺点是受跨域策略限制只能依赖允许跨域的公共接口而且公共服务都有速率限制不适合做高频批量查询。第二条方案是自己写一个轻量后端代理前端请求自己的服务由后端去查询并缓存结果。优势是彻底规避了跨域和限流问题还能自己控制查询上游代价是多维护一套服务部署和运维成本上去了对个人小工具来说偏重。第三条是接入现成的第三方查询 API轮子很现成但可控性差、免费额度有限而且这类服务经常调整规则依赖它们等于把关键链路交给别人。综合比较后我选择了第一条路线纯前端直连公共DoH接口把端点配置做成前端可切换的选项。查询频率不高的话完全够用又避开了后端维护的成本。这篇文章的核心也是这条路线下 JS 实现的关键细节。2. DNS查询交互协议与数据格式详解2.1 常见DNS记录类型速查做查询工具之前先把要支持的记录类型捋清楚。DNS 的记录类型有几十种日常排查用得最多的是下面这七种记录类型用途典型返回值A域名指向的 IPv4 地址93.184.216.34AAAA域名指向的 IPv6 地址2606:2800:220:1:...CNAME别名指向另一个域名target.example.comMX邮件接收服务器及优先级10 mail.example.comTXT任意文本信息常用于 SPF/DKIM 验证vspf1 include:...NS域名使用的权威解析服务器ns1.example.comSOA起始授权记录含区域主服务器、序列号等master.example.com ...接口里的记录类型最终要映射成数字编码比如 A 记录对应 1、AAAA 对应 28、MX 对应 15、TXT 对应 16。不过 JSON 接口会直接返回类型数字前端把数字转成可读的类型名称就行。2.2 DoH的JSON响应格式DNS over HTTPS简称 DoH本质就是把传统的 DNS 查询封装进 HTTPS 请求里。对前端开发者来说最友好的形态是 JSON 式接口构造一个 GET 请求带上域名和记录类型参数接口返回结构化的 JSON 数据省去了自己解析二进制 DNS 报文的麻烦。一个标准响应长这样{ Status: 0, TC: false, RD: true, RA: true, AD: true, CD: false, Question: [ { name: example.com, type: 1 } ], Answer: [ { name: example.com, type: 1, TTL: 3600, data: 93.184.216.34 } ] }这里几个字段的含意分别是Status表示 DNS 响应状态码0 是正常Question是查询的问题部分回显你查的域名和类型Answer是答案部分真正展示给用户的数据都在这TTL是缓存的存活时间单位是秒AD字段表示响应是否通过了 DNSSEC 校验这个在判断解析是否被篡改时有用。2.3 响应状态码与边界情况DNS 协议里有一组标准的响应码DoH 接口会原样放进Status字段里。做前端展示时必须对几个关键状态码做专门处理不然用户看到一堆数字根本不知道发生了什么Status 值含义前端提示0NOERROR查询正常正常展示结果2SERVFAIL解析服务器故障提示服务器端异常稍后重试3NXDOMAIN域名不存在明确提示域名不存在4NOTIMPL不支持查询类型提示换一种记录类型5REFUSED查询被拒绝提示更换解析服务或检查参数另外有两种返回情况特别容易让新手困惑。第一种是查询成功但Answer数组为空比如你查一个不存在的子域名下的 A 记录状态码可能是 0 但没有答案。第二种是查询 MX 或 TXT 时答案可能不在Answer数组里而在接口的Authority或额外字段中尤其当查询的域名本身没有配置该类型记录时。所以前端千万别只盯着Answer解析还要看Status和数组长度组合起来才能给出准确的用户提示。3. 核心JS实现逐步拆解3.1 域名输入校验与归一化处理输入校验是这类工具最容易翻车的地方。用户输入五花八门可能带空格、带http://前缀、带路径甚至直接输入中文域名。我第一版只做了简单的非空判断结果一个https://www.example.com/path输进去后端查询直接报错界面上一片红。后来补了一套完整的归一化流程。function normalizeDomain(rawInput) { let domain rawInput.trim().toLowerCase(); // 去掉常见的 URL 前缀 domain domain.replace(/^https?:\/\//i, ); // 去掉路径和端口部分 domain domain.split(/)[0].split(:)[0]; // 去掉结尾的句点 domain domain.replace(/\.$/, ); // 中文域名转换为 punycode 编码 if (/[\u4e00-\u9fa5]/.test(domain)) { try { domain new URL(http:// domain).hostname; } catch (e) { return null; } } // 基本的域名格式校验 const DOMAIN_RE /^(?.{1,253}$)(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)(?:[a-zA-Z]{2,}|xn--[a-zA-Z0-9-]{2,})$/; return DOMAIN_RE.test(domain) ? domain : null; }这段代码里几个细节值得说。用new URL(http:// domain).hostname来转中文域名是浏览器原生能力能自动把例子.cn转成xn--fsqu00a.cn少引一个 JS 依赖库。正则里的xn--分支是为了兼容已经转成 punycode 形式的后缀。校验不通过时返回null调用方统一提示域名格式不正确避免出现一套校验逻辑多处散落的局面。3.2 查询请求的构造与发送请求构造要关注三件事端点和路径、查询参数、请求头。DoH 的 JSON 接口通常支持/dns-query和/resolve两种路径风格但参数格式大同小异核心是name域名和type记录类型。请求头必须带上Accept: application/dns-json有些服务不带头会返回二进制 DNS 报文而不是 JSON。const DOH_ENDPOINTS [ { label: 公共解析服务一, base: https://1.1.1.1/dns-query }, { label: 公共解析服务二, base: https://8.8.8.8/resolve } ]; async function queryDNS(domain, type, baseUrl) { const url new URL(baseUrl); url.searchParams.set(name, domain); url.searchParams.set(type, type); const controller new AbortController(); const timeoutTimer setTimeout(() controller.abort(), 8000); try { const resp await fetch(url.toString(), { method: GET, headers: { Accept: application/dns-json }, signal: controller.signal }); if (!resp.ok) { throw new Error(HTTP resp.status); } const data await resp.json(); return data; } finally { clearTimeout(timeoutTimer); } }用URLSearchParams来拼参数而不是手动拼字符串是为了处理特殊字符的转义比如_dmarc.example.com这类带下划线的域名。超时控制用AbortController实现8 秒没响应就主动中断请求避免用户在慢网环境下对着转圈等半天。这是纯前端网络请求里最容易被忽略但非常重要的一层防护。3.3 响应数据的解析与表格渲染拿到 JSON 之后的处理逻辑分三步判状态、抽答案、渲染表格。function parseDNSResponse(data) { if (data.Status 3) { return { ok: false, message: 域名不存在NXDOMAIN }; } if (data.Status ! 0) { return { ok: false, message: 查询异常状态码 data.Status }; } const records (data.Answer || []).map(item ({ name: item.name, type: recordTypeToString(item.type), ttl: formatTTL(item.TTL), data: item.data })); if (records.length 0) { return { ok: true, records: [], empty: true }; } return { ok: true, records }; }渲染部分我用原生 DOM 操作生成表格没有引框架。由于查询结果字段固定用createElement逐行构建反而比操作字符串模板更直观、更好控制样式。这里有个经验type字段要把数字映射成名称比如1转成A、15转成MX展示清楚还方便用户理解。TTL我做了人性化格式化比如 3600 秒显示成 1小时比干巴巴的数字友好得多。function formatTTL(seconds) { if (!seconds) return -; if (seconds 60) return seconds 秒; if (seconds 3600) return Math.floor(seconds / 60) 分钟; if (seconds 86400) { const h Math.floor(seconds / 3600); const m Math.floor((seconds % 3600) / 60); return h 小时 (m ? m 分 : ); } return Math.floor(seconds / 86400) 天; }3.4 UI状态管理与交互反馈UI 状态我提炼成四种空闲、加载中、成功、失败。页面上的按钮在加载时禁用并显示查询中…结果区域在失败时显示红色错误条成功时显示表格。这块逻辑不复杂但状态切换如果没理顺容易出现用户连续点击后拿到重复结果或错误提示残留的问题。我的做法是点击查询后先清空上一次的结果区再置为加载状态等请求返回后根据结果决定渲染表格还是提示错误。同时给查询按钮和回车键绑定同一个处理函数这样键盘习惯的用户可以全程用回车操作。移动端适配也简单输入框和按钮都按全宽排列结果表格在小屏上撑满容器宽度即可。4. 完整实操代码与界面设计4.1 页面结构设计页面结构保持极简就一个输入区加一个结果区。HTML 骨架如下div classcontainer h1在线域名DNS查询/h1 div classquery-bar input typetext iddomainInput placeholder请输入域名如 example.com / select idtypeSelect option valueAA/option option valueAAAAAAAA/option option valueCNAMECNAME/option option valueMXMX/option option valueTXTTXT/option option valueNSNS/option option valueSOASOA/option /select select idendpointSelect/select button idqueryBtn查询/button /div div idstatusBar/div div idresultArea/div /div下拉框里的解析服务选项在页面加载时从DOH_ENDPOINTS数组动态生成这样以后新增服务商只改数组一处地方。4.2 完整JS代码走读把前面拆解的片段整合在一起就是完整的核心逻辑const DOH_ENDPOINTS [ { label: 公共解析服务一, base: https://1.1.1.1/dns-query }, { label: 公共解析服务二, base: https://8.8.8.8/resolve } ]; const TYPE_NAMES { 1: A, 28: AAAA, 5: CNAME, 15: MX, 16: TXT, 2: NS, 6: SOA }; function recordTypeToString(type) { return TYPE_NAMES[type] || TYPE- type; } function initEndpoints() { const select document.getElementById(endpointSelect); DOH_ENDPOINTS.forEach((ep, i) { const option document.createElement(option); option.value i; option.textContent ep.label; select.appendChild(option); }); } async function handleQuery() { const input document.getElementById(domainInput).value; const domain normalizeDomain(input); if (!domain) { showStatus(域名格式不正确请检查后重试, error); return; } const type document.getElementById(typeSelect).value; const endpointIndex document.getElementById(endpointSelect).value; const baseUrl DOH_ENDPOINTS[endpointIndex].base; const btn document.getElementById(queryBtn); btn.disabled true; btn.textContent 查询中…; document.getElementById(resultArea).innerHTML ; showStatus(, ); try { const data await queryDNS(domain, type, baseUrl); const result parseDNSResponse(data); if (!result.ok) { showStatus(result.message, error); } else if (result.empty) { showStatus(该域名没有 type 记录, warning); } else { renderTable(domain, type, result.records); showStatus(查询成功共 result.records.length 条记录, success); } } catch (err) { if (err.name AbortError) { showStatus(请求超时请检查网络或更换解析服务, error); } else { showStatus(查询失败 err.message, error); } } finally { btn.disabled false; btn.textContent 查询; } } function renderTable(domain, type, records) { const area document.getElementById(resultArea); const table document.createElement(table); table.className result-table; const thead document.createElement(thead); thead.innerHTML trth名称/thth类型/ththTTL/thth解析值/th/tr; table.appendChild(thead); const tbody document.createElement(tbody); records.forEach(rec { const tr document.createElement(tr); const cells [rec.name, rec.type, rec.ttl, rec.data]; cells.forEach(text { const td document.createElement(td); td.textContent text; tr.appendChild(td); }); tbody.appendChild(tr); }); table.appendChild(tbody); area.appendChild(table); } document.getElementById(queryBtn).addEventListener(click, handleQuery); document.getElementById(domainInput).addEventListener(keydown, e { if (e.key Enter) handleQuery(); }); initEndpoints();这里要专门提示一点渲染解析值data字段时我用的是textContent而不是innerHTML因为 TXT 记录里可能出现\n、引号等特殊字符用innerHTML直接插入既有 XSS 风险又可能破坏排版。用textContent是安全且省心的习惯。4.3 查询效果实测验证代码写完我做了多组验证。查example.com的 A 记录返回正常查一个不存在的nonexist.example.com状态码 3页面正确提示域名不存在查gmail.com的 MX 记录返回了带优先级的邮件服务器地址输入https://www.example.com/path?x1这种脏数据归一化函数也能正确提取出www.example.com并完成查询。其中一个比较有意思的测试是查一个做了 CDN 加速的域名A 记录一次性返回了多个 IP表格整齐排列出来非常直观。换到命令行去 dig 看输出反而没有页面排版清晰。这也侧面验证了网页版工具在展示层确实有独特优势。5. 常见问题排查与避坑记录5.1 接口跨域与可用性问题纯前端直连公共 DoH 接口最大的坑就在跨域。幸好在浏览器里测试多种公共解析服务的 JSON 接口时发现普遍允许跨域请求。但如果接入了一个不支持跨域的私有服务或自建接口浏览器会直接拦截响应报 CORS 错误。遇到这种情况我的排查路径是先看浏览器控制台具体错误信息确认是 CORS 拦截还是网络不通再用 curl 试一下相同请求排除服务本身的问题最后考虑换一个端点或自建代理转发。另外公共解析服务通常有速率限制频繁查询可能返回 HTTP 429。工具里对这种情况要单独提示请求过于频繁请稍后再试而不是笼统地报网络错误。对于个人使用频率这个限制基本碰不到。5.2 解析结果显示的边界怪相实际使用中还有几类容易误会的边界情况。第一类是查询多级子域名比如a.b.example.com如果中间某级没有配置接口可能返回空 Answer 但状态码是 0这时要判断是没有该记录而不是查询失败。第二类是某些域名开启了 CNAME 拉平A 记录查询会直接返回 CNAME 目标的 IP数据展示上看起来和普通 A 记录一样不必惊讶。第三类是 DNSSEC 校验字段AD它只表示递归解析器校验过签名不代表域名一定可信页面里如果展示这个字段措辞要严谨别让用户误解成安全评级。5.3 性能优化与功能扩展方向基础版跑通之后还有几个值得继续打磨的方向。一是加查询历史用localStorage存最近查询的域名点击即可重复查询省去重复输入。二是支持批量查询比如粘贴一组域名逐个查询但要注意控制请求并发数避免触发速率限制。三是增加结果导出把表格内容序列化成 CSV 下载这个对运维批量核对配置很有用。四是接入自定义 DoH 端点在页面设置项里允许用户填自己的服务地址方便内网场景使用。性能方面公共解析接口和浏览器本身都有 HTTP 缓存同一域名的重复查询第二次会快很多。代码层面不需要做额外缓存保持简单即可。结尾的几点体会这套工具从设计到跑通让我重新理解了简单功能背后不简单这句话。最深的体会是网络查询类工具的核心不在 UI 多花哨而在协议理解、边界处理和数据准确性这三件事上。把 DNS 的响应码含义搞清楚、把输入归一化做好、把特殊字符渲染问题兜住工具就能稳定地工作用户才会真正信任它。最后再分享一个小技巧如果日后想给这个工具加国际化域名支持千万别自己写 punycode 转换算法直接用内置的URL对象去解析域名浏览器会帮你处理所有编码细节。另外页面里留一个更换解析服务的下拉框是我认为性价比最高的功能它让工具在某个上游接口出问题时还能继续用下去。当前这个版本我已经放进自己的工具箱排查域名问题的时候随手就打开下一步计划把查询历史加上做成越用越顺手的小工具。

相关新闻

SpringBoot 入门+ApiPost 测试接口

SpringBoot 入门+ApiPost 测试接口

创建springboot项目,选择springboot,语言选java,类型选maven,打包选jar选完下一步添加依赖,勾选web里的springweb,然后就创建完成了代码演示:打包运行:选择右边的m也就是maven&#…

2026/10/14 16:57:11 阅读更多 →
Akka.Hosting 日志系统实战:Akka.NET 与 Microsoft.Extensions.Logging、Serilog 的集成与日志过滤

Akka.Hosting 日志系统实战:Akka.NET 与 Microsoft.Extensions.Logging、Serilog 的集成与日志过滤

后端微服务 【免费下载链接】akka.net Canonical actor model implementation for .NET with local distributed actors in C# and F#. 项目地址: https://gitcode.com/gh_mirrors/ak/akka.net 点击查看 免费下载 在 Akka.NET 应用中,ActorSystem 的日…

2026/10/14 16:57:11 阅读更多 →
ORA-12537根因诊断:listener.ora配置错误精准定位指南

ORA-12537根因诊断:listener.ora配置错误精准定位指南

1. 项目概述:这不是网络故障,是监听器在“装死”“ORA-12537:TNS连接已关闭”——这个报错在Oracle DBA的日常里出现频率极高,但它的杀伤力却常被低估。它不像ORA-00942(表或视图不存在)那样直白&#xff0…

2026/10/14 16:57:11 阅读更多 →

最新新闻

IntelliJ IDEA 的Qoder CN 插件报错“暂时无法同步代码库索引选择”怎么解决?完整排查与修复指南

IntelliJ IDEA 的Qoder CN 插件报错“暂时无法同步代码库索引选择”怎么解决?完整排查与修复指南

摘要 本文针对在IDEA中使用Qoder CN时出现的报错,给出官方说明与实战解决步骤。官方指出需启用代码库索引:可点击“创建”手动启用,或切换“自动索引”。实战中,先登录Qoder CN且不要输入提示词,再进入设置—>Qode…

2026/10/14 17:36:00 阅读更多 →
PTZ-C8T6.zip 云台控制 C 工程解析:编译、协议与避坑指南

PTZ-C8T6.zip 云台控制 C 工程解析:编译、协议与避坑指南

简介:PTZ-C8T6.zip 是一份面向嵌入式开发初学者与 STM32 学习者的云台控制项目工程源码,基于 STM32F103 系列芯片与 HAL 库构建,适合用于课程设计、毕业设计或电赛类云台控制方案的参考与二次开发。压缩包共 98 个文件,约 675KB&a…

2026/10/14 17:36:00 阅读更多 →
Python 读取 Excel 并写入 zip:自动化报表打包实战

Python 读取 Excel 并写入 zip:自动化报表打包实战

简介:这份资源面向需要处理表格数据的Python学习者与开发者,聚焦Excel文件的读取与写入这一常见任务,帮助解决数据分析、数据清洗与自动化报告中的表格操作问题。压缩包共4个文件,全部为py脚本,整体约6KB,涵…

2026/10/14 17:36:00 阅读更多 →
Terminus(Olares)RabbitMQ 中间件集成实战:用 OlaresManifest.yaml 声明式接入消息队列

Terminus(Olares)RabbitMQ 中间件集成实战:用 OlaresManifest.yaml 声明式接入消息队列

云原生后端运维 【免费下载链接】Terminus Open-Source Personal Cloud OS for Always-On Agents 项目地址: https://gitcode.com/gh_mirrors/terminus2/Terminus 点击查看 免费下载 本篇技术指南基于 Terminus 仓库中 RabbitMQ 集成文档 展开,讲解如何…

2026/10/14 17:36:00 阅读更多 →
九轴姿态解算实战:C语言实现Mahony互补滤波与四元数融合

九轴姿态解算实战:C语言实现Mahony互补滤波与四元数融合

简介:九轴姿态解算源代码是一套面向嵌入式开发、无人机及机器人等场景的C语言实现,融合加速度计、磁力计与陀螺仪数据,用于实时估计物体的横滚、俯仰与偏航角。资源包含4个文件,全部为2个C源文件和2个头文件,压缩包仅5…

2026/10/14 17:36:00 阅读更多 →
C#工业级图形面积周长计算:精度、鲁棒性与性能平衡

C#工业级图形面积周长计算:精度、鲁棒性与性能平衡

1. 项目概述:为什么一个“图形周长、面积计算”功能值得花时间深挖? 你可能第一眼看到“C# 图形周长、面积计算”这个标题,会下意识觉得:“不就是几个数学公式封装成方法吗?勾股定理、πr、底高2……抄一遍就完事。”我…

2026/10/14 17:34:59 阅读更多 →

日新闻

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/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/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/14 7:53:34 阅读更多 →