組件(3):自定義事件

自定義事件

自定義事件有能力使子組件事件觸發父組件中的動做。
那麼父組件如何監聽事件呢?可使用指令v-on:event-name="callback"監聽。
而子組件又是如何觸發事件的呢? 很簡單,調用this.$emit(eventName)
先來個簡單例子javascript

new Vue({
    el:'#app-1',
    methods:{
        callback:function(){
            alert('父組件監聽到事件觸發,執行回調。')
        }
    },
    components: {
        "component-1":{
            template:'<button v-on:click="emitor" class="btn btn-info">\
                        子組件模板:點擊觸發自定義事件</button>',
            methods:{
                emitor:function(){
                    console.log('111')
                    this.$emit('alert-event')
                }
            }
        }
    }
})
<div id="app-1">
    <component-1 v-on:alert-event="callback"></component-1>
</div>

根據以上代碼,能夠得出個事件執行流程,基本上無論什麼自定義事件都是這個流程java

  • 子組件某方法
  • this.$emit('event')
  • DOM上v-on監聽event
  • 父組件methods中的callback

clipboard.png

clipboard.png

子組件已經和它外部徹底解耦了。它所作的只是報告本身的內部事件,由於父組件可能會關心這些事件。

事件負載

在事件執行的同時,子組件還能夠在觸發事件的同時掛上一些負載的數據,隨着事件傳遞給父組件。
使用API的第二個參數this.$emit(event, payload),具體以下。app

new Vue({
    el: '#app-2',
    data: {
        message: ''
    },
    methods: {
        handleMessage: function (payload) {
            this.message = payload.message
        }
    },
    components: {
        "component-2": {
            data: function () {
                return { message: '' }
            },
            template: '\
            <div>\
                <input type="text" v-model="message" class="form-control w-25 mt-2">\
                <button @click="sendMsg" class="btn btn-info mt-2">發送</button>\
            </div>',
            methods: {
                sendMsg: function () {
                    this.$emit('message', { message: this.message })
                }
            }
        }
    }
})
<div id="app-2">
    <input v-model="message" class="form-control w-25">
    <component-2 @message="handleMessage"></component-2>
</div>

clipboard.png

點擊發送按鈕觸發事件並把信息傳遞給父組件,能夠看到仍是這裏一樣遵循自定義事件流程,其餘都是煙霧,之多了個負載this.$emit('message', { message: this.message })this

原生事件

經過.native後綴還能夠在子組件元素根節點上綁定原生事件的監聽。spa

new Vue({
    el:'#app-3',
    methods:{
        todo:function(){
            alert('事件回調方法是父組件中的')
        }
    },
    components:{
        "component-3":{
            template:'<button class="btn btn-info mt-2">原生事件監聽</button>'
        }
    }
})
<div id="app-3">
    <component-3 @click.native="todo"></component-3>
</div>

仍是要千萬注意,回調方法在父組件做用域上。雙向綁定

雙向綁定(.sync修飾符與update事件)

能夠經過如下步驟實現一個雙向綁定:code

  • 在子組件VirtualDOM節點上,經過v-bind:prop.sync="foo"進行數據下發,這裏以.sync修飾符標註,通知父組件子組件須要進行props的更新。
  • 子組件經過this.$emit("update:prop",newValue)通知父組件本身須要把prop更新爲一個新值newValue以子組件的data選項做爲過渡變量
  • 父組件會監聽這個更新事件,從而在負載上拿到newValue,更新foo的數據,並把新數據從新下發給子組件的prop
注意:這裏並非子組件 props值的改變引起父組件數據的改變,而是利用子組件的 data作橋樑,經過事件及其負載引發父組件的變更。
new Vue({
    el: '#app-4',
    data: { parentMsg: 'parent\'s message' },
    components: {
        "component-4": {
            props: ['child_msg'],
            data: function () {
                return { inputText: this.child_msg }
            },
            template: '\
                <div>\
                    <input v-model="inputText" class="form-control form-control-sm w-25" type="text">\
                </div>',
            watch: {
                child_msg: function (val) {
                    this.inputText = val
                },
                inputText: function (val) {
                    this.$emit('update:child_msg', val)
                }
            }
        }
    }
})
<div id="app-4">
    <input v-model="parentMsg" class="form-control form-control-sm w-25" type="text">
    <component-4 :child_msg.sync="parentMsg" class="mt-2"></component-4>
