Vue自定义事件【十三】

自定义事件

事件名

推荐你始终使用 kebab-case 的事件名

<div id="app">
      <blog-post @my-welcome="sayHi()"></blog-post>
    </div>

    <script>
      Vue.component('blog-post', {
        props: ['post'],
        template: `<button @click="$emit('my-welcome')">click me</button>`
      })
      let vm = new Vue({
        el: '#app',
        data: function () {
          return {}
        },
        methods: {
          sayHi: function () {
            console.log('hi')
          }
        }
      })
    </script>

自定义组件的v-model

一个组件上的 v-model 默认会利用名为 value 的 prop 和名为 input 的事件,但是像单选框、复选框等类型的输入控件可能会将 value attribute 用于不同的目的。model 选项可以用来避免这样的冲突。

<div id="app">
      <check-input v-model="checkbox"></check-input>
      <check-input v-model="checkbox"></check-input>
      <p>checkbox : {{checkbox}}</p>
    </div>

    <script>
      Vue.component('check-input', {
        // 组件默认model
        // model: {
        //   prop: 'value',
        //   event: 'input'
        // },
        model: {
          prop: 'checked',
          event: 'change'
        },
        props: ['checked', 'value'],
        template: `
                <div>
                  <input
                    type='checkbox'
                    :checked="checked"
                    @change="$emit('change',$event.target.checked)"
                  />
                  <input 
                    type='text' 
                    :value='value'
                    @input="$emit('input',$event.target.value)"
                  />
                </div>`
      })
      let vm = new Vue({
        el: '#app',
        data: function () {
          return {
            checkbox: ''
          }
        }
      })
    </script>

上述代码中:

  • template中的valuechecked,分别绑定到了propsvaluechecked,用于父组件向子组件传递数据,在此例所要展示的功能并无关系。

  • 上面提到过,一个组件上的 v-model 默认会利用名为 valueprop 和名为 input 的事件,而在前面的表单输入绑定中提到过image-20200810223507857因此,如果我们的想要v-model的子组件中除了texttextarea之外的其他input标签,就需要对子组件的默认model进行改变

    // 组件默认model
    // model: {
    //   prop: 'value',
    //   event: 'input'
    // },
    // 修改成所要的model
    model: {
        prop: 'checked',
        event: 'change'
    },
    

    此时,再对子组件整体进行v-model绑定,就可以绑定到所需的propevent

  • 对于v-model绑定的事件,子组件中也需要通过v-on绑定,并通过$emit('eventName',$event.target.propName)进行回调,而你所要传递的数据,则通过$event.target.propName进行传递。

将原生事件绑定到组件

在一个组件的根元素上直接监听一个原生事件。这时,你可以使用 v-on.native 修饰符:

<base-input v-on:focus.native="onFocus"></base-input>
Vue.component('base-input', {
        template: `
          <input type='text'
            v-on="inputListeners"
          />`
      })

如果上述 <base-input> 组件做了如下重构,所以根元素实际上是一个 <label> 元素:

<label>
  {{ label }}
  <input
    v-bind="$attrs"
    v-bind:value="value"
    v-on:input="$emit('input', $event.target.value)"
  >
</label>

这时,父级的 .native 监听器将静默失败。它不会产生任何报错,但是 onFocus 处理函数不会如你预期地被调用。

为了解决这个问题,Vue 提供了一个 $listeners property,它是一个对象,里面包含了作用在这个组件上的所有监听器。

<div id="app">
      <base-input
        @focus="onFocus()"
        @focus2="onFocus2()"
        v-model="value1"
        :value="value2"
        @input2="val=>{value2=val}"
      ></base-input>
      <br />
      <span>value1 : {{value1}}</span><br />
      <span>focus1 : {{focus1}}</span><br />
      <span>value2 : {{value2}}</span><br />
      <span>focus2 : {{focus2}}</span>
    </div>

    <script>
      Vue.component('base-input', {
        model: {
          prop: 'value',
          event: 'input1'
        },
        computed: {
          inputListeners: function () {
            let vm = this
            return Object.assign({}, this.$listeners, {
              input: function (event) {
                vm.$emit('input1', event.target.value)
              },
              focus: function (event) {
                vm.$emit('focus')
              }
            })
          },
          inputListeners2: function () {
            let vm = this
            return Object.assign({}, this.$listeners, {
              input: function (event) {
                vm.$emit('input2', event.target.value)
              },
              focus: function (event) {
                vm.$emit('focus2')
              }
            })
          }
        },
        template: `
        <label>
          <span>input1</span>
          <input type='text'
            v-on="inputListeners"
          /><br />
          <span>input2</span>
          <input type='text'
            v-on="inputListeners2"
          />
        </label>`
      })
      let vm = new Vue({
        el: '#app',
        data: function () {
          return {
            value1: '',
            value2: '',
            focus1: '',
            focus2: ''
          }
        },
        methods: {
          onFocus: function () {
            return (this.focus1 = 'focusing1')
          },
          onFocus2: function () {
            return (this.focus2 = 'focusing2')
          }
        }
      })
    </script>
  • image-20200811184149442

  • 上述代码中,v-model="value1"可以被:value="value1" @input1="value=>{value2=value}"代替,即v-model的另一种写法。

  • base-input组件中,modelproperty重写了组件的model传参,默认组件model为prop: 'value', event: 'input',将event修改为下面input1的监听器中改写的input1

  • 计算属性中的inputListeners,通过this.$listeners从父级添加所有的监听器,之后添加或覆写了一些自定义的监听器行为,然后通过Object.assign()将所有的对象合并为一个对象进行返回,从而就返回了所需要的监听器。

.sync修饰符

对一个 prop 进行“双向绑定”

<div id="app">
      <!-- 方法一 -->
      <base-input
        :title="doc.title"
        @update:title="doc.title=$event"
        :name="doc.name"
        @update:name="doc.name=$event"
      ></base-input>
      <hr />
      <!-- 方法二:方法一的简化 -->
      <base-input :title.sync="doc.title" :name.sync="doc.name"></base-input>
      <hr />
      <!-- 方法三:用一个对象设置多个prop时使用 -->
      <base-input v-bind.sync="doc"></base-input>
      <p>doc.title : {{doc.title}}</p>
      <p>doc.name : {{doc.name}}</p>
    </div>

    <script>
      Vue.component('base-input', {
        props: ['title', 'name'],
        data: function () {
          return {
            newUpdate: {
              title: 'Vue',
              name: 'Evan You'
            }
          }
        },
        template: `
          <label>
            <p>{{title}}</p>
            <p>{{name}}</p>
            <button @click="$emit('update:title', newUpdate.title)">change title</button>
            <br />
            <button @click="$emit('update:name', newUpdate.name)">change name</button>
          </label>`
      })
      let vm = new Vue({
        el: '#app',
        data: function () {
          return {
            doc: {
              title: 'docTitle',
              name: 'docName'
            }
          }
        }
      })
    </script>

image-20200811184113580

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值