除了 defineProps/defineEmits,还有哪些 Vue 3 编译时优化?

5 人参与

在 Vue 3 的 <script setup> 生态里,definePropsdefineEmits 只是一枚敲门砖,真正的性能提升往往隐藏在编译阶段的细节打磨中。换句话说,编译器在把 SFC 交给浏览器之前,已经悄悄完成了一系列“零运行时”改造,让开发者在写代码时几乎感受不到这些背后的算计。

模板静态提升(Static Hoisting)

如果模板中出现纯文本、常量属性或不依赖响应式数据的节点,编译器会把这些节点提升为常量 VNode,直接写进渲染函数的闭包里。这样在每次组件更新时,虚拟 DOM 只需要对比动态部分,省去无意义的创建与销毁。

v-oncev-memo 的编译时折叠

使用 v-once 标记的节点在第一次渲染后会被编译器视为“不可变”,后续的补丁过程直接跳过。v-memo 则通过比较提供的依赖列表,在依赖不变时复用上一次的渲染产物。两者都把运行时的条件判断压缩到编译期,让渲染路径更简洁。

宏式 API:defineExposedefineOptionsdefineModel

这些宏同样在编译阶段被剥离,生成的代码直接写入组件选项对象。例如 defineExpose({ foo }) 会被转成 expose: ['foo'],不再保留函数调用痕迹;defineModel 则把双向绑定的逻辑提前拆解为 propsemits 的组合体,省去运行时的包装层。

编译时的 Tree‑shaking 与指令优化

  • 未使用的 refcomputed 会在生成的模块中被剔除。
  • 自定义指令如果只在模板里出现一次,编译器会把指令函数内联,避免额外的函数引用。
  • v-if / v-else 的分支进行常量折叠,若条件在编译期可确定,则只保留必然执行的分支。

异步组件的预取(Prefetch)与懒加载标记

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 “编译时即优化”的生态链,让开发者在写业务代码时感受到的不是性能瓶颈,而是一种几乎透明的流畅体验。

参与讨论

5 条评论
  • 翡翠咏者

    这个v-memo用起来会不会很麻烦?

  • 烬火

    静态提升确实省心,之前项目里没注意这块

  • 影子追光者

    v-once用来做啥的,感觉用得不多啊

  • 比特猎人

    编译时优化听着高大上,实际用起来感知强吗🤔

  • 夜语低吟

    那defineModel是不是就不用写emit了?