自定义事件
父组件使用 prop 传递数据给子组件。但子组件怎么跟父组件通信呢?这个时候 Vue 的自定义事件系统就派得上用场了。
1. 使用 v-on
绑定自定义事件
每个vue实例都实现了事件接口,即:
- 使用 $on(eventName) 监听事件
- 使用 $emit(eventName, optionPayload) 触发事件
注意点: 父组件可以在使用子组件的地方直接用 v-on
来监听子组件触发的事件
<div id="example"> {{msg}} <h1>this is father component</h1> <span>{{"儿子发来的数据:"+msgFromSon}}</span> <son @tofather='receiveMsg'></son> <!-- 2. 使用 $on(eventName) 监听事件:父组件使用v-on 来接收监听子组件触发的事件 --> </div> <script> Vue.component('son', { template: `<div> <h1>This is son component</h1> <input type="text" v-model="kw"/> <button @click="handleClick">sendToFather</button> </div>`, data(){ return { kw:'' } }, methods:{ handleClick(){ console.log(this.kw); this.$emit('tofather', this.kw); //1. $emit(eventName)触发事件,还可以发送数据kw到父组件 } } }) new Vue({ el: '#example', data: { msg: 'Hello Directive', msgFromSon:'' }, methods: { receiveMsg(msg) { //3. 接收从子组件传递过来的数据 console.log('msg',msg); this.msgFromSon += ' '+msg; } } }) </script>
2. 给组件绑定原生事件
有时候,你可能想在某个组件的根元素上监听一个原生事件。可以使用 v-on
的修饰符 .native
。例如:
3.使用自定义事件的表单输入组件
自定义事件可以用来创建自定义的表单输入组件,使用v-model来进行数据双向绑定。注意:
所以在组件中使用时,它相当于下面的简写:
所以要让组件的v-model生效,它应该:
- 接受一个 value 的prop
- 在有新的值时触发 input 事件并将新值作为参数
Vue.component('currency-input', { template: `<span>$ <input ref='input' :value='value' @input='updateValue($event.target.value)'> </span>`, props: ['value'], // 1. 接受一个value的props methods: { updateValue(value){ // 删除两侧的空格符,保留2位小数,这个例子还是比较初级的。比如,用户输入多个小数点或句号也是允许的 var formattedValue = value.trim().slice(0, value.indexOf('.') === -1 ? value.length : value.indexOf('.') + 3); // 如果值尚不合规,则手动覆盖为合规的值 if (formattedValue !== value) { this.$refs.input.value = formattedValue; } this.$emit('input', Number(formattedValue)); // 2. 触发input事件到父组件; } } }) new Vue({ el: '#app', data() { return { price: '' } } })
4. 自定义组件的 v-model
默认情况下,一个组件的v-model会使用 value prop和 input 事件。但如单选框,复选框之类的输入类型可能会把 value 用作它用。model
选项可以避免这样的冲突:
注意你仍然需要显式声明 checked
这个 prop。
5. 非父子组件的通信
有时候,非父子关系的两个组件之间也需要通信。在简单的场景下,可以使用一个空的 Vue 实例作为事件总线:
页面如下:
<div id="example"> <laoda></laoda> <hr/> <laoer></laoer> </div> <script type="text/x-template" id="laoda-template"> <div> <h1>laoda</h1> <button @click="tellLaoer">回家吃饭</button> </div> </script> <script type="text/x-template" id="laoer-template"> <div> <h1>laoer</h1> <span v-if="msgFromLaoDa">{{"老大说:"+msgFromLaoDa}}</span> </div> </script>
JS如下:
//新建一个Vue的实例,通过bus完成事件的绑定和触发 var bus = new Vue(); Vue.component('laoda', { template: '#laoda-template', methods:{ tellLaoer: function () { //1. 触发事件通知老二回家吃饭 bus.$emit('eventToBrother','赶紧回家吃饭') } } }) Vue.component('laoer', { template: '#laoer-template', data:function(){ return { msgFromLaoDa:null } }, mounted: function () { console.log(this, bus); // 3.外面的this指向laoer这个组件 // 2. 事件的绑定 bus.$on('eventToBrother', function (result) { console.log('this:', this); // 4. 里面的this原指向bus,使用bind后,就指向了组件 this.msgFromLaoDa = result; }.bind(this)); } }) new Vue({ el: '#example', data: { msg: 'Hello Directive' } })