面试官:说一下v-model把?你说的defineModel,是什么?没听说过。

1,493 阅读2分钟

前言

在一次面试的时候,面试官让我说说vue3的v-model。我说现在vue3.4提供了一个defineModel编译宏,通过这个实现自定义组件的双向绑定,但当我说到这个defineModel以后他表示很困惑,他说有这个吗?....然后赶紧讲了之前vue3怎么声明v-model的方式。 不过写这篇也是为了加深这块的记忆,以及defineModel具体的实现原理。

defineModel()

Vue 3.4 引入的 defineModel 是一个编译宏(Compiler Macro)​,其核心原理是通过编译时的代码转换,简化组件间双向数据绑定的实现。它是对传统 v-model 用法的封装,自动处理了 props 和事件触发的逻辑。


核心原理分步解析:

  1. 编译时转换

    • 当你使用 defineModel() 时,Vue 编译器会在构建阶段将其转换为标准的 props 和 emit 代码。

    • 输入代码​(开发者编写的):

      javascript
      const model = defineModel()
      
    • 编译后代码​(实际运行的):

      javascript
      const model = computed({
        get() {
          return props.modelValue // 来自父组件的 prop
        },
        set(value) {
          emit('update:modelValue', value) // 触发父组件更新
        }
      })
      
  2. 双向绑定自动化

    • 父组件通过 v-model 传递数据时,defineModel 自动生成对应的 modelValue prop。
    • 子组件修改 model.value 时,自动触发 update:modelValue 事件,无需手动 emit
  3. 支持多模型和配置

    • 可通过参数定义多个模型,如 defineModel('title') 会生成 title prop 和 update:title 事件。

    • 允许配置默认值、校验规则等:

      javascript
      const model = defineModel({
        default: '',
        validator: (v) => v.length > 0
      })
      
  4. 底层响应式依赖

    • 返回的 model 是一个 ​Computed Ref,依赖 Vue 的响应式系统。修改 model.value 会触发父组件的重新渲染。

与传统方式的对比:

传统写法​(手动处理 prop + emit):

javascript
// 子组件
props: ['modelValue'],
emits: ['update:modelValue'],
setup(props, { emit }) {
  const value = computed({
    get: () => props.modelValue,
    set: (v) => emit('update:modelValue', v)
  })
  return { value }
}
// setup写法
const props = defineProps(['modelValue']);
const emit = defineEmits(['update:modelValue']);
const count = computed({
  get: () => props.modelValue,
  set: (val) => emit('update:modelValue', val)
})

使用 defineModel

javascript
// 子组件
const model = defineModel() // 一行替代所有样板代码

优点总结:

  1. 代码简洁:减少手动编写 prop/emit 的重复代码。
  2. 类型安全:在 TypeScript 中自动推断类型,提升开发体验。
  3. 可维护性:集中管理模型逻辑(默认值、校验等)。
  4. 兼容性:支持 Vue 3.4+ 的 Options API 和 Composition API。

注意事项:

  • 需使用 @vue/compiler-sfc 5.0+ 版本(与 Vue 3.4 配套)。
  • 在 JSX/TSX 中需通过插件支持。
  • 底层依然是标准的 props + emit,无运行时性能损耗。

通过 defineModel,Vue 将双向绑定的实现细节隐藏在了编译层,让开发者更专注于业务逻辑。