组件使用v-model、$listeners、.sync(区别于v-model的双向数据绑定)

更新日期: 2019-12-23阅读: 2.1k标签: 数据绑定

自定义组件

自定义组件的v-model

首先我们先说一下在自定义组件中使用v-model的必要条件

在自定义的组件中要有input(这里我们先不讨论单选复选框)
在自定义组件的模板对象中要有props属性,且里面要含有一个value
在自定义组件的input标签上要绑定value属性值为props中传入的值,且还需要发出一个input事件

这样讲可能会有点难理解,还是上代码吧...

<div id="app">
  <child-com v-model="message"></child-com>
  <span>{{ message }}</span>
</div>
<template id="childCom">
  <div>
    <input type="text" :value="value" @input='inputEvent'>
  </div>
</template>

<script>
  const childCom = {
    template: '#childCom',
    props: ['value'],
    methods: {
      inputEvent(event) {
        this.$emit('aaa', event.target.value)
      }
    },
  }

  const vm = new vue({
    el: '#app',
    data: {
      message: '可以双向绑定的了'
    },
    components: {
      childCom
    }
  })
</script>

这是最终实现效果需要必备的,看完这些代码如果你是小白,你可能会有点不理解为什么要这样做,下面我告诉你原理。

首先在我们使用的v-model 中,其内部实现的原理就是一个 value属性和一个input事件,其主要步骤就是,用v-bind绑定value,然后用input事件监听值的变化,当文本框中的值发生变化的时候,input事件就会触发,那么我们可以在input事件中获取到改变后的值然后赋值给value,这样是不是就完成了双向数据绑定了。上代码:

<div id="app">
  <input type='text' :value='message' @input='inputEvent'>
  <span>{{ message }}</span>
</div>

<script>
  const vm = new Vue({
    el: '#app',
    data: {
      message: '可以双向绑定的了'
    },
    methods: {
      inputEvent(event) {
        this.message = event.target.value;
      }
    }
  })
</script>

就这样几个步骤,就达到了v-model的效果了,这就是他的原理,然后让我们深一步想,让自定义组件使用双向数据绑定。因为我们知道其内部就是value和input事件,

所以有了如下代码:

<div id="app">
    <child-com :value='message' @input='message=$event'></child-com> <!-- 此代码就这里和最开始代码不同 -->
    <span>{{ message }}</span>
  </div>

  <template id="childCom">
    <div>
      <input type="text" :value="value" @input='inputEvent'>
    </div>
  </template>

  <script>
    const childCom = {
      template: '#childCom',
      props: ['value'],
      methods: {
        inputEvent(event) {
          this.$emit('input', event.target.value)
        }
      },
    }

    const vm = new Vue({
      el: '#app',
      data: {
        message: '可以双向绑定的了'
      },
      components: {
        childCom
      }
    })
</script>

根据上面的原理,现在你应该知道了为什么要传一个value在子组件了吧,明白之后,您就可以把 <child-com :value='message' @input='message=$event'></child-com> 替换成 <child-com v-model="message"></child-com> 了。


$listeners的使用

由来:当我们在项目开发过程中会出现很多组件嵌套的关系,那么如果还要在最外层的组件向内部传递数据的话,有如下几种方式:

从父向子传递,子再向孙传递,一直传递下去,那么最里面的组件想往最外层传东西则可以从最里面向外面逐层$emit发送出去,但是仔细想想,一个简单的传递信息,却涉及到了这之间所有的组件,而他们只是一个中间者,这让代码维护起来非常困难

使用vuex来进行传递,这样确实方便了很多,但是这样做如果没有其他用处的话就有点大材小用了

使用事件总线,这样使用也不容易维护

而 $listeners 和 $attrs 的出现,就完美的解决了第一种情况的发生

<div id="app">
    <child-com :name='name' :age='age' @test-listeners='testListeners'></child-com>
  </div>

  <script>
    const vm = new Vue({
      el: '#app', //  父组件
      data: {
        name: 'lyl',
        age: 20,
      },
      methods: {
        testListeners(arg) {
          console.log(arg)
        }
      },
      components: {
        childCom: { //  子组件
          inheritAttrs: false,
          template: `
            <div>
              <span> {{name}} </span>
              <grand-com v-bind='$attrs' v-on='$listeners'></grand-com>
            </div>
          `,
          props: ['name'],
          components: {
            grandCom: { //  孙子组件
              inheritAttrs: false,
              template: `
                <div>
                  <span @click='listenClick'>{{$attrs.age}}</span>
                </div>
              `,
              methods: {
                listenClick() {
                  this.$emit('test-listeners','aaaaaaa');
                }
              },
            }
          }
        }
      }
    })
</script>

上面代码中,孙子组件要发出一个是将让父组件调用,这个时候我们可以在中间过渡的子组件模板使用的孙子组件上绑定这个属性,即:v-on='$listeners',这样一来父组件就能直接调用孙子组件发出的方法了,并且在中间层的子组件上并没有什么多余的部分

注意:

Vue3移除了Vue 2的listeners,Vue 3照搬2的写法的话会报错:

[Vue warn]: Property "$listeners" was accessed during render but is not defined on instance.

正确做法:

