大家好,打給厚,胎嘎後,身為一個專業程式踩雷王,種田的時候挖到地雷也是無可厚非(欸,今天要給大家講一下關於 VueJS 元件(Component 與 Directive 通用)之間的溝通,的一些小技(ㄉㄧˋ)巧(ㄌㄟˊ)
首先要看一下官方給的說明,
接著看一下一個 Vue 元件的結構,
收工(喂!
元件溝通
不外乎就幾種方式,
- 利用 Event 來傳遞訊息
$dispatch()
對上屬元件做廣播(會一直廣播到$root
$broadcast()
對下屬元件做監聽廣播$emit()
對屬於 同一個元件 內的監聽做觸發動作$on()
設定一個監聽
- 利用
$watch
來監聽某個物件或資料 - 利用雙向綁定(例如
:data.sync="data"
)搭配 2 來變更資料
最偷懶的方式其實是第 3 種(炸
溝通之後的 DOM 資料更新
Vue 提供了一個 $nextTick
的方法來更新你的 DOM(類似於 ng 裡面的 scope.$digest()
這樣的方法,問題來了,要放在哪裡?
- 放在 Event 接收的地方?
- 放在
methods
由data
變更來呼叫? - 放在
$watch
?
答案是全部都可以,但是全部都有雷。
使用 Event 溝通
單純使用 $on
來接收,並搭配 $nextTick
基本上不會有問題。但是,如果又加上 $watch
就會有問題,
this.$on('data-changed', (newData) => {
this.data = newData
// 或
this.$nextTick(() => {
this.data = newData
})
})
...
watch: {
data (newData) {
console.log(newData)
// 或加上
this.$nextTick(() => {
console.log(newData)
// 做一些 DOM 改變
})
}
}
這樣的作法,如果觸發 data-changed
,那麼 watch
需要搭配 deep
才會被觸發(如果你是 Object 或是 Array,你可以使用 setTimeout
測試看看。另外,如果是改了資料來源,例如他是一個 JSON 檔案,那麼修改來源檔案(或許由 AJAX 讀入,觸發的結果跟上述相同,watch
也是需要搭配 deep
另外,Event 搭配 methods
呼叫是可以的,不管你的 $nextTick
要放在 methods
裡面,還是 Event 裡面都可以。只要你是要改變 DOM 所產出的東西,都記得要放在 $nextTick
裡面。
Methods 方法
你可以在 Components 裡面的 methods
加入一個函式,用他來處理一些事情之後,最後在將資料返回,或是直接做 $nextTick
處理掉。
$watch 到底在看誰?
Watch 只會看元件的 props
, data
,所以其他的東西 watch
是不會理你的。同樣的,watch
裡面也必須要有 $nextTick
來更新你的 DOM 所顯示的資料。
小結
元件的溝通,用雙向綁定會有一個問題,當你的元件越來越多,或是深度越來越深的時候,你的綁定資料就會變得複雜,舉例來說,
元件順序 A > B > C
這是元件 A,提供最原始的 data
<template>
<div>
<b-component :data.sync="data"></b-component>
</div>
</template>
<script>
import bComponent from './bComponent'
export default {
name: 'aComponent',
components: {
bComponent
},
data: {
data: { name: 'a' }
}
}
</script>
這是元件 B
<template>
<div>
<c-component :data.sync="data"></c-component>
</div>
</template>
<script>
import cComponent from './cComponent'
export default {
name: 'bComponent',
components: {
cComponent
}
}
</script>
這是元件 C,我要用 data
<template>
<div>
<c-component></c-component>
</div>
</template>
<script>
export default {
name: 'cComponent',
attached () {
// 這裡可以拿到從 A > B 過來的 data 資料
console.log(this.data)
}
}
</script>
如果你只用雙向綁定,那你必須要每一層 Component 都把 data
傳進去。這樣會造成元件後續開發上的難度。所以,在這種情況下,改用 Event 會比較容易理解,維護上也比較輕鬆一點。
這是元件 A,提供最原始的 data
<template>
<div>
<b-component></b-component>
</div>
</template>
<script>
import bComponent from './bComponent'
export default {
name: 'aComponent',
components: {
bComponent
},
data: {
data: { name: 'a' }
},
compiled () {
this.$broadcast('send-data-to-c-component', this.data)
}
}
</script>
這是元件 C,我要用 data
<template>
<div>
<c-component></c-component>
</div>
</template>
<script>
export default {
name: 'cComponent',
created () {
this.$on('send-data-to-c-component', (data) => {
// 這裡可以拿到從 A 過來的 data 資料
console.log(data)
// 可以把 data 指給自己
this.data = data
})
},
data: {
data: undefined
}
}
</script>
中間的 bComponent
不需要做什麼其他的事情。其實作法跟 ng 很類似。