iT邦幫忙

2024 iThome 鐵人賽

DAY 15
1
JavaScript

不會 VueUse 而被提分手的我系列 第 15

D-15 用 useAsyncState 來 call 個 api 試試看吧 2 - 用 stackblitz 來施展魔法吧

  • 分享至 

  • xImage
  •  

有次結帳時,忘記用了免運卷。不僅多花了錢,還因為這件小事和程世社季子吵了一架。雖然事後想起來很可笑,但那次的爭吵讓我明白了許多。我們之間的溝通有時就像 useAsyncState,總有等待與意外,但結果往往出乎意料。
https://ithelp.ithome.com.tw/upload/images/20240928/20162115qDv8yjfTpL.png

在這篇中,我們將使用 Vue 3、TypeScript 和 Tailwind CSS 建立一個動態的商品列表。使用 Fake Store API 作為假資料的來源,並實現一些基本的查詢功能。

設定專案

首先,確保專案已經設定好 Vue 3、TypeScript 和 Tailwind CSS。如果還沒有,你可以使用 Vue CLI 或 Vite 來快速搭建一個新專案。

成果展覽

https://ithelp.ithome.com.tw/upload/images/20240928/20162115lyv55hZZvB.jpg

這次筆者用 stackblitz 當作 Demo 平台,因為 VueUse Playground 不支援 typescript,範例主要是安裝 Vite + Vue3 + Typescript + Tailwind + VueUse

環境好了就開始吧~

定義類型

我們先定義幾個重要的類型:

interface ProductQuery {
  limit?: number;
  sort?: 'asc' | 'desc';
}

interface Product {
  id: number;
  title: string;
  price: number;
  description: string;
  category: string;
  image: string;
  rating: {
    rate: number;
    count: number;
  };
}

type ProductList = Product[];

實現 API 請求函式

接下來,我們實現一個函式來發送 API 請求:

const fetchProducts = async (query?: ProductQuery): Promise<ProductList> => {
  const queryString = query
    ? `?${new URLSearchParams(query as any).toString()}`
    : '';
  const response = await fetch(
    `https://fakestoreapi.com/products${queryString}`
  );
  return await response.json();
};

組件實現

現在,讓我們來看看主要的 Vue 組件實現:

<template>
  <div class="container mx-auto p-4">
    <h1 class="text-2xl font-bold mb-4">商品列表</h1>

    <!-- 查詢控制項 -->
    <div class="py-3">
      <label for="product_limit" class="block mb-2 text-sm font-medium text-gray-900">查詢上限</label>
      <input v-model="productQuery.limit" type="number" id="product_limit" class="border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5" required />
    </div>
    <div class="pb-3">
      <label for="product_sort" class="block mb-2 text-sm font-medium text-gray-900">選擇排序方式</label>
      <select v-model="productQuery.sort" id="product_sort" class="border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-blue-500 focus:border-blue-500 block w-full p-2.5">
        <option value="asc">升序</option>
        <option value="desc">降序</option>
      </select>
    </div>

    <!-- 載入中狀態 -->
    <div v-if="isLoading" class="flex justify-center items-center h-64">
      載入中...
    </div>

    <!-- 錯誤狀態 -->
    <div v-else-if="error" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative" role="alert">
      <strong class="font-bold">錯誤!</strong>
      <span class="block sm:inline">無法載入商品資料。</span>
    </div>

    <!-- 商品列表 -->
    <div v-else class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
      <div v-for="product in state" :key="product.id" class="bg-white shadow-md rounded-lg overflow-hidden">
        <img :src="product.image" :alt="product.title" class="w-full h-48 object-cover" />
        <div class="p-4">
          <h2 class="font-bold text-xl mb-2 truncate">{{ product.title }}</h2>
          <p class="text-gray-700 text-base mb-2 truncate">{{ product.description }}</p>
          <div class="flex justify-between items-center">
            <span class="text-gray-900 font-bold">${{ product.price.toFixed(2) }}</span>
            <span class="bg-blue-500 text-white px-3 py-1 rounded-full text-sm">{{ product.category }}</span>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<script setup lang="ts">
import { useAsyncState } from '@vueuse/core';
import { ref, watch } from 'vue';

// ... 在這裡放置之前定義的介面和類型 ...

const productQuery = ref<ProductQuery>({ limit: 1, sort: 'desc' });

const { state, isLoading, error, execute } = useAsyncState(
  (query: ProductQuery = productQuery.value) => {
    return fetchProducts(query);
  },
  [] as ProductList
);

watch(
  productQuery,
  () => {
    execute(0);
  },
  {
    deep: true,
  }
);
</script>

功能解釋

  1. 非同步狀態管理: 我們使用 useAsyncState 來管理從 API 獲取資料的非同步操作。這讓我們可以輕鬆處理載入中、錯誤和成功狀態。
  2. 響應式查詢: productQuery 是一個響應式引用,包含了 limitsort 參數。當這些值改變時,我們重新執行查詢。
  3. 狀態監聽: 使用 watch 來監聽 productQuery 的變化,當它改變時重新執行查詢。
  4. 條件渲染: 根據不同的狀態 (isLoading, error, 資料已載入) 來渲染不同的 UI。
  5. 響應式設計: 使用 Tailwind CSS 的網格系統來實現響應式的商品列表佈局。

總結

這個範例結合 Vue 3、TypeScript 和 Tailwind CSS 來建立一個商品列表。通過使用 useAsyncState 和 Vue 的響應式系統,我們實現了一個動態更新的列表,可以根據用戶的輸入即時調整查詢參數。

這個基礎可以進一步擴展,例如添加更多的過濾選項、實現分頁功能,或者添加商品詳情頁面。

明天我們繼續介紹其他 VueUse 的套件來完善 PM 的需求

OKOK 今天就先到這邊~如果有任何錯誤再麻煩留言讓我知道喔~ 又或是想看看什麼主題也可以許願看看


上一篇
D-14 用 useAsyncState 來 call 個電商 api 試試看吧 1
下一篇
D-16 useOffsetPagination 文件說明與範例 - 分分合合的資料流
系列文
不會 VueUse 而被提分手的我30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言