新闻详情

新闻详情

首页 / 资讯中心 / 详情

Delphi中使用TEdgeBrowser实现流畅打字机效果:从ExecuteScript误区到WebView2正确姿势

发布时间:2026/9/30 7:39:32来源:尧图网络
Delphi中使用TEdgeBrowser实现流畅打字机效果:从ExecuteScript误区到WebView2正确姿势
TEdgeBrowser 这个组件刚出来的时候我一直拿它当能打开 HTML 报告的高级浏览器用把 ECharts 图表、富文本说明、排版复杂的页面塞进去然后就是用户自己看。直到最近一个项目需要用户点击按钮后正文一个字一个字地出现——就是打字机效果——我才意识到TEdgeBrowser 底层的 WebView2 渲染逻辑跟老 TWebBrowserIE 内核完全是两个时代的东西过去那套Delphi 代码逐字往页面里塞字符的写法不仅效率低而且直接行不通。这篇文章就完整记录一下我踩过的坑和最终落地方案适合在 Delphi 10.3 环境下使用 TEdgeBrowser 做动态文本展示、阅读器、演示类界面的开发者参考。1. 在 VCL 里做打字机的惯性思维为什么放到 TEdgeBrowser 上就失灵了1.1 第一次尝试Delphi 循环 ExecuteScript 逐字追加接到需求的第一反应所有 Delphi 老手应该都一样——写个循环for i : 1 to Length(FullText) do begin EdgeBrowser1.ExecuteScript(appendChar( FullText[i] );); Sleep(40); end;页面那边放一个空的div idcontent定义 appendChar 函数script function appendChar(ch) { var el document.getElementById(content); el.innerText ch; } /script代码看起来天经地义每 40 毫秒向页面追加一个字符不就成了打字机吗实际跑起来我遇到了两个非常打击人的现象。第一种现象点了按钮之后页面像死了一样毫无动静大概过了一两秒整段文字哗地一下全部出现在屏幕上。第二种现象如果我把 Sleep 改成 200 毫秒加大间隔文字确实会一个一个蹦出来但页面滚动、按钮点击、鼠标悬停全部卡顿CPU 占用还特别高。我一开始以为是 EdgeBrowser 初始化没完成或者 HTML 写错了。后来开了 DevTools 仔细看了 Console 日志才发现问题根本出在跨进程通信这个层面。1.2 为什么逐字调用 ExecuteScript 是错的TEdgeBrowser 本质上是微软 WebView2 运行时在 Delphi 里的封装它不是一个控件在 VCL 窗口里直接绘画而是独立进程里的 Chromium 渲染引擎。Delphi 进程和 WebView2 渲染进程之间的每一次通信都要走一遍跨进程消息IPC。你把 ExecuteScript 当作一个立即执行的同步调用但实际上它是异步的调用后立刻返回真正的 JavaScript 执行被投递到了 WebView2 的消息队列里等它自己的 UI 线程去取。这时候Delphi 主线程里的Sleep(40)又把整个 VCL 窗口的消息循环堵死了。于是 WebView2 那边的渲染更新请求、JavaScript 执行结果、页面重绘事件全都排着队等不到处理——当 Sleep 结束Delphi 终于对外发消息时浏览器就把积压的几十个 appendChar 一次性全部执行掉。这就是卡一下然后全部出来的直接原因。再看性能账每调一次 ExecuteScript 就是一次 IPCIPC 有开销每次 appendChar 在 DOM 里执行innerText 又会触发一次文本重排。一千字的文章你循环调用一千次 ExecuteScript页面就要做一千次 IPC 加一千次重排前端优化指南里最忌讳的写法全被我们写出来了。更隐蔽的问题是时序ExecuteScript 虽然基本保持投递顺序但它本身不保证你在 Delphi 侧写一个ExecuteScript(appendChar(A))之后再写一个ExecuteScript(appendChar(B))B 就一定能等 A 执行完成。如果中间穿插了其他代码或者页面正在做重排结果不可预料。1.3 思维转变把节奏交给浏览器而不是自己数拍子这次踩坑让我想明白了一件事TEdgeBrowser 的核心优势在于它拥有完整的 HTML/CSS/JavaScript 运行时。凡是需要连续变化的东西——动画、滚动、渐入、打字机——都应该把变化逻辑写在页面内部用 requestAnimationFrame、setInterval 这类浏览器原生机制去驱动Delphi 只负责发一个开始的指令然后就是收消息。打个比方就很好理解你站在乐队前面是指挥不是乐手。你要做的是给一个手势剩下的旋律让每一个乐手自己去演奏。如果你非要自己每次掐着秒表往舞台上的乐谱架放一个音符这演出肯定崩。明白了这个原理后面的方案就顺理成章了。2. 跑起来的最小工程TEdgeBrowser 的 Runtime 依赖与本地页面加载2.1 先确认你的目标机器具备运行条件TEdgeBrowser 不是一个自带了浏览器内核的组件。它加载的 HTML 全部交给系统里安装的 Microsoft Edge WebView2 Runtime 来渲染。Win11 自带这个运行时但很多 Win10 甚至在老版本的 Win11 上都没装。我在实际项目里遇到过开发机跑得好好的用户机器打开后 EdgeBrowser 区域一片空白的情况。排查了半天最后发现用户机器上没有 WebView2 Runtime。如果你的程序对运行环境不可控发布包里建议带上 WebView2 Runtime 的独立安装程序 或者在初始化时给一个友好提示。另外还有一个容易被忽略的部署文件WebView2Loader.dll。Delphi 的 TEdgeBrowser 在大多数版本里是通过静态链接方式加载 WebView2但部分 Delphi 版本在 Debug 和 Release 模式下对 loader 的处理不一样发布到其它机器后如果出现无法启动浏览器进程之类的错误建议把 SDK 目录里的 WebView2Loader.dll 放到 exe 同目录再试。这个文件本身很小加进发布成本几乎为零能省掉很多环境问题。TEdgeBrowser 组件在 Delphi 10.3 之后的版本开始集成10.4、11、12 都稳定可用。老项目如果还在 10.2 及以下需要手动引入第三方封装或者升级 IDE。我个人建议至少用 10.4因为对 WebView2 API 的封装更完整调试体验也好。2.2 两种加载本地页面的方式把 HTML 内容喂给 TEdgeBrowser 有两种常用手法。第一种把 HTML 写成临时文件然后 Navigate 过去uses System.IOUtils, System.SysUtils; procedure TForm1.FormCreate(Sender: TObject); var HtmlFile: string; begin HtmlFile : TPath.Combine(TPath.GetTempPath, typewriter_demo.html); TFile.WriteAllText(HtmlFile, BuildHtml(), TEncoding.UTF8); EdgeBrowser1.Navigate(file:/// StringReplace(HtmlFile, \, /, [rfReplaceAll])); end;注意file:///必须写三个斜杠Windows 路径里的反斜杠要替换成斜杠否则 WebView2 解析不出本地路径。第二种如果 HTML 内容不长可以直接用 NavigateToStringEdgeBrowser1.NavigateToString(BuildHtml());NavigateToString 的问题在于没办法用 DevTools 调试源码内容太大时比如几万字的文本字符串处理也容易出幺蛾子。所以我推荐的方式是调试阶段必须写临时文件 Navigate等逻辑稳定了再考虑是否切换成 NavigateToString。2.3 TEdgeBrowser 的初始化时序很多人第一次用 TEdgeBrowser 就在初始化这里踩坑明明在 FormCreate 里直接调用了 Navigate结果页面没加载出来。原因在于 TEdgeBrowser 的浏览器内核是异步创建的Form 刚创建时内核可能还没准备好。最稳妥的姿势是触发初始化后等待 OnCreateWebViewCompleted 事件再在这个事件里调用 Navigate。放到窗体上的 TEdgeBrowser 通常会自己走初始化流程但如果你在 FormCreate 里抢跑可能就会失败。我习惯用一个布尔标记FRuntimeReady来控制所有和 JS 相关的操作只有在 OnCreateWebViewCompleted 和 OnNavigationCompleted 都触发过之后才允许用户点击交互按钮。后面讲时序问题的时候还会专门提这个坑。3. 真正的主力让 JavaScript 自己逐字打字Delphi 只需发一条指令3.1 一份可以直接抄的 HTML 模板下面的 HTML 结构是一个最完整的打字机页面。核心思路非常简单把完整文本放在一个隐藏节点里JS 用时间戳计算应该显示到第几个字符然后更新可见区域的 textContent。!DOCTYPE html html head meta charsetutf-8 / style body { background: #ffffff; margin: 0; padding: 24px; font-family: Microsoft YaHei, PingFang SC, sans-serif; font-size: 18px; line-height: 1.9; color: #333333; white-space: pre-wrap; } #typewriter { min-height: 400px; } #cursor { display: inline-block; width: 2px; height: 1.1em; background: #666666; vertical-align: text-bottom; margin-left: 2px; animation: blink 0.8s steps(1) infinite; } keyframes blink { 50% { opacity: 0; } } /style /head body div idtypewriter/divspan idcursor/span script typetext/plain idtextSource 在这里放置你要显示的完整正文可以包含中文、英文、标点、换行。 这里的内容不会被浏览器当作 HTML 渲染也不会被当作可执行脚本。 注意唯一不能出现的就是闭合的 \/script 标签否则会提前结束这一段。 /script script var el document.getElementById(typewriter); var source document.getElementById(textSource); var fullText source.textContent; var chars Array.from(fullText); // 按 Unicode 码点切分避免拆坏 emoji 等特殊字符 var idx 0; var elapsed 0; var lastTs 0; var paused false; var cps 60; // 每秒显示的字符数可以根据阅读速度调整 function tick(ts) { if (!lastTs) { lastTs ts; } var delta ts - lastTs; lastTs ts; if (!paused) { elapsed delta; } var target Math.floor((elapsed / 1000) * cps); if (target chars.length) target chars.length; if (target idx) { idx target; el.textContent chars.slice(0, idx).join(); // 每 50 个字符回传一次进度后续章节可以接到 Delphi 进度条 if (idx % 50 0) { window.chrome.webview.postMessage(JSON.stringify({ kind: progress, percent: Math.round(idx / chars.length * 100) })); } } if (idx chars.length) { requestAnimationFrame(tick); } else { window.chrome.webview.postMessage(JSON.stringify({ kind: finished })); } } function startTyping() { if (idx chars.length) return; if (!lastTs) { requestAnimationFrame(tick); } } function pauseTyping() { paused true; } function resumeTyping() { paused false; lastTs 0; } function setTypingSpeed(s) { cps s; } /script /body /html这段 JS 有讲究的地方我都标了注释几个关键点拆开说一下。3.2 用时间戳驱动而不是 setInterval很多人写打字机效果喜欢直接用 setInterval每 50 毫秒加一个字符var timer setInterval(function() { idx; el.textContent fullText.slice(0, idx); if (idx fullText.length) clearInterval(timer); }, 50);这种写法能跑但有个隐患如果渲染线程因为某些原因卡顿比如页面滚动、后台任务setInterval 的回调会被推迟文字显示速度就忽快忽慢。而且 setInterval 的最小间隔和浏览器空闲时间不完全严格对应长文章累积下来误差很明显。我用的方案是 requestAnimationFrame elapsed累计时间。每一帧计算按当前速度应当显示第几个字符不管中间发生多少次卡顿只要恢复之后elapsed继续累加最终的总时间和总字数一定是对得上的。速度稳定不会越到后面越飘。3.3 Delphi 侧只需要发一条命令HTML 准备妥当后Delphi 那边干净得令人感动procedure TForm1.btnStartClick(Sender: TObject); begin if not FReady then begin ShowMessage(页面还在加载稍等一下再试试); Exit; end; EdgeBrowser1.ExecuteScript(startTyping();); end;暂停、继续、调速全部走同样的套路EdgeBrowser1.ExecuteScript(pauseTyping();); EdgeBrowser1.ExecuteScript(resumeTyping();); EdgeBrowser1.ExecuteScript(setTypingSpeed(100););注意 ExecuteScript 执行 JavaScript 是异步的如果你需要拿 JS 的返回值比如读取某个 JS 变量的值要使用带 CompletionHandler 的重载版本在回调里解析结果。上面这些场景只是发指令不拿返回值所以都不用关心回调。3.4 为什么把全文一次性放进页面而不是从 Delphi 逐字传这是我在踩坑之后总结出来的一个核心原则一次性把全文交出去让页面拥有完整数据Delphi 和浏览器之间只传递控制指令和状态消息。如果你尝试从 Delphi 侧逐字传字符每次 ExecuteScript 都要构造一个包含一个字符的 JS 表达式。一方面 IPC 开销大另一方面字符串里的引号、反斜杠、换行天生就是转义泥潭一个单引号没处理好整个页面的 JS 就崩了。把全文放在 HTML 里Delphi 侧完全不需要碰文本内容转义问题被限制在构建 HTML 那一步可控得多。用更直白的话说页面是演员Delphi 是导演。导演喊一声开始演员自己背完整段台词。导演要是非拿着提词板一个字一个字地催戏肯定演砸。4. 从单次播放到可控交互让 Delphi 随时暂停、调速、回传进度打字机效果如果只支持点一下开始然后播完那是玩具。真实需求往往更复杂用户可能要暂停下来仔细看某一段要拖动一个速度滑块界面上还需要一个进度条告诉用户还有多少内容甚至播放完成后要自动滚动到当前段落。4.1 暂停、继续、调速的 JS 接口设计我在模板里预留了pauseTyping、resumeTyping、setTypingSpeed三个函数这里解释一下暂停逻辑的设计。暂停的直觉做法是直接不执行tick里的累加但是 requestAnimationFrame 的回调一旦挂上去只要页面活着就会一直触发。我的处理方式是在 tick 里判断pausedif (!paused) { elapsed delta; }这样暂停时间段的 delta 不会累计进elapsed就相当于时间被冻结了。resumeTyping里把lastTs归零是为了让恢复后的第一个 delta 归零避免把暂停瞬间到恢复瞬间的时间差误算进去。调速就简单了setTypingSpeed直接改cps变量下一次 tick 自动按新速度计算目标字符数。注意一点如果从慢速切换到快速target会突然变大文字会瞬间跳出一大截这是所有基于时间计算实现的通用问题。如果你希望平滑过渡可以在 setSpeed 里对elapsed做一个微调或者引入一个过渡变量但对阅读场景来说瞬间跳变反而符合直觉——速度快了就是应该马上显示更多内容。4.2 JS 通过 postMessage 把进度回传给 Delphi页面向 Delphi 回传消息用的是 WebView2 注入到页面里的window.chrome.webview.postMessage接口。我习惯在 JS 侧把消息包装成 JSON 字符串window.chrome.webview.postMessage(JSON.stringify({ kind: progress, percent: Math.round(idx / chars.length * 100) }));Delphi 侧在 TEdgeBrowser 的 OnWebMessageReceived 事件里接收procedure TForm1.EdgeBrowser1WebMessageReceived( Sender: TCustomEdgeBrowser; const AWebMessageArgs: IWebView2WebMessageReceivedEventArgs); var Msg: string; JsonValue: TJSONValue; JsonObj: TJSONObject; Kind: string; begin Msg : AWebMessageArgs.WebMessageAsJson; JsonValue : TJSONObject.ParseJSONValue(Msg); if JsonValue nil then Exit; try JsonObj : JsonValue as TJSONObject; Kind : JsonObj.GetValuestring(kind); if Kind progress then ProgressBar1.Position : JsonObj.GetValueInteger(percent) else if Kind finished then ShowMessage(播放完成); finally JsonValue.Free; end; end;这里有个细节容易迷惑WebMessageAsJson返回的并不一定是纯字符串。如果 JS 侧postMessage(finished)直接传一个字符串Delphi 这边收到的 JSON 格式会是finished带引号的 JSON 字符串字面量如果传 JSON 对象收到的才是{kind:finished}。所以上面我从一开始就统一约定JS 侧永远传JSON.stringify(...)的字符串Delphi 侧永远按 JSON 对象解析两边就不会有歧义。4.3 进度状态机与播放结束标记多轮播放的坑值得提前预防第一次播放结束之后用户如果再次点击开始startTyping应该怎么处理我建议在 JS 里加一个状态var done false; // 在 tick 里播完时设置 done true if (idx chars.length) { done true; window.chrome.webview.postMessage(JSON.stringify({ kind: finished })); return; } function startTyping() { if (done) { // 重新播放清零所有状态 idx 0; elapsed 0; lastTs 0; done false; el.textContent ; requestAnimationFrame(tick); } else if (!lastTs) { requestAnimationFrame(tick); } }这样 Delphi 那边一个startTyping()同时具备了开始播放和重新播放两个语义不用额外做两个入口。如果用户还需要支持从头播放就在 Delphi 侧再调一次startTyping()即可JS 内部自己判断。4.4 可选进阶分段自动滚动长文本播放还有一个体验问题内容超过一屏时文字在页面底部陆续出现但用户看不到。我常用的手段是让 JS 在显示进度超过某个阈值时自动滚动到当前段落附近if (idx % 30 0) { var elTop el.getBoundingClientRect().top window.scrollY; window.scrollTo({ top: elTop - 20, behavior: smooth }); }这段代码的问题是每次滚动可能造成用户手动滚动被干扰。更好的做法是只在用户没有手动滚动的时候才自动滚这需要监听wheel、touchmove事件去标记用户意图实现不复杂但牵扯到的交互细节很多项目时间紧的话可以先不做。我这里只是提个方向具体取舍看你的应用场景。5. 不亲自踩一遍根本想不到的坑编码、转义、时序与卡顿5.1 中文乱码与 HTML 文件编码如果你把 HTML 写到临时文件再用 Navigate 加载第一个大坑就是编码。TFile.WriteAllText默认按什么编码写取决于你是否传了第三个参数。很多人图方便省略了TEncoding.UTF8结果写出来的是当前系统的 ANSI 编码简体中文环境下就是 GBK但 HTML 里声明了meta charsetutf-8 /WebView2 按 UTF-8 去解析 GBK 文件中文自然全是乱码。所以必须明确指定TFile.WriteAllText(HtmlFile, BuildHtml(), TEncoding.UTF8);另外如果你的 HTML 里本身不含charset声明WebView2 也可能根据 BOM 和系统区域设置去做各种猜测结果不可控。HTML 文件的head里写上meta charsetutf-8 /代码里写文件的编码也用 UTF-8两头都对得上才能彻底消灭乱码。5.2 文本内容里的 /script 会让整页崩溃假设你要展示的正文是用户录入的里面有一段文字恰好出现了/script这种序列。如果直接把全文塞进script标签浏览器会认为 JS 代码在这里结束了后面所有内容都变成 HTML 代码页面直接崩掉。这个问题用 textarea 或者隐藏 div 也一样存在。我在模板里用的解决办法是把全文放在script typetext/plain idtextSource里。这个标签的特殊之处在于浏览器不会执行其中的内容而是把它当成纯文本数据可以用textContent读取。相对于把文本放在 HTML 属性里再转义一遍这个方法更省心——你唯一需要处理的边界就是文本内部不能再出现闭合的/script。Delphi 构建 HTML 时对正文做一次替换即可function SanitizeScriptSequence(const S: string): string; begin Result : StringReplace(S, /script, \/script, [rfReplaceAll]); end;\/script在 HTML 解析阶段不会闭合脚本而 JS 字符串里读到\/就是/行为完全一致。这个替换是我在项目里踩过真实坑之后加的没有它任何包含代码示例、HTML 片段、或者用户粘贴内容的正文都可能变成炸弹。5.3 NavigationCompleted 之前 ExecuteScript 必然失败页面还在加载时DOM 可能尚未构建document.getElementById(typewriter)返回 null你的 startTyping 函数当然也执行不了。更常见的是页面加载慢用户手快点了按钮按钮事件里 ExecuteScript 发出一条指令但页面里的函数还不存在于是没有任何反应也没报错——用户莫名其妙。我的固定做法是维护一个FReady布尔变量procedure TForm1.EdgeBrowser1NavigationCompleted( Sender: TCustomEdgeBrowser; const AArgs: IWebView2NavigationCompletedEventArgs); begin FReady : True; btnStart.Enabled : True; end;页面加载完成之前把按钮禁用掉同时在事件里设置标记确保后面所有 ExecuteScript 调用都有保障。如果你有多个页面导航完成事件可能触发多次还需要判断当前导航的 URL 是否是你加载的目标页面再决定是否置位标记。对单页面场景这个细节可以先忽略。5.4 为什么偶尔会出现首字延迟甚至白屏TEdgeBrowser 在 Form 首次显示时WebView2 内核需要经历一个冷启动过程创建浏览器进程、初始化渲染器、加载 HTML。这个时间在性能较差的机器上可能持续一两秒。如果你的交互流程是Form 一显示就自动播放用户看到的往往是一段白屏时间然后文字突然开始滚。我处理这个问题的手段是预加载FormCreate 阶段就把页面导航到打字机 HTML让浏览器内核在这段时间里完成初始化用户等真正点击开始阅读时页面早就 ready 了执行 startTyping 立即出字没有任何可见等待。如果连打开页面时白屏都不能忍也可以在 Form 里做一个骨架屏或者加载提示等 OnNavigationCompleted 再显示真实内容。5.5 长文本性能textContent 和 innerHTML 要分清更新已显示文字有两种常见 APIinnerHTML和textContent。写内联 DOM 的时候很多从 jQuery 时代过来的开发者习惯用innerHTML但在打字机场景上这是性能陷阱浏览器收到 innerHTML 赋值会把字符串当作 HTML 解析一遍找出所有标签、实体、节点然后重建 DOM 子树。textContent 则明确告诉浏览器这就是纯文本不经过 HTML 解析直接替换文本节点。我实测过一篇三千字的中文文章全文重新赋值的场景下textContent 比 innerHTML 快接近一个数量级。对于更长文本即使逐字更新也能保持流畅。如果正文特别长比如上万字还有一个优化点不要每显示一个字符就更新一次 textContent而是累积到 3~5 个字符再更新一次肉眼几乎分辨不出来区别性能提升立竿见影。把每帧更新改成攒够一批再刷新可以显著减少布局重排次数。5.6 常见问题排查表下面这张表是我在项目后整理的遇到类似问题可以直接对照排查。现象可能原因快速确认方式解决方案页面区域空白目标机器没装 WebView2 Runtime查看系统环境或安装官方运行时安装 WebView2 Runtime发布包带上安装程序点按钮无任何反应页面还没加载完成 / JS 函数未定义OnNavigationCompleted 是否触发过用 FReady 标记控制未 Ready 前禁止交互中文乱码HTML 文件编码不是 UTF-8用文本编辑器查看文件编码TFile.WriteAllText 指定 TEncoding.UTF8文字一次性全部出现Delphi 循环 Sleep 阻塞消息去掉 Sleep 观察现象改用 JS 侧 requestAnimationFrame 驱动页面 JS 崩掉正文里出现查看生成后的 HTML 源码用 text/plain 容器 替换闭合序列Chrome 白屏无报错初始化失败或内核未创建检查 OnCreateWebViewCompleted 是否触发等内核创建完成后再 Navigate5.7 速度参数的直观标定最后分享一个调参经验cps每秒字符数这个参数怎么定正常阅读中文的速度大约在每分钟 300~500 字也就是每秒 5~8 个字。但你做的是打字机效果不是纯阅读——用户是在看字蹦出来而不是读完整段话速度体验完全不同。我测试下来30~60 字符/秒的视觉节奏最有打字机感觉既能看清文字逐字出现的过程又不会等得心烦。如果文字本身是代码示例或需要强调的段落可以把速度调低到 15~20 字符/秒如果是大段说明性的正文80 字符/秒也不嫌快。用户还可能需要自己调速做成滑块的话范围建议 5~200初始值 40。我在实际使用中最深的体会是TEdgeBrowser 这类 WebView2 方案最大的价值不是让你把网页塞进桌面程序而是让你把动态界面的复杂度全部交给浏览器生态去解决。打字机效果用 VCL 原生控件做得自己管理定时器、绘制、重绘用 TEdgeBrowser 做你只需要写好 HTML/JSDelphi 端就像个遥控器一样发指令、收状态既简单又稳定。如果你接下来的项目还要做滚动阅读、Markdown 渲染、图表联动这类功能建议把本文的思路沉淀成一个通用模板类HTML 模板放资源文件Delphi 端统一封装 LoadAndStart、Pause、Resume、SetSpeed、OnProgress 几个方法以后任何项目直接复用能省下大量重复调试的工夫。
网站建设高端定制企业官网
RELATED

相关资讯

更多精彩内容,欢迎继续阅读

较早相关资讯

最新相关资讯

HRTOS实战示例:24C02 EEPROM掉电保存与4位数码管显示 2026/9/30 16:53:51

HRTOS实战示例:24C02 EEPROM掉电保存与4位数码管显示

EEPROM 是 8051 嵌入式开发中非常常见的非易失性存储器件,可以用于保存配置参数、计数值、设备状态等数据。本次 HRTOS 基础示例增加 24C02 EEPROM 4位数码管显示案例,通过 24C02 保存一个计数值,并使用 HRTOS 任务完成 EEPROM 数据读取、加…

阅读更多 →
如何大幅降低LLM调用成本:commerce-agents的Prompt缓存字节级稳定优化实践与验证方法 2026/9/30 16:53:43

如何大幅降低LLM调用成本:commerce-agents的Prompt缓存字节级稳定优化实践与验证方法

如何大幅降低LLM调用成本:commerce-agents的Prompt缓存字节级稳定优化实践与验证方法 【免费下载链接】commerce-agents Reference blueprint for building shopping and merchant agents with Claude. Examples in retail, commerce, telecom, and entertainment i…

阅读更多 →
Quill CLI命令大全:doctor、install、run参数详解,新手也能玩转命令行 2026/9/30 16:53:36

Quill CLI命令大全:doctor、install、run参数详解,新手也能玩转命令行

Quill CLI命令大全:doctor、install、run参数详解,新手也能玩转命令行 【免费下载链接】quill Ultra-minimalist macOS recording transcription. 项目地址: https://gitcode.com/gh_mirrors/quill26/quill Quill 是一款完全在本地运行的 macOS …

阅读更多 →
第314篇_域名交易行情爬虫 2026/9/30 16:53:13

第314篇_域名交易行情爬虫

【Python爬虫实战】第314篇:域名交易行情爬虫:成交价格分布与后缀分析——实战项目 所属专栏:【Python爬虫实战】从零到企业级爬虫工程师(CSDN 付费专栏) 本篇篇目:第 314 篇(垂直行业爬虫 域名行情专题) 难度等级:中级,有 requests 基础即可上手 阅读时长:约 25 分…

阅读更多 →
HarmonyOS 7 + Form Kit + UIAbility 实战:运营看板卡片的刷新策略、状态同步与点击回流【鸿蒙心迹】 2026/9/30 16:53:05

HarmonyOS 7 + Form Kit + UIAbility 实战:运营看板卡片的刷新策略、状态同步与点击回流【鸿蒙心迹】

这一篇我想聊的是看起来很轻、做起来却很容易失衡的一类功能:桌面卡片。很多人第一次做运营看板卡片,最先关注的是“怎么把一块卡片显示出来”。但真正把它做成一个能用、可信、可跳转、可同步的业务入口以后,你会发现难点根本不在“显示”&a…

阅读更多 →
OS3.【Linux】基本指令入门(2) 2026/9/30 16:52:50

OS3.【Linux】基本指令入门(2)

目录 1.root用户的家目录 2.非root用户的家目录 2.简单介绍一些基本指令 1.继续介绍cd 1.cd ~ 2.cd - 2.mkdir 1.mkdir 目录 创建一串目录 传统方法 ​编辑 补:查看树状结构的方法:tree指令 tree . 使用mkdir的-p选项来创建一串目录 3.touch 4.rmdir 5.rm r…

阅读更多 →

今日资讯

本周资讯

本月资讯

看完文章仍有疑问?

联系尧图顾问,获取一对一建站咨询

立即免费咨询 📞 400-888-8888
📞 ✉