如何在vue3中优雅地使用v-model?

文章目录

  • ps:vue3中推荐使用的是reactive写法,文中这种格式只是为了和vue2做对照,只是为了凸显vue3中对v-model的改进,仅供参考。
  • Vue中的数据绑定
      • v-bind
      • v-bind支持的类型
      • v-bind使用
      • v-model
  • vue2中v-model用法
    • 不使用v-model的双向绑定
      • 父组件
      • 子组件
    • 使用v-model后
      • 父组件
      • 子组件
      • 问题
  • .sync
  • vue2中使用v-model + .sync
      • 父组件
      • 子组件
  • vue3中v-model的使用
    • 父组件
    • 子组件
  • 总结

ps:vue3中推荐使用的是reactive写法,文中这种格式只是为了和vue2做对照,只是为了凸显vue3中对v-model的改进,仅供参考。

Vue中的数据绑定

绑定数据有三种方式:

  • 插值,也就是{{name}}的形式,以文本的形式和实例data中对应的属性进行绑定
  • v-bind
  • v-model

v-bind

eg:v-bind:class 可简写为 :class

当加上v-bind:之后,它的值classe不是字符串,而是vue实例对应的data.class的这个变量。也就是说data.class是什么值,它就会给class属性传递什么值,当data.class发生变化的时候,class属性也发生变化,这非常适合用在通过css来实现动画效果的场合。他只是单向变动

v-bind支持的类型

html中的属性、css的样式、对象、数组、number 类型、bool类型

v-bind使用

// 绑定文本
<p v-bind="message">p>

// 绑定属性

<p v-bind:src="http://....">p>
<p v-bind:class="http://....">p>
<p v-bind:style="http://....">p>
// 绑定表达式
:class{className:true}

v-model

v-model 指令在表单 、 及 元素上创建双向数据绑定。它会根据控件类型自动选取正确的方法来更新元素。尽管有些神奇,但 v-model 本质上不过是语法糖。它负责监听用户的输入事件以更新数据,并对一些极端场景进行一些特殊处理

主要是用在表单元素中,它实现了双向绑定。在同时使用v-bind和v-model中,v-model建立的双向绑定对输入型元素input, textarea, select等具有优先权,会强制实行双向绑定。很多时候v-model使用在表单的中实现双向绑定。

v-model实现了表单输入的双向绑定,一般是这么写的:

 <div id="app">
     <input v-model="price">
 div>
<script>
    new Vue({
        el: '#app',
        data: {
            price: ''
        }
    });
script>

通过该语句实现price变量与输入值双向绑定

实际上v-model只是一个语法糖,真正的实现是这样的:

<input type="text" 
      :value="price" 
      @input="price=$event.target.value">

以上代码分几个步骤:

  • 将输入框的值绑定到price变量上,这个是单向绑定,意味着改变price变量的值可以改变input的value,但是改变value不能改变price

  • 监听input事件(input输入框都有该事件,当输入内容时自动触发该事件),当输入框输入内容就单向改变price的值

这样就实现了双向绑定。

<input v-model="giveDate" />
<input :value="giveDate" @input="giveDate = $event.target.value" /> 

第一行的代码其实只是第二行的语法糖。

vue2中v-model用法

不使用v-model的双向绑定

如下代码所示,这是不用v-model时候的写法。

父组件使用v-bind给子组件中的props传值。

子组件需要使用emit方法来实现子组件---->父组件方向上的绑定。

父组件


<template>
    <div>
        
        <search @test="getData" :keywords="keywords">search>
        <button @click="submit">提交button>
    div>
template>
<script>
import search from '@/components/index/search.vue'
export default {
    data() {
        return {
            keywords: ''
        }
    },
    components: {
        search
    },
    methods: {
        // 在这里实现更改父组件的值
        getData(val){
            this.keywords = val
        },
        submit() {
            console.log('keywords:', this.keywords)
        }
    }
}
script>

子组件


<template>
    <div>
        <input @input="inputChange" type="text" name="keywords">
    div>
template>
<script>
export default {
    props: ['keywords'],
    methods: {
        inputChange(e) {
          	// 传给父元素的test函数
            this.$emit('test', e.target.value)
        }
    }
}
script>

即,在父组件中,通过v-bind给子组件传参,同时指定v-on和指定修改目标值的函数。

在子组件中,接受父组件v-bind传过来的值,实现父->子方向上的绑定,然后通过emit方法,向父组件传出$event.target.value,实现子->父方向上的绑定。

使用v-model后

父组件


<template>
  <div class="parent"><p>父组件将传递给子组件的数据是:
    <input type="text" v-model="giveChildData"/>p>
    <Child v-model="giveChildData">Child>
  div>
template>
<script>
import Child from './Child.vue';
export default {
  name: "Father",
  data() {
    return {
      todosth: 0,
      giveChildData: '儿子,爸要把家产传给你啊!'
    };
  },
  components: {
    Child
  }
}
script>

子组件