</div>

clipboard.png

clipboard.png

父、子組件各包含一個輸入框,而且將它們綁定到本身的某data屬性上。watch子組件的該data屬性,一有輸入就觸發事件通知父組件,並payload新值。父組件經過payload更新本身的data,並經過prop將新值下發給子組件,子組件watch本身的propprop一旦變更,將新變更賦給datacomponent

理解v-model

在input元素上使用

咱們在用輸入框時,會用v-model進行雙向綁定。orm

<input v-mode="message">
等價於
<input :value="message" @input="message = $event.target.value" />事件

具體行爲:① 在inputvalue屬性上引用組件的data ② 發生oninput事件時,更新組件data,從而更新value

new Vue({
    el:'#app-6',
    data:{
        message:"Hello"
    }
})
<div id="app-6">
    <input v-model="message"/>
    <input :value="message" @input="message = $event.target.value" />
    <div>{{message}}</div>
</div>

在自定義事件的表單輸入組件上使用

前提:
<myComponent v-model="price">
等價於
<myComponent :value="price" @input="price = arguments[0]" >
new Vue({
    el: '#app-7',
    data: {
        price: 100
    },
    components: {
        "component-7": {
            props: ['value'],
            template: '\
                <div>\
                    <input :value="value" @input="updateValue($event.target.value)" class="form-control form-control-sm w-25">\
                    <slot></slot>\
                    <div><span class="badge badge-info">子組件Prop:【{{value}}】</span></div>\
                </div>',
            methods:{
                updateValue:function(value){
                    value = value+"-"
                    this.$emit('input',value)
                }
            }
        }
    },
    
})
<div id="app-7">
    <!-- <component-7 v-model="price"></component-7> -->
    <component-7 :value="price" @input="price = arguments[0]" >
        <div>
            <span class="badge badge-info">父組件數據:【{{price}}】</span>
        </div>
    </component-7>
</div>

clipboard.png

這裏的流程,輸入框輸入時,觸發子組件上input事件並執行updateValue方法,方法參數爲輸入框中的value(經過$emit.target.value獲取),方法能夠先對value進行一系列加工處理造成super_value,最後使用this.$emit('input',super_value)觸發父組件在子組件節點上監聽的input事件,並將加工過的super_value負載在事件上。父組件@input="price = arguments[0]"中的arguments[0]便是這個super_value,父組件經過input的回調更新本身的data,在將data下發給子組件的value特性。

這裏若是使用v-model指令,那麼子組件特性value、父組件監聽的事件input及其回調price = arguments[0], 這些都是固定的,不能變化。

此示例的執行流程其實與雙向綁定(.sync修飾符與update事件)中的例子是同樣的。
只是這裏父組件上的input事件功效沒有雙向綁定中update事件那麼強大。

在組件上使用

在輸入框組件中已經說過v-model的種種限制,其中最主要的兩點,下發的組件特性必須命名爲value和父組件監聽的只能綁定事件input,不靈活, 例如在checkbox中,我要給下發的特性取名爲checked代替value,而且父組件不想監聽@input事件,而是@change事件。

爲了解決這個不靈活的問題,能夠在組件model選項設置propevent,以下

new Vue({
    el: '#app-9',
    data: {
        isChecked: false,
        message:'please choose this box'
    },
    components: {
        'component-9': {
            model: {
                prop: 'checked',
                event: 'change'
            },
            props:{
                checked:Boolean,
                value:String
            },
            template: '<div><input type="checkbox" @click="choose($event.target.checked)"><slot></slot></div>',
            methods:{
                choose:function(checked){
                    this.$emit('change',checked)
                }
            }
        }
    }
})
<div id="app-9" style="height:500px">
    <component-9 v-model="isChecked" v-bind:value="message">
        <span class="badge badge-info">特性`value`如今從v-model綁定中解放出來了,可自定義使用</span>
    </component-9>
</div>

clipboard.png

相關文章
相關標籤/搜索