在 Vue 3 的 <script setup> 生态里,defineProps 与 defineEmits 只是一枚敲门砖,真正的性能提升往往隐藏在编译阶段的细节打磨中。换句话说,编译器在把 SFC 交给浏览器之前,已经悄悄完成了一系列“零运行时”改造,让开发者在写代码时几乎感受不到这些背后的算计。
如果模板中出现纯文本、常量属性或不依赖响应式数据的节点,编译器会把这些节点提升为常量 VNode,直接写进渲染函数的闭包里。这样在每次组件更新时,虚拟 DOM 只需要对比动态部分,省去无意义的创建与销毁。
v-once 与 v-memo 的编译时折叠使用 v-once 标记的节点在第一次渲染后会被编译器视为“不可变”,后续的补丁过程直接跳过。v-memo 则通过比较提供的依赖列表,在依赖不变时复用上一次的渲染产物。两者都把运行时的条件判断压缩到编译期,让渲染路径更简洁。
defineExpose、defineOptions、defineModel这些宏同样在编译阶段被剥离,生成的代码直接写入组件选项对象。例如 defineExpose({ foo }) 会被转成 expose: ['foo'],不再保留函数调用痕迹;defineModel 则把双向绑定的逻辑提前拆解为 props 与 emits 的组合体,省去运行时的包装层。
ref、computed 会在生成的模块中被剔除。v-if / v-else 的分支进行常量折叠,若条件在编译期可确定,则只保留必然执行的分支。在 defineAsyncComponent 中加入 /* webpackPrefetch: true */ 注释,编译器会把对应的 chunk 标记为预取,浏览器在空闲时就把代码拉下来,实际渲染时几乎没有等待。
// 编译前
const props = defineProps({ title: String })
const emit = defineEmits(['close'])
// 编译后(简化)
export default {
props: { title: String },
emits: ['close'],
setup(__props, { emit }) { /* ... */ }
}
这些手法共同构筑了 Vue 3 “编译时即优化”的生态链,让开发者在写业务代码时感受到的不是性能瓶颈,而是一种几乎透明的流畅体验。
参与讨论
这个v-memo用起来会不会很麻烦?
静态提升确实省心,之前项目里没注意这块
v-once用来做啥的,感觉用得不多啊
编译时优化听着高大上,实际用起来感知强吗🤔
那defineModel是不是就不用写emit了?