前言
之前我寫了一篇《為什么說組合式API解決了mixins的痛點(diǎn)玫坛?》结笨,介紹了一下如何用函數(shù)封裝代碼,這種封裝沒什么深?yuàn)W的道理湿镀,它既不屬于構(gòu)造函數(shù)禀梳,也不屬于面向?qū)ο螅敹嗑褪莻€(gè)閉包肠骆。它類似于React里的自定義Hook算途。
為什么Vue 2沒人提到這種簡(jiǎn)單的設(shè)計(jì)模式?
原因也很簡(jiǎn)單蚀腿,Vue 2的數(shù)據(jù)響應(yīng)式基于Vue實(shí)例嘴瓤,任何數(shù)據(jù)都是實(shí)例上的數(shù)據(jù),所以只能引入另一個(gè).vue文件莉钙,做一次選項(xiàng)合并廓脆,而不能把數(shù)據(jù)和方法獨(dú)立成.js文件。現(xiàn)在Vue 3的數(shù)據(jù)響應(yīng)式基于Proxy磁玉,不依賴Vue實(shí)例停忿,當(dāng)然就可以寫?yīng)毩?js文件然后引入了。
那么蚊伞,函數(shù)封裝的模式席赂,是不是可以用在組合式API里呢吮铭?當(dāng)然能,而且也是推薦做法颅停。
題目
比如現(xiàn)在一個(gè)組件谓晌,引入了3個(gè)Dialog子組件,我要為它們提供title癞揉、opened狀態(tài)纸肉、submitDisabled(用來控制按鈕是否可點(diǎn)擊)……等等,還要提供幾個(gè)方法喊熟。這三個(gè)Dialog組件的樣子和代碼各異柏肪,不適合合并。
代碼怎么寫芥牌?
按照基礎(chǔ)教程烦味,你要寫let aDialogTitle = ...
、let bDialogTitle = ...
胳泉、let cDialogTitle = ...
拐叉,是不是想死?
于是你打算試試設(shè)計(jì)模式扇商,寫成let aDialog = {title: ..., opened: ...}
凤瘦、let bDialog = {title: ..., opened: ...}
、let cDialog = {title: ..., opened: ...}
案铺,這特么跟選項(xiàng)式API也沒什么區(qū)別蔬芥,干嘛還要用組合式API呢?
于是你甚至打算用構(gòu)造函數(shù)……打住控汉,沒那么復(fù)雜笔诵,用閉包就可以了。
方案
基礎(chǔ)代碼是這樣:
let aDialogTitle = ...;
let aDialogOpened = ...;
function openADialog() {
//...
}
閉包寫法是這樣:
function useADialog() {
let title = ...;
let opened = ...;
function openDialog() {
//...
}
return reactive(ADialog: {title, opened, openDialog});
}
在<template>里的用法舉例:
<template>
<ADialog v-if="ADialog.opened" />
</template>
在ref語法糖中的寫法差別
常規(guī)寫法舉例
<template>
<div class="hello" @click="x.c = x.c + 1">
{{ x.c }}
</div>
</template>
<script>
import { ref } from "vue";
export default {
setup() {
function x() {
let c = ref(3);
return {
c,
};
}
return reactive({x: x()});
},
};
</script>
ref語法糖寫法舉例
<template>
<div class="hello" @click="x.c = x.c + 1">
{{ x.c }}
</div>
</template>
<script setup>
import { reactive, ref } from "vue";
const x = (function () {
let c = ref(3);
return reactive({
c,
});
})();
</script>
差別
由于語法糖寫法沒有return姑子,所以必須聲明變量(x
)乎婿,而且必須使用自執(zhí)行函數(shù),才能將數(shù)據(jù)返給模板街佑。常規(guī)寫法沒什么可說的谢翎。
返給模板的都得是Proxy對(duì)象。
如果模板不用x.c
格式沐旨,也就是不用模塊前綴森逮,只寫c
,怎么做磁携?
常規(guī)寫法
太簡(jiǎn)單了褒侧,把return reactive({x: x()});
改成return x();
就行了,清爽!
語法糖寫法
太復(fù)雜了闷供,呵呵烟央。你必須給返回的對(duì)象解構(gòu),也就是const {c} =
这吻,但是解構(gòu)一定會(huì)丟失響應(yīng)吊档,所以還要用上toRefs包住自執(zhí)行函數(shù)篙议。
總之
常規(guī)寫法 | 語法糖寫法 | |
---|---|---|
模板加模塊前綴 | return reactive({x: x()})即可 | return reactive({c})即可 |
模板不加模塊前綴 | 無任何心智負(fù)擔(dān)唾糯,無需reactive | 需用:解構(gòu)熬苍、toRefs宙项、reactive |
所以惑朦,采用Hook模式的話承边,是不是采用語法糖寫法会涎,你自己慎重考慮吧枫甲。
跨Hook調(diào)用數(shù)據(jù)和方法怎么做颠毙?
這時(shí)候魔招,就不可在末行return執(zhí)行Hook了姻乓,必須提前賦值給變量嵌溢。以常規(guī)寫法為例,比如:
<template>
<div class="hello" @click="y.changeC">{{ x.c }}</div>
</template>
<script>
import { reactive, ref, shallowRef } from "vue";
export default {
setup() {
function useX() {
let c = shallowRef(3);
return {
c,
};
}
function useY() {
function changeC() {
x.c.value = 33;
}
return {
changeC,
};
}
const x = useX();
const y = useY();
return reactive({ x, y });
},
};
</script>
總結(jié)
這個(gè)模式真的沒什么深?yuàn)W蹋岩,setup(){}
本身就是這種設(shè)計(jì)模式赖草!