網頁

2026/1/8

Vue Nuxt 3 抽取下拉選單為元件(Components)

本篇把Vue Nuxt 3頁面中的下拉選單抽出為元件/組件(Components)


元件/組件是可被其他頁面複用的template,Nuxt約定統一放在components目錄,範例上方的導覽列compnents/Header.vue和左側功能選單components/Sidebar.vue即為元件,也就是layouts/default.vue中的<Header /><Sidebar />

和Composable的區別是,Composable是用來封裝可複用的邏輯,而元件則是可複用的模板(畫面),兩者的責任是分開的,例如可能多個元件使用同一個composable。


事前要求

參考「Vue Nuxt 3 抽取共用邏輯為組合式函式(Composables)」。


建立下拉選單元件

在Nuxt專案的components目錄下新增CountrySelect.vue,內容為國家代碼的下拉選單的模板及取得選單內容的邏輯。

使用defineModel(Vue 3.4開始支援)設定雙向綁定屬性modelValue,並在<select>的使用v-model="modelValue"實現雙向綁定。

所謂的雙向綁定(two-way binding),是指元件內部的值會與父組件的值同步,當元件內的值改變時,父組件也會跟著改變;雙向是指父組件傳入子組件的參數(props)和子組件異動這個屬性值時對父組件發出的事件通知(emits)。

components/CountrySelect.vue

<template>
  <select v-model="modelValue" :disabled="pending">
    <option value="">全部國家</option>
    <option v-for="(name, code) in countryMap" :key="code" :value="code">
      {{ name }} ({{ code }})
    </option>
  </select>
</template>

<script setup lang="ts">
import { useCountries } from '~/composables/useCountries'

const modelValue = defineModel();

const { countryMap, pending, loadCountries } = useCountries()

loadCountries()

</script>

使用下拉選單元件

把原本pages/home/export/index.vue的下拉選單改為<CountrySelect>

要注意仍要保留loadCountries()來取得從後端獲取值的countryMap,你可能會想在<CountrySelect>中不是已經取過一次這個「跨元件的共享狀態」了嗎?問題是如果重新整理http://localhost:3000/home/export這個頁面,對Nuxt來說是初始整個應用程式,此時在渲染此頁面時會先於子組件<CountrySelect>去後端取countryMap的值,所以countryMap仍會是null,然後在父組件使用{{ countryMap[order.country] }}就發生錯誤。

pages/home/export/index.vue

<template>
  <div>
    <h1>出口業務</h1>

    <!--...-->
    
    <label>
      出口國家:
      <CountrySelect v-model="country" />
    </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, loadCountries } = useCountries()
loadCountries()

// ...
</script>

<!--...-->

github


接下來參考「Vue Nuxt 3 分頁」。

沒有留言:

張貼留言