本篇主要是透過線上課程:HiSKIO
、及網路上搜尋資源所學習的。
自訂事件 Custom events
v-on 綁定自訂事件
在組件中發出自訂事件。
例子1
在組件中寫了一個 my-button ,渲染出來後是一個 button ,偵聽了 click 事件,執行 click 後就會用this.$emit
發出my-click
事件,就會被上層的 v-on 的 my-click 捕捉到,就會執行 clicked 函式。
$emit的第二個之後的參數是資料
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
| <div id="app"> <my-button v-on:my-click="clicked"></my-button> </div>
<script> Vue.component('my-button',{ template:`<button @click="myClick">my-button</button>`, methods:{ myClick(){ this.$emit('my-click', 100,200,300); }, }, }), new Vue({ el:'#app', methods:{ clicked(x,y,z){ alert('clicked' + `${x},${y},${z}`); }, }, }) </script>
|
例子2
內容有一個 +1 的 button 的 counter ,另一個 button 則是點擊後會執行 emit 函式。
在上層綁定下層傳來的count-emit
事件,如果接收到事件後則執行 countEmit 函式。
emit 函式執行時,會用 $emit 傳送事件到上層,綁定上層事件後執行 count 給值。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| <div id="app"> <my-counter @count-emit="countEmit"></my-counter> <h1>{{count}}</h1> </div>
<script> Vue.component('my-counter', { template: ` <div> <h1>{{count}}</h1> <button @click="count+=1">+1</button> <button @click="emit">emit</button> </div> `, data() { return { count: 0, } }, methods: { emit() { this.$emit('count-emit', this.count); }, }, }), new Vue({ el: '#app', data: { count: 0, }, methods: { countEmit(count) { this.count = count; }, }, }) </script>
|
組件綁定原生事件
要偵聽原生事件的時候,一般的偵聽是不會有效果的,需要在事件後面加上native
這個修飾符。
ex.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| <div id="app"> <my-button @click="btnClicked"></my-button> <my-button @click.native="btnClicked"></my-button> </div>
<script> Vue.component('my-button',{ template:` <button>My Button</button> ` }), new Vue({ el:'#app', methods:{ btnClicked(){ alert('clicked'); }, }, }) </script>
|
.sync修飾符
例子說明
第一個功能
下方計數數值為上層的 outerCount ,預設值為 10 。
上方計數數值為下層的 innerCount ,預設值想要綁定跟 outerCount 一樣的預設值 10 ,讓 +1 從 10 開始計算。
這邊需要用 props 上層傳到下層來綁定,需要注意命名, html 中只能用 kebab case 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39
| <div id="app"> <my-counter :outer-count="outerCount" @update="setOuterCount"></my-counter> <h1>{{outerCount}}</h1> </div>
<script> Vue.component('my-counter',{ props:['outerCount'], data(){ return{ innerCount: this.outerCount, }; }, template: ` <div> <h1>{{innerCount}}</h1> <button @click="innerCount+=1">+1</button> <button @click="sync">sync</button> </div> `, methods:{ sync(){ this.$emit('update', this.innerCount) }, }, }) new Vue({ el:'#app', data:{ outerCount: 10, }, methods:{ setOuterCount(count){ this.outerCount = count; }, }, }) </script>
|
第二個功能
把下層的 innerCount 數值設定到上層的 outerCount 。
一般的作法則是用 this.$emit 把事件傳到上層,並偵聽更新的函式來將 outerCount 的數值更新即可。
在這邊則會用.sync
,不需要偵聽update函式的方式來簡單的實現。
把上層的@update="setOuterCount"
、setOuterCount
的 method 拿掉, this.$emit 發送的事件改為:update:outerCount
,原本綁定的 outer-count 加上 .sync 修飾符。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
| <div id="app"> <my-counter :outer-count.sync="outerCount"></my-counter>
<h1>{{outerCount}}</h1> </div>
<script> Vue.component('my-counter',{ props:['outerCount'], data(){ return{ innerCount: this.outerCount, }; }, template: ` <div> <h1>{{innerCount}}</h1> <button @click="innerCount+=1">+1</button> <button @click="sync">sync</button> </div> `, methods:{ sync(){ this.$emit('update:outerCount', this.innerCount) }, }, }) new Vue({ el:'#app', data:{ outerCount: 10, }, }) </script>
|
.sync
做的事情就是綁定 update 事件,把值回傳
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
| <div id="app"> <my-counter :outer-count.sync="outerCount"></my-counter> <h1>{{outerCount}}</h1> </div>
<script> Vue.component('my-counter',{ props:['outerCount'], data(){ return{ innerCount: this.outerCount, }; }, template: ` <div> <h1>{{innerCount}}</h1> <button @click="innerCount+=1">+1</button> <button @click="sync">sync</button> </div> `, methods:{ sync(){ this.$emit('update:outerCount', this.innerCount) }, }, }) new Vue({ el:'#app', data:{ outerCount: 10, }, }) </script>
|
自訂組件的v-model
v-model 可以做到與 .sync 修飾符相似的效果。
v-model 就是用 v-bind 綁定 value 加上 v-on 綁定 input。
所以在組件中要針對 value 的屬性設定跟 $emit 一個 input 事件。
但 v-model 也可以在組件中指定一個 model 的屬性,裡面可以有兩個屬性 prop 跟 event ,兩者都是字串。
指定後,綁定的屬性(預設是 value )與事件名稱(預設是 input )就需要與指定後的名稱相同。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45
| <div id="app"> <my-counter v-model="outerCount"></my-counter> <my-counter :count="outerCount" @update:count="val => outerCount = val"></my-counter>
<h1>{{outerCount}}</h1> </div>
<script> Vue.component('my-counter', { model: { prop: 'count', event: 'update:count', }, props: ['count'], data() { return { innerCount: this.count, }; }, template: ` <div> <h1>{{innerCount}}</h1> <button @click="innerCount+=1">+1</button> <button @click="sync">sync</button> </div> `, methods: { sync() { this.$emit('update:count', this.innerCount) }, }, }) new Vue({ el: '#app', data: { outerCount: 10, }, }) </script>
|
跨組件的溝通
兩個組件不是上下層,不是用 $emit 、 props 傳遞等等。如果兩個組件是同一層,沒有上下關係的話,則要用別的方式。
方式1
- 把 data 的 count 初始值設定移動到上層
- 偵聽 button 原生事件
- 綁定 count 屬性帶回 count 值
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25
| <div id="app"> <my-count :count="count"></my-count> <my-button @click.native="count+=1"></my-button>
</div>
<script> Vue.component('my-count', { props:['count'], template: ` <h1>{{count}}</h1> ` }), Vue.component('my-button', { template: ` <button>+1</button> ` }), new Vue({ el: '#app', data:{ count:0, } }) </script>
|
方式2- vuex(後面單獨篇章)
方式1是比較簡單的方式,要注意的是,如果是要把 data 中的設定值保留在原本 component 裡面,或是很多個組件時就需要用 vuex 的方式來實現。
vuex會有統一的資料,會對每一個組件去做溝通,雙向綁定,是集中管理的概念。
方式3- event bus
先在最外層宣告共用的 event bus ,之後可以用這個 bus 來 $emit 發出事件, $on 偵聽事件等等。
$on 用來偵聽, $on 與 $emit 都是 vue 實例的一個方法。
偵聽 $on 需要在生命週期 mounted 的時期去偵聽,需要用到箭頭函式,因為箭頭函式的 this 是繼承而來,指的就是這個組件,如果不是用箭頭函式, this 則會指到 bus 這個 Vue 。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
| <div id="app"> <my-count></my-count> <my-button></my-button> </div>
<script> const bus = new Vue(); Vue.component('my-count', { data() { return { count: 0, }; }, mounted() { bus.$on('add',() => { this.count += 1; }); }, template: `<h1>{{count}}</h1>`, }), Vue.component('my-button', { template: `<button @click="click">+1</button>`, methods: { click() { bus.$emit('add') }, }, }), new Vue({ el: '#app', }) </script>
|