有次結帳時,忘記用了免運卷。不僅多花了錢,還因為這件小事和程世社季子吵了一架。雖然事後想起來很可笑,但那次的爭吵讓我明白了許多。我們之間的溝通有時就像 useAsyncState,總有等待與意外,但結果往往出乎意料。
在這篇中,我們將使用 Vue 3、TypeScript 和 Tailwind CSS 建立一個動態的商品列表。使用 Fake Store API 作為假資料的來源,並實現一些基本的查詢功能。
首先,確保專案已經設定好 Vue 3、TypeScript 和 Tailwind CSS。如果還沒有,你可以使用 Vue CLI 或 Vite 來快速搭建一個新專案。
這次筆者用 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 請求:
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>
useAsyncState
來管理從 API 獲取資料的非同步操作。這讓我們可以輕鬆處理載入中、錯誤和成功狀態。productQuery
是一個響應式引用,包含了 limit
和 sort
參數。當這些值改變時,我們重新執行查詢。watch
來監聽 productQuery
的變化,當它改變時重新執行查詢。isLoading
, error
, 資料已載入) 來渲染不同的 UI。這個範例結合 Vue 3、TypeScript 和 Tailwind CSS 來建立一個商品列表。通過使用 useAsyncState
和 Vue 的響應式系統,我們實現了一個動態更新的列表,可以根據用戶的輸入即時調整查詢參數。
這個基礎可以進一步擴展,例如添加更多的過濾選項、實現分頁功能,或者添加商品詳情頁面。
明天我們繼續介紹其他 VueUse 的套件來完善 PM 的需求
OKOK 今天就先到這邊~如果有任何錯誤再麻煩留言讓我知道喔~ 又或是想看看什麼主題也可以許願看看