接下來我們就以前面創建的mini-vue3為基礎來實現Vue3新增的API,首先要講的就是Composition API。
什么是Compostion API呢?顧名思義,組合式API。相對而言我們在Vue2中使用的叫Options API,也即配置型API,我們的代碼通過Vue給定的options,將代碼寫入到各個options中。比如mountd是虛擬dom裝載完成之后的鈎子,因此在SSR的服務端這個鈎子不會執行。然后我們的data定義必須放到data中,計算屬性則需要放在computed,以及methods等不同的options。
使用Option API的好處,我個人而言,覺得這個最大的好處就是降低了門檻,也就是說:因為你必須按照給定的option來組織你的代碼,即使你的代碼寫的真的挺亂,但是因為有這個Options在,所以別人閱讀你的代碼還是有章可循的,知道從哪里開始,對應的方法應該去哪里找,也就是說提升了你的下限。既然提到了好處,自然要說下缺點。缺點就是因為功能點的分散。
比如我要想在created里面去拉取數據,然后進行渲染,我們可能得在method里定義兩個方法,一個是拉取數據,一個是渲染數據。
然后我們要在created里面調用method里的方法A,在方法A里面我們去調用拉取數據的方法,然后再調用渲染數據的方法,因此會導致我們原本應該是寫在一起的一個業務邏輯的代碼要被分散到各處。當然這個主要是針對一些對性能或是代碼架構有高要求的場景。一般的情況下確實並不需要這個。不過既然有場景,那也還是要能滿足,因此Vue3推出了Composition API,通過setup方法來實現,我們可以看到官方文檔對這個的定義:一組低侵入式的、函數式的 API,使得我們能夠更靈活地「組合」組件的邏輯。而且這個對於Vue3來說是可選的,也就是說如果你不想用組合式API,那么你仍然可以繼續使用Vue2的OptionAPI。
具體的相關API的文檔可以參考Composition API
對於一些統一的這種業務邏輯代碼,原來可能每個組件內都需要去寫,而組件的重用並不是那么容易實現。不過通過組合式API,則可以將很多公共邏輯抽象為公共方法,然后就可以很好的復用了.
但是組合式API也有缺點,他的缺點就是因為太自由了,所以對於水品沒那么高的同學來說,很可能寫的代碼就能了一團亂麻,也就是所謂的面條代碼。因此組合式API雖然可以提高上限,但是下限也低了。ok,說完了概念,就應該來具體實現了:
<html> <head> <title>Composition API</title> <script src="https://unpkg.com/vue@next"></script> </head> <body> <div id="app"></div> <script> const { createApp, ref, onMounted, onUnmounted } = Vue; // options寫法 // const App = { // template: `X: {{ x }}, Y:{{ y }}`, // data() { // return { // x: 0, // y: 0, // } // }, // mounted() { // window.addEventListener('mousemove', this.moveFunc); // }, // unmounted() { // window.removeEventListener('mousemove', this.moveFunc) // }, // methods: { // moveFunc(e) { // this.x = e.pageX; // this.y = e.pageY; // }, // } // }; // 組合式API寫法 function useMouse() { const x = ref(0); const y = ref(0); const moveFunc = (e) => { x.value = e.pageX; y.value = e.pageY; }; // 掛載時綁定方法 onMounted(() => { window.addEventListener('mousemove', moveFunc); }); // 卸載時移除綁定 onUnmounted(() => { window.removeEventListener('mousemove', moveFunc); }); return { x, y }; } const App = { setup() { const { x, y } = useMouse(); // 關於這里為什么要解構而不是直接返回函數結果,尤大表示,這里可能會有其他的變量需要返回,以及可能會有沖突的情況需要 // 來進行處理 return { x, y, }; }, template: `X: {{ x }}, Y:{{ y }}`, }; createApp(App).mount('#app'); </script> </body> </html>
這段代碼同時使用了options API和組合式API,可以看到使用組合式API的情況下,代碼的復用變得簡單,而且邏輯清晰。
接下來我們可以看一個上文提到的例子,也就是說,一個立即獲取數據並展示,然后當屬性變化時,再次重新獲取數據並展示的例子:
<html> <head> <title>Vue3 Fetch Demo</title> </head> <body> <div id="app"></div> <script src="https://unpkg.com/vue@next"></script> <script> const { createApp, ref, watchEffect } = Vue; // fetch相關邏輯 // 通過使用watchEffect方法,第一次就會立即獲取數據 // 后續id發生改變的情況下則會重新獲取數據 function useFetch(fetchUrl) { // 尤大這里不建議使用reactive,類似 const state = reactive({}); // 的方式是因為reactive的值不能被解構,否則會失去響應性。如果一定要用reactive // 則需要在返回時通過toRefs封裝 const data = ref(null); const error = ref(null); const isLoading = ref(true); watchEffect(() => { // 開始獲取數據時需要重置原數據 data.value = null; error.value = null; isLoading.value = true; fetch(fetchUrl()).then(res => res.json()).then(result => { // 加載完成,賦值並將loading置為false data.value = result; isLoading.value = false; }).catch(err => { error.value = err; isLoading.value = false; }); }); return { data, error, isLoading, }; } const Post = { template: ` <div v-if="isLoading">Loading...</div> <div v-else-if="data">{{ data }}</div> <div v-else-if="error"> Fetch Data Error: {{ error.message }} </div> `, props: { id: Number, }, setup(props) { console.log(props) const fetchUrl = () => { return `https://jsonplaceholder.typicode.com/todos/${props.id}`; }; const { data, error, isLoading } = useFetch(fetchUrl); return { data, error, isLoading, } }, }; // 通過props的方式傳入id,並且在id改變后會觸發Post組件的重新獲取數據和重新渲染 const App = { components: { Post, }, data() { return { id: 1, }; }, template: ` <button @click="id += 1">Plus ID</button> <Post :id="id" /> `, }; createApp(App).mount('#app'); </script> </body> </html>
在這段示例代碼中我們就不必像options API那樣在多個options處增加不相關聯的邏輯,在組合式API中,相關邏輯可以寫在一處。當然,組合式API也不是銀彈。並不是適合所有場景,尤其是當作者對抽象把握還沒有那么好的時候,options API更能保證大家寫的代碼具有一定的條理性。在Vue3的官網也有許多的關於在什么場景如何應用組合式API的例子,可以去看。我也會繼續閱讀並通過自己的理解來寫出更多的關於Vue3的API的示例和解釋。
下一章節目標通過組合式API來實現一個小的系統。