[{"data":1,"prerenderedAt":3288},["ShallowReactive",2],{"all-posts":3},[4,1789,2047,2510,2922],{"id":5,"title":6,"accent":7,"body":8,"description":1768,"draft":1769,"experience":1770,"extension":1771,"featured":308,"image":1772,"meta":1778,"navigation":308,"path":1779,"publishedAt":1780,"readingTime":1781,"seo":1782,"stem":1783,"tags":1784,"updatedAt":1770,"__hash__":1788},"posts\u002Fblog\u002Fvue-3-6-rc-upgrade-guide.md","Vue 3.6 RC 升级全景：从 3.5 到 Vapor Mode 与 alien-signals","mint",{"type":9,"value":10,"toc":1729},"minimark",[11,20,35,38,48,53,199,202,207,211,214,251,254,260,264,271,274,419,422,429,435,439,442,455,461,464,467,484,487,490,507,524,527,531,534,537,546,549,553,556,686,689,718,746,749,753,756,806,809,813,820,881,884,887,893,899,902,906,909,923,932,980,987,990,1003,1006,1010,1013,1151,1158,1164,1167,1255,1258,1292,1295,1308,1311,1315,1318,1321,1324,1328,1331,1335,1338,1341,1344,1348,1351,1369,1376,1393,1396,1422,1425,1429,1432,1452,1455,1459,1462,1485,1488,1492,1495,1498,1518,1521,1525,1528,1532,1535,1539,1542,1546,1549,1552,1635,1638,1641,1649,1652,1659,1662,1665,1668,1720,1725],[12,13,14,15,19],"p",{},"如果只看版本号，Vue 3.6 像是一次普通的次版本升级。但真正读完发布说明后，我的感受是：",[16,17,18],"strong",{},"它更像 Vue 为下一阶段准备的一次“换发动机”","。",[12,21,22,23,27,28,27,31,34],{},"一边是基于 alien-signals 重构的响应式内核，公开的 ",[24,25,26],"code",{},"ref","、",[24,29,30],{},"computed",[24,32,33],{},"watch"," 用法基本不变；另一边是正式进入 RC 阶段的 Vapor Mode，它让 Vue 单文件组件可以绕过虚拟 DOM，编译为更直接的 DOM 更新逻辑。",[12,36,37],{},"不过，先把最重要的版本信息放在前面：",[39,40,41],"blockquote",{},[12,42,43,44,47],{},"截至 2026 年 7 月 26 日，Vue 3.6 的最新版本是 ",[16,45,46],{},"3.6.0-rc.2","。RC 是候选发布版，不是稳定版。本文适合用来评估、试验和准备迁移，不代表所有生产项目都应该立即升级。",[49,50,52],"h2",{"id":51},"先看结论36-改变了什么","先看结论：3.6 改变了什么",[54,55,56,75],"table",{},[57,58,59],"thead",{},[60,61,62,66,69,72],"tr",{},[63,64,65],"th",{},"对比维度",[63,67,68],{},"Vue 3.5",[63,70,71],{},"Vue 3.6 RC",[63,73,74],{},"对现有项目的实际影响",[76,77,78,93,107,121,135,149,167,185],"tbody",{},[60,79,80,84,87,90],{},[81,82,83],"td",{},"响应式系统",[81,85,86],{},"3.5 已做过内部优化",[81,88,89],{},"基于 alien-signals 大幅重构",[81,91,92],{},"常规 Composition API 写法通常不需要重写，但应回归复杂副作用与调度逻辑",[60,94,95,98,101,104],{},[81,96,97],{},"默认渲染模式",[81,99,100],{},"Virtual DOM",[81,102,103],{},"仍然是 Virtual DOM",[81,105,106],{},"单纯升级依赖不会让应用自动进入 Vapor",[60,108,109,112,115,118],{},[81,110,111],{},"Vapor Mode",[81,113,114],{},"尚未作为 3.5 的正式能力交付",[81,116,117],{},"功能集进入 RC，100% 显式启用",[81,119,120],{},"可以从单个性能敏感区域开始试验",[60,122,123,126,129,132],{},[81,124,125],{},"应用体积",[81,127,128],{},"默认包含 VDOM runtime",[81,130,131],{},"纯 Vapor 应用可不引入 VDOM runtime",[81,133,134],{},"只有纯 Vapor 或边界清晰的场景，体积收益才容易兑现",[60,136,137,140,143,146],{},[81,138,139],{},"API 覆盖",[81,141,142],{},"完整 Vue 3 API 生态",[81,144,145],{},"Vapor 支持 Vue API 的一个子集",[81,147,148],{},"Options API、实例代理相关能力不能直接迁移",[60,150,151,154,157,164],{},[81,152,153],{},"VDOM \u002F Vapor 混用",[81,155,156],{},"不适用",[81,158,159,160,163],{},"通过 ",[24,161,162],{},"vaporInteropPlugin"," 互操作",[81,165,166],{},"标准 props、事件、slots 已覆盖，但复杂嵌套仍需测试",[60,168,169,172,175,182],{},[81,170,171],{},"事件委托",[81,173,174],{},"原生监听语义",[81,176,177,178,181],{},"rc.2 起默认直接绑定；",[24,179,180],{},".delegate"," 才显式委托",[81,183,184],{},"不要沿用 rc.1 的默认委托认知",[60,186,187,190,193,196],{},[81,188,189],{},"适合生产吗",[81,191,192],{},"稳定版",[81,194,195],{},"当前仍是预发布版",[81,197,198],{},"生产项目应等待稳定版和上层生态明确支持",[12,200,201],{},"一句话概括：",[12,203,204],{},[16,205,206],{},"Vue 3.5 的亮点更接近“开发体验和 SSR 能力升级”，Vue 3.6 的核心则是“响应式内核与渲染架构升级”。",[49,208,210],{"id":209},"从-35-到-36变化重心已经不同","从 3.5 到 3.6：变化重心已经不同",[12,212,213],{},"Vue 3.5 没有破坏性变化，它带来的能力大多能直接被业务开发感知：",[215,216,217,221,227,233,239,242,248],"ul",{},[218,219,220],"li",{},"响应式 Props 解构正式稳定；",[218,222,223,226],{},[24,224,225],{},"useTemplateRef()"," 改善模板引用的组织方式；",[218,228,229,232],{},[24,230,231],{},"onWatcherCleanup()"," 让 watcher 清理逻辑更自然；",[218,234,235,238],{},[24,236,237],{},"useId()"," 提供 SSR 友好的稳定 ID；",[218,240,241],{},"异步组件支持懒激活（Lazy Hydration）；",[218,243,244,247],{},[24,245,246],{},"data-allow-mismatch"," 可以声明允许的 hydration 差异；",[218,249,250],{},"Deferred Teleport 与自定义元素能力得到增强。",[12,252,253],{},"3.6 当然也包含大量修复，但它最值得关注的两件事——alien-signals 与 Vapor——都位于框架的底层。你可能不会在业务代码里多写一个新 API，却会面对新的性能特征、编译产物和运行时边界。",[12,255,256,257],{},"这也决定了升级思路：",[16,258,259],{},"不要只检查“代码能不能编译”，还要检查响应式行为、SSR hydration、组件互操作和真实性能。",[49,261,263],{"id":262},"变化一响应式内核切换到-alien-signals","变化一：响应式内核切换到 alien-signals",[12,265,266,267,270],{},"Vue 3.6 对 ",[24,268,269],{},"@vue\u002Freactivity"," 做了大规模重构，其基础来自 StackBlitz 开源的 alien-signals。",[12,272,273],{},"对于绝大多数业务开发者，下面这些代码仍然是熟悉的 Vue：",[275,276,281],"pre",{"className":277,"code":278,"language":279,"meta":280,"style":280},"language-ts shiki shiki-themes github-dark github-dark","import { computed, ref, watchEffect } from 'vue'\n\nconst price = ref(99)\nconst count = ref(2)\nconst total = computed(() => price.value * count.value)\n\nwatchEffect(() => {\n  console.log(total.value)\n})\n","ts","",[24,282,283,303,310,336,355,383,388,401,413],{"__ignoreMap":280},[284,285,288,292,296,299],"span",{"class":286,"line":287},"line",1,[284,289,291],{"class":290},"sOPea","import",[284,293,295],{"class":294},"suv1-"," { computed, ref, watchEffect } ",[284,297,298],{"class":290},"from",[284,300,302],{"class":301},"s4wv1"," 'vue'\n",[284,304,306],{"class":286,"line":305},2,[284,307,309],{"emptyLinePlaceholder":308},true,"\n",[284,311,313,316,320,323,327,330,333],{"class":286,"line":312},3,[284,314,315],{"class":290},"const",[284,317,319],{"class":318},"s8ozJ"," price",[284,321,322],{"class":290}," =",[284,324,326],{"class":325},"sFR8T"," ref",[284,328,329],{"class":294},"(",[284,331,332],{"class":318},"99",[284,334,335],{"class":294},")\n",[284,337,339,341,344,346,348,350,353],{"class":286,"line":338},4,[284,340,315],{"class":290},[284,342,343],{"class":318}," count",[284,345,322],{"class":290},[284,347,326],{"class":325},[284,349,329],{"class":294},[284,351,352],{"class":318},"2",[284,354,335],{"class":294},[284,356,358,360,363,365,368,371,374,377,380],{"class":286,"line":357},5,[284,359,315],{"class":290},[284,361,362],{"class":318}," total",[284,364,322],{"class":290},[284,366,367],{"class":325}," computed",[284,369,370],{"class":294},"(() ",[284,372,373],{"class":290},"=>",[284,375,376],{"class":294}," price.value ",[284,378,379],{"class":290},"*",[284,381,382],{"class":294}," count.value)\n",[284,384,386],{"class":286,"line":385},6,[284,387,309],{"emptyLinePlaceholder":308},[284,389,391,394,396,398],{"class":286,"line":390},7,[284,392,393],{"class":325},"watchEffect",[284,395,370],{"class":294},[284,397,373],{"class":290},[284,399,400],{"class":294}," {\n",[284,402,404,407,410],{"class":286,"line":403},8,[284,405,406],{"class":294},"  console.",[284,408,409],{"class":325},"log",[284,411,412],{"class":294},"(total.value)\n",[284,414,416],{"class":286,"line":415},9,[284,417,418],{"class":294},"})\n",[12,420,421],{},"变化主要发生在内部：依赖如何建立、状态如何标记、计算属性何时失效、effect 如何沿依赖图传播，以及这些关系如何被清理和复用。",[12,423,424],{},[425,426],"img",{"alt":427,"src":428},"由活跃与休眠路径组成的细粒度响应式依赖网络","\u002Fmedia\u002Fposts\u002Fvue-3-6-rc-upgrade-guide\u002Freactivity-core-v1.webp",[12,430,431],{},[432,433,434],"em",{},"这张图表达的是依赖传播的概念，不是 Vue 内部数据结构的逐字段示意。",[436,437,438],"h3",{"id":438},"性能数字应该怎样理解",[12,440,441],{},"Vue 核心仓库的 alien-signals 合并 PR 给出过两类微基准结果：",[215,443,444,452],{},[218,445,446,447,27,449,451],{},"在大量 ",[24,448,26],{},[24,450,30],{}," 和 effect 实例的构造场景中，示例内存从约 2.3 MB 降至约 2.0 MB，约减少 13%；",[218,453,454],{},"在“一个源变化后，大量 computed 被读取”的特定压力场景中，性能提升可以超过 30 倍。",[12,456,457,458],{},"第二个数字很亮眼，但不能直接翻译成“Vue 3.6 应用快 30 倍”。它证明的是：",[16,459,460],{},"新的依赖传播算法在某些过去成本会随规模显著上升的图结构中，复杂度表现更好。",[12,462,463],{},"真实页面还包含 DOM、布局、网络、组件库、序列化和业务计算。升级前后的判断应基于自己项目的 flame chart、交互延迟、内存曲线和打包结果，而不是套用框架微基准。",[436,465,466],{"id":466},"哪些项目最可能感受到收益",[215,468,469,472,475,478,481],{},[218,470,471],{},"大量 computed 相互依赖的数据看板；",[218,473,474],{},"高频修改、筛选和派生状态很多的编辑器；",[218,476,477],{},"长时间运行、创建和销毁大量 effect 的工作台；",[218,479,480],{},"表单字段多、验证规则和联动关系复杂的应用；",[218,482,483],{},"基于 Vue 响应式原语构建的状态管理或工具库。",[12,485,486],{},"普通内容页也能得到内部改进，但用户未必会感知到明显差异。",[436,488,489],{"id":489},"谁需要格外谨慎",[12,491,492,493,27,495,27,498,27,500,502,503,506],{},"如果代码只使用公开的 ",[24,494,26],{},[24,496,497],{},"reactive",[24,499,30],{},[24,501,33],{}," 与 ",[24,504,505],{},"effectScope","，迁移风险相对可控。真正需要警惕的是：",[215,508,509,512,515,518,521],{},[218,510,511],{},"直接读取或修改 Vue 响应式内部字段；",[218,513,514],{},"依赖未公开的 effect、Dep 或 scheduler 实现细节；",[218,516,517],{},"自行 monkey patch 响应式函数；",[218,519,520],{},"对 watcher 执行顺序、同步刷新和清理时机有隐含假设；",[218,522,523],{},"库代码同时支持多个 Vue 次版本，却没有覆盖这些边界测试。",[12,525,526],{},"内部重构的意义，正是 Vue 可以在保持公开 API 的同时更换实现。反过来说，依赖内部实现的代码也最容易在这类升级中暴露问题。",[49,528,530],{"id":529},"变化二vapor-mode-不再只是实验概念","变化二：Vapor Mode 不再只是实验概念",[12,532,533],{},"Vapor Mode 是 Vue SFC 的一种新编译模式。传统 Vue 模板会生成 VNode，由 Virtual DOM runtime 对比前后两棵树，再把差异更新到真实 DOM；Vapor 则让编译器提前识别动态部分，生成更直接、粒度更细的更新逻辑。",[12,535,536],{},"它的目标有两个：",[538,539,540,543],"ol",{},[218,541,542],{},"降低应用的基础包体积；",[218,544,545],{},"减少运行时创建、遍历和比较 VNode 的成本。",[12,547,548],{},"官方发布说明称，Vapor 已在第三方 js-framework-benchmark 中达到与 Solid、Svelte 5 相近的级别。这个结论同样应该按“基准场景”理解，而不是把不同框架、不同应用的真实性能压缩成一条排名。",[436,550,552],{"id":551},"vapor-不会在升级后自动开启","Vapor 不会在升级后自动开启",[12,554,555],{},"Vue 3.6 依然默认使用 VDOM。要让一个 SFC 使用 Vapor，需要显式标记：",[275,557,561],{"className":558,"code":559,"language":560,"meta":280,"style":280},"language-vue shiki shiki-themes github-dark github-dark","\u003Cscript setup vapor lang=\"ts\">\nimport { ref } from 'vue'\n\nconst count = ref(0)\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n  \u003Cbutton @click=\"count++\">\n    Count: {{ count }}\n  \u003C\u002Fbutton>\n\u003C\u002Ftemplate>\n","vue",[24,562,563,590,601,605,622,631,635,644,662,667,677],{"__ignoreMap":280},[284,564,565,568,572,575,578,581,584,587],{"class":286,"line":287},[284,566,567],{"class":294},"\u003C",[284,569,571],{"class":570},"sxg3X","script",[284,573,574],{"class":325}," setup",[284,576,577],{"class":325}," vapor",[284,579,580],{"class":325}," lang",[284,582,583],{"class":294},"=",[284,585,586],{"class":301},"\"ts\"",[284,588,589],{"class":294},">\n",[284,591,592,594,597,599],{"class":286,"line":305},[284,593,291],{"class":290},[284,595,596],{"class":294}," { ref } ",[284,598,298],{"class":290},[284,600,302],{"class":301},[284,602,603],{"class":286,"line":312},[284,604,309],{"emptyLinePlaceholder":308},[284,606,607,609,611,613,615,617,620],{"class":286,"line":338},[284,608,315],{"class":290},[284,610,343],{"class":318},[284,612,322],{"class":290},[284,614,326],{"class":325},[284,616,329],{"class":294},[284,618,619],{"class":318},"0",[284,621,335],{"class":294},[284,623,624,627,629],{"class":286,"line":357},[284,625,626],{"class":294},"\u003C\u002F",[284,628,571],{"class":570},[284,630,589],{"class":294},[284,632,633],{"class":286,"line":385},[284,634,309],{"emptyLinePlaceholder":308},[284,636,637,639,642],{"class":286,"line":390},[284,638,567],{"class":294},[284,640,641],{"class":570},"template",[284,643,589],{"class":294},[284,645,646,649,652,655,657,660],{"class":286,"line":403},[284,647,648],{"class":294},"  \u003C",[284,650,651],{"class":570},"button",[284,653,654],{"class":325}," @click",[284,656,583],{"class":294},[284,658,659],{"class":301},"\"count++\"",[284,661,589],{"class":294},[284,663,664],{"class":286,"line":415},[284,665,666],{"class":294},"    Count: {{ count }}\n",[284,668,670,673,675],{"class":286,"line":669},10,[284,671,672],{"class":294},"  \u003C\u002F",[284,674,651],{"class":570},[284,676,589],{"class":294},[284,678,680,682,684],{"class":286,"line":679},11,[284,681,626],{"class":294},[284,683,641],{"class":570},[284,685,589],{"class":294},[12,687,688],{},"官方还支持两个等价入口：",[275,690,692],{"className":558,"code":691,"language":560,"meta":280,"style":280},"\u003Cscript vapor>\n\u002F\u002F \u003Cscript setup vapor> 的简写\n\u003C\u002Fscript>\n",[24,693,694,704,710],{"__ignoreMap":280},[284,695,696,698,700,702],{"class":286,"line":287},[284,697,567],{"class":294},[284,699,571],{"class":570},[284,701,577],{"class":325},[284,703,589],{"class":294},[284,705,706],{"class":286,"line":305},[284,707,709],{"class":708},"sJ8bj","\u002F\u002F \u003Cscript setup vapor> 的简写\n",[284,711,712,714,716],{"class":286,"line":312},[284,713,626],{"class":294},[284,715,571],{"class":570},[284,717,589],{"class":294},[275,719,721],{"className":558,"code":720,"language":560,"meta":280,"style":280},"\u003Ctemplate vapor>\n  \u003C!-- 整个 SFC 使用 Vapor 编译 -->\n\u003C\u002Ftemplate>\n",[24,722,723,733,738],{"__ignoreMap":280},[284,724,725,727,729,731],{"class":286,"line":287},[284,726,567],{"class":294},[284,728,641],{"class":570},[284,730,577],{"class":325},[284,732,589],{"class":294},[284,734,735],{"class":286,"line":305},[284,736,737],{"class":708},"  \u003C!-- 整个 SFC 使用 Vapor 编译 -->\n",[284,739,740,742,744],{"class":286,"line":312},[284,741,626],{"class":294},[284,743,641],{"class":570},[284,745,589],{"class":294},[12,747,748],{},"也就是说，团队可以升级到 3.6 后继续运行现有 VDOM 应用，再有选择地迁移某个组件或页面。",[436,750,752],{"id":751},"纯-vapor-应用","纯 Vapor 应用",[12,754,755],{},"如果应用完全由 Vapor 组件组成，可以使用：",[275,757,759],{"className":277,"code":758,"language":279,"meta":280,"style":280},"import { createVaporApp } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateVaporApp(App).mount('#app')\n",[24,760,761,772,784,788],{"__ignoreMap":280},[284,762,763,765,768,770],{"class":286,"line":287},[284,764,291],{"class":290},[284,766,767],{"class":294}," { createVaporApp } ",[284,769,298],{"class":290},[284,771,302],{"class":301},[284,773,774,776,779,781],{"class":286,"line":305},[284,775,291],{"class":290},[284,777,778],{"class":294}," App ",[284,780,298],{"class":290},[284,782,783],{"class":301}," '.\u002FApp.vue'\n",[284,785,786],{"class":286,"line":312},[284,787,309],{"emptyLinePlaceholder":308},[284,789,790,793,796,799,801,804],{"class":286,"line":338},[284,791,792],{"class":325},"createVaporApp",[284,794,795],{"class":294},"(App).",[284,797,798],{"class":325},"mount",[284,800,329],{"class":294},[284,802,803],{"class":301},"'#app'",[284,805,335],{"class":294},[12,807,808],{},"这种模式不需要引入 Virtual DOM runtime，最容易获得基础包体积收益。它更适合小型新应用、交互小工具，或可以明确约束技术栈的独立页面。",[436,810,812],{"id":811},"在现有-vdom-应用中使用-vapor","在现有 VDOM 应用中使用 Vapor",[12,814,815,816,819],{},"现有 ",[24,817,818],{},"createApp()"," 项目需要安装互操作插件：",[275,821,823],{"className":277,"code":822,"language":279,"meta":280,"style":280},"import { createApp, vaporInteropPlugin } from 'vue'\nimport App from '.\u002FApp.vue'\n\ncreateApp(App)\n  .use(vaporInteropPlugin)\n  .mount('#app')\n",[24,824,825,836,846,850,858,869],{"__ignoreMap":280},[284,826,827,829,832,834],{"class":286,"line":287},[284,828,291],{"class":290},[284,830,831],{"class":294}," { createApp, vaporInteropPlugin } ",[284,833,298],{"class":290},[284,835,302],{"class":301},[284,837,838,840,842,844],{"class":286,"line":305},[284,839,291],{"class":290},[284,841,778],{"class":294},[284,843,298],{"class":290},[284,845,783],{"class":301},[284,847,848],{"class":286,"line":312},[284,849,309],{"emptyLinePlaceholder":308},[284,851,852,855],{"class":286,"line":338},[284,853,854],{"class":325},"createApp",[284,856,857],{"class":294},"(App)\n",[284,859,860,863,866],{"class":286,"line":357},[284,861,862],{"class":294},"  .",[284,864,865],{"class":325},"use",[284,867,868],{"class":294},"(vaporInteropPlugin)\n",[284,870,871,873,875,877,879],{"class":286,"line":385},[284,872,862],{"class":294},[284,874,798],{"class":325},[284,876,329],{"class":294},[284,878,803],{"class":301},[284,880,335],{"class":294},[12,882,883],{},"反过来，Vapor 应用也能安装这个插件以容纳 VDOM 组件，但这样会重新带入 VDOM runtime，削弱体积优势。",[12,885,886],{},"Render Function 与 JSX 编写的组件仍然属于 VDOM 组件。在纯 Vapor 应用中使用它们，同样需要互操作。",[12,888,889],{},[425,890],{"alt":891,"src":892},"现有 VDOM 区域通过明确的互操作边界连接到独立 Vapor 区域","\u002Fmedia\u002Fposts\u002Fvue-3-6-rc-upgrade-guide\u002Fvapor-boundaries-v1.webp",[12,894,895,896],{},"我的建议与官方一致：",[16,897,898],{},"把两种渲染模式组织成边界清楚的区域，而不是在每一层组件中来回套娃。",[12,900,901],{},"例如，把一个高频更新的数据画布做成 Vapor 页面或子树，周围的后台导航、弹窗和成熟组件库继续使用 VDOM。这样的收益、故障范围和回退路径都更容易测量。",[49,903,905],{"id":904},"rc2-最容易踩坑的变化事件委托改为显式启用","rc.2 最容易踩坑的变化：事件委托改为显式启用",[12,907,908],{},"如果你在 rc.1 阶段已经试过 Vapor，这一节尤其重要。",[12,910,911,912,915,916,919,920,922],{},"rc.1 的说明中，符合条件的事件会自动委托到 ",[24,913,914],{},"document","。但这与原生 DOM 和标准 Vue 的行为存在差异：如果祖先元素提前调用 ",[24,917,918],{},"stopPropagation()","，事件到不了 ",[24,921,914],{},"，子元素被委托的 handler 也不会执行。",[12,924,925,926,928,929,931],{},"从 ",[16,927,46],{}," 开始，Vapor 默认把 DOM 监听器直接绑定到元素。只有明确希望使用文档级委托时，才添加 Vapor 专用的 ",[24,930,180],{}," 修饰符：",[275,933,935],{"className":558,"code":934,"language":560,"meta":280,"style":280},"\u003Cbutton @click.delegate=\"onClick\">\n  保存\n\u003C\u002Fbutton>\n",[24,936,937,967,972],{"__ignoreMap":280},[284,938,939,941,943,946,949,952,955,957,960,963,965],{"class":286,"line":287},[284,940,567],{"class":294},[284,942,651],{"class":570},[284,944,945],{"class":294}," @",[284,947,948],{"class":325},"click",[284,950,951],{"class":294},".",[284,953,954],{"class":325},"delegate",[284,956,583],{"class":294},[284,958,959],{"class":301},"\"",[284,961,962],{"class":294},"onClick",[284,964,959],{"class":301},[284,966,589],{"class":294},[284,968,969],{"class":286,"line":305},[284,970,971],{"class":294},"  保存\n",[284,973,974,976,978],{"class":286,"line":312},[284,975,626],{"class":294},[284,977,651],{"class":570},[284,979,589],{"class":294},[12,981,982,983,986],{},"同时，旧的 ",[24,984,985],{},"compilerOptions.eventDelegation"," 已被移除。",[12,988,989],{},"迁移时应该做两件事：",[538,991,992,998],{},[218,993,994,995,997],{},"删除已有的 ",[24,996,985],{},"；",[218,999,1000,1001,19],{},"只对数量大、事件稳定，并且传播路径已验证的监听器使用 ",[24,1002,180],{},[12,1004,1005],{},"这不是一个适合全局机械替换的优化。事件是否值得委托，要结合节点数量、生命周期和传播语义判断。",[49,1007,1009],{"id":1008},"vapor-与标准-vue-的兼容边界","Vapor 与标准 Vue 的兼容边界",[12,1011,1012],{},"Vapor 的目标不是在第一天覆盖 Vue 的所有历史能力。官方明确说明，它支持现有 Vue API 的一个子集；依赖 VNode 或组件公开实例代理的功能，目前不能直接使用。",[54,1014,1015,1028],{},[57,1016,1017],{},[60,1018,1019,1022,1025],{},[63,1020,1021],{},"能力",[63,1023,1024],{},"VDOM 组件",[63,1026,1027],{},"Vapor 组件",[76,1029,1030,1041,1051,1062,1078,1089,1101,1113,1140],{},[60,1031,1032,1035,1038],{},[81,1033,1034],{},"Composition API",[81,1036,1037],{},"支持",[81,1039,1040],{},"主要编程方式",[60,1042,1043,1046,1048],{},[81,1044,1045],{},"Options API",[81,1047,1037],{},[81,1049,1050],{},"不支持",[60,1052,1053,1058,1060],{},[81,1054,1055],{},[24,1056,1057],{},"app.config.globalProperties",[81,1059,1037],{},[81,1061,1050],{},[60,1063,1064,1069,1072],{},[81,1065,1066],{},[24,1067,1068],{},"getCurrentInstance()",[81,1070,1071],{},"返回当前实例",[81,1073,1074,1075],{},"返回 ",[24,1076,1077],{},"null",[60,1079,1080,1083,1086],{},[81,1081,1082],{},"Render Function \u002F JSX",[81,1084,1085],{},"原生 VDOM",[81,1087,1088],{},"仍是 VDOM，混用需要 interop",[60,1090,1091,1096,1098],{},[81,1092,1093],{},[24,1094,1095],{},"v-memo",[81,1097,1037],{},[81,1099,1100],{},"不适用 \u002F 不支持",[60,1102,1103,1109,1111],{},[81,1104,1105,1108],{},[24,1106,1107],{},"@vue:xxx"," 元素生命周期事件",[81,1110,1037],{},[81,1112,1050],{},[60,1114,1115,1118,1121],{},[81,1116,1117],{},"组件模板引用",[81,1119,1120],{},"暴露公开实例能力",[81,1122,1123,1124,27,1127,27,1130,27,1133,27,1136,1139],{},"不暴露 ",[24,1125,1126],{},"$el",[24,1128,1129],{},"$props",[24,1131,1132],{},"$attrs",[24,1134,1135],{},"$slots",[24,1137,1138],{},"$refs"," 等属性",[60,1141,1142,1145,1148],{},[81,1143,1144],{},"自定义指令",[81,1146,1147],{},"标准指令生命周期对象",[81,1149,1150],{},"使用 Vapor 专用函数接口",[436,1152,1154,1157],{"id":1153},"slotsdefault-不能再当作无副作用探测",[24,1155,1156],{},"slots.default()"," 不能再当作无副作用探测",[12,1159,1160,1161,1163],{},"在 VDOM 世界中，一些组件会先调用 ",[24,1162,1156],{},"，检查返回内容，再决定渲染 fallback。Vapor 中调用 slot 可能直接创建 Block 和 DOM、注册响应式 effect，或在 hydration 时认领已有 SSR DOM。",[12,1165,1166],{},"因此不要这样写：",[275,1168,1170],{"className":558,"code":1169,"language":560,"meta":280,"style":280},"\u003Cscript setup vapor>\nimport { useSlots } from 'vue'\n\nconst slots = useSlots()\nconst content = slots.default?.()\nconst showFallback = !content\n\u003C\u002Fscript>\n",[24,1171,1172,1184,1195,1199,1214,1232,1247],{"__ignoreMap":280},[284,1173,1174,1176,1178,1180,1182],{"class":286,"line":287},[284,1175,567],{"class":294},[284,1177,571],{"class":570},[284,1179,574],{"class":325},[284,1181,577],{"class":325},[284,1183,589],{"class":294},[284,1185,1186,1188,1191,1193],{"class":286,"line":305},[284,1187,291],{"class":290},[284,1189,1190],{"class":294}," { useSlots } ",[284,1192,298],{"class":290},[284,1194,302],{"class":301},[284,1196,1197],{"class":286,"line":312},[284,1198,309],{"emptyLinePlaceholder":308},[284,1200,1201,1203,1206,1208,1211],{"class":286,"line":338},[284,1202,315],{"class":290},[284,1204,1205],{"class":318}," slots",[284,1207,322],{"class":290},[284,1209,1210],{"class":325}," useSlots",[284,1212,1213],{"class":294},"()\n",[284,1215,1216,1218,1221,1223,1226,1229],{"class":286,"line":357},[284,1217,315],{"class":290},[284,1219,1220],{"class":318}," content",[284,1222,322],{"class":290},[284,1224,1225],{"class":294}," slots.",[284,1227,1228],{"class":325},"default",[284,1230,1231],{"class":294},"?.()\n",[284,1233,1234,1236,1239,1241,1244],{"class":286,"line":385},[284,1235,315],{"class":290},[284,1237,1238],{"class":318}," showFallback",[284,1240,322],{"class":290},[284,1242,1243],{"class":290}," !",[284,1245,1246],{"class":294},"content\n",[284,1248,1249,1251,1253],{"class":286,"line":390},[284,1250,626],{"class":294},[284,1252,571],{"class":570},[284,1254,589],{"class":294},[12,1256,1257],{},"让模板负责 slot 的实际渲染，通常更安全：",[275,1259,1261],{"className":558,"code":1260,"language":560,"meta":280,"style":280},"\u003Ctemplate>\n  \u003Cslot \u002F>\n\u003C\u002Ftemplate>\n",[24,1262,1263,1271,1284],{"__ignoreMap":280},[284,1264,1265,1267,1269],{"class":286,"line":287},[284,1266,567],{"class":294},[284,1268,641],{"class":570},[284,1270,589],{"class":294},[284,1272,1273,1275,1278,1282],{"class":286,"line":305},[284,1274,648],{"class":294},[284,1276,1277],{"class":570},"slot",[284,1279,1281],{"class":1280},"sX7ps"," \u002F",[284,1283,589],{"class":294},[284,1285,1286,1288,1290],{"class":286,"line":312},[284,1287,626],{"class":294},[284,1289,641],{"class":570},[284,1291,589],{"class":294},[436,1293,1294],{"id":1294},"自定义指令需要单独迁移",[12,1296,1297,1298,27,1301,27,1304,1307],{},"Vapor 自定义指令接收节点和响应式 getter，并且可以返回 cleanup 函数。它不是把 VDOM 指令的 ",[24,1299,1300],{},"mounted",[24,1302,1303],{},"updated",[24,1305,1306],{},"unmounted"," 对象原样搬过去。",[12,1309,1310],{},"如果项目或组件库依赖大量自定义指令，应该把它们列成独立迁移项，逐个验证响应式更新、清理和 SSR 行为。",[49,1312,1314],{"id":1313},"nuxtrouterpinia-和组件库应该怎么看","Nuxt、Router、Pinia 和组件库应该怎么看",[12,1316,1317],{},"Vue 核心可用，不代表上层框架已经自动完成 Vapor 集成。",[12,1319,1320],{},"Vue 官方的 Vapor Roadmap 单独列出了 Router、Pinia、Nuxt、DevTools 和 Vue Test Utils 等生态工作，其中 Nuxt 还依赖 SSR hydration 能力。对真实项目来说，编译器能否识别 Vapor、服务端产物如何生成、客户端如何 hydration、开发工具怎样检查组件，都是完整链路的一部分。",[12,1322,1323],{},"因此我会把项目分成两类：",[436,1325,1327],{"id":1326},"普通-vue-vite-应用","普通 Vue \u002F Vite 应用",[12,1329,1330],{},"可以在分支中升级 3.6 RC，先验证现有 VDOM 模式，再挑一个叶子页面试用 Vapor。依赖关系比较直接，问题也更容易归因。",[436,1332,1334],{"id":1333},"nuxtssr-或重度组件库应用","Nuxt、SSR 或重度组件库应用",[12,1336,1337],{},"不要仅通过 package override 强行把底层 Vue 换成 RC，就把它视为完成 Vapor 升级。应该先确认当前框架、构建插件和 SSR 链路明确支持对应版本，再进行 hydration、路由切换、异步组件和组件库互操作测试。",[12,1339,1340],{},"即使暂时不使用 Vapor，测试 Vue 3.6 的新响应式内核仍然有价值；只是这类试验最好留在独立分支或测试环境。",[49,1342,1343],{"id":1343},"一套更稳妥的升级路线",[436,1345,1347],{"id":1346},"第一阶段只升级依赖不启用-vapor","第一阶段：只升级依赖，不启用 Vapor",[12,1349,1350],{},"普通 Vue \u002F Vite 项目可以在实验分支中安装 RC：",[275,1352,1356],{"className":1353,"code":1354,"language":1355,"meta":280,"style":280},"language-bash shiki shiki-themes github-dark github-dark","pnpm add vue@3.6.0-rc.2\n","bash",[24,1357,1358],{"__ignoreMap":280},[284,1359,1360,1363,1366],{"class":286,"line":287},[284,1361,1362],{"class":325},"pnpm",[284,1364,1365],{"class":301}," add",[284,1367,1368],{"class":301}," vue@3.6.0-rc.2\n",[12,1370,1371,1372,1375],{},"如果项目直接依赖 ",[24,1373,1374],{},"@vue\u002Fserver-renderer","，让它与 Vue 保持同一版本：",[275,1377,1379],{"className":1353,"code":1378,"language":1355,"meta":280,"style":280},"pnpm add vue@3.6.0-rc.2 @vue\u002Fserver-renderer@3.6.0-rc.2\n",[24,1380,1381],{"__ignoreMap":280},[284,1382,1383,1385,1387,1390],{"class":286,"line":287},[284,1384,1362],{"class":325},[284,1386,1365],{"class":301},[284,1388,1389],{"class":301}," vue@3.6.0-rc.2",[284,1391,1392],{"class":301}," @vue\u002Fserver-renderer@3.6.0-rc.2\n",[12,1394,1395],{},"然后先验证原有 VDOM 应用：",[215,1397,1398,1401,1404,1407,1410,1413,1416,1419],{},[218,1399,1400],{},"单元测试与组件测试；",[218,1402,1403],{},"首屏 SSR 与客户端 hydration；",[218,1405,1406],{},"watch、computed、effectScope 的复杂场景；",[218,1408,1409],{},"快速路由切换后的副作用清理；",[218,1411,1412],{},"自定义指令、Teleport、Suspense 和异步组件；",[218,1414,1415],{},"开发与生产构建；",[218,1417,1418],{},"浏览器控制台 warning 和错误；",[218,1420,1421],{},"关键交互的耗时与内存基线。",[12,1423,1424],{},"如果这一阶段失败，问题大概率来自新响应式实现或 RC 回归，而不是 Vapor，定位会简单得多。",[436,1426,1428],{"id":1427},"第二阶段选择一个边界清楚的区域","第二阶段：选择一个边界清楚的区域",[12,1430,1431],{},"合适的试点通常同时满足：",[215,1433,1434,1437,1440,1443,1446,1449],{},[218,1435,1436],{},"使用 Composition API；",[218,1438,1439],{},"很少依赖组件实例；",[218,1441,1442],{},"不依赖复杂的 VDOM 组件库；",[218,1444,1445],{},"更新频率高，确实存在性能压力；",[218,1447,1448],{},"有独立路由或明确的组件树边界；",[218,1450,1451],{},"有可重复的性能测试。",[12,1453,1454],{},"先记录迁移前的 JavaScript 体积、交互延迟、CPU 时间和内存，再启用 Vapor。没有基线，就很容易把“技术上成功运行”误判成“值得迁移”。",[436,1456,1458],{"id":1457},"第三阶段验证互操作与-ssr","第三阶段：验证互操作与 SSR",[12,1460,1461],{},"至少覆盖这些组合：",[538,1463,1464,1467,1470,1473,1476,1479,1482],{},[218,1465,1466],{},"VDOM 父组件嵌套 Vapor 子组件；",[218,1468,1469],{},"Vapor 父组件嵌套 VDOM 子组件；",[218,1471,1472],{},"props、emit 与双向绑定；",[218,1474,1475],{},"默认 slot、具名 slot 与作用域 slot；",[218,1477,1478],{},"Suspense、异步组件与错误边界；",[218,1480,1481],{},"服务端输出、hydration 和 mismatch 恢复；",[218,1483,1484],{},"组件卸载后的 watcher、事件与指令 cleanup。",[12,1486,1487],{},"rc.2 的修复列表中有相当多内容正好集中在 hydration、Suspense、slot anchor 和 VDOM interop。这说明这些区域正在快速收敛，也说明它们是当前最值得测试的地方。",[436,1489,1491],{"id":1490},"第四阶段等稳定版再决定生产节奏","第四阶段：等稳定版再决定生产节奏",[12,1493,1494],{},"RC 的意义是功能集已经接近确定，接下来集中发现回归和边缘问题；它不等于生态已经完全稳定。",[12,1496,1497],{},"生产项目至少应该同时满足：",[215,1499,1500,1503,1506,1509,1512,1515],{},[218,1501,1502],{},"Vue 3.6 正式版已发布；",[218,1504,1505],{},"使用的框架和构建工具明确支持；",[218,1507,1508],{},"核心组件库通过互操作验证；",[218,1510,1511],{},"DevTools、测试工具和 SSR 链路可用；",[218,1513,1514],{},"性能收益足以覆盖迁移与维护成本；",[218,1516,1517],{},"有随时退回 VDOM 的方案。",[49,1519,1520],{"id":1520},"不建议做的四件事",[436,1522,1524],{"id":1523},"_1-不要把-rc-当成普通补丁版","1. 不要把 RC 当成普通补丁版",[12,1526,1527],{},"预发布阶段的行为仍可能调整。rc.1 到 rc.2 的事件委托变化，就是最直接的例子。",[436,1529,1531],{"id":1530},"_2-不要为了用上-vapor迁移全部组件","2. 不要为了“用上 Vapor”迁移全部组件",[12,1533,1534],{},"导航、静态内容、低频设置页未必能获得有意义的收益。优先迁移性能瓶颈，而不是追求迁移比例。",[436,1536,1538],{"id":1537},"_3-不要把微基准数字当作应用承诺","3. 不要把微基准数字当作应用承诺",[12,1540,1541],{},"“超过 30 倍”来自特定响应式压力场景；“与 Solid、Svelte 5 同级”来自第三方基准。它们适合证明技术方向，不适合替代项目测量。",[436,1543,1545],{"id":1544},"_4-不要忽略生态边界","4. 不要忽略生态边界",[12,1547,1548],{},"Vue 核心、SFC 编译器、Vite 插件、Nuxt、SSR、Router、Pinia、组件库、DevTools 和测试工具共同组成开发体验。只让其中一个包进入 RC 版本，不代表整条链路已经升级完毕。",[49,1550,1551],{"id":1551},"我的项目类型建议",[54,1553,1554,1567],{},[57,1555,1556],{},[60,1557,1558,1561,1564],{},[63,1559,1560],{},"项目类型",[63,1562,1563],{},"现在是否值得试",[63,1565,1566],{},"建议",[76,1568,1569,1580,1591,1602,1613,1624],{},[60,1570,1571,1574,1577],{},[81,1572,1573],{},"小型新工具、内部实验",[81,1575,1576],{},"值得",[81,1578,1579],{},"可以尝试纯 Vapor，重点测包体积与交互",[60,1581,1582,1585,1588],{},[81,1583,1584],{},"已有 Vue SPA",[81,1586,1587],{},"值得在分支试",[81,1589,1590],{},"先保持 VDOM，再迁移单个性能敏感页面",[60,1592,1593,1596,1599],{},[81,1594,1595],{},"Nuxt \u002F SSR 站点",[81,1597,1598],{},"适合研究，生产宜等待",[81,1600,1601],{},"先确认框架支持，重点测 hydration",[60,1603,1604,1607,1610],{},[81,1605,1606],{},"组件库",[81,1608,1609],{},"应尽早做兼容测试",[81,1611,1612],{},"检查实例代理、slots、自定义指令和 interop",[60,1614,1615,1618,1621],{},[81,1616,1617],{},"大型数据看板 \u002F 编辑器",[81,1619,1620],{},"很值得压测",[81,1622,1623],{},"单独比较 alien-signals 与 Vapor 各自带来的收益",[60,1625,1626,1629,1632],{},[81,1627,1628],{},"内容型官网",[81,1630,1631],{},"优先级较低",[81,1633,1634],{},"等稳定版和生态成熟，避免为有限收益增加复杂度",[49,1636,1637],{"id":1637},"最后的判断",[12,1639,1640],{},"Vue 3.6 RC 最有价值的地方，不是提供了多少个可以立刻写进业务的新 API，而是同时推进了两个长期问题：",[215,1642,1643,1646],{},[218,1644,1645],{},"Vue 的响应式系统能否在更复杂的依赖图中保持高效；",[218,1647,1648],{},"Vue 的模板能否在保留熟悉开发体验的同时，摆脱必须依赖虚拟 DOM 的成本。",[12,1650,1651],{},"alien-signals 回答第一个问题，Vapor 尝试回答第二个问题。",[12,1653,1654,1655,1658],{},"对于现有项目，我认为最合理的策略是：",[16,1656,1657],{},"先把 3.6 当作响应式内核升级来测试，再把 Vapor 当作可选的局部编译模式来评估。"," 两件事分开验证，才能知道性能变化来自哪里，也能把风险控制在清晰的边界内。",[12,1660,1661],{},"Vue 3.6 还没有到“所有人今天就应该升级”的阶段，但它已经到了“值得所有 Vue 团队开始理解”的阶段。",[1663,1664],"hr",{},[49,1666,1667],{"id":1667},"资料说明",[215,1669,1670,1680,1688,1696,1704,1712],{},[218,1671,1672,1679],{},[1673,1674,1678],"a",{"href":1675,"rel":1676},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Fblob\u002Fminor\u002FCHANGELOG.md",[1677],"nofollow","Vue 3.6 minor 分支 Changelog","：3.6.0-rc.1 的 Vapor 总览、兼容边界，以及 rc.2 的事件委托变更。",[218,1681,1682,1687],{},[1673,1683,1686],{"href":1684,"rel":1685},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Freleases\u002Ftag\u002Fv3.6.0-rc.2",[1677],"Vue 3.6.0-rc.2 Release","：本文发布时的最新候选版本。",[218,1689,1690,1695],{},[1673,1691,1694],{"href":1692,"rel":1693},"https:\u002F\u002Fblog.vuejs.org\u002Fposts\u002Fvue-3-5",[1677],"Vue 3.5 发布说明","：Vue 3.5 的响应式优化、SSR 与开发者 API 基线。",[218,1697,1698,1703],{},[1673,1699,1702],{"href":1700,"rel":1701},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Fpull\u002F12349",[1677],"Vue core：alien-signals 重构 PR","：响应式实现、内存与特定压力场景微基准。",[218,1705,1706,1711],{},[1673,1707,1710],{"href":1708,"rel":1709},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Fissues\u002F13687",[1677],"Vapor Mode Roadmap","：核心能力与 Vue 生态集成路线。",[218,1713,1714,1719],{},[1673,1715,1718],{"href":1716,"rel":1717},"https:\u002F\u002Fgithub.com\u002Fkrausest\u002Fjs-framework-benchmark",[1677],"js-framework-benchmark","：Vue 官方发布说明引用的第三方前端框架基准。",[39,1721,1722],{},[12,1723,1724],{},"本文中的升级建议是我基于官方资料和工程经验做出的判断，文章由 AI 协助资料整理与语言校对；具体项目仍应以自身测试结果为准。",[1726,1727,1728],"style",{},"html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sxg3X, html code.shiki .sxg3X{--shiki-default:#85E89D;--shiki-dark:#85E89D}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sX7ps, html code.shiki .sX7ps{--shiki-default:#FDAEB7;--shiki-default-font-style:italic;--shiki-dark:#FDAEB7;--shiki-dark-font-style:italic}",{"title":280,"searchDepth":312,"depth":312,"links":1730},[1731,1732,1733,1738,1743,1744,1749,1753,1759,1765,1766,1767],{"id":51,"depth":305,"text":52},{"id":209,"depth":305,"text":210},{"id":262,"depth":305,"text":263,"children":1734},[1735,1736,1737],{"id":438,"depth":312,"text":438},{"id":466,"depth":312,"text":466},{"id":489,"depth":312,"text":489},{"id":529,"depth":305,"text":530,"children":1739},[1740,1741,1742],{"id":551,"depth":312,"text":552},{"id":751,"depth":312,"text":752},{"id":811,"depth":312,"text":812},{"id":904,"depth":305,"text":905},{"id":1008,"depth":305,"text":1009,"children":1745},[1746,1748],{"id":1153,"depth":312,"text":1747},"slots.default() 不能再当作无副作用探测",{"id":1294,"depth":312,"text":1294},{"id":1313,"depth":305,"text":1314,"children":1750},[1751,1752],{"id":1326,"depth":312,"text":1327},{"id":1333,"depth":312,"text":1334},{"id":1343,"depth":305,"text":1343,"children":1754},[1755,1756,1757,1758],{"id":1346,"depth":312,"text":1347},{"id":1427,"depth":312,"text":1428},{"id":1457,"depth":312,"text":1458},{"id":1490,"depth":312,"text":1491},{"id":1520,"depth":305,"text":1520,"children":1760},[1761,1762,1763,1764],{"id":1523,"depth":312,"text":1524},{"id":1530,"depth":312,"text":1531},{"id":1537,"depth":312,"text":1538},{"id":1544,"depth":312,"text":1545},{"id":1551,"depth":305,"text":1551},{"id":1637,"depth":305,"text":1637},{"id":1667,"depth":305,"text":1667},"截至 Vue 3.6.0-rc.2，系统对比 Vue 3.5 与 3.6 的响应式内核、Vapor Mode、事件委托、互操作边界和渐进升级策略。",false,null,"md",{"src":1773,"alt":1774,"caption":1775,"width":1776,"height":1777},"\u002Fmedia\u002Fposts\u002Fvue-3-6-rc-upgrade-guide\u002Fcover-v1.webp","Vue 3.5 的虚拟 DOM 组件树经过晶体通道转向 Vue 3.6 的细粒度信号网络","Vue 3.6 RC 更像一次编译器与运行时的架构换挡，而不是一份新 API 清单",1600,900,{},"\u002Fblog\u002Fvue-3-6-rc-upgrade-guide","2026-07-26",18,{"title":6,"description":1768},"blog\u002Fvue-3-6-rc-upgrade-guide",[1785,1786,1787],"Vue","JavaScript","前端工程","90kB3giNZBguJRcl7lFnw6zSSsK7k7YgTuWATo_cMIQ",{"id":1790,"title":1791,"accent":1792,"body":1793,"description":2027,"draft":1769,"experience":2028,"extension":1771,"featured":308,"image":2033,"meta":2037,"navigation":308,"path":2038,"publishedAt":2039,"readingTime":403,"seo":2040,"stem":2041,"tags":2042,"updatedAt":1770,"__hash__":2046},"posts\u002Fblog\u002Ffrom-gpt-4-invite-to-agents.md","从一封 GPT‑4 邀请邮件，到智能体时代","violet",{"type":9,"value":1794,"toc":2019},[1795,1798,1801,1806,1813,1819,1822,1825,1829,1832,1835,1841,1844,1847,1850,1853,1867,1870,1887,1893,1896,1899,1903,1906,1909,1923,1926,1940,1943,1947,1950,1953,1970,1973,1977,1980,1983,1986,1989,1991,1993],[12,1796,1797],{},"今天整理旧邮件时，我重新翻到了一封 2023 年 3 月 18 日的邮件。",[12,1799,1800],{},"邮件的标题很直接：",[39,1802,1803],{},[12,1804,1805],{},"You’re invited to use the OpenAI GPT-4 API.",[12,1807,1808,1809,1812],{},"正文告诉我，已经可以通过 API 使用拥有 ",[16,1810,1811],{},"8K context"," 的 GPT‑4。",[12,1814,1815],{},[425,1816],{"alt":1817,"src":1818},"2023 年 GPT‑4 API 邀请邮件，正文说明可使用 8K context","\u002Fmedia\u002Fposts\u002Ffrom-gpt-4-invite-to-agents\u002Femail-invite-v1.webp",[12,1820,1821],{},"那时 GPT‑4 才刚发布几天。API 需要申请、等待，再由 OpenAI 逐步邀请开发者。官方发布资料里，标准 GPT‑4 的上下文长度是 8,192 tokens，同时有限开放 32,768-token 版本。对当时的我来说，一次 API 访问资格本身，就像一扇刚刚推开的小门。",[12,1823,1824],{},"三年多以后再看，这封邮件已经不像一封产品通知，更像一枚时间胶囊。",[49,1826,1828],{"id":1827},"数字变大只是最容易看到的变化","数字变大，只是最容易看到的变化",[12,1830,1831],{},"早期开发者熟悉的是 4K、8K 量级的上下文。写提示词时，我们会计算还剩多少空间，主动裁剪资料，把长文拆成小段，再想办法让下一次请求接住上一次的结果。",[12,1833,1834],{},"到了 2026 年，百万级上下文已经进入主流前沿模型。OpenAI 当前模型目录列出的 GPT‑5.6 系列拥有约 1.05M context window；Google 的长上下文文档也说明，多款 Gemini 模型支持 1M 或更多 tokens。",[12,1836,1837],{},[425,1838],{"alt":1839,"src":1840},"从 4K、8K 到 1M 以上，上下文从短纸条延伸为多模态资料长廊","\u002Fmedia\u002Fposts\u002Ffrom-gpt-4-invite-to-agents\u002Fcontext-window-v1.webp",[12,1842,1843],{},"这不只是“能塞进更多文字”。代码、图片、音频、视频和大批文档可以共同成为一次任务的背景。以前必须先切碎、索引和摘要的资料，现在有更多机会被放进同一个工作上下文。",[12,1845,1846],{},"但更长的上下文不等于完美记忆，也不等于模型会自动抓住重点。资料越多，噪声、成本、延迟和错误关联也可能越多。真正重要的能力，仍然是选择什么信息、以什么顺序提供，以及如何验证模型是否理解了关键约束。",[49,1848,1849],{"id":1849},"真正改变的是交互单位",[12,1851,1852],{},"三年前，我最常做的是“提问—回答”：",[538,1854,1855,1858,1861,1864],{},[218,1856,1857],{},"写一段提示词；",[218,1859,1860],{},"等待模型生成；",[218,1862,1863],{},"复制结果；",[218,1865,1866],{},"自己完成剩余工作。",[12,1868,1869],{},"今天，越来越多任务变成了“目标—上下文—行动—验证”：",[538,1871,1872,1875,1878,1881,1884],{},[218,1873,1874],{},"说明最终目标与完成标准；",[218,1876,1877],{},"提供代码库、文档和当前状态；",[218,1879,1880],{},"允许模型调用搜索、文件、终端或其他工具；",[218,1882,1883],{},"让它执行一连串相互依赖的步骤；",[218,1885,1886],{},"用测试、构建、差异检查或人工评审确认结果。",[12,1888,1889],{},[425,1890],{"alt":1891,"src":1892},"聊天框扩展为包含文档、代码、浏览器、工具调用和人工确认的智能体工作流","\u002Fmedia\u002Fposts\u002Ffrom-gpt-4-invite-to-agents\u002Fagent-workflow-v1.webp",[12,1894,1895],{},"OpenAI 当前的模型与 Responses API 文档已经把函数调用、网页搜索、文件搜索和计算机操作列为可用工具。模型不再只告诉我们“可以怎么做”，而是开始在获得授权的范围内参与真实工作过程。",[12,1897,1898],{},"这也是我认为“聊天模型”和“智能体”之间最重要的区别：前者交付一段内容，后者推进一个状态会变化的任务。",[49,1900,1902],{"id":1901},"程序员的工作没有消失而是向两端移动","程序员的工作没有消失，而是向两端移动",[12,1904,1905],{},"当模型可以读代码、改文件、运行命令和检查结果时，程序员并不是突然变得不重要。相反，我们的注意力开始向任务的两端移动。",[12,1907,1908],{},"在行动之前，需要把问题定义得更清楚：",[215,1910,1911,1914,1917,1920],{},[218,1912,1913],{},"目标是什么，什么不在范围内；",[218,1915,1916],{},"哪些资料可信，哪些只是参考；",[218,1918,1919],{},"哪些权限可以授予，哪些操作必须确认；",[218,1921,1922],{},"失败时应该停止、重试，还是回滚。",[12,1924,1925],{},"在行动之后，需要把结果验证得更扎实：",[215,1927,1928,1931,1934,1937],{},[218,1929,1930],{},"代码能否通过测试与构建；",[218,1932,1933],{},"页面是否真的符合预期，而不只是生成成功；",[218,1935,1936],{},"外部状态是否已经改变；",[218,1938,1939],{},"结果能否被解释、复现和审计。",[12,1941,1942],{},"三年前，我们努力学习怎样写出一个更好的提示词。今天，我们还要学习怎样设计上下文、工具、权限、检查点和验收标准。",[49,1944,1946],{"id":1945},"能力可以委托责任不能外包","能力可以委托，责任不能外包",[12,1948,1949],{},"智能体越能执行真实操作，边界就越重要。",[12,1951,1952],{},"让模型总结一篇文章，与让它修改生产数据库，不是同一种授权；让它提出邮件草稿，与让它直接发送给客户，也不是同一种风险。一个成熟的智能体系统，不应该只追求“能做多少”，还应该明确：",[215,1954,1955,1958,1961,1964,1967],{},[218,1956,1957],{},"默认只读还是默认可写；",[218,1959,1960],{},"哪些动作需要人在执行前确认；",[218,1962,1963],{},"如何限制目标、目录、账号和数据范围；",[218,1965,1966],{},"如何保存必要的操作记录；",[218,1968,1969],{},"发生错误时怎样停止并恢复。",[12,1971,1972],{},"模型可以承担越来越多行动，但判断和责任并不会因此消失。最终仍然需要有人回答：为什么要这样做，依据是什么，风险由谁承担，结果是否可以接受。",[49,1974,1976],{"id":1975},"再过三年我们会怎样看今天","再过三年，我们会怎样看今天",[12,1978,1979],{},"这封旧邮件如今很像一张偶然保存下来的车票。",[12,1981,1982],{},"2023 年，我申请的是一次访问模型的机会。2026 年，我们已经在学习如何让模型理解一个长期目标、使用工具、跨越多个步骤，并在人的监督下完成工作。",[12,1984,1985],{},"让我感慨的不是某一个参数增长了多少倍，而是时间被压缩了。许多曾经只存在于想象中的能力，没有在遥远的未来慢慢到来，而是在几次版本更新之间，悄悄变成了日常。",[12,1987,1988],{},"也许再过三年回头看，今天让我们惊叹的百万级上下文和智能体，也会像这封邮件一样，成为未来刚刚开始时留下的一件旧物。",[1663,1990],{},[49,1992,1667],{"id":1667},[215,1994,1995,2003,2011],{},[218,1996,1997,2002],{},[1673,1998,2001],{"href":1999,"rel":2000},"https:\u002F\u002Fopenai.com\u002Findex\u002Fgpt-4-research\u002F",[1677],"OpenAI：GPT‑4 发布资料","：GPT‑4 于 2023 年 3 月发布，标准上下文为 8,192 tokens，并有限开放 32,768-token 版本。",[218,2004,2005,2010],{},[1673,2006,2009],{"href":2007,"rel":2008},"https:\u002F\u002Fdevelopers.openai.com\u002Fapi\u002Fdocs\u002Fmodels",[1677],"OpenAI：模型目录","：截至本文发布时，前沿 GPT‑5.6 系列列出约 1.05M context window，并支持多类工具。",[218,2012,2013,2018],{},[1673,2014,2017],{"href":2015,"rel":2016},"https:\u002F\u002Fai.google.dev\u002Fgemini-api\u002Fdocs\u002Flong-context",[1677],"Google AI：Long context","：介绍 Gemini 模型的 1M+ 长上下文能力、使用场景与限制。",{"title":280,"searchDepth":312,"depth":312,"links":2020},[2021,2022,2023,2024,2025,2026],{"id":1827,"depth":305,"text":1828},{"id":1849,"depth":305,"text":1849},{"id":1901,"depth":305,"text":1902},{"id":1945,"depth":305,"text":1946},{"id":1975,"depth":305,"text":1976},{"id":1667,"depth":305,"text":1667},"2023 年，我等来的是一次 8K context 的 API 访问资格；三年后，我们开始学习如何向智能体交代目标、权限、边界与验收标准。",{"src":2029,"title":2030,"description":2031,"duration":2032,"hasAudio":308},"\u002Fexperiences\u002Ffrom-gpt-4-invite-to-agents","从 GPT-4 邀请到智能体时代 · 六幕互动叙事","用 2 分 26 秒回看上下文、智能体工作流，以及能力与责任的变化。",146,{"src":2034,"alt":2035,"caption":2036,"width":1776,"height":1777},"\u002Fmedia\u002Fposts\u002Ffrom-gpt-4-invite-to-agents\u002Fcover-v1.webp","一封来自 2023 年的邮件从玻璃时间胶囊延伸为现代智能体工作空间","三年前申请访问模型，三年后学习与模型共同工作",{},"\u002Fblog\u002Ffrom-gpt-4-invite-to-agents","2026-07-25",{"title":1791,"description":2027},"blog\u002Ffrom-gpt-4-invite-to-agents",[2043,2044,2045],"AI","GPT-4","智能体","IodRMucRez876MDzkQQLQGaiyJPEgwFZiNkQ6fYBNJ8",{"id":2048,"title":2049,"accent":2050,"body":2051,"description":2495,"draft":1769,"experience":1770,"extension":1771,"featured":308,"image":2496,"meta":2500,"navigation":308,"path":2501,"publishedAt":2502,"readingTime":403,"seo":2503,"stem":2504,"tags":2505,"updatedAt":2508,"__hash__":2509},"posts\u002Fblog\u002Fedge-stateful-apps.md","在边缘构建有状态应用","cyan",{"type":9,"value":2052,"toc":2487},[2053,2059,2062,2065,2068,2118,2121,2126,2129,2132,2135,2212,2215,2298,2301,2304,2307,2385,2399,2402,2405,2408,2411,2422,2425,2428,2431,2434,2448,2451,2455,2458,2461,2478,2484],[12,2054,2055,2056],{},"边缘计算最吸引人的承诺，是让代码离用户更近。但当应用开始保存账号、会话和留言，问题就不再只是“把函数部署到更多节点”，而是：",[16,2057,2058],{},"状态究竟在哪里，谁对它负责，失败时系统会怎样退化？",[12,2060,2061],{},"这篇文章用一个带登录和留言的博客作为最小案例，梳理在 Cloudflare Workers 与 D1 上构建有状态应用时真正重要的设计边界。",[49,2063,2064],{"id":2064},"先画清楚状态地图",[12,2066,2067],{},"一个看起来很小的博客，至少有三类完全不同的状态：",[54,2069,2070,2083],{},[57,2071,2072],{},[60,2073,2074,2077,2080],{},[63,2075,2076],{},"状态",[63,2078,2079],{},"来源",[63,2081,2082],{},"生命周期",[76,2084,2085,2096,2107],{},[60,2086,2087,2090,2093],{},[81,2088,2089],{},"文章正文",[81,2091,2092],{},"Git 中的 Markdown",[81,2094,2095],{},"随部署更新",[60,2097,2098,2101,2104],{},[81,2099,2100],{},"用户与留言",[81,2102,2103],{},"D1",[81,2105,2106],{},"长期持久化",[60,2108,2109,2112,2115],{},[81,2110,2111],{},"登录会话",[81,2113,2114],{},"D1 + Cookie",[81,2116,2117],{},"有明确过期时间",[12,2119,2120],{},"文章内容适合跟着代码版本走。它需要评审、回滚和构建时校验，却不需要读者在运行时修改。用户数据则刚好相反：它必须脱离部署生命周期存在。",[39,2122,2123],{},[12,2124,2125],{},"选择数据库之前，先判断一份数据应该跟随代码版本，还是跟随用户行为。",[12,2127,2128],{},"这个区分会自然导向一个简单架构：Nuxt Content 负责 Markdown，D1 负责运行时业务数据，Worker 负责把两者组合成一次请求。",[49,2130,2131],{"id":2131},"绑定比网络请求更重要",[12,2133,2134],{},"Worker 访问 D1 时不需要自己保存 API Token，也不应该在运行时调用 Cloudflare REST API。D1 通过 binding 直接进入运行时：",[275,2136,2141],{"className":2137,"code":2138,"filename":2139,"language":2140,"meta":280,"style":280},"language-json shiki shiki-themes github-dark github-dark","{\n  \"d1_databases\": [\n    {\n      \"binding\": \"DB\",\n      \"database_name\": \"signal-log-db\",\n      \"database_id\": \"\u003CYOUR_DATABASE_ID>\"\n    }\n  ]\n}\n","wrangler.jsonc","json",[24,2142,2143,2148,2156,2161,2175,2187,2197,2202,2207],{"__ignoreMap":280},[284,2144,2145],{"class":286,"line":287},[284,2146,2147],{"class":294},"{\n",[284,2149,2150,2153],{"class":286,"line":305},[284,2151,2152],{"class":318},"  \"d1_databases\"",[284,2154,2155],{"class":294},": [\n",[284,2157,2158],{"class":286,"line":312},[284,2159,2160],{"class":294},"    {\n",[284,2162,2163,2166,2169,2172],{"class":286,"line":338},[284,2164,2165],{"class":318},"      \"binding\"",[284,2167,2168],{"class":294},": ",[284,2170,2171],{"class":301},"\"DB\"",[284,2173,2174],{"class":294},",\n",[284,2176,2177,2180,2182,2185],{"class":286,"line":357},[284,2178,2179],{"class":318},"      \"database_name\"",[284,2181,2168],{"class":294},[284,2183,2184],{"class":301},"\"signal-log-db\"",[284,2186,2174],{"class":294},[284,2188,2189,2192,2194],{"class":286,"line":385},[284,2190,2191],{"class":318},"      \"database_id\"",[284,2193,2168],{"class":294},[284,2195,2196],{"class":301},"\"\u003CYOUR_DATABASE_ID>\"\n",[284,2198,2199],{"class":286,"line":390},[284,2200,2201],{"class":294},"    }\n",[284,2203,2204],{"class":286,"line":403},[284,2205,2206],{"class":294},"  ]\n",[284,2208,2209],{"class":286,"line":415},[284,2210,2211],{"class":294},"}\n",[12,2213,2214],{},"在服务端路由里，数据库对象来自当前请求上下文。每一条 SQL 都使用 prepared statement：",[275,2216,2218],{"className":277,"code":2217,"language":279,"meta":280,"style":280},"const comment = await db\n  .prepare(`\n    SELECT id, body, created_at\n    FROM comments\n    WHERE post_slug = ?1 AND status = 'published'\n    ORDER BY created_at DESC\n    LIMIT 100\n  `)\n  .bind(postSlug)\n  .all()\n",[24,2219,2220,2235,2247,2252,2257,2262,2267,2272,2279,2289],{"__ignoreMap":280},[284,2221,2222,2224,2227,2229,2232],{"class":286,"line":287},[284,2223,315],{"class":290},[284,2225,2226],{"class":318}," comment",[284,2228,322],{"class":290},[284,2230,2231],{"class":290}," await",[284,2233,2234],{"class":294}," db\n",[284,2236,2237,2239,2242,2244],{"class":286,"line":305},[284,2238,862],{"class":294},[284,2240,2241],{"class":325},"prepare",[284,2243,329],{"class":294},[284,2245,2246],{"class":301},"`\n",[284,2248,2249],{"class":286,"line":312},[284,2250,2251],{"class":301},"    SELECT id, body, created_at\n",[284,2253,2254],{"class":286,"line":338},[284,2255,2256],{"class":301},"    FROM comments\n",[284,2258,2259],{"class":286,"line":357},[284,2260,2261],{"class":301},"    WHERE post_slug = ?1 AND status = 'published'\n",[284,2263,2264],{"class":286,"line":385},[284,2265,2266],{"class":301},"    ORDER BY created_at DESC\n",[284,2268,2269],{"class":286,"line":390},[284,2270,2271],{"class":301},"    LIMIT 100\n",[284,2273,2274,2277],{"class":286,"line":403},[284,2275,2276],{"class":301},"  `",[284,2278,335],{"class":294},[284,2280,2281,2283,2286],{"class":286,"line":415},[284,2282,862],{"class":294},[284,2284,2285],{"class":325},"bind",[284,2287,2288],{"class":294},"(postSlug)\n",[284,2290,2291,2293,2296],{"class":286,"line":669},[284,2292,862],{"class":294},[284,2294,2295],{"class":325},"all",[284,2297,1213],{"class":294},[12,2299,2300],{},"参数绑定不是代码风格问题，而是输入边界的一部分。文章路径、邮箱、昵称和留言正文都来自用户，任何一个值都不应该被拼接进 SQL。",[49,2302,2303],{"id":2303},"会话只存不可逆摘要",[12,2305,2306],{},"登录成功后，浏览器拿到一个随机会话令牌；数据库只保存它的 SHA-256 摘要。这样即使会话表意外暴露，原始令牌也不能被直接拿去登录。",[275,2308,2310],{"className":277,"code":2309,"language":279,"meta":280,"style":280},"const token = createRandomToken()\nconst tokenHash = await sha256(token)\n\nawait db\n  .prepare('INSERT INTO sessions (token_hash, user_id, expires_at) VALUES (?1, ?2, ?3)')\n  .bind(tokenHash, userId, expiresAt)\n  .run()\n",[24,2311,2312,2326,2343,2347,2354,2367,2376],{"__ignoreMap":280},[284,2313,2314,2316,2319,2321,2324],{"class":286,"line":287},[284,2315,315],{"class":290},[284,2317,2318],{"class":318}," token",[284,2320,322],{"class":290},[284,2322,2323],{"class":325}," createRandomToken",[284,2325,1213],{"class":294},[284,2327,2328,2330,2333,2335,2337,2340],{"class":286,"line":305},[284,2329,315],{"class":290},[284,2331,2332],{"class":318}," tokenHash",[284,2334,322],{"class":290},[284,2336,2231],{"class":290},[284,2338,2339],{"class":325}," sha256",[284,2341,2342],{"class":294},"(token)\n",[284,2344,2345],{"class":286,"line":312},[284,2346,309],{"emptyLinePlaceholder":308},[284,2348,2349,2352],{"class":286,"line":338},[284,2350,2351],{"class":290},"await",[284,2353,2234],{"class":294},[284,2355,2356,2358,2360,2362,2365],{"class":286,"line":357},[284,2357,862],{"class":294},[284,2359,2241],{"class":325},[284,2361,329],{"class":294},[284,2363,2364],{"class":301},"'INSERT INTO sessions (token_hash, user_id, expires_at) VALUES (?1, ?2, ?3)'",[284,2366,335],{"class":294},[284,2368,2369,2371,2373],{"class":286,"line":385},[284,2370,862],{"class":294},[284,2372,2285],{"class":325},[284,2374,2375],{"class":294},"(tokenHash, userId, expiresAt)\n",[284,2377,2378,2380,2383],{"class":286,"line":390},[284,2379,862],{"class":294},[284,2381,2382],{"class":325},"run",[284,2384,1213],{"class":294},[12,2386,2387,2388,2391,2392,2395,2396,19],{},"Cookie 使用 ",[24,2389,2390],{},"HttpOnly","，让前端 JavaScript 无法读取；使用 ",[24,2393,2394],{},"SameSite=Lax"," 降低跨站请求风险；生产环境再加上 ",[24,2397,2398],{},"Secure",[12,2400,2401],{},"这并不意味着系统从此“绝对安全”。更准确的说法是：我们让每一层只持有它完成工作所需的最少能力。",[49,2403,2404],{"id":2404},"一致性要围绕用户动作设计",[12,2406,2407],{},"D1 是关系数据库，但边缘应用仍然需要明确哪些流程要求强一致。",[12,2409,2410],{},"对博客来说，关键动作很少：",[538,2412,2413,2416,2419],{},[218,2414,2415],{},"注册后应当立刻能够登录；",[218,2417,2418],{},"留言成功后应当立刻在当前页面出现；",[218,2420,2421],{},"退出后旧会话应当立刻失效。",[12,2423,2424],{},"这些流程都应该从同一个主数据库完成写入和紧随其后的读取。文章列表、公开留言列表等纯读取场景，才有更大的缓存空间。",[12,2426,2427],{},"不要从“哪里可以缓存”出发，而要从“用户刚刚完成了什么承诺”出发。",[49,2429,2430],{"id":2430},"失败也需要产品设计",[12,2432,2433],{},"边缘运行时会失败，数据库也可能暂时不可用。区别只在于，我们有没有把失败变成用户能理解的状态。",[215,2435,2436,2439,2442,2445],{},[218,2437,2438],{},"登录失败时，不区分“邮箱不存在”和“密码错误”，避免暴露账号枚举信息；",[218,2440,2441],{},"留言写入失败时，保留输入内容，让用户可以重试；",[218,2443,2444],{},"内容查询失败时，返回真正的错误状态，而不是空白页面；",[218,2446,2447],{},"所有服务端错误使用结构化日志，但不把内部 SQL 或堆栈发给浏览器。",[12,2449,2450],{},"系统可靠性不是“永远不出错”，而是错误发生时边界仍然完整。",[49,2452,2454],{"id":2453},"最后让架构保持无聊","最后：让架构保持无聊",[12,2456,2457],{},"这个方案没有消息队列、没有分布式锁，也没有额外的身份服务。不是因为那些工具没有价值，而是当前问题还没有提出相应要求。",[12,2459,2460],{},"一个长期可维护的个人站，最好的基础通常是：",[215,2462,2463,2466,2469,2472,2475],{},[218,2464,2465],{},"内容跟着 Git；",[218,2467,2468],{},"数据进入 D1；",[218,2470,2471],{},"密钥留在 Worker secret；",[218,2473,2474],{},"输入在边界验证；",[218,2476,2477],{},"失败清楚、可观察、可恢复。",[12,2479,2480,2481],{},"当流量或产品需求真正变化时，再让架构跟着证据演进。",[16,2482,2483],{},"边缘已经足够快，工程的任务是让它继续简单。",[1726,2485,2486],{},"html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}",{"title":280,"searchDepth":312,"depth":312,"links":2488},[2489,2490,2491,2492,2493,2494],{"id":2064,"depth":305,"text":2064},{"id":2131,"depth":305,"text":2131},{"id":2303,"depth":305,"text":2303},{"id":2404,"depth":305,"text":2404},{"id":2430,"depth":305,"text":2430},{"id":2453,"depth":305,"text":2454},"Cloudflare Workers 很快，D1 很近，但真正困难的是把状态、一致性与失败边界设计清楚。",{"src":2497,"alt":2498,"caption":2499,"width":1776,"height":1777},"\u002Fmedia\u002Fposts\u002Fedge-stateful-apps\u002Fcover-v1.webp","边缘节点围绕中心数据库形成有状态应用拓扑","状态、边缘节点与一致性边界",{},"\u002Fblog\u002Fedge-stateful-apps","2026-07-21",{"title":2049,"description":2495},"blog\u002Fedge-stateful-apps",[2506,2103,2507],"Cloudflare","工程","2026-07-24","qVlQ94aZiwjIHRu5cLeMAhbFz-mhfV7p2j4KjRlWv_U",{"id":2511,"title":2512,"accent":7,"body":2513,"description":2908,"draft":1769,"experience":1770,"extension":1771,"featured":1769,"image":2909,"meta":2913,"navigation":308,"path":2914,"publishedAt":2915,"readingTime":385,"seo":2916,"stem":2917,"tags":2918,"updatedAt":1770,"__hash__":2921},"posts\u002Fblog\u002Fnuxt-4-architecture-subtraction.md","Nuxt 4 的架构减法",{"type":9,"value":2514,"toc":2900},[2515,2518,2524,2528,2531,2586,2589,2596,2599,2602,2731,2734,2752,2755,2758,2761,2764,2767,2772,2776,2783,2791,2794,2797,2800,2803,2868,2871,2874,2877,2894,2897],[12,2516,2517],{},"很多 Nuxt 项目变复杂，并不是因为业务真的复杂，而是因为我们太早把所有可能性都放进了架构。",[12,2519,2520,2521,19],{},"个人博客是一个很好的校准器：页面数量有限、内容结构稳定、交互集中在少数区域。它提醒我们，框架最有价值的部分往往不是增加能力，而是",[16,2522,2523],{},"删除不必要的选择",[49,2525,2527],{"id":2526},"从默认-ssr-开始","从默认 SSR 开始",[12,2529,2530],{},"技术文章需要被搜索引擎理解，也需要在弱网下尽早出现正文。Nuxt 默认的服务端渲染刚好符合这个目标。",[275,2532,2535],{"className":277,"code":2533,"filename":2534,"language":279,"meta":280,"style":280},"export default defineNuxtConfig({\n  modules: ['@nuxt\u002Fcontent'],\n  nitro: {\n    preset: 'cloudflare',\n  },\n})\n","nuxt.config.ts",[24,2536,2537,2551,2562,2567,2577,2582],{"__ignoreMap":280},[284,2538,2539,2542,2545,2548],{"class":286,"line":287},[284,2540,2541],{"class":290},"export",[284,2543,2544],{"class":290}," default",[284,2546,2547],{"class":325}," defineNuxtConfig",[284,2549,2550],{"class":294},"({\n",[284,2552,2553,2556,2559],{"class":286,"line":305},[284,2554,2555],{"class":294},"  modules: [",[284,2557,2558],{"class":301},"'@nuxt\u002Fcontent'",[284,2560,2561],{"class":294},"],\n",[284,2563,2564],{"class":286,"line":312},[284,2565,2566],{"class":294},"  nitro: {\n",[284,2568,2569,2572,2575],{"class":286,"line":338},[284,2570,2571],{"class":294},"    preset: ",[284,2573,2574],{"class":301},"'cloudflare'",[284,2576,2174],{"class":294},[284,2578,2579],{"class":286,"line":357},[284,2580,2581],{"class":294},"  },\n",[284,2583,2584],{"class":286,"line":385},[284,2585,418],{"class":294},[12,2587,2588],{},"这里没有把整个站改成 SPA，也没有为每个页面单独发明数据加载方式。页面在服务端完成第一次查询和渲染，客户端接管后继续使用同一套路由。",[12,2590,2591,2592,2595],{},"如果某一小块必须依赖浏览器 API，再局部使用 ",[24,2593,2594],{},"ClientOnly","。不要为了一个客户端组件牺牲整页的语义和首屏。",[49,2597,2598],{"id":2598},"内容查询留在页面边界",[12,2600,2601],{},"首页、文章列表和文章详情需要的数据粒度不同。查询应该贴近消费它的页面：",[275,2603,2605],{"className":277,"code":2604,"language":279,"meta":280,"style":280},"const { data: posts } = await useAsyncData('latest-posts', () => {\n  return queryCollection('posts')\n    .where('draft', '=', false)\n    .order('publishedAt', 'DESC')\n    .limit(6)\n    .all()\n})\n",[24,2606,2607,2645,2660,2686,2705,2719,2727],{"__ignoreMap":280},[284,2608,2609,2611,2614,2618,2620,2623,2626,2628,2630,2633,2635,2638,2641,2643],{"class":286,"line":287},[284,2610,315],{"class":290},[284,2612,2613],{"class":294}," { ",[284,2615,2617],{"class":2616},"s-3mD","data",[284,2619,2168],{"class":294},[284,2621,2622],{"class":318},"posts",[284,2624,2625],{"class":294}," } ",[284,2627,583],{"class":290},[284,2629,2231],{"class":290},[284,2631,2632],{"class":325}," useAsyncData",[284,2634,329],{"class":294},[284,2636,2637],{"class":301},"'latest-posts'",[284,2639,2640],{"class":294},", () ",[284,2642,373],{"class":290},[284,2644,400],{"class":294},[284,2646,2647,2650,2653,2655,2658],{"class":286,"line":305},[284,2648,2649],{"class":290},"  return",[284,2651,2652],{"class":325}," queryCollection",[284,2654,329],{"class":294},[284,2656,2657],{"class":301},"'posts'",[284,2659,335],{"class":294},[284,2661,2662,2665,2668,2670,2673,2676,2679,2681,2684],{"class":286,"line":312},[284,2663,2664],{"class":294},"    .",[284,2666,2667],{"class":325},"where",[284,2669,329],{"class":294},[284,2671,2672],{"class":301},"'draft'",[284,2674,2675],{"class":294},", ",[284,2677,2678],{"class":301},"'='",[284,2680,2675],{"class":294},[284,2682,2683],{"class":318},"false",[284,2685,335],{"class":294},[284,2687,2688,2690,2693,2695,2698,2700,2703],{"class":286,"line":338},[284,2689,2664],{"class":294},[284,2691,2692],{"class":325},"order",[284,2694,329],{"class":294},[284,2696,2697],{"class":301},"'publishedAt'",[284,2699,2675],{"class":294},[284,2701,2702],{"class":301},"'DESC'",[284,2704,335],{"class":294},[284,2706,2707,2709,2712,2714,2717],{"class":286,"line":357},[284,2708,2664],{"class":294},[284,2710,2711],{"class":325},"limit",[284,2713,329],{"class":294},[284,2715,2716],{"class":318},"6",[284,2718,335],{"class":294},[284,2720,2721,2723,2725],{"class":286,"line":385},[284,2722,2664],{"class":294},[284,2724,2295],{"class":325},[284,2726,1213],{"class":294},[284,2728,2729],{"class":286,"line":390},[284,2730,418],{"class":294},[12,2732,2733],{},"这样做有几个直接好处：",[215,2735,2736,2743,2746,2749],{},[218,2737,2738,2739,2742],{},"数据参与 SSR，不需要 ",[24,2740,2741],{},"onMounted"," 后再闪烁加载；",[218,2744,2745],{},"查询键明确，Nuxt 可以在 hydration 时复用 payload；",[218,2747,2748],{},"页面知道自己需要什么，不必经过一层通用 store；",[218,2750,2751],{},"草稿过滤与排序规则出现在真正相关的位置。",[12,2753,2754],{},"只有当多个页面共享了同一段复杂业务状态时，才值得抽成 composable 或 store。",[49,2756,2757],{"id":2757},"组件只抽取稳定重复",[12,2759,2760],{},"“看起来可能复用”不是组件边界。真正稳定的重复通常同时包含结构、语义和行为。",[12,2762,2763],{},"这个博客里，文章卡片是合适的组件：它在首页和索引页重复出现，拥有固定的元数据、标题、摘要、标签和跳转行为。",[12,2765,2766],{},"相反，首页 Hero 即使很大，也未必需要拆成五个小组件。它只在一个页面出现，内部元素共同完成一段视觉叙事。过早拆分只会让样式关系跨文件跳转。",[39,2768,2769],{},[12,2770,2771],{},"组件不是文件整理工具，而是可独立理解和复用的 UI 契约。",[49,2773,2775],{"id":2774},"服务端-api-保持窄接口","服务端 API 保持窄接口",[12,2777,2778,2779,2782],{},"登录与留言属于运行时数据，放在 ",[24,2780,2781],{},"server\u002Fapi","。每个 endpoint 只做一件事：",[275,2784,2789],{"className":2785,"code":2787,"language":2788,"meta":280},[2786],"language-text","POST \u002Fapi\u002Fauth\u002Fregister\nPOST \u002Fapi\u002Fauth\u002Flogin\nPOST \u002Fapi\u002Fauth\u002Flogout\nGET  \u002Fapi\u002Fauth\u002Fsession\nGET  \u002Fapi\u002Fcomments\u002F:post\nPOST \u002Fapi\u002Fcomments\u002F:post\n","text",[24,2790,2787],{"__ignoreMap":280},[12,2792,2793],{},"窄接口使授权规则更容易检查。读取留言是公开的，创建留言需要会话；退出只销毁当前令牌，不需要触碰用户资料。",[12,2795,2796],{},"如果一个 endpoint 同时处理读取、写入、登录和格式转换，它就已经失去了清晰边界。",[49,2798,2799],{"id":2799},"把状态放回正确的位置",[12,2801,2802],{},"Vue 的响应式状态很方便，但不是所有状态都应该进入 JavaScript：",[54,2804,2805,2814],{},[57,2806,2807],{},[60,2808,2809,2811],{},[63,2810,2076],{},[63,2812,2813],{},"最合适的位置",[76,2815,2816,2824,2834,2842,2853,2860],{},[60,2817,2818,2821],{},[81,2819,2820],{},"当前文章",[81,2822,2823],{},"路由",[60,2825,2826,2829],{},[81,2827,2828],{},"搜索词",[81,2830,2831,2832],{},"页面 ",[24,2833,26],{},[60,2835,2836,2839],{},[81,2837,2838],{},"标签筛选",[81,2840,2841],{},"URL query + 页面状态",[60,2843,2844,2847],{},[81,2845,2846],{},"登录用户",[81,2848,2849,2850],{},"SSR 安全的 ",[24,2851,2852],{},"useState",[60,2854,2855,2857],{},[81,2856,2089],{},[81,2858,2859],{},"Nuxt Content",[60,2861,2862,2865],{},[81,2863,2864],{},"会话真实性",[81,2866,2867],{},"服务端 D1",[12,2869,2870],{},"当状态位置正确时，大量“同步逻辑”会自然消失。我们不再需要监听路由去修复 store，也不需要用 localStorage 假装登录状态可靠。",[49,2872,2873],{"id":2873},"减法不是功能更少",[12,2875,2876],{},"好的架构减法不会让用户少得到什么。相反，它把复杂度预算留给真正有感知的部分：",[215,2878,2879,2882,2885,2888,2891],{},[218,2880,2881],{},"更清晰的排版；",[218,2883,2884],{},"更快的首屏；",[218,2886,2887],{},"更可靠的登录；",[218,2889,2890],{},"更舒服的移动端阅读；",[218,2892,2893],{},"更容易坚持的 Markdown 写作流程。",[12,2895,2896],{},"Nuxt 4 提供了足够多的能力。项目成熟的标志，不是把它们全部用上，而是知道哪些能力可以暂时不用。",[1726,2898,2899],{},"html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html pre.shiki code .s-3mD, html code.shiki .s-3mD{--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}",{"title":280,"searchDepth":312,"depth":312,"links":2901},[2902,2903,2904,2905,2906,2907],{"id":2526,"depth":305,"text":2527},{"id":2598,"depth":305,"text":2598},{"id":2757,"depth":305,"text":2757},{"id":2774,"depth":305,"text":2775},{"id":2799,"depth":305,"text":2799},{"id":2873,"depth":305,"text":2873},"一个内容型站点不需要复杂前端状态。让路由、服务端渲染和内容查询各自回到最擅长的位置。",{"src":2910,"alt":2911,"caption":2912,"width":1776,"height":1777},"\u002Fmedia\u002Fposts\u002Fnuxt-4-architecture-subtraction\u002Fcover-v1.webp","复杂模块逐步收敛为清晰精简的 Nuxt 应用核心","用架构减法换取清晰边界",{},"\u002Fblog\u002Fnuxt-4-architecture-subtraction","2026-07-16",{"title":2512,"description":2908},"blog\u002Fnuxt-4-architecture-subtraction",[2919,1785,2920],"Nuxt","架构","St4BQLFtVYREpXSjTqcxEzk7igp5zuh24rE67bUhxyc",{"id":2923,"title":2924,"accent":1792,"body":2925,"description":3275,"draft":1769,"experience":1770,"extension":1771,"featured":1769,"image":3276,"meta":3280,"navigation":308,"path":3281,"publishedAt":3282,"readingTime":390,"seo":3283,"stem":3284,"tags":3285,"updatedAt":1770,"__hash__":3287},"posts\u002Fblog\u002Fobservable-ai-systems.md","AI 系统的可观测性，不只是 Token 账单",{"type":9,"value":2926,"toc":3262},[2927,2930,2935,2938,2941,2944,2948,2951,2955,2958,2962,2965,2969,2972,2975,2979,2986,3102,3105,3108,3125,3128,3131,3134,3188,3191,3194,3197,3214,3217,3220,3223,3226,3240,3243,3247,3250,3256,3259],[12,2928,2929],{},"传统服务出问题时，我们习惯查看状态码、延迟和错误堆栈。AI 应用出问题时，这些指标仍然重要，却经常不足以回答最关键的问题：",[12,2931,2932],{},[16,2933,2934],{},"模型为什么做出了这个选择？",[12,2936,2937],{},"答案不在某一条日志里，而在从用户目标、上下文组装、模型决策、工具执行到最终结果的整条链路中。",[49,2939,2940],{"id":2940},"先区分四种失败",[12,2942,2943],{},"把所有失败都记成“模型效果不好”，团队就无法采取具体行动。至少应该区分：",[436,2945,2947],{"id":2946},"_1-输入失败","1. 输入失败",[12,2949,2950],{},"用户目标不完整，或系统没有拿到完成任务所需的上下文。此时升级模型通常没有帮助。",[436,2952,2954],{"id":2953},"_2-推理失败","2. 推理失败",[12,2956,2957],{},"上下文正确，但模型选择了错误路径、遗漏约束或过早得出结论。",[436,2959,2961],{"id":2960},"_3-工具失败","3. 工具失败",[12,2963,2964],{},"模型选择正确，实际执行却因为权限、网络、参数或外部状态失败。",[436,2966,2968],{"id":2967},"_4-交付失败","4. 交付失败",[12,2970,2971],{},"内部工作已经完成，但最终回答没有把结果、限制和下一步说清楚。",[12,2973,2974],{},"这四类问题需要不同的修复者：产品、提示设计、平台工程或交互设计。可观测性的第一份价值，是让问题回到正确的人手里。",[49,2976,2978],{"id":2977},"一次运行需要共同的-trace","一次运行需要共同的 Trace",[12,2980,2981,2982,2985],{},"每个用户请求都应该有稳定的 ",[24,2983,2984],{},"trace_id","，贯穿模型调用和工具执行：",[275,2987,2989],{"className":277,"code":2988,"language":279,"meta":280,"style":280},"type AgentEvent = {\n  traceId: string\n  spanId: string\n  parentSpanId?: string\n  type: 'model' | 'tool' | 'handoff' | 'result'\n  startedAt: string\n  durationMs?: number\n  status: 'running' | 'ok' | 'error'\n}\n",[24,2990,2991,3003,3014,3023,3033,3059,3068,3078,3098],{"__ignoreMap":280},[284,2992,2993,2996,2999,3001],{"class":286,"line":287},[284,2994,2995],{"class":290},"type",[284,2997,2998],{"class":325}," AgentEvent",[284,3000,322],{"class":290},[284,3002,400],{"class":294},[284,3004,3005,3008,3011],{"class":286,"line":305},[284,3006,3007],{"class":2616},"  traceId",[284,3009,3010],{"class":290},":",[284,3012,3013],{"class":318}," string\n",[284,3015,3016,3019,3021],{"class":286,"line":312},[284,3017,3018],{"class":2616},"  spanId",[284,3020,3010],{"class":290},[284,3022,3013],{"class":318},[284,3024,3025,3028,3031],{"class":286,"line":338},[284,3026,3027],{"class":2616},"  parentSpanId",[284,3029,3030],{"class":290},"?:",[284,3032,3013],{"class":318},[284,3034,3035,3038,3040,3043,3046,3049,3051,3054,3056],{"class":286,"line":357},[284,3036,3037],{"class":2616},"  type",[284,3039,3010],{"class":290},[284,3041,3042],{"class":301}," 'model'",[284,3044,3045],{"class":290}," |",[284,3047,3048],{"class":301}," 'tool'",[284,3050,3045],{"class":290},[284,3052,3053],{"class":301}," 'handoff'",[284,3055,3045],{"class":290},[284,3057,3058],{"class":301}," 'result'\n",[284,3060,3061,3064,3066],{"class":286,"line":385},[284,3062,3063],{"class":2616},"  startedAt",[284,3065,3010],{"class":290},[284,3067,3013],{"class":318},[284,3069,3070,3073,3075],{"class":286,"line":390},[284,3071,3072],{"class":2616},"  durationMs",[284,3074,3030],{"class":290},[284,3076,3077],{"class":318}," number\n",[284,3079,3080,3083,3085,3088,3090,3093,3095],{"class":286,"line":403},[284,3081,3082],{"class":2616},"  status",[284,3084,3010],{"class":290},[284,3086,3087],{"class":301}," 'running'",[284,3089,3045],{"class":290},[284,3091,3092],{"class":301}," 'ok'",[284,3094,3045],{"class":290},[284,3096,3097],{"class":301}," 'error'\n",[284,3099,3100],{"class":286,"line":415},[284,3101,2211],{"class":294},[12,3103,3104],{},"工具调用是模型决策的外部表现。只记录工具返回值而不记录调用原因，会失去决策上下文；只保存提示词而不保存工具结果，又无法判断执行是否符合预期。",[12,3106,3107],{},"需要注意的是，观测并不意味着无限制收集。原始提示词可能包含个人信息、凭据或业务数据。更稳妥的做法是：",[215,3109,3110,3113,3116,3119,3122],{},[218,3111,3112],{},"默认记录结构化元数据；",[218,3114,3115],{},"对正文做字段级脱敏；",[218,3117,3118],{},"为调试样本设置短保留期；",[218,3120,3121],{},"允许用户和业务明确选择不进入训练或分析；",[218,3123,3124],{},"不在日志中保存密钥与完整身份信息。",[49,3126,3127],{"id":3127},"质量指标必须贴近任务",[12,3129,3130],{},"Token、延迟和模型错误率是平台指标，但它们不能代表用户是否得到结果。",[12,3132,3133],{},"一个代码修复 Agent 更有意义的指标可能是：",[54,3135,3136,3146],{},[57,3137,3138],{},[60,3139,3140,3143],{},[63,3141,3142],{},"层级",[63,3144,3145],{},"指标示例",[76,3147,3148,3156,3164,3172,3180],{},[60,3149,3150,3153],{},[81,3151,3152],{},"结果",[81,3154,3155],{},"受影响测试是否通过",[60,3157,3158,3161],{},[81,3159,3160],{},"过程",[81,3162,3163],{},"是否读取了相关约束文件",[60,3165,3166,3169],{},[81,3167,3168],{},"安全",[81,3170,3171],{},"是否修改了任务范围外文件",[60,3173,3174,3177],{},[81,3175,3176],{},"效率",[81,3178,3179],{},"有效工具调用 \u002F 总工具调用",[60,3181,3182,3185],{},[81,3183,3184],{},"交付",[81,3186,3187],{},"是否提供可验证的文件或提交",[12,3189,3190],{},"“有效工具调用”比单纯调用次数更有价值。一次准确的搜索可能替代十次猜测式读取。",[49,3192,3193],{"id":3193},"把评估变成日常反馈",[12,3195,3196],{},"离线 benchmark 能比较模型版本，却不能覆盖你的真实工作流。更可持续的评估集来自生产中的高价值样本：",[538,3198,3199,3202,3205,3208,3211],{},[218,3200,3201],{},"收集明确成功与明确失败的运行；",[218,3203,3204],{},"删除隐私信息，保留决策结构；",[218,3206,3207],{},"为失败标注类型，而不是只打一个总分；",[218,3209,3210],{},"在提示、模型或工具变化时重新运行；",[218,3212,3213],{},"同时比较结果质量、成本和延迟。",[12,3215,3216],{},"评估集不需要一开始就很大。二十个边界清楚、能稳定复现的案例，通常比两千个来源模糊的样本更能指导工程。",[49,3218,3219],{"id":3219},"面向人的可观测性",[12,3221,3222],{},"最后一层观测不在仪表盘，而在产品界面。",[12,3224,3225],{},"当 Agent 正在读取文件、等待授权或执行部署时，用户需要知道它处于什么阶段。好的状态更新不是倾倒内部思维，而是给出：",[215,3227,3228,3231,3234,3237],{},[218,3229,3230],{},"当前正在完成什么；",[218,3232,3233],{},"已经确认了什么；",[218,3235,3236],{},"是否需要用户采取行动；",[218,3238,3239],{},"哪些风险仍未关闭。",[12,3241,3242],{},"这类可见性会直接改变用户对系统可靠性的判断。一个需要几十秒完成的任务，如果过程清楚，往往比十秒后突然返回不透明结果更让人放心。",[49,3244,3246],{"id":3245},"可解释不等于暴露全部过程","可解释，不等于暴露全部过程",[12,3248,3249],{},"AI 系统不需要把每一个内部 token 展示给用户，也不应该把敏感上下文原样写入日志。真正需要的是一条足够清楚的因果链：",[275,3251,3254],{"className":3252,"code":3253,"language":2788,"meta":280},[2786],"用户目标\n  → 使用了哪些上下文\n  → 选择了什么工具\n  → 工具返回了什么状态\n  → 最终交付了什么结果\n",[24,3255,3253],{"__ignoreMap":280},[12,3257,3258],{},"当这条链路可以被查询、比较和复现时，团队才有能力把“偶尔很聪明”变成“长期可信赖”。",[1726,3260,3261],{},"html pre.shiki code .sOPea, html code.shiki .sOPea{--shiki-default:#F97583;--shiki-dark:#F97583}html pre.shiki code .sFR8T, html code.shiki .sFR8T{--shiki-default:#B392F0;--shiki-dark:#B392F0}html pre.shiki code .suv1-, html code.shiki .suv1-{--shiki-default:#E1E4E8;--shiki-dark:#E1E4E8}html pre.shiki code .s-3mD, html code.shiki .s-3mD{--shiki-default:#FFAB70;--shiki-dark:#FFAB70}html pre.shiki code .s8ozJ, html code.shiki .s8ozJ{--shiki-default:#79B8FF;--shiki-dark:#79B8FF}html pre.shiki code .s4wv1, html code.shiki .s4wv1{--shiki-default:#9ECBFF;--shiki-dark:#9ECBFF}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}",{"title":280,"searchDepth":312,"depth":312,"links":3263},[3264,3270,3271,3272,3273,3274],{"id":2940,"depth":305,"text":2940,"children":3265},[3266,3267,3268,3269],{"id":2946,"depth":312,"text":2947},{"id":2953,"depth":312,"text":2954},{"id":2960,"depth":312,"text":2961},{"id":2967,"depth":312,"text":2968},{"id":2977,"depth":305,"text":2978},{"id":3127,"depth":305,"text":3127},{"id":3193,"depth":305,"text":3193},{"id":3219,"depth":305,"text":3219},{"id":3245,"depth":305,"text":3246},"从提示词到工具调用，再到用户可见结果，AI 应用需要一条能解释“为什么”的观测链路。",{"src":3277,"alt":3278,"caption":3279,"width":1776,"height":1777},"\u002Fmedia\u002Fposts\u002Fobservable-ai-systems\u002Fcover-v1.webp","AI 请求从输入经过模型和工具节点形成完整观测链路","从提示词到结果的可解释 Trace",{},"\u002Fblog\u002Fobservable-ai-systems","2026-07-09",{"title":2924,"description":3275},"blog\u002Fobservable-ai-systems",[2043,3286,2507],"可观测性","38fR6dgXWRrU_24Zye-8Fgu9kmY9Urv0ucmeiPbyneo",1785034336925]