vue3 基础-父子组件间如何通过事件通信
前几篇讨论的父子组件间如何进行传数据的话题. 即父组件在调用子组件的时候, 通过自定义属性 (v-bind) 的方式传递数据, 同时子组件通过 props 属性进行接收. 子组件可以对数据进行各种校验, 但不能修改, 即所谓的 "单项数据流''的概念, 这样其实是合理的, 不能混乱. 若是在要改就另存一份副本再进行操作即可.
本篇的学习是父子组件如何通过事件进行通信, 即子组件想要搞一个操作, 向父组件进行请示 (emit), 父组件同意后帮其在父组件的 methods 中进行回应.
子组件向父组件通信
<!DOCTYPE html>
<html lang="en">
<head>
<title>组件事件通信 $emit</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
const app = Vue.createApp({
data () { return { count: 1 }},
methods: {
handleAddOne () {
this.count += 1
}
},
// 3. 父组件接收到信息并进行全局处理
template: `
<div>
<Son :count=count @add-one="handleAddOne" />
</div>
`
})
app.component('Son', {
props: ['count'],
methods: {
handleClick () {
// 1. 当用户点击, 触发 handleClick 事件
// 2. 然后其向父组件发射(请示) emit 一个名为 addOne 的事件
this.$emit('addOne')
}
},
template: '<div @click="handleClick">{{count}}</div>'
})
const vm = app.mount('#root')
</script>
</body>
</html>
以上例, 梳理组件事件传递的基础逻辑:
- 在子组件中, 当用户点击, 触发 handleClick 事件
- 然后其向父组件发射(请示) $emit 一个名为 addOne 的事件
- 父组件在调用子组件的地方接收 add-one 事件 (注意子emit用驼峰, 父接收用短横) 能默认识别
- 父组件在自己的 methods 中进行全局处理哦
补充一下这里的 $emit 是可以进行校验和传参的, 就校验举个栗子:
// 子组件首层
emit: {
add: (count) => {
if (count < 0) {
return true;
}
return false;
}
传参也演示一下:
const app = Vue.createApp({
data () { return { count: 1 }},
methods: {
handleAdd (arg1, arg2) {
this.count += 1
}
},
// 3. 父组件接收到信息并进行全局处理
template: `
<div>
<Son :count=count @add="handleAdd" />
</div>
`
}
app.component('Son', {
props: ['count'],
methods: {
handleClick () {
this.$emit('add', 1, 2)
}
},
template: '<div @click="handleClick">{{count}}</div>'
})
const vm = app.mount('#root')
modelValue 关键字
这种父组件向子组件传值, 子组件向父组件 emit 事件的过程, 岂不和 v-model 这个指令有异曲同工之妙嘛. 因此在约定俗成下, 我们能用 modelValue 这个关键字进行改写:
<!DOCTYPE html>
<html lang="en">
<head>
<title>modelValue</title>
<script src="https://unpkg.com/vue@3"></script>
</head>
<body>
<div id="root"></div>
<script>
const app = Vue.createApp({
data () { return { count: 1 } },
// 1. 父组件通过 v-model 来监听 count 的变化
template: `<Son v-model=count />`
})
app.component('Son', {
props: ['modelValue'],
methods: {
handleClick () {
// 监控数据变化
this.$emit('update:modelValue', this.modelValue + 3)
}
},
template: '<div @click="handleClick">{{modelValue}}</div>'
})
const vm = app.mount('#root')
</script>
</body>
</html>
当然也是可以改名字的, 通过 v-model:xxx 的写法:
<script>
const app = Vue.createApp({
data () { return { count: 1 } },
// 1. 父组件通过 v-model 来监听 count 的变化
template: `<Son v-model:cj=count />`
})
app.component('Son', {
props: ['cj'],
methods: {
handleClick () {
this.$emit('update:cj', this.cj + 3)
}
},
template: '<div @click="handleClick">{{cj}}</div>'
})
const vm = app.mount('#root')
</script>
小结
最后再来对整个子组件向父组件 emit 事件的操作流程做一个小结
- 在子组件中, 触发一个名为 nb 的时间
- 然后其向父组件发射(请示) $emit 一个名为 nb 的事件
- 父组件在调用子组件的地方接收 nb 事件
- 父组件在自己的 methods 中进行全局处理哦
- 可以用 v-model 的写法来进行简写这种父子通信的代码
耐心和恒心, 总会获得回报的.