Aha~終於來到首頁了,這邊規劃很簡單,一個輪播的廣告版位、及每個產品分類的前四個商品hightlights,用lazy loading來處理這邊,往下滑才去要資料。
這邊沒有什麼大數據,單純把前4筆拉出來而已...
這邊規劃是:
isLoading
的flag,讓user辨別。loadash.throttle
來把關。安裝lodash.throttle
npm i --save lodash.throttle
npm i --save-dev @types/lodash.throttle
// ./pages/index.vue
<script lang="ts" setup>
import type { ProductListModel, ProductDetailModel, CategoryNameListModel } from '~/models/apiModel';
import type { landingPageProdGroupModel } from '~/models/viewModel';
import throttle from 'lodash.throttle';
const cateStore = useCategory();
const {
cateNameList,
} = storeToRefs(cateStore);
const {
getCateNameList
} = cateStore;
// carousel
const currentIndex: Ref<number> = ref(0);
const images = ref([
'https://dummyjson.com/image/800x400/008080/ffffff?text=Hello+World+NO+1',
'https://dummyjson.com/image/800x400/ff9b9b/ffffff?text=Hello+Athem+NO+2',
'https://dummyjson.com/image/800x400/ffe1b2/ffffff?text=Hello+World+NO+3',
'https://dummyjson.com/image/800x400/95e5da/ffffff?text=Hello+Athem+NO+4',
'https://dummyjson.com/image/800x400/bfc2ff/ffffff?text=Hello+World+NO+5',
]);
// product's highlight
const cateList: Ref<CategoryNameListModel> = ref([]);
const productGroupList: Ref<Array<landingPageProdGroupModel>> = ref([]);
const isLoading: Ref<boolean> = ref(false);
const idx: Ref<number> = ref(0);
const isEnd: Ref<boolean> = ref(false);
const container: Ref<HTMLElement | null> = ref(null);
// ======= carousel =======
let intervalId = null;
const carouselStyle = computed(() => {
return {
transform: `translateX(-${currentIndex.value * 100}%)`,
transition: 'transform 1s ease'
};
});
const nextSlide = () => {
currentIndex.value = (currentIndex.value + 1) % images.value.length;
};
const startCarousel = () => {
intervalId = setInterval(nextSlide, 3000);
};
const stopCarousel = () => {
if (intervalId) {
clearInterval(intervalId);
}
};
// ====== product ======
const loadMoreData = async () => {
if (isLoading.value || isEnd.value) return;
isLoading.value = true;
const cateVal = cateList.value;
try {
const data: ProductListModel = await $fetch(`https://dummyjson.com/products/category/${cateVal[idx.value]}?limit=4`, { responseType: 'json' });
const products = data.products;
if (idx.value >= cateVal.length) {
isEnd.value = true;
} else {
productGroupList.value = [...productGroupList.value, {categoryName: products[0].category, productList: products}]
idx.value += 1;
}
} catch (err) {
console.error('Failed to load data:', err);
} finally {
isLoading.value = false;
}
};
let throt_fun = throttle(async () => {
await loadMoreData();
}, 1000);
const handleScrollAction = async () => {
const scrollTop = window.scrollY || window.pageYOffset;
const scrollHeight = document.documentElement.scrollHeight;
const clientHeight = window.innerHeight;
// determinate is user scrolls to the buttom
if (scrollTop + clientHeight >= scrollHeight - 10) {
throt_fun();
}
};
onMounted(async() => {
startCarousel();
await getCateNameList().then(() => {
cateList.value = cateNameList.value;
}).then(() => {
loadMoreData();
}).catch( err => {console.error(err)})
nextTick(() => {
window.addEventListener('scroll', handleScrollAction);
});
});
onUnmounted(() => {
stopCarousel();
if (container.value) {
window.removeEventListener('scroll', handleScrollAction);
}
});
</script>
<template>
<div class="container" ref="container">
<div>
<div class="carousel">
<div class="carousel-wrapper">
<div class="carousel-images" :style="carouselStyle">
<img v-for="(image, index) in images" :key="index" :src="image" class="carousel-image" />
</div>
</div>
</div>
</div>
<div v-for="list in productGroupList">
<div>
<h2>{{ list.categoryName }}</h2>
<div class="flex">
<div v-for="prod in list.productList" :key="prod.id">
<p>{{ prod.title }}</p>
<img :src="prod.thumbnail" alt="">
<p>{{ prod.price }} cad</p>
<p>{{ prod.stock }} left</p>
<p>rating: {{ prod.rating }}</p>
<button>add to cart</button>
</div>
</div>
</div>
</div>
<div v-if="isLoading">
<h1>Loading...</h1>
</div>
</div>
</template>
<style scoped>
.flex {
display: flex;
}
.container {
height: 100%;
}
.carousel {
overflow: hidden;
width: 800px;
height: 400px;
position: relative;
}
.carousel-wrapper {
display: flex;
}
.carousel-images {
display: flex;
width: 100%;
height: 100%;
}
.carousel-image {
width: 100%;
height: 100%;
object-fit: cover;
}
</style>
問你喲~(講得好像有人會回我)像Carousel之類的元件,你通常會直接使用外掛?還是自己手刻呢?我沒有一個答案,好像如果功能很簡單,手刻沒什麼不好,但如果很龐大那種操作,就用外掛吧。今天的滾動拉資料,因為過去都做後台還真沒機會做到這樣的動作,這就是做side project的意義吧,學習工作外想envolve的部分。