猿来如此

张赫

这里没有高深的理论,只有和你一样关心科技和生活的普通人。每一期,我用最简单的语言聊聊新技术、行业故事,还有那些让人会心一笑的小发现。关注《猿来如此》,和我一起,慢慢变好。

  1. Jun 12

    什么是 HTML-in-Canvas?

    Google I/O 2026 上,Chrome 团队推了一个很容易被低估的新 Web 平台特性:HTML-in-Canvas。 很多人第一眼看到这个名字,脑子里会冒出一句话:哦,不就是终于可以在 canvas 里写 HTML 了吗? 这理解不能说错,但太浅了。 HTML-in-Canvas 真正重要的地方,不是“Canvas 能不能塞一个 div”。它动的是前端一条存在了很多年的边界:DOM 负责语义、交互和浏览器能力,Canvas / WebGL / WebGPU 负责像素、图形和 GPU 管线。过去这两套世界像两个国家,边境线画得很硬。你要 DOM 的可访问性、复制、查找、翻译、表单和 DevTools,就老老实实待在 DOM 里。你要 shader、3D、游戏、复杂画布和极致视觉控制,就进 canvas,但很多浏览器能力要自己重造,或者直接放弃。 HTML-in-Canvas 的变化是:浏览器开始给这两个国家修桥。 说白了,它不是让 Canvas 变成 DOM,也不是让 DOM 消失。它更像是让真实 DOM 变成图形管线的一种输入。 这就有意思了。 先说它到底是什么 按 Chrome 官方介绍,HTML-in-Canvas 是一个实验性 API,目前已经进入 origin trial。它允许你把 DOM 内容直接绘制进 2D canvas,或者作为 WebGL / WebGPU texture 使用,同时保留交互、可访问性和浏览器内建能力。 人话版是: 你可以写一个真实的 HTML 组件,比如一个表单、一段富文本、一个设置面板、一个复杂卡片。它不是截图,不是手搓像素,不是用 JS 模拟 CSS。它仍然是 DOM。然后你可以把这个 DOM 的渲染结果画进 canvas,甚至贴到一个 3D 物体表面上。 更关键的是,它还尽量保留浏览器能力。 文本可以被选中,内容可以被复制,页面查找能找到,屏幕阅读器能读,浏览器翻译能处理,DevTools 能检查,扩展也可能继续工作。 以前 canvas 应用最尴尬的地方就在这里:画面看起来很高级,但对浏览器来说经常只是一张大图。用户看到的是文字,浏览器看到的是像素。用户以为这是界面,辅助技术可能只看到一块空白。 这就像你开了一家装修很豪华的店,但门口没有门牌,消防图也没有,导盲系统也进不去。普通人看着炫,真到基础能力上,全是坑。 HTML-in-Canvas 要补的,就是这个坑。 过去为什么这么难? 前端一直有两套 UI 世界。 第一套是 DOM 世界。 DOM 的好处不是“能写 div”这么简单。它真正值钱的是浏览器几十年积累下来的能力:文本布局、换行、国际化、双向文字、表单控件、焦点管理、选择复制、右键菜单、无障碍树、页面查找、浏览器翻译、自动填充、缩放、暗色模式、扩展、DevTools。 这些东西你平时不觉得牛,是因为浏览器替你兜底太久了。 第二套是 Canvas / WebGL / WebGPU 世界。 它的好处也很明显:你可以直接控制像素,进 GPU,做 3D,跑 shader,做游戏,做白板,做设计工具,做复杂可视化。Figma、Miro、Google Docs 这类复杂应用,很多地方都离不开这种底层图形能力。 问题是,这两套世界以前很难同时拿。 你用 DOM,就很难把一个真实按钮贴到 3D 模型上,然后让它被玻璃折射、被 shader 扭曲、跟着模型旋转。 你用 Canvas,就要开始重造很多浏览器已经做好的东西。比如文本换行、光标、输入框、复制粘贴、焦点、可访问性。重造到最后,你会怀疑人生:我不是做一个 3D 面板吗,怎么突然在写半个浏览器? 这就是 HTML-in-Canvas 的背景。 大多数人以为前端缺的是更炫的渲染 API。但真相是,前端真正缺的是一条能把“语义 UI”和“图形管线”接起来的路。 它怎么工作? HTML-in-Canvas 的核心设计可以拆成几个部分。 第一,在 上加 layoutsubtree。 这一步的意思是:让 canvas 里面的子元素参与布局和 hit testing。也就是说,你可以把 DOM 子节点放到 canvas 里,它们会像真实 DOM 一样被浏览器布局、参与可访问性和交互计算。但它们不会自动显示出来。想显示,必须显式绘制。 第二,用 drawElementImage() 把 DOM 画进 2D canvas。 比如你有一个 ,里面是文本、按钮、输入框、CSS 样式。你在 canvas 的 paint event 里调用 ctx.drawElementImage(element, x, y),浏览器就会把这个元素的渲染结果画到 canvas 上。 第三,如果你在 WebGL 里,可以用 texElementImage2D;如果在 WebGPU 里,可以用 copyElementImageToTexture。 这一步才是很多 3D 开发者眼睛一亮的地方。因为 HTML 不只是画到 2D canvas,它还可以成为 texture。texture 意味着它可以贴到 3D 物体上,可以进 shader,可以被 post-processing 处理,可以成为沉浸式场景的一部分。 第四,要同步 transform。 这是最容易被忽略、但最关键的一步。 你看到的 HTML 已经被画到 canvas 里了,但真实 DOM 仍然要知道自己“在屏幕的哪里”。不然用户点击画面上的按钮,浏览器不知道该把这个点击交给哪个真实 DOM 元素;屏幕阅读器、hover、focus、选中文本也会错位。 所以 drawElementImage() 会返回一个 transform。你要把这个 transform 应用到元素的 CSS transform 上。3D 场景里则可以用 getElementTransform() 辅助计算。说白了,视觉位置和真实 DOM 位置必须对齐。 这也是为什么它不是“截一张图塞进 canvas”。截图不会有焦点,不会有输入,不会有可访问性,不会有浏览器查找。HTML-in-Canvas 玩的是活的 DOM。 这和 html2canvas 有什么区别? 很多人会问:这不就是官方版 html2canvas 吗? 不是。 html2canvas 文档自己说得很清楚,它会遍历 DOM,读取元素信息,然后构建一个页面表示。它不是浏览器截图。它能画对多少,取决于它理解多少 CSS、多少布局细节、多少边界情况。 这就像你照着网页临摹一幅画。画得再像,也还是临摹。 HTML-in-Canvas 更接近浏览器自己把那层真实 HTML 渲染结果交给你当素材。它不是第三方库猜浏览器怎么画,而是浏览器平台提供“把 DOM 绘制进 canvas / texture”的能力。 还有一种老办法是 DOM overlay。 比如你在 WebGL canvas 上面盖一个绝对定位的 div,看起来像在 3D 场景里。但它本质上是贴在画面外面的透明贴纸。它可以交互,但它没有真正进入 3D 管线。它不能自然被 shader 处理,不能真实贴在一个旋转的 3D 屏幕上,也很难和复杂后处理融在一起。 DOM overlay 像把字幕贴在电视屏幕外面。 HTML-in-Canvas 是把字幕送进画面信号里。 差别就在这。 它带来的第一个变化:Canvas 应用不用再那么“装瞎” Canvas 应用有个老问题:用户看得到,浏览器看不懂。 你在 canvas 里画了一个图表,用户看到坐标轴、图例、tooltip、按钮。但对浏览器来说,那可能就是一堆像素。你要想支持无障碍、搜索、复制、翻译、右键、自动填充,就得自己额外维护一套 fallback DOM 或自定义逻辑。 很多团队不是不知道该做,而是成本太高。 结果就是大量 canvas 应用变成“视觉上很强,浏览器能力很弱”。尤其是复杂白板、设计工具、图表工具、游戏 UI、3D 场景,一旦进入 canvas,很多普通网页里默认免费的能力突然都变成工程债。 HTML-in-Canvas 的价值,是让真实 DOM 可以继续存在于浏览器语义系统里,同时又能被画进 canvas。 这对大型 canvas-based 应用很重要。比如官方提到 Google Docs、Miro、Figma 这类重量级 Web 应用。它们不一定会马上把所有 UI 改掉,但某些复杂组件、嵌入式面板、文本区域、可访问内容,未来有机会少写很多自研逻辑。 你以为这只是省几行代码。 不,这是把“可访问性”和“浏览器能力”从补丁变回地基。 第二个变化:3D 和 WebXR 终于能吃上真实 Web UI 做过 WebGL / Three.js / WebXR 的人都知道,3D 里的 UI 很折磨。 你想在 3D 场景里的一个电脑屏幕上放一个网页界面。简单做法是贴一张图片,不能交互。高级一点,用 canvas texture,自己画内容。再高级,用 DOM overlay,让一个 HTML 面板跟着 3D 位置走。但它经常有遮挡、透视、事件、深度关系和真实嵌入感的问题。 所以很多 3D UI 最后都变成手搓。 按钮自己画,输入框自己写,布局自己算,hover 自己做,focus 自己接,事件自己 raycast。写着写着,你不是在做产品,你是在复刻浏览器。 HTML-in-Canvas 的想象空间就在这里:你可以用 HTML / CSS 写 UI,再把它作为 texture 放进 3D 场景。 Three.js r184 已经加了 HTMLTexture。PlayCanvas 也有 HTML-in-Canvas 文档,讲如何把 live HTML / CSS 渲染成 WebGL texture,并保留 hit testing、focus 和 accessibility。 这说明生态已经开始接线了。 对游戏、WebXR、沉浸式官网、3D 产品展示来说,这不是小改动。以前你要在“网页 UI 的成熟能力”和“3D 世界的真实嵌入感”之间选一个。现在至少有机会两个都要。 当然,别激动过头。 3D transform、hit testing、texture 更新、fallback、浏览器兼容,还是工程活。HTML-in-Canvas 不是仙丹,它只是把最底层那堵墙打开了一扇门。 门开了,不等于路铺好了。 第三个变化:AI Agent 更容易读懂“视觉应用” 这个点很多人会忽略,但我觉得很关键。 Google 在 I/O 2026 的语境里,不只是讲 Web UI,还在讲 agentic web。HTML-in-Canvas 官方介绍里也提到,内容如果仍然是 DOM,爬虫和 AI agent 可以更容易索引和读取 2D / 3D 场景里的文本。 这句话背后其

    什么是 HTML-in-Canvas?
  2. Jun 9

    什么是 Loop Engineering?

    这两天有句话在 AI 开发圈里传得很猛。 Claude Code 的 Boris Cherny 被转述说: > I don't prompt Claude anymore. I have loops running that prompt Claude and figuring out what to do. My job is to write loops. 翻成人话就是:我已经不再手动 prompt Claude 了。我有一堆 loop 在跑,它们会去 prompt Claude,自己判断下一步该做什么。我的工作是写这些 loop。 很多人看到这句话,第一反应是:好家伙,Prompt Engineering 又过气了?现在要改名 Loop Engineering 了? 先别急着换头像简介。 大多数人以为 Loop Engineering 是“不用再写 prompt,让 AI 自己干”。但真相是:Prompt 没消失,它只是从主角变成了零件。 以前你坐在 Claude 面前,一句一句催它:查一下、改一下、跑一下、再修一下、再验证一下。现在这些动作被写进了一个可以反复运行的系统里。这个系统知道什么时候启动,知道去哪拿上下文,知道能用哪些工具,知道怎么检查结果,也知道什么时候停。 这才叫 loop。 不是你写个 while true,里面塞一句“继续努力”。 那不叫 Loop Engineering,那叫自动烧钱。 ## 先把 Loop Engineering 说清楚 Prompt Engineering 解决的是:这一句话怎么问,模型更容易给出好答案。 Loop Engineering 解决的是:这件事怎么反复做,怎么知道做对了,什么时候该停,出了事谁来接手。 一个真正能跑的 loop,至少有九个东西。 第一,有触发器。什么时候启动?每天早上?CI 挂了?PR 有新评论?还是你手动说“开始”? 第二,有目标。不是“帮我看看”,而是“直到 auth 相关测试全绿,且没有新的 lint 错误”。 第三,有上下文获取。它要读哪些文件、哪些日志、哪些 issue、哪些文档?没有上下文,agent 就是在黑屋里挥刀。 第四,有工具权限。它只能读?能改文件?能跑命令?能开 PR?能发 Slack?权限边界不清,loop 越强越危险。 第五,有执行者。谁负责推进?一个 coding agent,还是多个 subagents 分工? 第六,有检查者。谁判断它做得对不对?测试?独立 reviewer agent?静态分析?浏览器自动化?别让写代码的 agent 自己给自己打满分,人类都不该这么干,AI 更不该。 第七,有状态记录。它今天试了什么,失败了什么,明天从哪里继续?没有状态文件的 loop,本质是每天失忆。 第八,有停止条件。什么时候算完?什么时候应该停?什么时候预算超了必须退出? 第九,有人工接管点。需求不清、权限不足、风险变大、结果互相矛盾,这些时候必须喊人。 你看,Loop Engineering 根本不是“更高级的 prompt 技巧”。 它更像是给 AI 工人搭一条小型生产线:进料、加工、质检、记录、返工、停机,全都要设计。 ## 为什么 Boris 会说“我不 prompt Claude 了”? 这句话最容易被误读。 他说“不 prompt”,不是说自然语言不重要了。恰恰相反,loop 里到处都是 prompt。 只不过这些 prompt 不再靠你一轮一轮手动输入,而是被封装进命令、skill、hook、workflow、schedule 里。 以前你是人肉调度器。 你看 CI 挂了,复制日志,贴给 Claude。Claude 改了,你跑测试。测试又挂,你再贴错误。它又改,你再看 diff。最后你说,行了,提交。 这个过程里,Claude 只是在干活。 你在当胶水。 Loop Engineering 的变化是:把“胶水”写出来。 比如一个 PR babysitter loop 可以这样跑: PR 一创建,loop 读取 diff、CI log、review comments 和项目规则。如果 CI 红了,它先定位失败原因,只做最小修复,然后跑相关测试。修完以后,另一个 reviewer agent 从安全、回归、可维护性角度审一遍。CI 绿了、评论清空了、没有新失败,它就停。遇到权限问题、需求争议、改动超过风险阈值,它就喊你。 这时候你确实没有每一步都 prompt Claude。 但你写了一个会替你 prompt Claude 的系统。 说白了,Prompt Engineering 是你会不会说话。Loop Engineering 是你会不会带队。 ## 这不是玄学,产品已经长出来了 如果这只是社区发明的新词,那可以先放一边。 但问题是,工具已经在朝这个方向长。 Claude Code 官方文档里有 /loop。它可以在当前 session 里按间隔重复跑一个 prompt,用来轮询部署、照看 PR、检查长任务。你可以写 /loop 5m check if the deployment finished,也可以让 Claude 自己决定下一次什么时候检查。 还有 /goal。你给它一个完成条件,它会跨 turn 继续工作,直到一个小模型判断这个条件已经满足。注意这个设计很关键:干活的是一个模型,判断是否完成的是另一个模型。写作业的人不自己给自己判卷。 还有 hooks。比如 Stop hook 可以在 Claude 准备停下来的时候触发,检查它是不是漏了测试、是不是还有错误、是不是还需要继续。 再往上,是 Dynamic Workflows。Claude 可以为一个复杂任务写 workflow script,在隔离 runtime 里调度多个 subagents。中间结果不会全塞回主对话上下文,而是存在脚本变量里。这个东西已经不太像聊天了,更像一个临时工程调度器。 OpenAI 也把 Codex 的 agent loop 拆开讲过。agent 的底层循环其实很朴素:模型读 prompt,决定要不要调用工具,工具结果再回到上下文里,模型继续判断,直到它输出最终消息。 所以你要分清两层。 第一层,是 agent 自己内部的 loop:推理、工具调用、观察、再推理。 第二层,是你设计的工作 loop:什么时候让 agent 开始,怎么给它上下文,怎么限制权限,怎么复核,怎么记录状态,怎么停。 现在真正值钱的是第二层。 ## 代码正在变便宜,判断正在变贵 为什么这个转向这么重要? 因为 AI coding 的瓶颈正在移动。 Anthropic 在《When AI builds itself》里提到,2026 年 5 月,Anthropic 合入代码中超过 80% 可归因于 Claude。典型工程师每天合入代码量大约是 2024 年的 8 倍。 这个数字你可以怀疑,可以打折,毕竟代码行数不是质量。 但方向很清楚:写代码这件事,正在变便宜。 真正变贵的是判断。 这个需求要不要做?这个错误是不是根因?这个测试绿了能不能代表没问题?这个 PR 看起来能跑,但三个月以后会不会把项目拖进泥坑?这个 agent 说它完成了,你信不信? 以前一个开发者的价值,很大一部分体现在“我能把代码写出来”。 现在这部分价值被模型疯狂挤压。 但另一些价值变得更重要:定义目标、拆任务、给边界、设计反馈、判断风险、验收结果。 你以为 AI 抢的是键盘,真相是它在逼你往上挪一层。 挪不上去的人,就会变成最尴尬的角色:代码不是你写的,系统你也不理解,出事还得你背锅。 这才是最扎心的地方。 ## 一个好 loop 长什么样? 拿一个最普通的场景:每天早上检查项目健康度。 低级做法是写个定时 prompt: “每天早上帮我检查项目有没有问题。” 听起来不错,实际很容易变成废话生成器。它每天扫一遍,说一些“建议优化测试覆盖率”“建议关注技术债”的正确废话。你看两天就烦了。 高级一点的 loop 会这样设计: 触发器:每天早上 9 点。 上下文:读取昨天的 commits、CI 失败记录、线上 error logs、未解决的 issue、最近合并的 PR。 规则:只读,不改文件;每个发现必须绑定证据;没有证据就不输出。 执行:把问题分成 bug risk、test gap、dependency risk、cleanup candidate。 检查:另一个 agent 反驳每个发现,问三个问题:证据够不够?是不是重复?是不是值得人类今天处理? 状态:把确认过的问题写入 triage.md,带上第一次发现时间、最近一次复现方式、建议 owner。 停止:如果没有高价值发现,只输出一行摘要,不要写小作文。 人工接管:凡是涉及架构方向、线上风险、用户数据、不可逆操作,一律只提建议,不执行。 这才是 loop。 它不是让 AI 更会“说”,而是让 AI 在一个有边界的轨道上持续“做”。 ## Loop Engineering 的三个坑 第一个坑:没有停止条件。 很多人一听 loop 就兴奋,恨不得让 agent 一直跑。问题是,模型并不知道你的钱包在哪。它会很认真地跑偏,很认真地重试,很认真地把 token 烧成烟花。 一个没有预算上限、没有失败上限、没有停止条件的 loop,不是自动化,是事故预备役。 第二个坑:没有独立验证。 Claude 说“我已经完成了”,这句话不能当验收。 人都会高估自己刚写完的代码,模型更会。尤其是 agent 一路改下来,它的上下文里全是“我为什么这么做”的理由,它天然会替自己的路径辩护。 所以 maker 和 checker 必须分开。写的人负责写,查的人负责查。能用测试就用测试,能用浏览器断言就用浏览器断言,能用静态分析就用静态分析。LLM judge 可以用,但别把它当神谕。 第三个坑:理解债。 这也是最阴的坑。 loop 跑得越顺,你越容易不看。PR 自动开了,CI 自动修了,评论自动回了,文档自动改了。你一开始觉得爽,过一个月发现:项目每个地方都变了,但你不知道为什么。 这叫 comprehension debt。 技术债至少还在代码里,理解债在你脑子外面。它不报错,但它会让你在关键时刻失去判断力。 说白了,loop 可以替你干活,但不能替你理解。 你要是把理解也外包出去,那不是工程

    什么是 Loop Engineering?

About

这里没有高深的理论,只有和你一样关心科技和生活的普通人。每一期,我用最简单的语言聊聊新技术、行业故事,还有那些让人会心一笑的小发现。关注《猿来如此》,和我一起,慢慢变好。

You Might Also Like