热门推荐
立即入驻

Vue3 TypeScript实战:电商组件库构建指南

# Vue3组合式API与TypeScript实战:构建可复用的电商组件库

## 引言

Vue3的组合式API(Composition API)与TypeScript的结合为前端开发者提供了强大的工具来构建可维护、可复用的组件库。电商网站通常包含大量重复使用的UI元素,如商品卡片、价格展示、评分组件等,通过系统性地构建这些组件库可以显著提高开发效率和代码质量。本文将深入探讨如何利用Vue3的组合式API和TypeScript来构建一个可复用的电商组件库,涵盖从基础设置到高级实践的全过程。

## 项目初始化与环境配置

### 创建Vue3项目

首先需要创建一个支持TypeScript的Vue3项目。使用Vue CLI或Vite都可以快速搭建项目基础结构。

“`bash
# 使用Vue CLI
vue create ecommerce-components –preset vue-ts

# 或使用Vite
npm create vite@latest ecommerce-components — –template vue-ts
“`

### 配置TypeScript

确保项目中的TypeScript配置文件(tsconfig.json)正确设置了编译选项,特别是严格模式(strict)和类型检查相关选项。

“`json
{
\”compilerOptions\”: {
\”target\”: \”ES2020\”,
\”useDefineForClassFields\”: true,
\”module\”: \”ESNext\”,
\”lib\”: [\”ES2020\”, \”DOM\”, \”DOM.Iterable\”],
\”skipLibCheck\”: true,
\”moduleResolution\”: \”bundler\”,
\”allowImportingTsExtensions\”: true,
\”resolveJsonModule\”: true,
\”isolatedModules\”: true,
\”noEmit\”: true,
\”jsx\”: \”preserve\”,
\”strict\”: true,
\”noUnusedLocals\”: true,
\”noUnusedParameters\”: true,
\”noFallthroughCasesInSwitch\”: true
},
\”include\”: [\”src/**/*.ts\”, \”src/**/*.d.ts\”, \”src/**/*.tsx\”, \”src/**/*.vue\”],
\”references\”: [{ \”path\”: \”./tsconfig.node.json\” }]
}
“`

### 安装必要依赖

除了Vue3和TypeScript的核心依赖外,还需要安装一些实用的开发工具。

“`bash
npm install vue-tsc @vitejs/plugin-vue -D
“`

## 构建基础电商组件

### 商品卡片组件

商品卡片是电商网站最常见的组件之一。我们将其设计为可复用的组件,支持多种配置选项。

#### 组件结构

“`vue

{{ product.name }}

{{ formattedPrice }}
{{ starRating }}
({{ product.rating }})

“`

#### 组合式API实现

“`typescript

import { defineComponent, computed, PropType } from \’vue\’

interface Product {
id: number
name: string
price: number
originalPrice?: number
image: string
rating: number
}

export default defineComponent({
name: \’ProductCard\’,
props: {
product: {
type: Object as PropType,
required: true
},
size: {
type: String as PropType,
default: \’medium\’
},
showOriginalPrice: {
type: Boolean,
default: true
}
},
emits: [\’add-to-cart\’],
setup(props, { emit }) {
const sizeClass = computed(() => `product-card–${props.size}`)

const formattedPrice = computed(() => {
const hasDiscount = props.product.originalPrice && props.product.originalPrice > props.product.price
if (hasDiscount && props.showOriginalPrice) {
return `
¥${props.product.originalPrice.toFixed(2)}
¥${props.product.price.toFixed(2)}
`
}
return `¥${props.product.price.toFixed(2)}`
})

const starRating = computed(() => {
const fullStars = Math.floor(props.product.rating)
const hasHalfStar = props.product.rating % 1 >= 0.5
let stars = \’\’

for (let i = 0; i {
emit(\’add-to-cart\’, props.product)
}

return {
sizeClass,
formattedPrice,
starRating,
addToCart
}
}
})

“`

#### 样式定义

“`css

.product-card {
border: 1px solid #eee;
border-radius: 8px;
overflow: hidden;
transition: transform 0.3s ease;
}

.product-card:hover {
transform: translateY(-5px);
box-shadow: 0 10px 20px rgba(0, 0, 0, 0.1);
}

.product-card–small {
width: 200px;
}

.product-card–medium {
width: 280px;
}

.product-card–large {
width: 360px;
}

.product-image {
height: 200px;
overflow: hidden;
}

.product-image img {
width: 100%;
height: 100%;
object-fit: cover;
}

.product-info {
padding: 15px;
}

.product-name {
margin: 0 0 10px;
font-size: 16px;
color: #333;
}

.product-price {
margin-bottom: 10px;
}

.price-original {
text-decoration: line-through;
color: #999;
margin-right: 8px;
}

.price-current {
color: #e4393c;
font-weight: bold;
}

.product-rating {
margin-bottom: 15px;
color: #666;
}

.stars {
color: #ff9900;
margin-right: 5px;
}

.add-to-cart {
width: 100%;
padding: 8px;
background-color: #e4393c;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background-color 0.3s;
}

.add-to-cart:hover {
background-color: #c1272d;
}

“`

### 价格展示组件

电商网站中价格展示需要考虑多种情况,如折扣价、会员价、区间价等。

#### 组件实现

