【Vue3从零开始-第三章】3-5 父子组件之间事件通信

704 阅读2分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第18天,点击查看活动详情

前言

上一篇的文章中,我们了解了vue组件Non-Props属性,今天我们继续深入了解一下vue组件中的父子组件之间通信

回顾

在前面的几篇文章中,我们知道组件之间传值的时候,可以通过父组件调用子组件标签,然后在子组件标签上用动态传参的方法传递给子组件,但是子组件没办法去修改父组件的值,因为子组件接收到的值是只读的。

<script>
  const app = Vue.createApp({
    data(){
      return {
        count: 1
      }
    },
    template: `
      <div>
        <counter :count="count" />
      </div>
    `
  });

  app.component('counter', {
    props: ['count'],
    methods: {
      handleClick(){
        this.count += 1
      }
    },
    template: `
      <div @click="handleClick">{{count}}</div>
    `
  })

  const vm = app.mount('#root');
</script>

[Vue warn]: Attempting to mutate prop "count". Props are readonly.

今天的文章中我们将详细讲解一下父子组件如何通过事件方法进行通信。

$emit

  • $emit:子组件触发父组件事件的方法 当子组件中无法完成的一些事件操作时,可以通过$emit方法通知父组件去完成。下面我们就来改造一下上面的代码。
handleClick(){
    this.$emit('addOne')
}
  • this.$emit():这个方法时一个固定的写法,也是vue提供的事件,意思就是我要向外部触发一个事件,传入事件名称即可。

既然子组件要通知父组件触发一个事件,那么父组件就得接收到子组件传递过来的事件。

const app = Vue.createApp({
    data(){
      return {
        count: 1
      }
    },
    methods: {
      handleAddOne(){
        this.count += 1
      }
    },
    template: `
      <div>
        <counter :count="count" @add-one="handleAddOne" />
      </div>
    `
  });
  • 父组件中可以在子组件标签上通过v-on指令(简写@)接收子组件触发的事件。

  • 子组件触发事件是采用驼峰形式去写方法名,父组件接收的时候需要改成-接收方法名。

  • 父组件接收到子组件传递过来的方法之后,就可以触发父组件内部方法了。

chrome-capture (5).gif

传参

通过上面的代码我们看到父子组件之间通过事件已经可以进行通信了,但是我们只是触发了事件而已,当我们需要在子组件里面传递参数要咋整呢?

  • 子组件触发的事件中,调用$emit方法时,除了定义方法名之外,后面还可以直接携带参数。
handleClick(){
    this.$emit('add', 2)
}
  • 父组件触发的事件中,可以直接在方法中接收子组件传递过来的参数。
const app = Vue.createApp({
    data(){
      return {
        count: 1
      }
    },
    methods: {
      handleAddOne(param){
        this.count += param
      }
    },
    template: `
      <div>
        <counter :count="count" @add="handleAddOne" />
      </div>
    `
});

chrome-capture (6).gif

  • $emit方法后面携带的参数可以是多个。
// 子组件中的事件
handleClick(){
    this.$emit('add', 2, 3)
}

// 父组件中的事件
handleAddOne(param1, param2){
    this.count += param2
}

chrome-capture (7).gif

  • $emit方法后面携带的参数也可以直接进行计算。
// 子组件中的事件
handleClick(){
    this.$emit('add', this.count + 4)
}

// 父组件中的事件
handleAddOne(count){
    this.count = count
}

chrome-capture (8).gif

emits

  • emits:校验对外触发的方法名
app.component('counter', {
    props: ['count'],
    emits: ['add'],
    methods: {
      handleClick(){
        this.$emit('add', this.count + 4)
      }
    },
    template: `
      <div @click="handleClick">{{count}}</div>
    `
})

在子组件中可以定义emits来校验$emit中的方法名,如果一致则可以触发成功。

emits: ['minus'],

如果我们将子组件中的emits定义的值和$emit中的方法名不一致,则会在浏览器控制台中进行提示。

[Vue warn]: Component emitted event "add" but it is neither declared in the emits option nor as an "onAdd" prop.

除了在emits中使用数组定义方法名之外,还可以通过对象的方式进行校验。

emits: {
    add: (count) => {
        if(count < 0){
          return true
        }
        return false
    }
},
  • emits对象中可以监听$emit中的方法名,触发一个事件的时候可以获取到$emit中传递的参数值。

当监听对象中校验不通过时,也会在浏览器控制台中进行提示。

[Vue warn]: Invalid event arguments: event validation failed for event "add".

v-model

在以前的文章中,我们知道v-model指令是做双向数据绑定的,其实它也可以做父子组件通信的指令。

<script>
  const app = Vue.createApp({
    data(){
      return {
        count: 1
      }
    },
    template: `
      <div>
        <counter v-model="count" />
      </div>
    `
  });

  app.component('counter', {
    props: ['modelValue'],
    methods: {
      handleClick(){
        this.$emit('update:modelValue', this.modelValue + 5)
      }
    },
    template: `
      <div @click="handleClick">{{modelValue}}</div>
    `
  })

  const vm = app.mount('#root');
</script>
  • 父组件中调用子组件标签时,可以直接使用v-model指令进行传值。

  • 子组件中通过props接收父组件中的参数modelValue

  • $emit方法中,直接通过update:modelValue的事件就可以对父组件的值进行修改了。

chrome-capture (9).gif

modelValue是默认写法,不可以随意更改。

但是有些时候,我们需要自定义父组件传递给子组件的参数名称要怎么做呢?

<script>
  const app = Vue.createApp({
    data(){
      return {
        count: 1
      }
    },
    template: `
      <div>
        <counter v-model:count="count" />
      </div>
    `
  });

  app.component('counter', {
    props: ['count'],
    methods: {
      handleClick(){
        this.$emit('update:count', this.count + 6)
      }
    },
    template: `
      <div @click="handleClick">{{count}}</div>
    `
  })

  const vm = app.mount('#root');
</script>
  • 直接在父组件中的子组件标签上使用v-model:xxx的方式进行自定义名称即可。

  • 子组件就接收自定义的名称进行渲染和操作。

chrome-capture (10).gif

总结

本篇文章主要讲解了vue中的父子组件之间通过事件通信的方法:$emitv-model,也讲解了在进行通信时可以传递参数,也可以对传递的方法和参数进行校验。