<template>
  <div class="child">
    <p>子组件将通过$emit调用父组件的函数并修改数据:{{ give }}p>
    <span @mousemove="returnBackFn">答复span>
  div>
template>
<script>
export default {
  name: "Child",
  props: {
    give:
        {
          type: String,
          default: '0'
        }
  },
  model: {
    prop: 'give',
    event: 'returnBack'
  },
  methods: {
    returnBackFn() {
      this.$emit('returnBack', '啥家产?有矿啊!');
    }
  }
};script>


这里的 giveChildData 的值将会传入这个名为 give 的 prop。同时当 触发一个 returnBack 事件并附带一个新的值的时候,这个 giveChildData 的属性将会被更新。

问题

但是当有多个数据需要父子组件之间双向绑定时,v-model只能传其中一个值。这时候需要使用.sync来实现其他数据的绑定。

.sync

从 2.3.0 起,vue重新引入了 .sync 修饰符,但是这次它只是作为一个编译时的语法糖存在。它会被扩展为一个自动更新父组件属性的 v-on 监听器。
示例代码如下:

<comp :foo.sync="bar">comp>

会被扩展为:

<comp :foo="bar" @update:foo="val => bar = val"></comp>

当子组件需要更新 foo 的值时,它需要显式地触发一个更新事件:

this.$emit('update:foo', newValue)

实例如下。(弹窗的关闭事件)

<template>
  <div class="details">
    <myComponent
        :show.sync='valueChild'
        style="padding: 30px 20px 30px 5px;border:1px solid #ddd;margin-bottom: 10px;"
    >
    myComponent>
    <button @click="changeValue">togglebutton>
  div>
template>
<script>
import Vue from 'vue'

Vue.component(
    'myComponent', {
      template: `
        

默认初始值是{{ show }},所以是显示的

`
, props: ['show'], methods: { closeDiv() { this.$emit('update:show', false); //触发 input 事件,并传入新值 } } }) export default { data() { return { valueChild: true, } }, methods: { changeValue() { this.valueChild = !this.valueChild } } }
script>

效果如下图。

如何在vue3中优雅地使用v-model?_第1张图片

vue2中使用v-model + .sync

由于v-model只能默认传进去一个值,剩下的需要使用.sync实现双向绑定。

对于v-model传进来的数,子组件用emit方法向父组件的“input”传值(注:是v-model默认监听的方法)。

而对于.sync传进来的数,则是通过在子组件中,使用emit向父组件的update:绑定数据名,进行传值(注:默认提供了update方法,当然也可以自己在父组件中自定义一个修改绑定值的函数)。

父组件

<h1>name01:{{ name01 }} h1>
<h1>age01:{{ age01 }} h1>
<model01
         :model="age01"
         :name.sync="name01"
         >
model01>
...

<script>
export default{
    data(){
        return{
            age01: 18,
            name01: "username"
        }
    }
}
    
script>

子组件

<template>
  <div class="custom-input">
    <h1>vue2中的v-modelh1>
    <input
        type="text"
        :value="age"
        @input="$emit('input', $event.target.value)"
    />
    
    <br>
    <input type="text" :value="name" @input="$emit('update:name', $event.target.value)"/>
     
  div>
template>

<script>
export default {
  name: "Model01",
  props: [
    'age',
    'name'
  ],
  model:{
      
  }
  methods: {
  }
}
script>

<style scoped>

style>

vue3中v-model的使用

接下来就是正点了。

父组件

vue3中,将之前的v-model和.sync整合到一起了,并淘汰了.sync写法。

现在,v-model在组件之间的使用再也不用像以前那样臃肿了,极其舒爽。

只需要在v-model上指定传值。例如:

<model02
	v-model:age="age02"
	v-model:name="name02"
 >model02>

这里就是将父组件中的age02变量传入到子模块的props中的age变量中。

子组件只要使用如下调用update:age的方式,就能将age的变化由子组件的age传入到父组件的age02变量上。

$emit('update:age', 传给父组件的值);
....
<h1>name02:{{ name02 }} h1>
<h1>age02:{{ age02 }} h1>

<model02
	v-model:age="age02"
	v-model:name="name02"
 >model02>
....

子组件

<template>
  <div class="custom-input">
    <h1>vue3中的v-modelh1>
    <input type="text" :value="age" @input="onAgeInput"/>
    <br>
    <input type="text" :value="name" @input="onNameInput"/>
  div>
template>

<script>
export default {
  name: "Model02",
  props: [
    'age',
    'name'
  ],
  methods: {
    onAgeInput(e) {
      this.$emit('update:age', parseFloat(e.target.value));
    },
    onNameInput(e) {
      this.$emit('update:name', e.target.value)
    }
  }
}
script>

<style scoped>

style>

实现效果图:

如何在vue3中优雅地使用v-model?_第2张图片

总结

vue3中的v-model比以前舒爽太多了!

ps:vue3中推荐使用的是setUp()写法,上面这种格式只是为了和vue2做对照,只是为了凸显vue3中对v-model的改进,仅供参考。

你可能感兴趣的:(前端,vue,js,javascript)