以前說過,能夠使用 props 將數據從父組件傳遞給子組件。其實還有其它種的通訊方式,下面咱們一一娓娓道來。html
經過自定義事件,咱們能夠把數據從子組件傳輸回父組件。子組件經過 $emit()
來觸發事件,而父組件經過 $on()
來監聽事件,這是典型的觀察者模式。vue
html:vuex
<div id="app">
<p>總數:{{total}}</p>
<deniro-component @increase="setTotal"
@reduce="setTotal"
></deniro-component>
</div>
複製代碼
js:bash
Vue.component('deniro-component', {
template: '\ <div>\ <button @click="increase">+1</button>\ <button @click="reduce">-1</button>\ </div>',
data: function () {
return {
counter: 0
}
},
methods: {
increase: function () {
this.counter++;
this.$emit('increase', this.counter);
},
reduce: function () {
this.counter--;
this.$emit('reduce', this.counter);
}
}
});
var app = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
setTotal: function (total) {
this.total = total;
}
}
});
複製代碼
效果: app
示例中有兩個按鈕,分別實現加 1 與減 1 操做。點擊按鈕後,執行組件中定義的 increase 或 reduce 方法,在方法內部,使用 $emit
把值傳遞迴父組件。 $emit
方法的第一個參數是使用組件時定義的事件名,示例中是 @increase
與 @reduce
:函數
<deniro-component @increase="setTotal"
@reduce="setTotal"
></deniro-component>
複製代碼
這兩個事件又綁定了 setTotal
方法,該方法修改了 total 值。 $emit
方法的其它參數是須要回傳給父組件的參數。ui
也能夠使用 v-on
加 .native
來監聽原生事件,好比這裏監聽組件的點擊事件:this
html:spa
<div id="app">
...
<deniro-component ...
@click.native="click"
></deniro-component>
</div>
複製代碼
js:.net
...
var app = new Vue({
el: '#app',
data: {
total: 0
},
methods: {
...
click: function () {
console.log("原生點擊事件");
}
}
});
複製代碼
這樣,點擊按鈕後,就能夠捕獲原生的點擊事件啦O(∩_∩)O~
**注意:**這裏監聽的是這個組件根元素的原生點擊事件。
也能夠使用 v-model 方式來直接綁定父組件變量,把數據從子組件傳回父組件。
html:
<div id="app2">
<p>總數:{{total}}</p>
<deniro-component2 v-model="total"
></deniro-component2>
</div>
複製代碼
js:
Vue.component('deniro-component2', {
template: '\ <div>\ <button @click="click">+1</button>\ </div>',
data: function () {
return {
counter: 0
}
},
methods: {
click: function () {
this.counter++;
this.$emit('input', this.counter);
}
}
});
var app2 = new Vue({
el: '#app2',
data: {
total: 0
}
});
複製代碼
效果:
咱們使用 v-model="total"
直接綁定變量 total。接着在子組件中,在 $emit
方法傳入事件名 input
,這樣 Vue.js 就會自動找到 `v-model 綁定的變量啦O(∩_∩)O~
咱們也能夠使用自定義事件來實現上述示例——
html:
<div id="app3">
<p>總數:{{total}}</p>
<deniro-component3 @input="setTotal"
></deniro-component3>
</div>
複製代碼
js:
Vue.component('deniro-component3', {
template: '\ <div>\ <button @click="click">+1</button>\ </div>',
data: function () {
return {
counter: 0
}
},
methods: {
click: function () {
this.counter++;
this.$emit('input', this.counter);
}
}
});
var app3 = new Vue({
el: '#app3',
data: {
total: 0
},
methods: {
setTotal: function (total) {
this.total = total;
}
}
});
複製代碼
效果與上例相同。
咱們還能夠在自定義的表單輸入組件中利用 v-model,實現數據雙向綁定:
html:
<div id="app4">
<p>總數:{{total}}</p>
<deniro-component4 v-model="total"
></deniro-component4>
<button @click="increase">+1</button>
</div>
複製代碼
js:
Vue.component('deniro-component4', {
props: ['value'],
template: '<input :value="value" @input="update">+1</input>',
data: function () {
return {
counter: 0
}
},
methods: {
update: function (event) {
this.$emit('input', event.target.value);
}
}
});
var app4 = new Vue({
el: '#app4',
data: {
total: 0
},
methods: {
increase: function () {
this.total++;
}
}
});
複製代碼
效果:
這裏咱們首先利用 v-model,在自定義組件中綁定了 total 變量。而後在組件內部,定義了 props 爲 ['value']
,注意這裏必須爲 value,才能接收綁定的 total 變量。接着在組件模板中把接收到的 value 值(即 total 變量值),做爲 <input>
元素的初始值,並綁定 input 事件。下一步,在 input 事件中,經過 this.$emit('input', event.target.value)
把 total 值傳回父組件的 <button @click="increase">+1</button>
。最後在 increase 方法中,遞增 total 值。
這個示例,咱們綜合使用了 props 、v-model和自定義事件,實現了數據的雙向綁定。
總的來講,一個具備雙向綁定的 v-model 組件具備如下特徵:
$emit
將新的 value 值回傳給父組件。非父子組件指的是兄弟組件或者跨多級組件。
咱們能夠建立一個空的 Vue 實例做爲中央事件總線,實現非父子組件之間的通訊。
html:
<div id="app5">
<p>監聽子組件消息:{{message}}</p>
<deniro-component5></deniro-component5>
</div>
複製代碼
js:
var bus = new Vue();
Vue.component('deniro-component5', {
template: '<button @click="sendMessage">發送消息</button>',
methods: {
sendMessage: function () {
bus.$emit('on-message', '來自於 deniro-component5 的消息');
}
}
});
var app5 = new Vue({
el: "#app5",
data: {
message: ''
},
mounted: function () {
var that = this;
bus.$on('on-message', function (message) {
that.message = message;
})
}
});
複製代碼
注意: 由於 bus.$on()
中的函數,this 指向的是自己,因此咱們必須在外層定義一個 that,讓它引用 mounted 對象。
效果:
首先建立了一個空的 Vue 實例做爲中央事件總線。而後在定義的子組件綁定的 click 事件中,經過 bus.$emit()
發送消息。接着在初始化 app 實例的 mounted
函數時,使用 bus.$on()
方法監聽消息。
這種方式能夠實現組件間任意通訊。咱們還能夠擴展 bus 實例,爲它添加 data、methods、computed 等屬性,這些都是公共屬性,能夠共用。因此在此能夠放置須要共享的信息,好比用戶登錄暱稱等。使用時只須要初始化一次 bus 便可,因此在單頁面富客戶端中應用普遍。
若是項目較大,那麼能夠使用具備狀態管理的 vuex 哦O(∩_∩)O~
子組件能夠使用 this.$parent
來訪問父組件實例;而父組件能夠使用 this.$children
來訪問它的全部子組件實例。這些方法能夠遞歸向上或向下,直到根實例或者葉子實例。
html:
<div id="app6">
<p>消息:{{message}}</p>
<deniro-component6></deniro-component6>
</div>
複製代碼
js:
Vue.component('deniro-component6', {
template: '<button @click="sendMessage">發送消息</button>',
methods: {
sendMessage: function () {
//經過父鏈找到父組件,修改相應的變量
this.$parent.message='來自於 deniro-component6 的消息';
}
}
});
var app6 = new Vue({
el: "#app6",
data: {
message: ''
}
});
複製代碼
效果:
**注意:**只有在萬不得已的狀況下,才使用父子鏈,實現組件間任意通訊。由於這樣作,會讓兩個組件之間緊耦合,代碼變得難理解與維護。若是隻是父子組件之間的通訊,儘可能採用 props
與自定義事件 $emit
來實現。
若是一個組件的子組件較多且是動態渲染的場景,使用 this.$children
來遍歷這些子組件較麻煩。這時就能夠使用 ref
來爲子組件指定索引名稱,方便後續查找。
html:
<div id="app7">
<button @click="getChild">獲取子組件實例</button>
<deniro-component7 ref="child"></deniro-component7>
</div>
複製代碼
js:
Vue.component('deniro-component7', {
template: '<div>deniro-component7</div>',
data: function () {
return {
message: '登錄不到兩週,InSight探測器意外捕捉到火星的風聲'
}
}
});
var app7 = new Vue({
el: "#app7",
methods: {
getChild: function () {
//使用 $refs 來訪問組件實例
console.log(this.$refs.child.message);
}
}
});
複製代碼
輸出結果:
登錄不到兩週,InSight探測器意外捕捉到火星的風聲
注意:$refs
只在組件渲染完成後纔會被賦值,並且它是非響應式的。因此只有在萬不得已的狀況下才使用它。
總結以下:
通訊方式 | 通訊方向 |
---|---|
props 【推薦】 |
父組件到子組件 |
自定義事件 $emit 【推薦】 |
子組件到父組件 |
中央事件總線【推薦】 | 組件間任意通訊 |
父子鏈 | 組件間任意通訊 |
子組件索引 | 父組件到子組件 |