您现在的位置是:首页 > 技术教程 正文

【Vue】v-model 的使用

admin 阅读: 2024-04-02
后台-插件-广告管理-内容页头部广告(手机)

双向绑定 v-model

  • v-model:value="值" 可简写为 v-model="值",用于双向绑定 [表单元素] 的信息
  • 双向绑定:表单元素的 value attribute 的值 ↔ data 中对应的值
  • 本质上,v-model 是由 v-bind 配合 input 事件实现的
    ① v-bind 绑定 value 属性、② 在 input 事件的回调函数中更新 value 的值

文本 input [text]

<div id="app"> <p>Message is: {{ msg }}p> <input v-model="msg" type="text" /> div>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
let vm = new Vue({ el: '#app', data: { msg: 'superman' }, });
  • 1
  • 2
  • 3
  • 4

上例中,input 的默认值为 data 中的 msg,更新 input 的 value 值 → msg 也会被更新 → p 显示的内容也会被更新


多行文本 textarea

  • 对于文本域标签 并不会生效,要设置 v-model 属性来替代
<textarea v-model="msg">textarea>
  • 1

单选框 input [radio]

  • 因为 v-model 绑定的是 value 值

    所以,我们需要设置 value 属性;如果没有设置 value 属性,则获取到的值为 null

