關(guān)于Vue3.0的Composition API 和 舊版本的Mixins【轉(zhuǎn)載】
原文:http://caibaojian.com/vue3-composition-api.html
在過往,如果你想共享組件間的代碼屁柏,一般使用的方式是利用 mixin 來實(shí)現(xiàn)。但是在Vue3.0 中提供了更好的解決方案 Composition API抖甘。
下面我們將介紹 Mixins 的缺點(diǎn),并且看看 Composition API 是如何克服這些缺點(diǎn)的蘑斧。
Mixins簡述
我們先來回顧以下Mixins的模式老厌。以下的內(nèi)容是十分重要的需要認(rèn)真閱讀。
通常情況下肃弟,一個(gè)Vue組件是由一個(gè)Javascript對象來定義的,這個(gè)Javascript對象具有各種屬性零蓉,代表著我們需要的功能 --- 比如data
,method
,computed
等笤受。
// MyComponent.js
export default {
data:()=>({
myDataProperty: null
}),
methods:{
myMethod () { ... }
},
// ....
}
當(dāng)我們想在組件之間共享相同的屬性的時(shí)候,就可以將共同的屬性提取出來敌蜂,存放到一個(gè)單獨(dú)的模塊中箩兽。
// MyMixins.js
export default {
data:()=>({
mySharedDataProperty: null
}),
methods:{
blabla(){...}
}
}
現(xiàn)在我們通過將其分配給 mixin config 屬性并將其添加到任何使用的組件中。在運(yùn)行時(shí)章喉,Vue將把組件的屬性與任何添加的 mixin 合并
// ConsumingComponent.js
import MyMixin from "./MyMixin.js";
export default {
mixins: [MyMixin],
data: () => ({
myLocalDataProperty: null
}),
methods: {
myLocalMethod () { ... }
}
}
在這個(gè)具體的例子中汗贫,運(yùn)行時(shí)使用的組件定義是這樣的身坐。
export default {
data:()=>({
mySharedDataProperty: null
myLocalDataProperty: null
}),
methods:{
mySharedMethod () { ... },
myLocalMethod () { ... }
}
}
Mixins 缺點(diǎn)
1. 命名沖突
我們看到minxin模式是如何在運(yùn)行時(shí)合并兩個(gè)對象的。如果它們都共享一個(gè)同名的屬性會發(fā)生什么
const mixin = {
data: ()=>({
myProp:null
})
}
export default {
mixins:[mixin],
data:()=>({
// same name !
myProp:null
})
}
這就是合并策略發(fā)揮的地方芳绩。這一組規(guī)則用于決定一個(gè)組件包含多個(gè)相同名稱的選項(xiàng)時(shí)的情況。
Vue組件的默認(rèn)(但可選擇配置)合并策略決定了本地選項(xiàng)將覆蓋混合器選項(xiàng)撞反。但也有例外妥色,例如我們有多個(gè)相同類型的生命周期鉤子,那么這些鉤子將被添加到鉤子數(shù)組中遏片,并且所有的鉤子將被依次調(diào)用嘹害。
盡管我們不應(yīng)該遇到任何實(shí)際的錯(cuò)誤,但當(dāng)我們在多個(gè)組件和混合體之間雜耍命名的屬性時(shí)吮便,寫代碼會越來越困難笔呀。尤其當(dāng)?shù)谌降幕旌辖M件被添加為npm包時(shí),就更難了髓需,因?yàn)樗鼈兊拿麑傩钥赡軙饹_突许师。
2.隱含的依賴關(guān)系
混合器和消耗它的組件之間沒有層次關(guān)系。這意味著僚匆,組件可以使用混入器中定義的數(shù)據(jù)屬性(如mySharedDataProperty)微渠,但混入器也可以使用它假定在組件中定義的數(shù)據(jù)屬性(如myLocalDataProperty)。當(dāng)混合器被用于共享輸入驗(yàn)證時(shí)咧擂,通常會出現(xiàn)這種情況逞盆。mixin可能會期望一個(gè)組件有一個(gè)輸入值,它將在自己的validate方法中使用松申。
但這可能會導(dǎo)致問題云芦。如果我們以后想重構(gòu)一個(gè)組件并改變了mixin需要的變量的名稱,我們會發(fā)現(xiàn)在運(yùn)行時(shí)會出現(xiàn)報(bào)錯(cuò)贸桶。
現(xiàn)在想象一下一個(gè)有一大堆 mixin 的組件舅逸,我們可以重構(gòu)本地?cái)?shù)據(jù)嗎?我們可以重構(gòu)一個(gè)本地?cái)?shù)據(jù)屬性嗎皇筛?或者會不會破壞一個(gè)混搭堡赔?哪一個(gè)混雜項(xiàng)呢?我們必須手動搜索它們才能知道设联。
這些缺點(diǎn)的存在善已,就是Composition API背后的主要?jiǎng)右蛑唬旅嫦却致缘慕榻B了Composition API的工作原理
關(guān)于Composition API 的工作原理
組成API 的關(guān)鍵思想是离例,我們將組件的功能(如狀態(tài)换团,方法,計(jì)算屬性等)定義為對象屬性宫蛆,而不是將其定義為從新得設(shè)置函數(shù)中返回的Javascript變量
以這個(gè)經(jīng)典的Vue 2組件為例艘包,它定義了一個(gè) "計(jì)數(shù)器 "功能的猛。
// counter.Vue
export default = {
data:()=>({
count:0
}),
methods:{
increment(){
this.count++
}
},
computed:{
double(){
return this.count * 2
}
}
}
下面是Composition API 定義的完全相同的組件
// Counter.vue
import {ref, computed } from "vue";
export default ={
setup(){
const count = ref(0)
const double = computed( ()=> count * 2 )
function increment(){
count.value++;
}
return {
count,
double,
increment
}
}
}
tips
- 反應(yīng)式變量: 在一個(gè) Y = f(x) 的函數(shù)中 Y隨著X的變化而變化,那么 Y 就是反應(yīng)式變量(又叫因變量)
首先你會注意到我們導(dǎo)入了一個(gè) ref 函數(shù)想虎,這使得我們可以定義一個(gè)反應(yīng)式變量,其功能與數(shù)據(jù)變量基本相同卦尊。計(jì)算函數(shù)也一樣
增量方法(increment)不是反應(yīng)式的,所以它可以被聲明為一個(gè)普通的Javascript函數(shù)舌厨。注意岂却,我們需要改變子屬性值,才能改變 count 反應(yīng)式變量的值裙椭。這是因?yàn)槭褂?ref 創(chuàng)建的反應(yīng)式變量在傳遞過程中躏哩,需要將其作為對象來保留反應(yīng)式變量
關(guān)于 ref 的工作原理的詳細(xì)解釋,請參考 Vue Composition API 文檔揉燃,這是個(gè)好主意扫尺。
一旦我們定義了這些功能,我們就從setup函數(shù)中返回這些功能炊汤。上面的兩個(gè)組件在功能上沒有什么區(qū)別正驻。我們所做的就是使用替代API。
代碼提取
Composition API 的第一個(gè)明顯的優(yōu)勢是很容易提取邏輯
讓我們用Composition API重構(gòu)上面定義的組件抢腐,這樣我們定義的特征就在一個(gè)Javascript模塊useCounter中拨拓。(用"use"作為特征描述的前綴是Composition API的命名慣例)
// useCounter.js
import { ref, computed } from "vue";
export default function () {
const count = ref(0);
const double = computed( () => count * 2 )
function increment () {
count.value++;
}
return {
count,
double,
increment
}
}
代碼重用
要在組件中使用該功能,我們只需將模塊導(dǎo)入到組件文件中氓栈,然后調(diào)用它(注意渣磷,導(dǎo)入是一個(gè)函數(shù))。這將返回我們定義的變量授瘦,隨后我們可以從 setup 函數(shù)中返回這些變量
// MyComponent.js
import useCounter from "./useCounter.js";
export default {
setup () {
const { count, double, increment } = useCounter() // 解構(gòu)
return {
count,
double,
increment
}
}
}
這一切可能看起來有點(diǎn)啰嗦醋界,也沒有意義,但是讓我們來看看這個(gè)模式如何克服我們前面看到的 mixins 的問題
命名沖突 解決了!
我們之前已經(jīng)看到了一個(gè)混搭元素如何使用可能與消耗組件中的屬性名稱相同的屬性提完,甚至更陰險(xiǎn)的是形纺,在消耗組件使用的其他混搭元素中也會有相同的名稱
這并不是 Composition API 的問題,因?yàn)槲覀冃枰@式命名任何狀態(tài)或從組成函數(shù)返回的方法
export default {
setup(){
const {someVar1, someMethod1 } = useCompFunction1()
const {someVar2, someMethod2 } = useCompFunction2()
return {
someVar1,
someMethod1,
someVar2,
someMethod2
}
}
}
命名碰撞的解決方法將與其他任何Javascript變量的命名方式一樣徒欣。
隱式依賴關(guān)系 解決了逐样!
我們之前也看到了一個(gè)組合函數(shù)可能會使用消耗組件上定義的數(shù)據(jù)屬性,這可能會使代碼變得很脆弱打肝,而且很難推理
而組合函數(shù)也可以調(diào)用消耗消耗組件中定義得變量脂新。但不同的是,這個(gè)變量現(xiàn)在必須顯式傳遞給組成函數(shù)
import useCompFunction from "./useCompFunction";
export default {
setup () {
// some local value the a composition function needs to use
const myLocalVal = ref(0);
// it must be explicitly passed as an argument
const {...} = useCompFunction(myLocalVal)
}
}
包裝起來
mixin 模式表面上看起來很安全粗梭。然而争便,通過合并對象來共享代碼,由于它給代碼增加了脆弱性断医,并且覆蓋了推理功能的能力滞乙,因此成為了一種反模式
Composition API 最聰明的地方在于奏纪,它允許 Vue 依靠原生 Javascript 內(nèi)置的保障措施來共享代碼,比如將變量傳遞給函數(shù)斩启,以及模塊系統(tǒng)序调。
這是否意味著 Composition API 在各方面都比 Vue的經(jīng)典 API 優(yōu)越呢?不是的兔簇,在大多數(shù)情況下发绢,你可以堅(jiān)持使用經(jīng)典的API。但是男韧,如果你打算重用代碼朴摊,Composition API 是更加優(yōu)越的默垄。