Vue.js 之 组件-监听子组件事件
1、监听子组件事件前面介绍了父组件如何通过 prop 向子组件传递数据,反过来,子组件如何向父组件通信呢?在 Vue.js 中,这是通过自定义事件来实现的,子组件使用 $emit() 方法触发事件,父组件使用 v-on 指令监听子组件的自定义事件。// evenName: 事件名// args: 事件传递的参数vm.$emit( evenName, [...args] )<div id="a
1、监听子组件事件
前面介绍了父组件如何通过 prop 向子组件传递数据,反过来,子组件如何向父组件通信呢?
在 Vue.js 中,这是通过自定义事件来实现的,子组件使用 $emit()
方法触发事件,父组件使用 v-on
指令监听子组件的自定义事件。
// evenName: 事件名
// args: 事件传递的参数
vm.$emit( evenName, [...args] )
<div id="app">
<child @greet="sayHello"></child>
</div>
<script src="vue.js"></script>
<script>
Vue.component('child', {
props: [],
data() {
return {
name: '张三'
}
},
methods: {
handleChick() {
this.$emit('greet', this.name)
}
},
template: `<button @click="handleChick">开始欢迎</button>`
});
new Vue({
el: '#app',
methods: {
sayHello(name) {
alert("Hello, " + name)
}
}
});
</script>
1.1、案例:点赞
<div id="app">
<post-list></post-list>
</div>
<script src="vue.js"></script>
<script>
// 父组件
Vue.component('PostList', {
data() {
return {
posts: [
{id: 1, title: '《Spring Boot实践》', author: '张三', date: '2019-10-21 20:10:15', vote: 0},
{id: 2, title: '《Vue.js入门》', author: '李四', date: '2019-10-10 09:15:11', vote: 0},
{id: 3, title: '《Python数据分析》', author: '王五', date: '2019-11-11 15:22:03', vote: 0}
]
}
},
methods: {
// 自定义事件vote的事件处理器方法
handleVote(post) {
post.vote = ++post.vote
return post
}
},
template: `
<div>
<ul>
<PostListItem
v-for="post in posts"
:key="post.id"
:post="post"
@vote="handleVote(post)"/>
</ul>
</div>`
});
// 子组件
Vue.component('PostListItem', {
methods: {
handleVote() {
// 触发自定义事件
this.$emit('vote');
}
},
props: ['post'],
template: `
<li>
<p>
<span>标题:{{ post.title }} | 发帖人:{{ post.author }} | 发帖时间:{{ post.date }} | 点赞数:{{ post.vote }}</span>
<button @click="handleVote">赞</button>
</p>
</li>
`
});
let vm = new Vue({
el: '#app'
});
</script>
2、将原生事件绑定到组件
在组件上也可以监听原生事件,在使用 v-on
命令时,添加一个 .native
修饰符即可。如:
<base-input @focus.native="onFocus"></base-input>
这种方式最终是在组件的根元素上添加了 focus
(聚焦)事件的监听,如果组件模板的根元素是 <input>
,那没有问题,但是如果不是,就有问题了。如:
Vue.component('MyInput', {
template: `
<label>
{{ label }}
<input class="child">
</lavel>`
})
根元素是 <label>
,相当于在<label>
上添加了 focus
事件监听器,这时,父级的 .native
监听器将静默失败,它不会报错
,但是 onFocus 处理函数不会被如期被调用
。
为了解决这个问题,Vue.js 提供了一个 $listeners
属性,它是一个对象,里面包含了作用在这个组件上的所有监听器,如:
{
focus(event) {...},
input(value) {...},
...
}
有了 $listeners
属性,就可以使用 v-on="$listeners" 将组件上的所有事件监听器发送到特定的子元素。对于需要那些使用 v-model 的元素(如 <input>
)来说,可以为这些监听器创建一个新的计算属性,如下面:
<div id="app">
<my-input :label="title" v-model="msg" @focus="onFocus"></my-input>
<p>{{ msg }}</p>
</div>
<script src="vue.js"></script>
<script>
Vue.component('MyInput', {
inheritAttrs: false,
// 父级传入数据:title -> label;msg -> value
props: ['label', 'value'],
data() {
return {}
},
computed: {
inputListeners() {
let vm = this
// 将所有的对象合并为一个新对象
return Object.assign({},
// 从父级添加的所有监控
this.$listeners,
// 添加自定义监控器或覆写一些监听器的行为
{
// 确保组件和 v-model 一起工作
input(event) {
vm.$emit('input', event.target.value)
}
}
)
}
},
template: `
<label>
{{ label }}
<input
v-bind="$attrs"
:value="value"
v-on="inputListeners">
</label>`
});
new Vue({
el: '#app',
data: {
title: '输入框:',
msg: '请输入'
},
methods: {
onFocus() {
console.log("不要摸人家么,好痒,臭流氓!")
}
}
});
</script>
2.1、.sync 修饰符
在某些情况下,可能需要对一个组件的 prop 进行双向绑定,Vue.js 推进以 update: myPropName 模式触发事件来实现。例如:
<div id="app">
<span>父组件计数值:{{ counter }}</span>
<!-- <child :val="counter" @update:val="addCounter"></child>-->
<!-- $event:自定义事件的附加参数 -->
<child :val="counter" @update:val="counter = $event"></child>
</div>
<script src="vue.js"></script>
<script>
Vue.component('child', {
props: {
val: {
type: Number,
default: 0
}
},
data() {
return {
count: this.val
}
},
methods: {
handleChick() {
this.$emit('update:val', ++this.count)
}
},
template: `
<div>
<span>子组件计数值:{{ val }}</span>
<button @click="handleChick">增加计数</button>
</div>`
});
new Vue({
el: '#app',
data: {
counter: 0
},
methods: {
addCounter(val) {
return this.counter = val;
}
}
});
</script>
为了方便起见,Vue.js 为了上述这种模式提供了一个缩写,即 .sync
修饰符(在 v-bind 指令上使用),修改如下:
<child :val="counter" @update:val="counter = $event"></child>
<child :val.sync="counter"></child>
当用一个对象同时设置多个 prop 的时候,也可以将 .sync
修饰符和 v-bind
一起使用:
<text-document v-bind.sync="doc"></text-document>
这里会把 doc
对象中的每一个属性作为一个单独的 prop 传进去,然后为每个属性添加 v-on:update 监听器。
<body>
<div id="app">
<span>父组件 post:{{ post.title }} | {{ post.author }} | {{ post.time }} | {{ post.vote }} | {{ post.price }}</span>
<child v-bind.sync="post"></child>
</div>
<script src="vue.js"></script>
<script>
Vue.component('child', {
props: {
title: { type: String },
author: { type: String },
time: { type: String },
vote: { type: Number },
price: { type: Number },
},
data() {
return {
title: this.vote,
author: this.vote,
time: this.vote,
vote: this.vote,
price: this.price
}
},
methods: {
handleChick() {
this.$emit('update:vote', this.vote += 1)
this.$emit('update:price', this.price += 4)
}
},
template: `
<div>
<span>子组件 post:{{ title }} | {{ author }} | {{ time }} | {{ vote }} | {{ price }}</span>
<br>
<button @click="handleChick">增加计数</button>
</div>`
});
new Vue({
el: '#app',
data: {
post: {
title: '《Spring Boot 从入门到入土》',
author: '张三',
time: '2021年05月16日00:05:50',
vote: 0,
price: 0
}
},
methods: {}
});
</script>
注:本篇主要来源:《Vue.js 从入门到实践》第十一章 组件,作者:孙鑫,出版社:中国水利水电出版社
更多推荐
所有评论(0)