本篇把Vue Nuxt 3頁面中的下拉選單的邏輯抽出為Vue 3的組合式函式(Composables)。
Composables是用來封裝「有狀態的邏輯的函式」以便於跨頁及跨組件重用。Composables函式包含在Vue生命週期內,所以裡面可以使用Vue 3的Composition API,例如ref、reactive、computed、watch等;而一般通用/工具函式(Utils)則是純粹的JavaScript函式,這是兩者的主要區別。
在Nuxt 3中,慣例把Composables放在composables目錄,並以"use"開頭命名檔案,例如useCountries.ts,這樣在Nuxt在頁面便會自動import並可直接使用,且通常一個composables檔案只會有一個同名的composables函式。
取得國家代碼是經常會被共用的邏輯,所以可抽出為useCountries組合式函式,並在pages/home/export/index.vue頁面中使用。
事前要求
建立Composables
在Nuxt專案根目錄下新增composables目錄,並建立useCountries.ts,即/composables/useCountries.ts。
在/composables/useCountries.ts中建立一個useCountries函式,用來封裝取得國家代碼資料的狀態管理邏輯。
其中使用Nuxt的useState建立跨頁的響應式共享狀態物件。useState函式的第一個參數是狀態名稱,是Nuxt用來區別各個狀態的key;第二個參數是提供初始值的函式,而useState後的角括弧<...>則是指定型態。
例如countryMap狀態的型態為Record<string, string> | null(Record or null),名稱為"countryMap",初始值為null。
在Composables中要避免被調用時會直接執行產生副作用(side effect)的邏輯,也就是會改變狀態的邏輯,會造成每次composables被調用時就會改變狀態,導致狀態的不確定。因此這類邏輯應封裝為load函式來管理,只在需要時才被外部調用。例如從後端API獲取國家代碼並將結果分派到各狀態中的邏輯是封裝在loadCountries並回傳,如此每次調用useCountries時才不會又去呼叫API。
composables/useCountries.ts
export function useCountries() {
const countryMap = useState<Record<string, string> | null>(
"countryMap",
() => null
);
const pending = useState("countryMapPending", () => false);
const error = useState<Error | null>("countryMapError", () => null);
const loadCountries = async () => {
if (countryMap.value) return; // 已有資料就不打API,直接回傳
pending.value = true;
error.value = null;
try {
countryMap.value = await $fetch("/api/countries");
} catch (err) {
error.value = err as Error;
} finally {
pending.value = false;
}
};
return {
countryMap,
pending,
error,
loadCountries,
};
}
修改頁面
在[出口業務]頁面pages/home/export/index.vue中刪除原本從後端取得國家代碼的邏輯,改為使用組合式函式useCountries及呼叫loadCountries。
pages/home/export/index.vue
<template>
<div>
<!--...-->
<label>
出口國家:
<select v-model="country" :disabled="countryPending">
<option value="">全部國家</option>
<option v-for="(name, code) in countryMap" :key="code" :value="code">
{{ name }} ({{ code }})
</option>
</select>
</label>
<br>
<!--...-->
<ul v-else>
<li v-for="order in orders" :key="order.id">
<NuxtLink :to="`/home/export/${order.id}`">
訂單 #{{ order.id }},
狀態: {{ order.status }},
出口國家:{{ countryMap[order.country] }}
</NuxtLink>
</li>
</ul>
</div>
</template>
<script setup>
//...
const { countryMap, pending: countryPending, loadCountries } = useCountries()
loadCountries()
//...
</script>
<!--...-->
完成以上後,在頁面點選到[出口業務]頁面,可以看到[出口國家]下拉選單的內容正常顯示。
沒有留言:
張貼留言