Vue 的核心仓库里有个还在讨论中的 PR,把编译器的 source map 换了一套做法,CPU 省掉三成左右,内存省掉三分之一,顺手修掉十几个报错指错行的问题。
提这个 PR 的是 johnsoncodehk,Vue 核心团队成员,muggle-string 就是他自己的库。
先说清楚 source map 是什么。
写 .vue 的时候,报错堆栈要指回第几行第几列,浏览器开发者工具里下断点也要落在 .vue 文件上,靠的就是编译时顺手生成的一份对照表,业内叫 source map。
旧的生成方式有点绕,一边用 magic-string 改源码字符串,一边用 source-map-js 另外记一份映射,两套并排走。
平时没事,一碰上多行表达式、v-for、v-model 这种要插代码的场景,两边就容易对不上。
新做法换了思路,每一段生成代码自带来源范围,映射直接由片段拼出来,不再单独维护。
那错位具体长什么样呢。
比如多行的 :class,生成的取值会被指到上一行的末尾。
v-for 的行里,生成的开始标记和文本转换会指错行。
v-model 编译出来的那层指令包装,在旧版里干脆没有映射,点了断点跳不到源码。
这些零碎的错位,PR 里一共列了十几个,散落在不同编译器包上,这次一起修了。
快多少呢。
编译脚本热启动的 CPU 从 2988 毫秒降到 2108 毫秒,少了 29%,峰值内存从 848 MB 降到 563 MB。
冷启动从 4517 毫秒降到 3575 毫秒,内存从 759 MB 降到 445 MB,少了四成。
带内联映射的编译模板少了 37%,解析阶段掉得最狠,从 491 毫秒降到 277 毫秒。
1297 个组件解析完,保留堆从 113.4 MB 降到 42.0 MB,少了六成多。
他交代了测试口径,1297 个 .vue 文件、Node 24、Apple Silicon、只跑一轮,没说这些数字可以当排名看。
装的东西也跟着少了一点。
compiler-sfc 的安装树从 12 个包降到 11 个,source-map-js 从几个编译器包里一起摘掉了,只剩一个间接依赖还在用。
浏览器产物那边,compiler-sfc 小 5%,compiler-vapor 小 8%,运行时那个全局包基本没动。
顺手还修了几个不算映射的问题。
await await foo() 这种写法,旧版外层拿到的是空值,现在能拿到正确结果。宏参数里写 await,以前直接报语法错误,现在能正常渲染。defineModel 的选项对象以前会被算三次,现在算一次。
代价呢。
最直接的一条,MagicString 不再从 @vue/compiler-sfc 导出了,PR 里写明这是故意的破坏性改动。在线演练场那类工具会受影响,修复已经进了主分支,等下一个版本发出来。
另一条是,这个改动会进小版本,不是补丁版本。
现在什么状态呢。
改动提出来之后,标题改了两次,从重构改成了性能优化。
第二天作者重跑了生态测试,十六个套件里还有八个是红的,nuxt、vue-i18n、vuetify、router 这些都在里面。
他自己分析过原因,一类是快照要更新,一类是新依赖发布太新,被包管理器的发布年龄策略拦住装不上,等版本老一点重跑就行。
同一天定时跑的基线是全绿的,所以红的那部分,是这次改动带出来的。
还有一点挺有意思,分支名里带着 cursor 字样,不少提交把 Cursor Agent 写成共同作者。
一个影响整条 Vue 编译链的改动,一部分提交已经交给 AI 编码助手来写,人负责看和改。
值不值得看呢,如果你只写业务代码,短期没什么感觉,除了升级时留意 MagicString 那一条。
要是你在做构建工具、在线演练场,或者自己包了一层编译器,这个 PR 值得从头读一遍,它把「生成的代码怎么留住出处」重新做了一遍。
PR 地址:https://github.com/vuejs/core/pull/16126。
关于我:
- 农村程序员(
2020年回农村,至今未出村) - 独立开发者(
致力于开发 1000 个小产品) - 技术观察者(
分享编程技术,行业资讯等) - 前端之虎陈随易(
自封的) - 我的官网是:
https://yicode.tech