vue3的深入组件-组件 v-model

组件 v-model
基本用法​

v-model 可以在组件上使用以实现双向绑定。

从 Vue 3.4 开始,推荐的实现方式是使用 defineModel() 宏:

复制代码
<script setup>
const model = defineModel()

function update() {
  model.value++
}
</script>

<template>
  <div>Parent bound v-model is: {{ model }}</div>
  <button @click="update">Increment</button>
</template>

父组件可以用 v-model 绑定一个值:

复制代码
<script setup lang="ts">
import { useAppStore } from '@/store/modules/app'
const appStore =  useAppStore()
import modelChild from './components/test/modelChild.vue'
const textColor = computed(() => appStore.getTextColor)
appStore.initTheme()
const countModel = ref(10)
</script>

<template>
  <ConfigGlobal>
    <p :style="{'color':textColor}"  >p标签</p>
    {{ countModel }}
    <modelChild v-model="countModel"></modelChild>
   </ConfigGlobal>
  
</template>

显示如下:

defineModel() 返回的值是一个 ref。它可以像其他 ref 一样被访问以及修改,不过它能起到在父组件和当前变量之间的双向绑定的作用:

  • 它的 .value 和父组件的 v-model 的值同步;

  • 当它被子组件变更了,会触发父组件绑定的值一起更新。
    这意味着你也可以用 v-model 把这个 ref 绑定到一个原生 input 元素上,在提供相同的 v-model 用法的同时轻松包装原生 input 元素:

    child.vue

    // app.vue

底层机制

defineModel 是一个便利宏。编译器将其展开为以下内容:

  • 一个名为 modelValue 的 prop,本地 ref 的值与其同步;

  • 一个名为 update:modelValue 的事件,当本地 ref 的值发生变更时触发。

因为 defineModel 声明了一个 prop,你可以通过给 defineModel 传递选项,来声明底层 prop 的选项:

子组件

复制代码
<script setup>
const model = defineModel({required:true, default: 1 }) // 对应 model 参数
</script>

<template>
  <input v-model="model" type="text">
  <!-- <input v-model="age" type="number"> -->
</template>

父组件

复制代码
<script setup lang="ts">
const countModel = ref()
console.log(countModel.value,'countModel'); // undefined
</script>
<template>
  <ConfigGlobal>
    {{ countModel }}
    <modelChild v-model="countModel"></modelChild>
   </ConfigGlobal>
  
</template>

如果为 defineModel prop 设置了一个 default 值且父组件没有为该 prop 提供任何值,会导致父组件与子组件之间不同步。在下面的示例中,父组件的 countModel 是 undefined,而子组件的 model 是 1:

v-model 的参数
复制代码
//子组件
<script setup>
const title = defineModel('title')
</script>

<template>
  <input type="text" v-model="title" />
</template>


// 父组件
<script setup>
import { ref } from 'vue'
import MyComponent from './MyComponent.vue'
  
const bookTitle = ref('v-model argument example')
</script>

<template>
  <h1>{{ bookTitle }}</h1>
  <MyComponent v-model:title="bookTitle" />
</template>
不同属性绑定多个 v-model

父组件​

复制代码
<UserForm 
  v-model:username="user.name"
  v-model:age="user.age"
/>

子组件

复制代码
<script setup>
const username = defineModel('username') // 对应 username 参数
const age = defineModel('age') // 对应 age 参数
</script>

<template>
  <input v-model="username" type="text">
  <input v-model="age" type="number">
</template>
处理 v-model 修饰符

父组件

使用内置修饰符(如 .trim):

复制代码
<Child v-model.trim="text" />

子组件

复制代码
<script setup>
const [model, modifiers] = defineModel() // 解构出修饰符

// 根据修饰符调整值
const processedModel = computed({
  get: () => model.value,
  set: (value) => {
    if (modifiers.trim) {
      model.value = value.trim()
    } else {
      model.value = value
    }
  }
})
</script>

<template>
  <input v-model="processedModel" />
</template>
  • 使用自定义修饰符 .capitalize:
    创建一个自定义的修饰符 capitalize,它会自动将 v-model 绑定输入的字符串值第一个字母转为大写

父组件

复制代码
<Child v-model.capitalize="text" />

子组件​​

通过 set 选项处理修饰符逻辑

复制代码
<script setup>
const [model, modifiers] = defineModel({
  set(value) {
    if (modifiers.capitalize) {
      return value.charAt(0).toUpperCase() + value.slice(1)
    }
    return value
  }
})
</script>

<template>
  <input type="text" v-model="model" />
</template>
带参数的 v-model 修饰符

父组件

复制代码
<UserForm 
  v-model:username.trim="user.name"
  v-model:age.number="user.age"
/>

子组件​​

分别处理每个参数的修饰符:

复制代码
<script setup>
const [username, usernameModifiers] = defineModel('username')
const [age, ageModifiers] = defineModel('age')

// 处理 username 的 trim 修饰符
const processedUsername = computed({
  get: () => username.value,
  set: (val) => {
    username.value = usernameModifiers.trim ? val.trim() : val
  }
})

// 处理 age 的 number 修饰符
const processedAge = computed({
  get: () => age.value,
  set: (val) => {
    age.value = ageModifiers.number ? Number(val) : val
  }
})
</script>

<template>
  <input v-model="processedUsername" />
  <input v-model="processedAge" type="number" />
</template>
相关推荐
01_ice13 分钟前
前端学习css
前端·css·学习
愚公搬代码32 分钟前
【愚公系列】《Web应用安全》010-Repeater模块的使用
前端·安全
Cache技术分享1 小时前
503. Java 反射 - 编写 ServiceFactory 类
前端·后端
赵大仁1 小时前
AI 限流 UX 设计:排队、降级、告知与用户预期管理
前端·ai·限流·用户体验·产品设计
__sjfzllv___1 小时前
在职前端Leader学习/转行 AI Agent -DAY37
前端
用户2181697049301 小时前
Flutter (二十二) GlobalKey
前端
用户2181697049301 小时前
Flutter (二十一) 下拉刷新,加载更多
前端
breeze jiang2 小时前
Next.js 16 App Router 实战:从 About、Blog 动态路由到全局 404
开发语言·javascript·ecmascript
小蒜学长2 小时前
vue旅游攻略网站(代码+数据库+LW)
java·数据库·vue.js·spring boot·后端·旅游
IT_陈寒2 小时前
Python装饰器把我坑惨了,原来这样用才不掉链子
前端·人工智能·后端