Vue - 元件規劃心得

簡介

閱讀patterns.dev內Vue元件相關的設計模式文章,做個筆記記錄一下

Container / Presentational Pattern

  • 其中一種分類原則,就是可以分為ContainerPresentational兩個種類
  • 將UI設計和邏輯分離,加強維護性,例如當我想改變按鈕樣式時,不會去影響應用邏輯

1. Presentational Components

  • 重點在於外觀展示
  • 不涉及資料修改
  • 透過props接收資料

2. Container Components

  • 處理功能邏輯,例如:串接API取得資料
  • 或是從Pinia / Vuex這類的全局狀態管理取得資料
  • 使用props傳入資料給Presentational Components

範例:顯示Pokemon卡片

  1. 創建展示元件:PokeCard.vue
<script setup>
const { pokemon } = defineProps({
  pokemon: {
    type: Array,
    default: () => []
  }
})
</script>

<template>
  <ul>
    <li v-for="poke in pokemon" :key="poke.id">
      <p>{{ poke.name }}</p>
      <img :src="poke.sprites.front_default" :alt="poke.name" />
    </li>
  </ul>
</template>
  1. 創建Container元件:PokeContainer.vue
<script setup>
  import { ref, onMounted } from "vue";
  const pokemon = ref([])

  onMounted(async () => {
    const response = await fetch('https://pokeapi.co/api/v2/pokemon/ditto')
    const data = await response.json()
    pokemon.value.push(data)
})
</script>
<template>
  <PokeCard :pokemon="pokemon" />
</template>
  • Demo

    Container 元件

    取得API資料放入UI元件

    UI-展示元件

    我是誰?

有什麼優點

  1. 職責清晰分離: 通過將UI和業務邏輯分開,提高程式碼的組織性和可維護性
  2. 提高重用性和測試便利: 展示元件易於重用和測試,由於它們不包含複雜的業務邏輯
  3. 簡化狀態管理: 容器元件集中處理狀態和邏輯,減少跨元件的狀態傳遞和混亂

Vue3 Composition API的影響

  • 開始模糊容器與呈現元件之間的界限
  • 提供更靈活的方式來組織元件和邏輯

Composables

  1. 允許封裝和重用狀態和邏輯
  2. 同樣可以達到UI畫面和商業邏輯分開處理的目的
  3. 就不需要另外寫Container元件

使用Composable改寫:

  1. 創建/composables/usePokemonInfos.js
import { ref, onMounted } from 'vue'
export default function usePokemonInfos() {
  const pokemon = ref([])

  onMounted(async () => {
    try {
      const response = await fetch('https://pokeapi.co/api/v2/pokemon/ditto')
      const data = await response.json()
      pokemon.value.push(data)
  } catch (error) {
      console.error(error)
  }
  })

  return { pokemon }
}
  1. PokeCard.vue從composables取得資料
<script setup>
  import usePokemonInfos from "../composables/usePokemonInfos";

  const { pokemon } = usePokemonInfos();
</script>

<template>
  <ul>
    <li v-for="poke in pokemon" :key="poke.id">
      <p>{{ poke.name }}</p>
      <img :src="poke.sprites.front_default" :alt="poke.name" />
    </li>
  </ul>
</template>

實際應用心得

先前在火箭隊的畢業專題中,使用Nuxt3開發,便有嘗試過Container元件的設計方式,雖然需要更多的時間去規劃,但結構確實較為清晰單純。

後來發現,若該頁面的內容不會太複雜,直接在Nuxt的pages / Vue的views頁面檔案中,使用composables取得API資料後,渲染UI畫面,會更為簡便快速,例如文章頁面內只會有文章相關的API資料,就可以直接在頁面發送API請求,再用props傳入UI元件。

如果較為複雜,例如首頁有許多不同種類的內容,再利用Container元件來分區塊,根據需求狀況不同,來選擇最適合的開發模式。

參考資料

# Vue