[{"data":1,"prerenderedAt":382},["ShallowReactive",2],{"content:\u002Fposts\u002F2025\u002Fvue-alpha-vapor-mode":3,"surround:\u002Fposts\u002F2025\u002Fvue-alpha-vapor-mode":370},{"id":4,"title":5,"body":6,"canonical":343,"categories":344,"date":346,"description":347,"draft":348,"extension":349,"image":350,"meta":351,"navigation":352,"path":353,"permalink":343,"readingTime":354,"recommend":343,"references":343,"seo":359,"sitemap":360,"stem":363,"tags":364,"type":343,"updated":346,"__hash__":369},"content\u002Fposts\u002F2025\u002Fvue-alpha-vapor-mode.md","Vue Vapor mode 来了",{"type":7,"value":8,"toc":326},"minimark",[9,14,18,23,26,34,37,48,52,55,82,93,97,100,106,109,115,118,121,129,132,135,148,158,169,173,179,185,191,200,204,212,228,231,256,259,275,278,299],[10,11,13],"h2",{"id":12},"为什么需要-vapor-mode","为什么需要 Vapor Mode",[15,16,17],"p",{},"从 Vue 发展的历史来看，Vue1 并没有虚拟 DOM，在 Vue2 中引入了虚拟 DOM。",[19,20,22],"h3",{"id":21},"为什么要在-vue2-中引入虚拟-dom","为什么要在 Vue2 中引入虚拟 DOM",[15,24,25],{},"如果您以前使用过 JavaScript 框架，您可能对虚拟 DOM 的概念并不陌生。它涉及创建和更新 DOM 的虚拟表示，并将其存储在内存中以与真实 DOM 同步。\n由于更新虚拟 DOM 比更新实际 DOM 更快（可以减少操作真实 DOM 的次数，降低了浏览器重绘和回流的频率，提高了渲染效率。），因此框架可以相对低成本地对虚拟 DOM 进行必要的更改。\n虚拟 DOM 还提供了跨平台能力，同一套代码可以运行到不同的平台上。",[15,27,28,29,33],{},"就 Vue 而言，其基于虚拟 DOM 的渲染系统将 ",[30,31,32],"code",{"code":32},"\u003Ctemplate>"," 部分中的代码转换为实际的 DOM 节点。\n该系统还能高效地管理节点的更改，这些更改可以通过 JavaScript 函数、API 调用等动态进行。",[15,35,36],{},"虽然虚拟 DOM 提升了速度和性能，但是更新 DOM 仍需要遍历虚拟 DOM并比较每个节点的变化，此过程中还包括需要为虚拟 DOM 树的每部分生成新的虚拟 DOM，这可能会导致不必要的内存压力。",[15,38,39,40,47],{},"在 Vue3 中引入了 ",[41,42,46],"a",{"href":43,"rel":44},"https:\u002F\u002Fcn.vuejs.org\u002Fguide\u002Fextras\u002Frendering-mechanism#compiler-informed-virtual-dom",[45],"nofollow","带编译时信息的虚拟 DOM","，它引入了一些有助于解决此问题的优化概念，包括",[19,49,51],{"id":50},"_1-缓存静态内容","1. 缓存静态内容",[15,53,54],{},"它允许 Vue 在多次重新渲染中重复使用该部分的虚拟 DOM。",[56,57,59,72],"tab",{":tabs":58},"[\"编译前\", \"编译后\"]",[60,61,63],"template",{"v-slot:tab1":62},"",[64,65,70],"pre",{"className":66,"code":68,"language":69,"meta":62},[67],"language-vue","\u003Cscript setup>\nimport { ref } from 'vue'\n\nconst msg = ref('Hello World!')\n\u003C\u002Fscript>\n\n\u003Ctemplate>\n    \u003Cdiv>\n        \u003Cdiv>foo\u003C\u002Fdiv> \u003C!-- cached -->\n        \u003Cdiv>bar\u003C\u002Fdiv> \u003C!-- cached -->\n        \u003Cdiv>{{ msg }}\u003C\u002Fdiv>\n    \u003C\u002Fdiv>\n\u003C\u002Ftemplate>\n","vue",[30,71,68],{"__ignoreMap":62},[60,73,74],{"v-slot:tab2":62},[64,75,80],{"className":76,"code":78,"language":79,"meta":62},[77],"language-js","\u002F* Analyzed bindings: {\n  \"ref\": \"setup-const\",\n  \"msg\": \"setup-ref\"\n} *\u002F\nimport { createCommentVNode as _createCommentVNode, createElementBlock as _createElementBlock, createElementVNode as _createElementVNode, openBlock as _openBlock, toDisplayString as _toDisplayString } from \"vue\"\nimport { ref } from 'vue'\n\nconst __sfc__ = {\n    __name: 'App',\n    setup(__props) {\n        const msg = ref('Hello World!')\n\n        return (_ctx, _cache) => {\n            return (_openBlock(), _createElementBlock(\"div\", null, [\n                _cache[0] || (_cache[0] = _createElementVNode(\"div\", null, \"foo\", -1 \u002F* CACHED *\u002F)),\n                _createCommentVNode(\" cached \"),\n                _cache[1] || (_cache[1] = _createElementVNode(\"div\", null, \"bar\", -1 \u002F* CACHED *\u002F)),\n                _createCommentVNode(\" cached \"),\n                _createElementVNode(\"div\", null, _toDisplayString(msg.value), 1 \u002F* TEXT *\u002F)\n            ]))\n        }\n    }\n\n}\n__sfc__.__file = \"src\u002FApp.vue\"\nexport default __sfc__\n","js",[30,81,78],{"__ignoreMap":62},[15,83,84,85,88,89,92],{},"我们可以看到静态的两个 div 标签被打上了 ",[30,86,87],{"code":87},"\u002F* CACHED *\u002F"," 标记，会变化的文本标签被打上了 ",[30,90,91],{"code":91},"\u002F* TEXT *\u002F","，这就引入了另一个优化点：更新类型标记",[19,94,96],{"id":95},"_2-更新类型标记","2. 更新类型标记",[15,98,99],{},"对于单个有动态绑定的元素来说，我们可以在编译时推断出大量信息：",[64,101,104],{"className":102,"code":103,"language":69,"meta":62},[67],"\u003C!-- 仅含 class 绑定 -->\n\u003Cdiv :class=\"{ active }\">\u003C\u002Fdiv>\n\n\u003C!-- 仅含 id 和 value 绑定 -->\n\u003Cinput :id=\"id\" :value=\"value\">\n\n\u003C!-- 仅含文本子节点 -->\n\u003Cdiv>\n{{ dynamic }}\n\u003C\u002Fdiv>\n",[30,105,103],{"__ignoreMap":62},[15,107,108],{},"在为这些元素生成渲染函数时，Vue 在 虚拟节点创建调用中直接编码了每个元素所需的更新类型：",[15,110,111,112,114],{},"就像上一个例子中的 ",[30,113,91],{"code":91},"，一个元素可以有多个更新类型标记，会被合并成一个数字。运行时渲染器也将会使用位运算来检查这些标记，确定相应的更新操作。\n位运算检查是非常快的。通过这样的更新类型标记，Vue 能够在更新带有动态绑定的元素时做最少的操作。",[19,116,117],{"id":117},"对树结构进行打平",[15,119,120],{},"Vue 会对虚拟 DOM 树的编译的结果会被打平为一个数组，仅包含所有动态的后代节点，当这个组件需要重渲染时，只需要遍历这个打平的树而非整棵树。这也就是我们所说的树结构打平，这大大减少了我们在虚拟 DOM 协调时需要遍历的节点数量。模板中任何的静态部分都会被高效地略过。",[15,122,123,124],{},"通过这些方法，Vue 得以在使用虚拟 DOM 的情况下在部分场景性能逼近 Svelte，并大大快于 React\n",[125,126],"img",{"alt":127,"src":128},"Vue vs React vs Svelte","https:\u002F\u002Fbitmc.uno\u002Fpicgo\u002Fvue-react-svelte.jpeg",[15,130,131],{},"尽管有了这些改进的方式，虚拟 DOM 的缺陷依然存在，比如一些不必要的内存占用，树结构的 Diff。这时我们就可以看下 Vapor Mode。",[15,133,134],{},"它的优点包括",[136,137,138,142,145],"ol",{},[139,140,141],"li",{},"没有虚拟 DOM 的运行时代码，减少包的体积并提升性能",[139,143,144],{},"可以细粒度的对需要更新的部分进行响应式更新",[139,146,147],{},"可选的，和 Vue 之前的语法没有差别。只需要添加一些配置就可以获得很多的性能优化",[149,150,151],"blockquote",{},[15,152,153],{},[41,154,157],{"href":155,"rel":156},"https:\u002F\u002Fkrausest.github.io\u002Fjs-framework-benchmark\u002F2025\u002Ftable_chrome_138.0.7204.50.html",[45],"最新的框架性能测试",[149,159,160],{},[15,161,162,163,168],{},"而且在结合了引入的",[41,164,167],{"href":165,"rel":166},"https:\u002F\u002Fgithub.com\u002Fstackblitz\u002Falien-signals",[45],"alien-signals","后，可以让响应式更加快速，性能会更加逼近原生 JS。",[10,170,172],{"id":171},"怎么使用-vapor-mode","怎么使用 Vapor Mode？",[15,174,175,176],{},"它只在使用 script setup 的单文件组件中有用，在组件的 script 中添加 ",[30,177,178],{"code":178},"vapor",[64,180,183],{"className":181,"code":182,"language":69,"meta":62},[67],"\u003Cscript setup vapor>\n\u002F\u002F ...\n\u003C\u002Fscript>\n",[30,184,182],{"__ignoreMap":62},[15,186,187,188],{},"除此之外，我们需要在 createApp时添加 ",[30,189,190],{"code":190},"vaporInteropPlugin",[64,192,198],{"className":193,"code":195,"filename":196,"language":197,"meta":62},[194],"language-ts","import { createApp, vaporInteropPlugin } from 'vue'\n\nimport App from '.\u002FApp.vue'\n\ncreateApp(App)\n    .use(vaporInteropPlugin) \u002F\u002F enable vapor interop\n    .mount('#app')\n","main.ts","ts",[30,199,195],{"__ignoreMap":62},[10,201,203],{"id":202},"什么时候可以使用-vapor-mode","什么时候可以使用 Vapor Mode？",[136,205,206,209],{},[139,207,208],{},"创建一个新的小型项目",[139,210,211],{},"现有应用对性能的需求很高的子页面",[149,213,214,217],{},[15,215,216],{},"当然，现在并不推荐大家使用 Vapor",[136,218,219,222,225],{},[139,220,221],{},"目前它只是 vue3.6 的 alpha 版本，还不是很稳定。",[139,223,224],{},"有一些功能还不能在 Vapor 中实现",[139,226,227],{},"一些边缘情况还没有被覆盖",[10,229,230],{"id":230},"使用时需要注意的地方",[136,232,233,236,239,250,253],{},[139,234,235],{},"最好不要 Vapor Mode 和虚拟 DOM 嵌套使用",[139,237,238],{},"不支持 Options API（当然，现在在普通模式中也不推荐使用 Options API）",[139,240,241,242,245,246,249],{},"模板表达式中不支持 ",[30,243,244],{"code":244},"$slot"," 和 ",[30,247,248],{"code":248},"$props"," 等隐式实例属性",[139,251,252],{},"@vue:xxx 生命周期事件",[139,254,255],{},"自定义指令也是不同的",[19,257,258],{"id":258},"更多部分我们可以查看",[136,260,261,268],{},[139,262,263],{},[41,264,267],{"href":265,"rel":266},"https:\u002F\u002Fgithub.com\u002Fvuejs\u002Fcore\u002Freleases\u002Ftag\u002Fv3.6.0-alpha.1",[45],"GitHub Release",[139,269,270],{},[41,271,274],{"href":272,"rel":273},"https:\u002F\u002Fwww.youtube.com\u002Fwatch?v=bhCIGxinkgo",[45],"YouTube Vue 3.6 ALPHA is out! All you need to know 👀\n",[10,276,277],{"id":277},"特别感谢",[136,279,280,288,296],{},[139,281,282,283],{},"本文的封面图来自",[41,284,287],{"href":285,"rel":286},"https:\u002F\u002Ficarusgk.hashnode.dev\u002Fvue-3-vapor-mode",[45],"icarusgk",[139,289,290,291],{},"对比图表来源",[41,292,295],{"href":293,"rel":294},"https:\u002F\u002Fx.com\u002Ficarusgkx\u002Fstatus\u002F1693620200543862852",[45],"icarusgkx",[139,297,298],{},"一些文章和视频参考",[300,301,302,309,314,319],"ul",{},[139,303,304],{},[41,305,308],{"href":306,"rel":307},"https:\u002F\u002Fcn.vuejs.org\u002Fguide\u002Fextras\u002Frendering-mechanism#patch-flags",[45],"Vue 渲染机制",[139,310,311],{},[41,312,267],{"href":265,"rel":313},[45],[139,315,316],{},[41,317,274],{"href":272,"rel":318},[45],[139,320,321],{},[41,322,325],{"href":323,"rel":324},"https:\u002F\u002Fwww.vuemastery.com\u002Fblog\u002Fthe-future-of-vue-vapor-mode",[45],"The Future of Vue: Vapor Mode",{"title":62,"searchDepth":327,"depth":327,"links":328},4,[329,337,338,339,342],{"id":12,"depth":330,"text":13,"children":331},2,[332,334,335,336],{"id":21,"depth":333,"text":22},3,{"id":50,"depth":333,"text":51},{"id":95,"depth":333,"text":96},{"id":117,"depth":333,"text":117},{"id":171,"depth":330,"text":172},{"id":202,"depth":330,"text":203},{"id":230,"depth":330,"text":230,"children":340},[341],{"id":258,"depth":333,"text":258},{"id":277,"depth":330,"text":277},null,[345],"代码","2025-07-19 17:59:31","Vue 3.6.0-alpha.1在近几日发布了，它引入了说了很久的 Vapor Mode，今天我们一起看下为什么需要 Vapor Mode 以及目前使用它需要知道的事情。",false,"md","https:\u002F\u002Fbitmc.uno\u002Fpicgo\u002Fvue-vapor-mode.png",{},true,"\u002Fposts\u002F2025\u002Fvue-alpha-vapor-mode",{"text":355,"minutes":356,"time":357,"words":358},"8 min read",7.235,434100,1447,{"title":5,"description":347},{"loc":353,"images":361},[362],{"loc":128},"posts\u002F2025\u002Fvue-alpha-vapor-mode",[365,366,367,368],"新东西","Vue","Vapor Mode","Svelte","Xlt4On0WUIJX7Qu6k02wUIa7UDW35vTNo9amf5mzq5w",[371,377],{"title":372,"path":373,"stem":374,"date":375,"type":376,"children":-1},"乙巳年六月廿四","\u002Fposts\u002F2025\u002F0718-life","posts\u002F2025\u002F0718-life","2025-07-18 17:21:11","story",{"title":378,"path":379,"stem":380,"date":381,"type":343,"children":-1},"在 nestjs 中使用 scalar","\u002Fposts\u002F2025\u002Fnestjs-scalar","posts\u002F2025\u002Fnestjs-scalar","2025-08-02 11:44:47",1789642589050]