Vue 3: Composable
甚麼是composable?
在寫純JavaScript的時候,有時會想將一些重複的邏輯抽離出來變成另一個可被其他程式碼使用的函式,在Vue3裡面,composables也是在做一樣的事情;只是跟傳統函式不一樣的是,composables內可以包含Vue的狀態,譬如響應式狀態變數ref、reactive等,或者lifecycle hooks像是onMounted、onUnmounted等。
基本用法
根據官方文件,Composable有幾個使用的注意事項:
習慣上是以
use開頭的小駝峰式(camelCase)命名;基本上只能在
<script setup>或setup()頂層呼叫(但也有特別提到某些情況下可以在lifecycle hooks裡面呼叫);習慣上,composable永遠回傳純物件(plain object),而不是響應式物件,而這個純物件裡面可包含多個響應式狀態refs;另外,若想以物件取值的方式取得這些refs的狀態,可將composable回傳的純物件放入一個
reactive:const mouse = reactive(useMouse()) // mouse.x is linked to original ref console.log(mouse.x)composable裡面可以呼叫其他的composables;
composable裡面可以有副作用(side effect),譬如一些DOM事件監聽或是異步拉取遠端資料(fetching data),但使用時需要注意的是:
- 在Server-Side Rendering (SSR)應用,需要確保DOM相關的副作用是在post-mount lifecycle hooks 如
onMounted()裡面被呼叫(因為這些hooks只會在瀏覽器內運作)。另外千萬要記得在onUnmounted()裡面清除這些DOM相關的副作用。
- 在Server-Side Rendering (SSR)應用,需要確保DOM相關的副作用是在post-mount lifecycle hooks 如
Watch composables’ arguments
如果要”監聽”傳入composable的參數,要在composable裡面使用 watchEffect 去監聽引數,並且在Vue 3.3以後版本要讓composable只接收 ref 或getter函式而不是一般的純值作為參數,以及搭配 toValue API去取得值:
// 官方範例 (applicable to Vue 3.3 or later)
import { ref, watchEffect, toValue } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const fetchData = () => {
// reset state before fetching..
data.value = null
error.value = null
fetch(toValue(url))
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
}
watchEffect(() => {
fetchData()
})
return { data, error }
}
如果是Vue 3.2以前的版本則是要接收refs並改用 unRef 去取得值並搭配 isRef 判斷是否要監聽函式:
// 改寫自官方範例 (applicable to Vue 3.2)
import { ref, isRef, unref, watchEffect } from 'vue'
export function useFetch(url) {
const data = ref(null)
const error = ref(null)
const fetchData = () => {
// reset state before fetching..
data.value = null
error.value = null
// unref() unwraps potential refs
fetch(unref(url))
.then((res) => res.json())
.then((json) => (data.value = json))
.catch((err) => (error.value = err))
}
if (isRef(url)) {
// setup reactive re-fetch if input URL is a ref
watchEffect(fetchData)
} else {
// otherwise, just fetch once
// and avoid the overhead of a watcher
fetchData()
}
return { data, error }
}
不過官方有提到如果這個composable要給其他開發者使用,建議假設composable的參數都是以 ref 或getter函式來處理。
使用Composables的優點
composables不一定要在需要抽離重複邏輯的時候才使用,有時候一個Vue程式碼太過複雜時,也能透過composable抽離裡面的邏輯,幫忙整理程式碼,有助於程式碼的可讀性。
Composables can be extracted not only for reuse, but also for code organization.
相較使用renderless components會有額外較重的元件實體(instance)呼叫成本,composables就是單純用函式去包住需要被隔離出來的程式碼邏輯。所以說,當有需要同時抽離程式碼和視覺上的邏輯可以使用renderless components,若只抽離單純的程式碼邏輯就優先建議使用composables。
References