AdSense

網頁

2026/1/7

Vue Nuxt 3 抽取共用邏輯為組合式函式(Composables)

本篇把Vue Nuxt 3頁面中的下拉選單的邏輯抽出為Vue 3的組合式函式(Composables)


Composables是用來封裝「有狀態的邏輯的函式」以便於跨頁及跨組件重用。Composables函式包含在Vue生命週期內,所以裡面可以使用Vue 3的Composition API,例如refreactivecomputedwatch等;而一般通用/工具函式(Utils)則是純粹的JavaScript函式,這是兩者的主要區別。

在Nuxt 3中,慣例把Composables放在composables目錄,並以"use"開頭命名檔案,例如useCountries.ts,這樣在Nuxt在頁面便會自動import並可直接使用,且通常一個composables檔案只會有一個同名的composables函式。

取得國家代碼是經常會被共用的邏輯,所以可抽出為useCountries組合式函式,並在pages/home/export/index.vue頁面中使用。


事前要求

參考「Vue Nuxt 3 code mapping」。


建立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>

<!--...-->

github


完成以上後,在頁面點選到[出口業務]頁面,可以看到[出口國家]下拉選單的內容正常顯示。

接著把國家代碼下拉選單抽出成為共用元件


沒有留言:

AdSense