<input type="radio" id="male" value="male" v-model="sex" /> <label for="male">malelabel> <br /> <input type="radio" id="female" value="female" v-model="sex" /> <label for="female">femalelabel> <br /> <span>sex: {{ sex }}span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
let vm = new Vue({ el: '#app', data: { sex: '', // sex: "male" // 设置默认值 'male',默认选中 male 选项 }, });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

复选框 input [checkbox]

  1. 没有配置 value 属性,收集的是 checkbox 的 checked 状态,所以会收集到布尔值
<input type="checkbox" id="checkbox" v-model="checked" /> <label for="checkbox">{{checked}}label>
  • 1
  • 2
let vm = new Vue({ el: '#app', data: { checked: true }, // 绑定 [布尔值] });
  • 1
  • 2
  • 3
  • 4
  1. 配置了 value 属性,绑定 [非数组],收集的也还是 checkbox 的 checked 状态
    所以也还是会收集到布尔值 → ture-全选、false-全不选
篮球 <input type="checkbox" value="篮球" v-model="checked" /> 足球 <input type="checkbox" value="足球" v-model="checked" /> 网球 <input type="checkbox" value="网球" v-model="checked" /> <p>{{checked}}p>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  1. 配置了 value 属性,绑定 [数组],才能收集到多选框的 value 值
篮球 <input type="checkbox" value="篮球" v-model="checked" /> 足球 <input type="checkbox" value="足球" v-model="checked" /> 网球 <input type="checkbox" value="网球" v-model="checked" /> <p>{{checked}}p>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
let vm = new Vue({ el: '#app', data: { checked: [], // 绑定到数组 }, });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 能获取到多个值的表单元素,都应该绑定 [数组],以存储多个数据

选择框 select > option

  • 对于下拉菜单,推荐提供一个值为空的禁用选项,作为默认显示选项
<select v-model="selected"> <option disabled value="" > 国家 option> <option value="China">中国option> <option value="America">美国option> <option value="Japan">日本option> select> <span>Selected: {{ selected }}span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
let vm = new Vue({ el: '#app', data: { selected: '', // selected: "China" // 设置默认值 }, });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 如果 option 标签不设置 value 属性,则获取到的是 option 标签的内容
<select v-model="selected"> <option disabled value="" > 国家 option> <option>中国option> <option>美国option> <option>日本option> select> <span>Selected: {{ selected }}span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 多选时,绑定到一个数组
<select multiple v-model="selected"> <option disabled value="" > 国家 option> <option>中国option> <option>美国option> <option>日本option> select> <span>Selected: {{ selected }}span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
let vm = new Vue({ el: '#app', data: { selected: [], // 绑定到数组 }, });
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

标签修饰符

  1. .lazy:在 change 事件之后同步,即失焦再同步(默认 input 事件之后同步,即一边写一边同步)
  2. .trim:过滤首尾空白字符
  3. .number:将输入数据使用 parseFloat() 转为 Number 类型;若这个值无法被 parseFloat() 解析,则会返回输入的字符串
<input type="text" v-model.lazy="username" /> <span>{{ username }}span> <br /> <input type="text" v-model.trim="password" /> <span>{{ password }}span> <br /> <input type="number" v-model.number="age" /> <span>{{ age }}span> <br /> <input type="text" v-model.number="height" /> <span>{{ height }}span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
表单提交事件
  • submit 提交表单时触发
  • .prevent 阻止默认行为
<form @submit.prevent="console.log('submit successfully')">form>
  • 1

Vue2 组件使用 v-model

v-model 其实是 v-bind:value + v-on:input 的语法糖

<template> <div> {{ demoText }} <input :value="demoText" @input="ev => (demoText = ev.target.value)" /> <input v-model="demoText" /> div> template> <script> export default { name: 'App', data() { return { demoText: 'demoText', }; }, }; script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

同理,在自定义组件上使用:

<template> <div> {{ demoText }} <HelloWorld :value="demoText" @input="val => (demoText = val)" /> <HelloWorld v-model="demoText" /> div> template> <script> import HelloWorld from '@/components/HelloWorld.vue'; export default { name: 'App', components: { HelloWorld }, data() { return { demoText: 'demoText', }; }, }; script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

需要注意的是:自定义组件上的 :value 变成了自定义属性,@input 变成了自定义事件

<template> <input type="text" :value="value" @input="$emit('input', $event.target.value)" /> template> <script> export default { name: 'HelloWorld', props: ['value'], // 接收自定义属性 }; script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

.sync 修饰符

上例可以优化成:

<template> <div> {{ demoText }} <HelloWorld :title="demoText" @update:title="demoText = $event" /> div> template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
<template> <input type="text" :value="title" @input="$emit('update:title', $event.target.value)" /> template> <script> export default { name: 'HelloWorld', props: ['title'], }; script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

这种写法可以使用语法糖

<template> <div> {{ demoText }} <HelloWorld v-bind:title.sync="demoText" /> div> template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

注意:带有 .sync 修饰符的 v-bind 不能和表达式一起使用;eg: v-bind:title.sync="doc.title + '!'" 是无效的

  • 当我们用一个对象同时设置多个 prop 的时候,也可以将这个 .sync 修饰符和 v-bind 配合使用
<HelloWorld v-bind.sync="doc">HelloWorld>
  • 1

这样会把 doc 对象中的每一个 property (如 title) 都作为一个独立的 prop 传进去,然后各自添加用于更新的 v-on 监听器

注意:将 v-bind.sync 用在一个字面量的对象上,eg: v-bind.sync="{ title: doc.title }",是无法正常工作的
因为在解析一个像这样的复杂表达式的时候,有很多边缘情况需要考虑

Vue3 组件使用 v-model

  • 在普通标签元素上使用 v-model
<template> {{ textDemo }} <input v-model="textDemo" /> template> <script setup lang="ts"> import { ref } from 'vue'; const textDemo = ref('textDemo'); script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

上例等价于

<template> {{ textDemo }} <input :value="textDemo" @input="textDemo = ($event.target as any).value" /> template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 在自定义组件上使用 v-model
<template> <HelloWorld v-model="textDemo" /> template> <script lang="ts" setup> import { ref } from 'vue'; import HelloWorld from '@/components/HelloWorld.vue'; const textDemo = ref('Vue.js'); script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

上例等价于

<template> <HelloWorld :modelValue="textDemo" @update:modelValue="(newValue:any) => (textDemo = newValue)" /> template>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

此时,子组件内需要做两件事:
① 将原生 input 元素的 value attribute 绑定到 modelValue prop
② 当原生的 input 事件触发时,触发一个携带了新值的 update:modelValue 自定义事件

<template> {{ modelValue }} <input type="text" :value="modelValue" @input="$emit('update:modelValue', ($event.target as any).value)" /> template> <script lang="ts" setup> defineProps<{ modelValue: string }>(); // 接收 `modelValue` prop defineEmits<{ (e: 'update:modelValue', val: string): void }>(); // 接收 `update:modelValue` 自定义事件 script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

v-model 的参数

  • 默认情况下,v-model 在组件上都是使用 modelValue 作为 prop,并以 update:modelValue 作为对应的事件
  • 可以给 v-model 指定一个参数来更改这些名字
<template> <HelloWorld v-model:title="textDemo" /> template> <script lang="ts" setup> import { ref } from 'vue'; import HelloWorld from '@/components/HelloWorld.vue'; const textDemo = ref('Vue.js'); script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
<template> {{ title }} <input type="text" :value="title" @input="$emit('update:title', ($event.target as any).value)" /> template> <script lang="ts" setup> defineProps<{ title: string }>(); // v-model 的参数名 title 作为 prop defineEmits<{ (e: 'update:title', val: string): void }>(); // 自定义事件名也变成了 update:title script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

v-model 的修饰符

  • v-model 有一些内置的修饰符 (.trim、.number、.lazy)
  • 若使用了自定义修饰符,可通过 modelModifiers prop 在组件内访问该自定义修饰符
  • 若 v-model 携带着参数,eg: v-model:title,则 prop 为 titleModifiers
<template> {{ title }} <input type="text" :value="title" @input="$emit('update:title', ($event.target as any).value)" /> template> <script lang="ts" setup> const props = withDefaults( defineProps<{ title: string; titleModifiers: {}; // 接收 prop titleModifiers }>(), { title: '', titleModifiers: () => ({}), // 设置默认值为空对象 } ); defineEmits<{ (e: 'update:title', val: string): void }>(); console.log(props.titleModifiers); // { capitalize: true } script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24

上例中,props.titleModifiers 的值为 { capitalize: true },是因为 v-model 使用了修饰符 capitalize。

有了这个 prop,就可以检查 modelModifiers 对象的 key,并编写一个处理函数以改变抛出的值

<template> {{ title }} <input type="text" :value="title" @input="emitValue" /> template> <script lang="ts" setup> const props = withDefaults( defineProps<{ title: string; titleModifiers: {}; }>(), { title: '', titleModifiers: () => ({}), } ); const emit = defineEmits<{ (e: 'update:title', val: string): void }>(); function emitValue(e: any) { let value = e.target.value; if ((props.titleModifiers as any).capitalize) { value = value.charAt(0).toUpperCase() + value.slice(1); } emit('update:title', value); } script>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30

标签:
声明

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

在线投稿:投稿 站长QQ:1888636

后台-插件-广告管理-内容页尾部广告(手机)
关注我们

扫一扫关注我们,了解最新精彩内容

搜索