“`vue

{{ formatPrice(originalPrice) }}


{{ formattedPrice }}

{{ unit }}

-{{ discountPercent }}%

import { defineComponent, computed, PropType } from \’vue\’

type Size = \’small\’ | \’medium\’ | \’large\’

export default defineComponent({
name: \’PriceDisplay\’,
props: {
price: {
type: Number,
required: true
},
originalPrice: {
type: Number,
default: undefined
},
size: {
type: String as PropType,
default: \’medium\’
},
showOriginalPrice: {
type: Boolean,
default: true
},
showUnit: {
type: Boolean,
default: true
},
unit: {
type: String,
default: \’¥\’
},
showDiscount: {
type: Boolean,
default: true
}
},
setup(props) {
const sizeClass = computed(() => `price-display–${props.size}`)

const isSale = computed(() => props.originalPrice !== undefined && props.originalPrice > props.price)

const discountPercent = computed(() => {
if (!props.originalPrice || !props.showDiscount) return 0
return Math.round((1 – props.price / props.originalPrice) * 100)
})

const formattedPrice = computed(() => {
return `${props.unit}${props.price.toFixed(2)}`
})

const formatPrice = (value: number) => {
return `${props.unit}${value.toFixed(2)}`
}

return {
sizeClass,
isSale,
discountPercent,
formattedPrice,
formatPrice
}
}
})

“`

### 评分组件

商品评分组件需要支持半星显示和自定义样式。

“`vue


{{ star <= fullStars ? \'★\' : \'☆\' }}

{{ value.toFixed(1) }}

import { defineComponent, computed, PropType } from \’vue\’

type Size = \’small\’ | \’medium\’ | \’large\’

export default defineComponent({
name: \’RatingDisplay\’,
props: {
value: {
type: Number,
required: true,
validator: (val: number) => val >= 0 && val val > 0
},
showValue: {
type: Boolean,
default: true
},
size: {
type: String as PropType,
default: \’medium\’
}
},
setup(props) {
const sizeClass = computed(() => `rating–${props.size}`)

const stars = computed(() => Array.from({ length: props.max }, (_, i) => i + 1))

const fullStars = computed(() => {
const hasHalfStar = props.value % 1 >= 0.5
return Math.floor(props.value) + (hasHalfStar ? 0.5 : 0)
})

const getStarClass = (star: number) => {
if (star <= fullStars.value) return \'full\'
if (star – fullStars.value === 0.5) return \'half\'
return \'empty\'
}

return {
sizeClass,
stars,
fullStars,
getStarClass
}
}
})

“`

## 构建高级组件

### 商品筛选组件

电商网站通常需要复杂的筛选功能,包括分类、品牌、价格区间等。

“`vue

分类

品牌

价格区间


import { defineComponent, ref, reactive } from \’vue\’

interface Category {
id: number
name: string
}

interface Brand {
id: number
name: string
}

export default defineComponent({
name: \’ProductFilter\’,
props: {
categories: {
type: Array as PropType,
required: true
},
brands: {
type: Array as PropType,
required: true
},
initialFilters: {
type: Object as PropType,
default: () => ({
categories: [],
brands: [],
priceRange: { min: 0, max: 10000 }
})
}
},
emits: [\’filter-change\’],
setup(props, { emit }) {
const selectedCategories = ref([…props.initialFilters.categories])
const selectedBrands = ref([…props.initialFilters.brands])
const priceRange = reactive({
min: props.initialFilters.priceRange.min,
max: props.initialFilters.priceRange.max
})

const applyFilters = () => {
emit(\’filter-change\’, {
categories: […selectedCategories.value],
brands: […selectedBrands.value],
priceRange: { …priceRange }
})
}

const resetFilters = () => {
selectedCategories.value = []
selectedBrands.value = []
priceRange.min = 0
priceRange.max = 10000
applyFilters()
}

return {
selectedCategories,
selectedBrands,
priceRange,
applyFilters,
resetFilters
}
}
})

“`

### 购物车组件

购物车组件需要管理商品数量、计算总价等功能。

“`vue

购物车

购物车为空

{{ item.name }}

<button
@click=\"decreaseQuantity(item)\"
:disabled=\"item.quantity


{{ item.quantity }}
{{ (item.price * item.quantity).toFixed(2) }}

0\” class=\”cart-summary\”>

商品总数:
{{ totalItems }}
商品总价:
{{ totalPrice.toFixed(2) }}

import { defineComponent, computed } from \’vue\’
import PriceDisplay from \’./PriceDisplay.vue\’

interface CartItem {
id: number
name: string
price: number
originalPrice?: number
image: string
quantity: number
}

export default defineComponent({
name: \’ShoppingCart\’,
components: {
PriceDisplay
},
props: {
items: {
type: Array as PropType,
required: true
}
},
emits: [\’update:quantity\’, \’remove-item\’],
setup(props, { emit }) {
const totalItems = computed(() => {
return props.items.reduce((sum, item) => sum + item.quantity, 0)
})

const totalPrice = computed(() => {
return props.items.reduce((sum, item) => {
return sum + (item.price * item.quantity)
}, 0)
})

const increaseQuantity = (item: CartItem) => {
emit(\’update:quantity\’, item.id, item.quantity + 1)
}

const decreaseQuantity = (item: CartItem) => {
if (item.quantity > 1) {
emit(\’update:quantity\’, item.id, item.quantity – 1)
}
}

const removeItem = (item: CartItem) => {
emit(\’remove-item\’, item.id)
}

return {
totalItems,
totalPrice,
increaseQuantity,
decreaseQuantity,
removeItem
}
}
})

“`

## 组件库的组织与导出

### 创建统一导出文件

© 版权声明

相关文章

暂无评论

您必须登录才能参与评论!
立即登录
none
暂无评论...