Vue v-model 实现

1. 基本原理

1.1 表单元素

v-model 指令在表单元素(<input><textarea><select>)上创建的双向数据绑定。会根据控件的类型自动选取正确的方法来更新元素值。

1.2 自定义组件

在自定义组件上,v-model 本质是语法糖,会将值绑定到默认的 prop(vue2:value) 上,监听组件内部抛出的默认事件(vue2: input )更新元素值。
v-model prop 和 事件 的默认名称:

Vue2 Vue3
prop value modelValue
事件 input update:modelValue

在自定义组件上,
原理是用 v-bind 绑定value值,用 v-on 监听值的变化并重新赋值,以 Vue2 自定义组件 <my-input> 为例,组件外部监听input事件变化代码如下:

<my-input v-model="myValue">
<!-- 是以下的简写: -->
<!-- 1. 组件上 -->
<my-input v-bind:value="myValue" v-on:input="(val) => { myValue = value}">
<!-- 2. 组件内部 -->
<template>
  <input
    v-bind="$attrs"
    v-bind:value="myValue"
    v-on:input="$emit('input', $event.target.value)"
  >
</template>

2. 自定义组件,prop 和 event 的默认值修改

2.1 Vue2

// 选项式 API
model: {
  prop: 'checked', // 默认为 value
  event: 'change' // 默认为 input
},
props: {
  checked: Boolean
},

2.2 Vue3

<ChildComponent v-model:title="pageTitle" />

<!-- 是以下的简写: -->

<ChildComponent :title="pageTitle" @update:title="pageTitle = $event" />

3. .sync修饰符(Vue3中已移除)

为 prop 提供从内部调用 this.$emit('update:title', newTitle) 修改能力的语法糖

prop是默认是不进行双向绑定的,因为双向绑定会带来维护上的问题(试想一下,子组件可以变更父组件,且在父组件和子组件两侧都没有明显的变更来源,都是直接赋值修改。那么父组件中的这个值会变的十分难以预判)。所以 Vue 推荐以 update:myPropName 形式触发事件,来实现 prop 的双向绑定,例如:

<text-document
  v-bind:title="doc.title"
  v-on:update:title="(newTitle) => doc.title = newTitle"
></text-document>
// text-document 组件内部,通过触发 `update:title` 方法修改外部的值
this.$emit('update:title', newTitle)

为了方便起见,我们为这种模式提供一个缩写(语法糖),即 .sync 修饰符:

<text-document v-bind:title.sync="doc.title" ></text-document>

PS: Vue3 中可通过 v-model 添加参数实现,上例在Vue3中同下

<text-document v-model:title="doc.title" ></text-document>

4. Vue3 中的 defineModel() 和 useModel()

4.1 defineModel()

仅在 3.4+ 版本中可用
这个宏可以用来声明一个双向绑定 prop,通过父组件的 v-model 来使用。
文档:https://cn.vuejs.org/api/sfc-script-setup.html#definemodel

// 声明 "modelValue" prop,由父组件通过 v-model 使用
const model = defineModel()
// 或者:声明带选项的 "modelValue" prop
const model = defineModel({ type: String })

// 在被修改时,触发 "update:modelValue" 事件
model.value = "hello"

// 声明 "count" prop,由父组件通过 v-model:count 使用
const count = defineModel("count")
// 或者:声明带选项的 "count" prop
const count = defineModel("count", { type: Number, default: 0 })

function inc() {
  // 在被修改时,触发 "update:count" 事件
  count.value++
}

4.2 useModel()

仅在 3.4+ 版本中可用
这是驱动 defineModel() 的底层辅助函数。如果使用 <script setup>,应当优先使用 defineModel()

示例:

export default {
  props: ['count'],
  emits: ['update:count'],
  setup(props) {
    const msg = useModel(props, 'count')
    msg.value = 1
  }
}

5. JSX / TSX 中的写法

render: scope => {
  return (
    <InputInteger model-value={scope.row.tchNum} onUpdate:modelValue={val => (scope.row.tchNum = val)} />
  );
}
posted @ 2024-07-05 15:57  Better-HTQ  阅读(55)  评论(0编辑  收藏  举报