其实更简单,读懂官方说的现在事件监听器是 $attrs 的一部分这句话就行,官方这话意思是,老头组件里的<son @dosomething="dosomething" />现在被当做$attrs的一部分,由$attrs负责传递给后代组件,这时候,儿子组件像Vue 2一样加上inheritAttrs: false,,以及<grandson v-bind="$attrs" />,孙子组件一字不用改,就可以用了。不再需要`v-on="$listeners"。


.sync的使用方法

我们都知道,在一个组件上,我们只能使用一个v-model,但是如果我们的组件中有多个input标签呢,并且每个input标签中的值都不同,且每个都想进行双向绑定,这个时候,v-model就不行了。于是乎就出现了.sync的出现。

根据上面我说的那些需求,我们写一下代码:


不使用.sync的代码

<div id="app">
    <child-com 
      :value='obj.value' @aaa='obj.value = $event'
      :name='obj.name'  @bbb='obj.name = $event'
      :age='obj.age' @ccc='obj.age = $event'>
    </child-com>

    <p>{{ obj }}</p>
    <p>{{ obj.value }}</p>
    <p>{{ obj.name }}</p>
    <p>{{ obj.age }}</p>
  </div>

<!-- childCom组件的模板 -->
  <template id="childCom">
    <div>
      <input type="text" :value="value" @input='inputValueEvent'>
      <br>
      <input type="text" :value="name" @input='inputNameEvent'>
      <br>
      <input type="text" :value="age" @input='inputAgeEvent'>
    </div>
  </template>

  <script>
    const childCom = {
      template: '#childCom',
      props: ['value','name','age'],
      methods: {
        inputValueEvent(event) {
          this.$emit('aaa', event.target.value)
        },
        inputNameEvent(event) {
          this.$emit('bbb', event.target.value)
        },
        inputAgeEvent(event) {
          this.$emit('ccc', event.target.value)
        }
      },
    }

    const vm = new Vue({
      el: '#app',
      data: {
        obj: { value: '双向绑定' , name: 'coderlyl' , age: 20}
      },
      components: {
        childCom
      }
    })
</script>

根据上面的代码,我们不难发现,我们在标签中书写了过多的重复的东西,可读性也不是很好,下面我们再使用 .sync 的方式


使用.sync的代码

<div id="app">
    <child-com v-bind:value.sync='obj.value' 
               v-bind:name.sync="obj.name" 
               v-bind:age.sync="obj.age">    <!--这里也发生了变化-->
    </child-com>

    <p>{{ obj }}</p>
    <p>{{ obj.value }}</p>
    <p>{{ obj.name }}</p>
    <p>{{ obj.age }}</p>
  </div>
  <template id="childCom">
    <div>
      <input type="text" :value="value" @input='inputValueEvent'>
      <br>
      <input type="text" :value="name" @input='inputNameEvent'>
      <br>
      <input type="text" :value="age" @input='inputAgeEvent'>
    </div>
  </template>

  <script>
    const childCom = {
      template: '#childCom',
      props: ['value','name','age'],
      methods: {
        inputValueEvent(event) {
          this.$emit('update:value', event.target.value) // 这里发生了变化
        },
        inputNameEvent(event) {
          this.$emit('update:name', event.target.value) // 这里发生了变化
        },
        inputAgeEvent(event) {
          this.$emit('update:age', event.target.value) // 这里发生了变化
        }
      },
    }

    const vm = new Vue({
      el: '#app',
      data: {
        message: '可以双向绑定的了',
        obj: { value: '双向绑定' , name: 'coderlyl' , age: 20}
      },
      components: {
        childCom
      }
    })
</script>

也许看完这里,你并没有觉得好到哪里去了,下面还有更简单的写法

<child-com v-bind.sync="obj"></child-com>
<!-- 其他代码一样 -->

对,没错!这是终极简化版,但是这只针对于对象才能用

注意带有 .sync 修饰符的 v-bind 不能和表达式一起使用 (例如 v-bind:title.sync=”doc.title + ‘!’” 是无效的)。取而代之的是,你只能提供你想要绑定的属性名,类似 v-model。

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


链接: https://fly63.com/article/detial/7354

vue在自定义组件中使用v-model进行数据绑定

有这么一句话: 默认情况下,一个组件上的 v-model 会把 value 用作 prop 且把 input 用作 event。先来一个组件,不用vue-model,正常父子通信;点击回应后,父亲对儿子说的话变成了儿子的回应。儿子收到的信息也变了,实现通信。

Angular双向数据绑定

双向数据绑定: 所谓双向数据绑定是指View(视图)与Model(模型)之间的绑定:View<=>Model。View的改变: 通过界面交互使视图发生改变,如Input框的输入,Select元素的选择,scrollBar滚动,浏览器窗口大小改变等等。

Nautil 中使用双向数据绑定

虽然是基于 react 的框架,但是在 nautil 中可以使用双向数据绑定,这得益于基于观察者模式的开发思路。在 react 中使用双向绑定并非没有需求,react 严格的单向数据流,严重影响了开发者的发挥空间,特别是在表单组件的使用中

Vue的双向数据绑定原理

Object属性分为两个类型:数据属性、访问器属性,每类属性又有其不同的特显,双向绑定的原理是根据其访问器属性的特性来实现的。Configurable:是否可以通过delete删除,能否修改他的属性特性,能否修改为访问器属性。默认值true

不要再搞混Vue的响应式原理和双向数据绑定了

之前公司招人,面试了一些的前端同学,因为公司使用的前端技术是 Vue ,所以免不了问到其响应式原理和 Vue 的双向数据绑定。但是这边面试到的80%的同学会把两者搞混,通常我要是先问响应式原理再问双向数据绑定原理,来面试的同学大都会认为是一回事

内容以共享、参考、研究为目的,不存在任何商业目的。其版权属原作者所有,如有侵权或违规,请与小编联系!情况属实本人将予以删除!