<?xml version="1.0" encoding="utf-8"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Vue Lynx 博客</title>
        <link>https://vue.lynxjs.org/</link>
        <description>Vue 3 framework for building Lynx apps</description>
        <lastBuildDate>Mon, 17 Aug 2026 20:14:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>zh-CN</language>
        <copyright>© 2026 Xuan Huang (huxpro). All Rights Reserved.</copyright>
        <item>
            <title><![CDATA[我如何在两周内用 AI 做出 Vue Lynx]]></title>
            <link>https://vue.lynxjs.org/zh/blog/announcing-vue-lynx</link>
            <guid isPermaLink="false">/zh/blog/announcing-vue-lynx</guid>
            <pubDate>Thu, 26 Mar 2026 00:00:00 GMT</pubDate>
            <description><![CDATA[Vue Lynx 从一个周末的架构实验，到初具生产级形态的双线程 Vue 框架，只用了两周。这篇文章讲讲它的架构、测试 Harness、Agent E2E 验证闭环，以及账单。]]></description>
            <content:encoded><![CDATA[<h1 class="rp-toc-include" id="我如何在两周内用-ai-做出-vue-lynx"><span>我如何在两周内用 AI 做出 Vue Lynx</span><a href="#我如何在两周内用-ai-做出-vue-lynx" class="rp-header-anchor rp-link" aria-hidden="true">#</a> </h1><div class="rp-not-doc rp-llms-container"><button class="rp-not-doc rp-llms-button rp-llms-copy-button"><div class="rp-llms-copy-button__icon-wrapper"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-llms-copy-button__icon-success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-llms-copy-button__icon-copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg></div><span>复制 Markdown</span></button><button class="rp-llms-button rp-llms-view-options__trigger "><svg width="1em" height="1em" viewBox="0 0 32 32" class="rp-llms-view-options__arrow "><path fill="currentColor" d="M16 22 6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z"></path></svg></button></div><!--$--><a href="/zh/blog/" class="backLink-OiU6me">← <!-- -->所有文章</a><div class="blogHeader-TGxjXC"><span class="date-GC1HAk">2026年3月26日</span><div class="blog-avatar-frame-tezKVf avatars-TetuDG"><span class="avatar-item-Mdx49S"><img class="avatar-img-WIennJ" src="https://github.com/huxpro.png" alt="黄玄"/><div class="avatar-text-KkTNZx"><span class="avatar-name-row-qlDUJL"><span class="avatar-name-QRE8vq">黄玄</span><span class="avatar-socials-kVoJOe"><span class="avatar-social-link-Kh0lPm" role="link"><svg class="icon-x-laLkVm" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true"><path d="M18.244 2.25h3.308l-7.227 8.26 8.502 11.24H16.17l-4.714-6.231-5.401 6.231H2.744l7.727-8.835L1.254 2.25H8.08l4.253 5.622L18.244 2.25zm-1.161 17.52h1.833L7.084 4.126H5.117L17.083 19.77z"></path></svg></span><span class="avatar-social-link-Kh0lPm" role="link"><span role="img" aria-label="github_logo" class="semi-icon semi-icon-default semi-icon-github_logo"><svg viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" focusable="false" aria-hidden="true"><path d="M12.01 1a11 11 0 0 0-3.48 21.45c.55.11.75-.23.75-.52l-.02-2.05c-3.06.66-3.7-1.3-3.7-1.3-.5-1.28-1.22-1.61-1.22-1.61-1-.68.07-.68.07-.68 1.11.08 1.7 1.13 1.7 1.13.98 1.68 2.56 1.2 3.2.92.1-.72.39-1.2.7-1.48-2.45-.26-5.02-1.2-5.02-5.43 0-1.2.44-2.2 1.13-2.96-.1-.27-.49-1.4.11-2.91 0 0 .93-.3 3.03 1.13a10.66 10.66 0 0 1 5.5 0c2.1-1.42 3.03-1.13 3.03-1.13.6 1.51.22 2.64.1 2.91a4.21 4.21 0 0 1 1.14 2.96c0 4.23-2.57 5.16-5.03 5.43.4.35.74 1 .74 2.04l-.01 3.03c0 .29.2.63.74.52A11 11 0 0 0 12.01 1Z" fill="currentColor"></path><path d="M5.17 16.8c-.01.06-.1.08-.18.04-.07-.04-.13-.1-.09-.16.02-.06.11-.08.18-.04.08.04.11.11.1.16Zm.44.5c-.05.05-.16.01-.22-.06-.07-.07-.09-.18-.03-.24.05-.05.14-.01.22.06.07.1.09.2.03.24Zm.44.63c-.07.06-.18 0-.24-.09-.07-.09-.07-.22 0-.25.08-.06.18 0 .24.09.07.09.07.2 0 .25Zm.6.62c-.05.08-.18.06-.3-.03-.08-.1-.12-.22-.05-.28.06-.07.19-.05.3.04.09.07.1.2.05.27Zm.82.35c-.02.1-.14.13-.27.1-.13-.04-.2-.15-.18-.23.01-.09.14-.12.27-.09.13.04.2.13.18.22Zm.9.07c0 .1-.12.17-.24.17-.13 0-.24-.08-.24-.17 0-.09.11-.16.24-.16.12 0 .23.07.23.16Zm.83-.14c.02.09-.07.18-.2.2-.13.02-.23-.04-.25-.13-.02-.1.07-.18.2-.2.13-.02.23.04.25.13Z" fill="currentColor"></path></svg></span></span></span></span><span class="avatar-role-iGDJdR">Vue Lynx 作者</span></div></span></div><div class="rp-not-doc rp-llms-container"><button class="rp-not-doc rp-llms-button rp-llms-copy-button"><div class="rp-llms-copy-button__icon-wrapper"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-llms-copy-button__icon-success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-llms-copy-button__icon-copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg></div><span>复制 Markdown</span></button><button class="rp-llms-button rp-llms-view-options__trigger "><svg width="1em" height="1em" viewBox="0 0 32 32" class="rp-llms-view-options__arrow "><path fill="currentColor" d="M16 22 6 12l1.4-1.4 8.6 8.6 8.6-8.6L26 12z"></path></svg></button></div></div>
<p>Vue 开发者想要原生能力，已经想了很多年。<a href="https://x.com/danielkelly_io/status/1899746975588737407" target="_blank" rel="noopener noreferrer" class="rp-link">「Vue + Lynx = Vue Native」</a>这条推文收获了 1.7k 个赞。我们仓库里的 <a href="https://github.com/lynx-family/lynx/issues/193" target="_blank" rel="noopener noreferrer" class="rp-link">Vue 集成 Issue</a> 也拿到了 1,600 个 upvote，是至今呼声最高的功能需求。需求很明确，问题一直是人手。</p>
<p><a href="https://lynxjs.org/" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx</a> 开源时，<a href="https://x.com/youyuxi/status/1898663514581168173" target="_blank" rel="noopener noreferrer" class="rp-link">Evan You</a> 和 <a href="https://x.com/Huxpro/status/1927276405328429259" target="_blank" rel="noopener noreferrer" class="rp-link">Rich Harris</a> 都为它打过 call。但要做出真正能用于生产的框架集成，过去一直需要投入大量工程资源。后来，<a href="https://vercel.com/blog/we-ralph-wiggumed-webstreams-to-make-them-10x-faster" target="_blank" rel="noopener noreferrer" class="rp-link">Vercel 重写 Web Streams</a>、<a href="https://blog.cloudflare.com/vinext/" target="_blank" rel="noopener noreferrer" class="rp-link">Cloudflare 做出 ViNext</a> 这类项目陆续出现。它们证明了一件事：一个工程师加上 AI，也能交付过去需要一个团队才能完成的工作。对我来说，投入产出比变了。</p>
<p>Vue 其实早就准备好了一个关键基础：成熟的 Custom Renderer API。于是我拿了一个周末试了试。一次约 $1,400、持续 37 小时的 Hackathon。最初只是做架构探索：「Vue 的 Custom Renderer 到底能不能和双线程拆包一起工作？如果能，该怎么做？」周日凌晨三点，我还在和 Claude 一起调试「点击后数字没有增加」。到了周一早上，<a href="https://x.com/Huxpro/status/2028672358912086524" target="_blank" rel="noopener noreferrer" class="rp-link">TodoMVC 已经跑起来了</a>。我没忍住发了一条若有所指的推文，结果立刻在 X 上火了。</p>
<h2 class="rp-toc-include" id="vue-lynx-登场"><a href="#vue-lynx-登场" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Vue Lynx 登场</h2>
<p>接下来的两周，我把晚上和周末都投了进去。160 多个 commit，大约 180 个 session，Vue Lynx 才真正有了形状。</p>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>▎ 第一周   ████████████░░░░░░░░  Runtime + Toolchain</span></span>
<span class="line"><span>▎ 第二周   ░░░░░░░░████████████  Docs + Examples + i18n</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<p>其实第一周结束时就可以发布。但熟悉我的人应该知道，我一直有个原则：</p>
<blockquote>
<p>真正做通了，就让 Demo 自己说话。</p>
</blockquote>
<p>打开 <a href="https://vue.lynxjs.org/" target="_blank" rel="noopener noreferrer" class="rp-link">vue.lynxjs.org</a>，你能看到 20 多个同时运行在原生端和 Web 端的示例。不离开浏览器就可以直接体验。</p>
<p>Vue Lynx 支持完整的 Composition API、<code>&lt;Transition&gt;</code>、<code>&lt;Suspense&gt;</code>，也接入了 Vue Router、Pinia、Tailwind CSS 和 TanStack Query。我们还移植了 Lynx 官方教程里的 Waterfall Gallery 和 Swiper，用原生组件和主线程脚本实现零延迟手势，再用一个 HackerNews Clone 把这些能力串到一起。</p>
<h3 class="rp-toc-include" id="现在就试试"><a href="#现在就试试" class="rp-header-anchor rp-link" aria-hidden="true">#</a>现在就试试</h3>
<div class="rp-codeblock language-bash"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="bash"><code><span class="line"><span style="color:var(--shiki-token-function)">npm</span><span style="color:var(--shiki-token-string)"> create</span><span style="color:var(--shiki-token-string)"> vue-lynx@latest</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<p>当然，它是<a href="https://github.com/huxpro/vue-lynx" target="_blank" rel="noopener noreferrer" class="rp-link">开源的</a>。如果这个项目让你觉得有点意思，欢迎点一个 Star。也请把一些爱分给 <a href="https://github.com/lynx-family/lynx" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx Engine</a> 和 <a href="https://github.com/lynx-family/lynx-stack" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx Frontend Stack</a>，Vue Lynx 正是站在它们的肩膀上。</p>
<p>我很希望 Vue 和 Lynx 社区能一起把它继续做下去。Issue、PR、反馈，全部欢迎。</p>
<h2 class="rp-toc-include" id="harness工程"><a href="#harness工程" class="rp-header-anchor rp-link" aria-hidden="true">#</a>「Harness」工程</h2>
<blockquote>
<p>继「AI」「vibe」「agentic」之后，当然得用上现在最火的词：harness。</p>
</blockquote>
<p>本项目开发过程中，没有人类因为写代码而受到伤害。</p>
<h3 class="rp-toc-include" id="为-ai-搭好架构"><a href="#为-ai-搭好架构" class="rp-header-anchor rp-link" aria-hidden="true">#</a>为 AI 搭好架构</h3>
<p>社区此前有过两次尝试。第二次来自 Vue Vine 的维护者 <a href="https://x.com/Shenqingchuan" target="_blank" rel="noopener noreferrer" class="rp-link">@Shenqingchuan</a>，已经走得相当远，<a href="https://x.com/Shenqingchuan/status/1996862232593129815" target="_blank" rel="noopener noreferrer" class="rp-link">甚至跑通了主线程脚本的 Demo</a>。不过，两次尝试都和 Web 一样，把 Vue 放在主线程运行。这在 Lynx 上可以工作，却没有真正用上 Lynx 标志性的<a href="https://lynxjs.org/blog/lynx-unlock-native-for-more#use-the-main-thread-responsibly-for-interactivity" target="_blank" rel="noopener noreferrer" class="rp-link">双线程架构</a>：繁重的框架重渲染放到后台，原生 UI 线程保持非阻塞，只在确有需要时通过主线程脚本介入。</p>
<p>这是我第一天验证的核心架构决策。Vue Lynx 中，整个 Vue Runtime 都运行在后台线程。一个轻量的 <code>ShadowElement</code> 链表树在内存里镜像原生元素树。所有 DOM 变更会被序列化到一个扁平的 ops buffer 中，每个 tick 一次性发送到主线程：</p>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>┌──────────────────────────────────────────────────────┐</span></span>
<span class="line"><span>│                        后台线程                       │</span></span>
<span class="line"><span>│    Vue 3 Runtime · 响应式 · 生命周期 · 用户代码      │</span></span>
<span class="line"><span>└──────────────┬──────────────────────▲────────────────┘</span></span>
<span class="line"><span>          ops  │                      │  events</span></span>
<span class="line"><span>               ▼                      │</span></span>
<span class="line"><span>┌──────────────────────────────────────┴───────────────┐</span></span>
<span class="line"><span>│                        主线程                         │</span></span>
<span class="line"><span>│      原生元素 · 布局 · 渲染 · 主线程脚本事件处理      │</span></span>
<span class="line"><span>└──────────────────────────────────────────────────────┘</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<p>为了让 Agent 始终遵守这套双线程架构，不要滑回它最熟悉的单线程 Web 模型，我把所有<a href="https://github.com/Huxpro/vue-lynx/tree/main/plans" target="_blank" rel="noopener noreferrer" class="rp-link">关键计划直接放进了源码仓库</a>。设计讨论、决策记录、实现后的经验，全都成为跨 session 的上下文。每个新 session 都能接着前一个继续工作，也能继承约束条件和这些约束背后的原因。</p>
<h3 class="rp-toc-include" id="接入-vue-上游测试"><a href="#接入-vue-上游测试" class="rp-header-anchor rp-link" aria-hidden="true">#</a>接入 Vue 上游测试</h3>
<p>AI 驱动开发中，最值得投入的是反馈机制。为了确保行为符合官方 Vue，理想方案当然是直接复用 Vue 的上游测试套件。但 Vue 的测试假设了一套单线程 DOM。要怎么用它测试一个双线程 Renderer？</p>
<p>好在 Lynx 已经提供了<a href="https://lynxjs.org/next/api/lynx-testing-environment/index.html#lynx-jstesting-environment" target="_blank" rel="noopener noreferrer" class="rp-link">双线程测试环境</a>。于是我们把测试套件重新接线，让它完整跑过这条双线程链路：后台线程的 <code>ShadowElement</code> → ops buffer → <code>syncFlush()</code> → 主线程的 <code>applyOps</code> → PAPI → jsdom。接下来，让 Agent 一直 grind，直到剩下的失败项都确实无法修复。这基本就是一个 <a href="https://ghuntley.com/loop/" target="_blank" rel="noopener noreferrer" class="rp-link">Ralph Loop</a>。</p>
<p>最终结果是：949 个上游测试中通过 852 个。每一个失败项都记录在 <a href="https://github.com/Huxpro/vue-lynx/blob/main/packages/upstream-tests/skiplist.json" target="_blank" rel="noopener noreferrer" class="rp-link">skiplist</a> 里，并写明了跳过原因。事实证明，它们都无关紧要。完整结果可以查看<a href="https://github.com/Huxpro/vue-lynx/blob/main/packages/upstream-tests/README.md" target="_blank" rel="noopener noreferrer" class="rp-link">测试报告和跳过项分析</a>。</p>
<p>我们也为 Lynx 特有的能力补了自己的测试，例如 <code>&lt;list&gt;</code> 元素、<code>bindtap</code> 事件和主线程脚本 API。整条测试链路验证完成后，我又往前走了一步，把 Vue 官方文档里的 <a href="https://vue.lynxjs.org/guide/7guis" target="_blank" rel="noopener noreferrer" class="rp-link">7GUIs Benchmark</a> 移植过来做压力测试。</p>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>                后台线程   ┃  主线程</span></span>
<span class="line"><span>                           ┃</span></span>
<span class="line"><span>Vue → ShadowElement → Ops  ┃  PAPI → Lynx Engine → UI</span></span>
<span class="line"><span>                           ┃</span></span>
<span class="line"><span>├─ vue runtime-core ─┤     ┃</span></span>
<span class="line"><span>├─── vue runtime-dom ──────╂──┤</span></span>
<span class="line"><span>        ├── E2E Pipeline ──╂─────────────┤</span></span>
<span class="line"><span>                           ┃         ├── Agentic ───┤</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<h3 class="rp-toc-include" id="agent-e2e-验证闭环"><a href="#agent-e2e-验证闭环" class="rp-header-anchor rp-link" aria-hidden="true">#</a>Agent E2E 验证闭环</h3>
<p>但传统的机械化测试发现不了真实 UI 中的 Bug。布局偏了几个像素，真机上的交互失效，这类问题过去需要人来判断。要验证 <code>&lt;Transition&gt;</code>、<code>&lt;Suspense&gt;</code> 这样的高级 Vue 特性，你必须亲眼看到它运行，再实际操作一次。</p>
<p>只要 Harness 搭得对，示例就不只是 Demo，也可以成为 Agent 自动评估的 workload。我接入了两个执行环境：一个是通过 <a href="https://lynxjs.org/next/ai/lynx-devtool-mcp.html" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx DevTool MCP</a>、CLI 和 Skill 控制的 iOS Simulator；另一个是通过 <a href="https://lynxjs.org/next/guide/start/integrate-with-existing-apps?platform=web" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx for Web</a> 运行、由 Agent 控制的浏览器。闭环很简单：让同一个示例分别在两个环境中运行，观察并验证结果，出现回归就继续修。整个过程不需要人介入。</p>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>┌──── 修复 ◀─────────────────────────────────┐</span></span>
<span class="line"><span>│                                            │</span></span>
<span class="line"><span>▼                                            │</span></span>
<span class="line"><span>示例 ─────┬──▶ iOS Simulator ───┬──▶ 评估</span></span>
<span class="line"><span>          │    (DevTool MCP)     │</span></span>
<span class="line"><span>          │                      │</span></span>
<span class="line"><span>          └──▶ Lynx for Web ─────┘</span></span>
<span class="line"><span>               (agent-browser)</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<p>我先从 Vue 核心特性开始，因为它们的正确行为有明确标准。Agent 阅读官方文档，编写示例，再检查运行结果是否符合文档。随后，范围扩大到生态集成：Vue Router、Pinia、TanStack Query 和 Tailwind CSS。</p>
<p>到了期末考试，我换了一种做法。后来才知道，它有一个名字：<em>差分评估（differential evaluation）</em>。我让 Agent 移植现有应用，再拿移植结果和原版逐项比较。第一个项目以经典的 Vue HackerNews 实现为 ground truth，在浏览器里并排运行 Web 原版和基于 Lynx for Web 的 Vue Lynx 版本。第二个项目以现有的 ReactLynx Demo 为参照，把它移植到 Vue Lynx，再通过 Lynx DevTool MCP 在 iOS Simulator 上验证两边是否一致。Harness 不需要凭空理解什么叫「正确」，它只需要确认两个输出一致。</p>
<div class="rp-table-scroll-container rp-scrollbar"><table><thead><tr><th>Ground Truth</th><th>Candidate</th><th>运行环境</th></tr></thead><tbody><tr><td>Vue HackerNews Web 版</td><td>Vue Lynx 移植版</td><td>Lynx Web（浏览器）</td></tr><tr><td>ReactLynx Demo</td><td>Vue Lynx 移植版</td><td>Lynx Native（Simulator）</td></tr></tbody></table></div>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>                    ┌─────────────────────────────┐</span></span>
<span class="line"><span>       ┌────────────▶  ground truth ──▶       A   │</span></span>
<span class="line"><span>       │            │                         │   │</span></span>
<span class="line"><span>输入 ──┤            │                        比较 ──▶ 差异</span></span>
<span class="line"><span>       │            │                         │   │      │</span></span>
<span class="line"><span>       └────────────▶  Lynx (candidate) ──▶    B   │      ▼</span></span>
<span class="line"><span>                    └─────────────────────────────┘    修复闭环</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<h3 class="rp-toc-include" id="账单"><a href="#账单" class="rp-header-anchor rp-link" aria-hidden="true">#</a>账单</h3>
<div class="rp-codeblock language-text"><div class="rp-codeblock__content"><div class="rp-codeblock__content__scroll-container rp-scrollbar rp-scrollbar--always"><pre class="shiki css-variables" style="background-color:var(--shiki-background);color:var(--shiki-foreground)" tabindex="0" data-lang="text"><code><span class="line"><span>┌──────────────────────────────────────────┐</span></span>
<span class="line"><span>│ 按 Opus API 价格计算                     │</span></span>
<span class="line"><span>├──────────────────────────────┬───────────┤</span></span>
<span class="line"><span>│ Input (3.8M tokens)          │ $      57 │</span></span>
<span class="line"><span>│ Output (6.8M tokens)         │ $     510 │</span></span>
<span class="line"><span>│ Cache Write (117.9M tokens)  │ $   2,211 │</span></span>
<span class="line"><span>│ Cache Read (2.5B tokens)     │ $   3,769 │</span></span>
<span class="line"><span>├──────────────────────────────┼───────────┤</span></span>
<span class="line"><span>│ 总计                         │ $   6,547 │</span></span>
<span class="line"><span>└──────────────────────────────┴───────────┘</span></span></code></pre></div><div class="rp-code-button-group"><button class="rp-code-button-group__button rp-code-wrap-button" title="Toggle code wrap"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrapped"><path fill="#22a041" d="M21 5H3v2h18zM3 19h7v-2H3zm0-6h15c1 0 2 .43 2 2s-1 2-2 2h-2v-2l-4 3 4 3v-2h2c2.95 0 4-1.27 4-4 0-2.72-1-4-4-4H3z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-button-group__icon--wrap"><path fill="currentColor" d="M16 7H3V5h13zM3 19h13v-2H3zm19-7-4-3v2H3v2h15v2z"></path></svg></button><button class="rp-code-button-group__button rp-code-copy-button" title="复制代码"><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--copy"><path fill="currentColor" d="M20 8v12H8V8zm0-2H8a2 2 0 0 0-2 2v12a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8a2 2 0 0 0-2-2"></path><path fill="currentColor" d="M4 16H2V4a2 2 0 0 1 2-2h12v2H4Z"></path></svg><svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24" class="rp-code-button-group__icon rp-code-copy-button__icon rp-code-copy-button__icon--success"><path fill="#49cd37" d="m9.55 18-5.7-5.7 1.425-1.425L9.55 15.15l9.175-9.175L20.15 7.4z"></path></svg></button></div></div></div>
<p>这组数字很说明问题。Output token，也就是 Claude 真正<em>写出</em>的代码和文字，只占总成本的 8%。另外 92% 都花在理解上：反复阅读代码库、吸收工具输出、在 31,700 次 API 调用中重新处理对话历史。为了写出 680 万 token，它读了 25 亿 token，读写比是 370:1。这才是「agentic」落到 API 账单上时的真实样子。</p>
<p>按 API 价格计算的 $6,500 值不值？幸好 Claude 通过<a href="https://x.com/Huxpro/status/2031973188440052119" target="_blank" rel="noopener noreferrer" class="rp-link">开源项目计划</a>送了我每月 $200 的 Claude Max。</p>
<h2 class="rp-toc-include" id="接下来呢"><a href="#接下来呢" class="rp-header-anchor rp-link" aria-hidden="true">#</a>接下来呢？</h2>
<p>这个项目始于一个人的晚上和周末。我很希望能和 Vue 核心团队一起探索 Vue 在原生端的未来。就我个人而言，也代表 Lynx 团队，我们会持续投入，支持它成长。</p>
<p>Vue Lynx 目前仍处于 Pre-Alpha。架构已经站稳，但 Vue 的 API 面很大，还有不少角落未经验证。</p>
<ul>
<li><code>KeepAlive</code>、<code>Teleport</code> 这类特性可能需要针对 Runtime 做适配。</li>
<li>原生输入元素上的 <code>&lt;style scoped&gt;</code> 和 <code>v-model</code> 可以实现，但目前还没有完成。</li>
<li>主线程脚本 API 现在复用了 ReactLynx 基于 Directive 的设计。更符合 Vue 习惯的形式，例如 <code>&lt;script main-thread setup&gt;</code>，值得继续探索。</li>
<li>将 Vue DevTools 集成进 Lynx DevTool。</li>
</ul>
<p>Vue 核心之外，还有一个庞大的 Vue 生态，等待我们把它带到原生端。</p>
<p>愿景很简单：Vue 开发者应该能像今天发布 Web 应用一样，自然地发布原生应用。我们还没走到那里，但地基已经打好，路径也已经清楚。</p>
<p>如果你读到了这里，<a href="https://vue.lynxjs.org/" target="_blank" rel="noopener noreferrer" class="rp-link">现在就试试</a>。做点东西，告诉我们还缺什么。</p>
<p>顺带一提：<a href="https://x.com/Huxpro/status/2032279225479176229" target="_blank" rel="noopener noreferrer" class="rp-link">Lynx 最初就是用 Vue 2 做出来的</a>。</p>
<hr/>
<p>本文最初于 2026 年 3 月 26 日发布在 <a href="https://x.com/Huxpro/status/2036993665965416601" target="_blank" rel="noopener noreferrer" class="rp-link">X Articles</a>。</p><!--/$-->]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Vue Lynx 博客]]></title>
            <link>https://vue.lynxjs.org/zh/blog/</link>
            <guid isPermaLink="false">/zh/blog/</guid>
            <content:encoded><![CDATA[<BlogList />
]]></content:encoded>
        </item>
    </channel>
</rss>