常見場景
? ? ? 有兩個非常相似的組件,他們的基本功能是一樣的,但他們之間又存在著足夠的差異性,此時的你就像是來到了一個分岔路口:我是把它拆分成兩個不同的組件呢?還是保留為一個組件,然后通過props傳值來創造差異性從而進行區分呢?
? ? ??兩種解決方案都不夠完美:如果拆分成兩個組件,你就不得不冒著一旦功能變動就要在兩個文件中更新代碼的風險,這違背了 DRY 原則。反之,太多的props傳值會很快變得混亂不堪,從而迫使維護者(即便這個人是你)在使用組件的時候必須理解一大段的上下文,拖慢寫碼速度。
? ? ??使用Mixin。Vue 中的Mixin對編寫函數式風格的代碼很有用,因為函數式編程就是通過減少移動的部分讓代碼更好理解(引自 Michael Feathers )。Mixin允許你封裝一塊在應用的其他組件中都可以使用的函數。如果使用姿勢得當,他們不會改變函數作用域外部的任何東西,因此哪怕執行多次,只要是同樣的輸入你總是能得到一樣的值,真的很強大!
基礎實例
? ? ?? 我們有一對不同的組件,它們的作用是通過切換狀態(Boolean類型)來展示或者隱藏模態框或提示框。這些提示框和模態框除了功能相似以外,沒有其他共同點:它們看起來不一樣,用法不一樣,但是邏輯一樣。
// 模態框
const Modal = {
template: '#modal',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
// 提示框
const Tooltip = {
template: '#tooltip',
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
},
components: {
appChild: Child
}
}
- 我們可以在這里提取邏輯并創建可以被重用的項:
const toggle = {
data() {
return {
isShowing: false
}
},
methods: {
toggleShow() {
this.isShowing = !this.isShowing;
}
}
}
const Modal = {
template: '#modal',
mixins: [toggle],
components: {
appChild: Child
}
};
const Tooltip = {
template: '#tooltip',
mixins: [toggle],
components: {
appChild: Child
}
};
用法
-
你可以按照你喜歡的任意方式設置你的目錄結構,但為了結構規整我喜歡新建一個mixin目錄。我們創建的這個文件含有.js擴展名(跟.vue相對,就像我們的其他文件),為了使用Mixin我們需要輸出一個對象。
目錄結構.jpg -
在Modal.vue使用這樣的寫法,來引入這個Mixin:
import Child from './Child' import { toggle } from './mixins/toggle' export default { name: 'modal', mixins: [toggle], components: { appChild: Child } }
? ? ??即便我們使用的是一個對象而不是一個組件,生命周期函數對我們來說仍然是可用的,理解這點很重要。我們也可以這里使用mounted()鉤子函數,它將被應用于組件的生命周期上。這種工作方式真的很靈活也很強大。
合并
? ? ??在下面的這個例子,我們可以看到,我們不僅僅是實現了自己想要的功能,并且Mixin中的生命周期的鉤子也同樣是可用的。因此,當我們在組件上應用Mixin的時候,有可能組件與Mixin中都定義了相同的生命周期鉤子,這時候鉤子的執行順序的問題凸顯了出來。默認Mixin上會首先被注冊,組件上的接著注冊,這樣我們就可以在組件中按需要重寫Mixin中的語句。組件擁有最終發言權。當發生沖突并且這個組件就不得不“決定”哪個勝出的時候,這一點就顯得特別重要,否則,所有的東西都被放在一個數組當中執行,Mixin將要被先推入數組,其次才是組件。
//mixin
const hi = {
mounted() {
console.log('hello from mixin!')
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
mounted() {
console.log('hello from Vue instance!')
}
});
//Output in console
> hello from mixin!
> hello from Vue instance!
- 如果這兩個沖突了,我們看看 Vue實例或組件是如何決定輸贏的:
//mixin
const hi = {
methods: {
sayHello: function() {
console.log('hello from mixin!')
}
},
mounted() {
this.sayHello()
}
}
//vue instance or component
new Vue({
el: '#app',
mixins: [hi],
methods: {
sayHello: function() {
console.log('hello from Vue instance!')
}
},
mounted() {
this.sayHello()
}
})
// Output in console
> hello from Vue instance!
> hello from Vue instance!
- 你可能已經注意到這有兩個console.log而不是一個——這是因為第一個函數被調用時,沒有被銷毀,它只是被重寫了。我們在這里調用了兩次sayHello()函數。
結論
? ? ??Mixin對于封裝一小段想要復用的代碼來講是有用的。對你來說Mixin當然不是唯一可行的選擇:比如說高階組件就允許你組合相似函數,Mixin只是的一種實現方式。我喜歡Mixin,因為我不需要傳遞狀態,但是這種模式當然也可能會被濫用,所以,仔細思考下哪種選擇對你的應用最有意義吧!