微信小程序电商实战 03:NutUI 配置与电商常用组件
微信小程序电商实战 01 技术选型 | 02 脚手架搭建 | 03 NutUI 配置 ← 本文 | 04 云开发环境 | 05 商品模块 | 06 购物车与订单 | 07 微信支付 | 08 用户中心 | 09 性能优化 | 10 上线部署
# NutUI 电商组件实战
## 安装与配置
### 安装 @nutui/nutui-taro
### babel-plugin-import 按需加载
### 主题定制(CSS变量)
### 全局注册 vs 按需引入
## 电商必用组件(6类)
### 展示类
- Image 懒加载图片
- Price 价格格式化
- Badge 角标
### 商品类
- Sku SKU选择器
- InfiniteLoading 无限滚动
### 购物类
- InputNumber 商品数量
- Checkbox 多选(购物车)
### 反馈类
- Toast 提示
- Dialog 确认框
- Loading 加载
### 表单类
- Form 订单填写
- Address 地址选择
### 导航类
- Tabs 商品分类
- Swipe 轮播图
## 主题定制
### 电商红色主题
### CSS 变量覆盖
一、安装
pnpm add @nutui/nutui-taro @nutui/icons-vue-taro
pnpm add -D @babel/core babel-plugin-import
二、按需加载配置
NutUI 组件库体积较大,必须开启按需加载,否则打包体积超标。
修改 babel.config.js:
// babel.config.js
module.exports = {
presets: [
['taro/babel', { framework: 'vue', ts: true }],
],
plugins: [
[
'import',
{
libraryName: '@nutui/nutui-taro',
libraryDirectory: 'dist/packages',
style: true,
camel2DashComponentName: false,
},
'nutui-taro',
],
],
}
配置后,只有实际引用的组件代码才会打进包里。
三、全局注册常用组件
在 app.ts 中注册高频组件,避免每个页面重复引入:
// src/app.ts
import { createApp } from 'vue'
import { createPinia } from 'pinia'
import {
Button, Toast, Dialog, Loading,
Image as NutImage, Price, Badge,
} from '@nutui/nutui-taro'
import App from './app.vue'
const pinia = createPinia()
const app = createApp(App)
app.use(pinia)
app.use(Button)
app.use(Toast)
app.use(Dialog)
app.use(Loading)
app.use(NutImage)
app.use(Price)
app.use(Badge)
export default app
电商页面特有组件(SKU 选择器、地址选择器等)在页面内按需引入,避免主包体积过大。
四、主题定制
NutUI 使用 CSS 变量实现主题,在 app.scss 中覆盖:
// src/app.scss
// 电商场景主色:红色系
:root {
--nutui-brand-color: #E31D1A;
--nutui-brand-color-start: #E31D1A;
--nutui-brand-color-end: #C01A17;
// 价格色
--nutui-price-symbol-color: #E31D1A;
--nutui-price-integer-color: #E31D1A;
// 按钮
--nutui-button-primary-background-color: #E31D1A;
--nutui-button-primary-border-color: #E31D1A;
// 徽标
--nutui-badge-background-color: #E31D1A;
}
五、电商核心组件用法
1. Price — 价格展示
电商最常用,自动处理整数、小数、货币符号:
<template>
<!-- 输出:¥ 199.00 -->
<nut-price :price="199" :decimal-digits="2" symbol="¥" />
<!-- 划线原价 -->
<nut-price :price="299" size="small" :strikethrough="true" />
</template>
<script setup lang="ts">
import { Price as NutPrice } from '@nutui/nutui-taro'
</script>
2. Image — 懒加载商品图
<template>
<nut-image
:src="product.coverImage"
width="100%"
height="200px"
fit="cover"
lazy-load
radius="8px"
/>
</template>
3. Badge — 购物车角标
<template>
<!-- tabBar 购物车图标上的数量角标 -->
<nut-badge :value="cartStore.totalCount" :max="99">
<nut-icon name="cart" />
</nut-badge>
</template>
<script setup lang="ts">
import { useCartStore } from '@/stores/cart'
const cartStore = useCartStore()
</script>
4. InputNumber — 商品数量调节
<template>
<nut-input-number
v-model="quantity"
:min="1"
:max="product.stock"
:disabled="product.stock === 0"
@change="handleQuantityChange"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { InputNumber as NutInputNumber } from '@nutui/nutui-taro'
const quantity = ref(1)
const handleQuantityChange = (val: number) => {
console.log('数量变化:', val)
}
</script>
5. Sku — SKU 选择器
SKU 选择器是电商最复杂的组件,NutUI 内置了完整的规格选择逻辑:
<template>
<nut-sku
v-model:visible="skuVisible"
:sku="skuData.tree"
:goods="skuData.goods"
:goods-id="productId"
:hide-one-sku="false"
:btn-extra-text="product.stock === 0 ? '已售罄' : ''"
@select-sku="onSelectSku"
@add-cart="onAddCart"
@buy-clicked="onBuyNow"
/>
</template>
<script setup lang="ts">
import { ref } from 'vue'
import { Sku as NutSku } from '@nutui/nutui-taro'
const skuVisible = ref(false)
// SKU 数据结构示例
const skuData = {
tree: [
{
k: '颜色',
k_s: 's1',
v: [
{ id: 1, name: '黑色', imgUrl: '' },
{ id: 2, name: '白色', imgUrl: '' },
],
},
{
k: '尺寸',
k_s: 's2',
v: [
{ id: 10, name: 'S' },
{ id: 11, name: 'M' },
{ id: 12, name: 'L' },
],
},
],
goods: [
{ id: '101', s1: 1, s2: 10, price: '199.00', stock_num: 50 },
{ id: '102', s1: 1, s2: 11, price: '199.00', stock_num: 30 },
{ id: '103', s1: 2, s2: 10, price: '209.00', stock_num: 0 },
],
}
const onSelectSku = (skuInfo: any) => {
console.log('选中规格:', skuInfo)
}
const onAddCart = (skuInfo: any) => {
// 加入购物车逻辑(下一篇详细实现)
}
const onBuyNow = (skuInfo: any) => {
// 立即购买逻辑
}
</script>
6. Toast 和 Dialog — 操作反馈
// 在 composable 或页面中使用
import { showToast, showDialog } from '@nutui/nutui-taro'
// 成功提示
showToast.success('已加入购物车')
// 失败提示
showToast.fail('库存不足')
// 确认删除
showDialog({
title: '删除商品',
content: '确认从购物车移除该商品?',
onOk: () => {
cartStore.removeItem(skuId)
},
})
六、商品列表页完整示例
把上面的组件组合成一个商品卡片:
<!-- src/components/ProductCard.vue -->
<template>
<view class="product-card" @tap="goDetail">
<nut-image
:src="product.coverImage"
width="100%"
height="180px"
fit="cover"
lazy-load
/>
<view class="product-info">
<text class="product-name">{{ product.name }}</text>
<view class="product-price-row">
<nut-price :price="product.price" />
<nut-price :price="product.originalPrice" size="small" :strikethrough="true" />
</view>
<view class="product-meta">
<text class="sold-count">已售 {{ product.soldCount }}</text>
<nut-button size="small" type="primary" @click.stop="openSku">加入购物车</nut-button>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import Taro from '@tarojs/taro'
import { Price as NutPrice, Button as NutButton, Image as NutImage } from '@nutui/nutui-taro'
interface Product {
id: string
name: string
price: number
originalPrice: number
coverImage: string
soldCount: number
}
const props = defineProps<{ product: Product }>()
const emit = defineEmits<{ openSku: [productId: string] }>()
const goDetail = () => {
Taro.navigateTo({ url: `/pages/product/index?id=${props.product.id}` })
}
const openSku = () => {
emit('openSku', props.product.id)
}
</script>
<style lang="scss">
.product-card {
background: #fff;
border-radius: 8px;
overflow: hidden;
margin-bottom: 16px;
}
.product-info {
padding: 8px 12px 12px;
}
.product-name {
font-size: 14px;
line-height: 1.4;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.product-price-row {
display: flex;
align-items: baseline;
gap: 8px;
margin: 6px 0;
}
.product-meta {
display: flex;
justify-content: space-between;
align-items: center;
}
.sold-count {
font-size: 12px;
color: #999;
}
</style>
微信小程序电商实战 01 技术选型 | 02 脚手架搭建 | 03 NutUI 配置 ← 本文 | 04 云开发环境 | 05 商品模块 | 06 购物车与订单 | 07 微信支付 | 08 用户中心 | 09 性能优化 | 10 上线部署
实操清单
-
pnpm add @nutui/nutui-taro @nutui/icons-vue-taro -
pnpm add -D @babel/core babel-plugin-import并配置babel.config.js开启按需加载 - 在
app.ts全局注册 Button、Toast、Dialog、Loading、Image、Price、Badge - 在
app.scss覆盖 CSS 变量,设置电商主色(--nutui-brand-color: #E31D1A) - 在商品列表页使用
<nut-image lazy-load>验证图片懒加载效果 - 用
<nut-price>展示商品价格,验证格式化输出正确 - 在购物车 tabBar 图标上添加
<nut-badge>角标,与useCartStore.totalCount联动 - 创建
src/components/ProductCard.vue,集成 Price + Image + Badge - 在测试页面引入
<nut-sku>,填入 mock 数据,验证 SKU 选择弹窗正常运作 - 调用
showToast.success('加入成功')验证反馈组件可用