1. 概述
相关定律告诉我们:这个世界上的任何事物之间都会存在一定联系,“城门失火,殃及池鱼”就是一个很好的例子。因此如果我们能够尽早发现这些看不见的联系,就能很好的解决更多遇见的难题。
言归正传,之前我们聊过如何在子组件中去修改主组件传递的参数的值,当时是在子组件中重新声明一个新数据,初始值为父组件传参的值,然后对子组件的数据进行计算。
今天我们使用事件的方式来实现对父组件的传参进行修改。
2. 组件间事件通信
2.1 子组件接收参数并实现自增
<body> <div id="myDiv"></div> </body> <script> const app = Vue.createApp({ data() { return { num : 1 } }, template:` <div> <test :num="num" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.num++; } }, template:` <div @click="incrNum" >{{num}}</div> ` }); const vm = app.mount("#myDiv");
这个例子咱们之前聊过,父组件有一个数据 num,父组件将这个参数传给 test 子组件,子组件使用 props:['num'] 的方式接收后,在事件方法中对其自增
很明显,自增时会报错,因为父组件传过来的 num 是只读的,子组件不能对其进行修改。
2.2 子组件中声明新的数据,将父组件的num作为初始值
const app = Vue.createApp({ data() { return { num : 1 } }, template:` <div> <test :num="num" /> </div> ` }); app.component("test", { props:['num'], data() { return { myNum : this.num } }, methods : { incrNum() { // this.num++; this.myNum++; } }, template:` <div @click="incrNum" >{{myNum}}</div> ` });
这是我们上节课的解决方案,在子组件中声明 myNum,把num当做初始值,然后自增 myNum,显示时也显示 myNum
很明显,这样做是可以的
2.3 子组件调用父组件的方法
既然子组件无权修改父组件传过来的参数,那我们就让父组件自己去修改这个参数
const app = Vue.createApp({ data() { return { num : 1 } }, methods : { handleIncr() { this.num++; } }, template:` <div> <test :num="num" @incrNum="handleIncr" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.$emit('incrNum') } }, template:` <div @click="incrNum" >{{num}}</div> ` });
这个例子中,父组件在使用子组件时,绑定了一个事件 incrNum,这个事件会调用父组件的 handleIncr 方法,这个方法中对数据 num 进行了自增。
子组件在自己的 incrNum 方法中使用 this.$emit('incrNum') 触发了父组件的 incrNum 事件,然后该事件调用父组件的 handleIncr 方法,对父组件的数据 num 进行修改。
父组件修改了数据 num,这个数据 num 修改后的值会传递给子组件,从而实现对 num 参数的修改。
说了这么一大堆,简单看就是子组件通过某种手段调用了父组件的方法。
经试验,完全没用问题,可以修改 num 的值
2.4 子组件调用父组件的方法,且传参
光调用还不行,我们还要传参
const app = Vue.createApp({ data() { return { num : 1 } }, methods : { handleIncr(param1) { this.num += param1; } }, template:` <div> <test :num="num" @incrNum="handleIncr" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.$emit('incrNum', 2) } }, template:` <div @click="incrNum" >{{num}}</div> ` });
这个例子中,我们根据传参的值,决定自增多少。
在 this.$emit('incrNum', 2) 这句代码中,除了指明触发的事件,还传了一个参数,父组件的 handleIncr(param1) 这个方法,就可以接收这个参数,并使用
2.5 子组件调用父组件的方法,且传多个参数
这次我们要传多个参数,当然也是可以的
const app = Vue.createApp({ data() { return { num : 1 } }, methods : { handleIncr(param1, param2) { this.num += param2; } }, template:` <div> <test :num="num" @incrNum="handleIncr" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.$emit('incrNum', 2, 3) } }, template:` <div @click="incrNum" >{{num}}</div> ` });
这么传:this.$emit('incrNum', 2, 3),这么收:handleIncr(param1, param2),以此类推
2.6 计算逻辑放在子组件中
自增本来是子组件的业务,我们不想把这个逻辑放到父组件中,耦合性太强了,我们可以这么写
const app = Vue.createApp({ data() { return { num : 1 } }, methods : { handleIncr(param1) { this.num = param1; } }, template:` <div> <test :num="num" @incrNum="handleIncr" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.$emit('incrNum', this.num + 1) } }, template:` <div @click="incrNum" >{{num}}</div> ` });
其实就是传参时在子组件中计算好了,然后父组件直接赋值就好
2.7 通过 v-model 的方式,修改父组件数据的值
上面的例子中,我们通过子组件调用父组件的方法去修改父组件数据的值,耦合性还是有点强,父组件需要去为子组件写一个方法。
其实还有一个更简洁的办法,就是通过 v-model 的方式,来看下面的例子
const app = Vue.createApp({ data() { return { num : 1 } }, template:` <div> <test v-model="num" /> </div> ` }); app.component("test", { props:['modelValue'], methods : { incrNum() { this.$emit('update:modelValue', this.modelValue + 1); } }, template:` <div @click="incrNum" >{{modelValue}}</div> ` });
这个例子中,父组件使用 test 子组件时,使用 v-model="num" 的方式来传参。
test 子组件接收时,使用 props:['modelValue'] 的方式接收,注意:modelValue 是一个固定写法。
在子组件的自增方法中使用 this.$emit('update:modelValue', this.modelValue + 1); 的形式去修改 modelValue 的值,注意:update:modelValue 是固定写法。
2.8 使用 num 替换 modelValue
上面的例子有点不好理解,无缘无故蹦出个 modelValue,父组件明明传的是 num,为啥我接受要用 modelValue,太奇怪了
下面的例子更易于我们的理解
const app = Vue.createApp({ data() { return { num : 1 } }, template:` <div> <test v-model:num="num" /> </div> ` }); app.component("test", { props:['num'], methods : { incrNum() { this.$emit('update:num', this.num + 1); } }, template:` <div @click="incrNum" >{{num}}</div> ` });
这个例子就好理解多了,首先父组件使用 test 子组件时,使用 v-model:num="num" 进行传参。
子组件接收时,接收的就是 num,使用 props:['num'] 接收。
最后使用 this.$emit('update:num', this.num + 1); 代码对接收到的 num 进行修改。
显示时显示的也是 num,{{num}}
这样写就清爽很多,父组件没有 method,子组件相当于自己对接收到的参数 num 进行操作,耦合度降低了很多。
3. 综述
今天聊了一下 VUE3 的 组件间事件通信,希望可以对大家的工作有所帮助,下一节我们继续讲组件的相关知识,敬请期待
欢迎帮忙点赞、评论、转发、加关注 :)
关注追风人聊Java,每天更新Java干货。
4. 个人公众号
追风人聊Java,欢迎大家关注