🎨 Taro 从零到一(五):样式与 UI 组件库
系列导读:页面光有逻辑不够,还要好看。本文教你 Taro 中的样式体系, 以及如何用 NutUI 组件库快速搭建精美界面。
📏 1. Taro 样式基础
尺寸单位:px 自动转 rpx
// Taro 基于 750px 设计稿,写 px 会自动转换为 rpx
.container {
width: 750px; // = 100% 屏幕宽度
padding: 32px; // → 自动转为 32rpx
font-size: 28px; // → 28rpx ≈ 14pt
border-radius: 16px;
}
// 如果不想被转换,用大写 PX
.border {
border: 1PX solid #eee; // 保持 1px 不转换(适合细线)
}
尺寸对照表
| 设计稿 (750px) | Taro 写法 | 实际效果 |
|---|---|---|
| 750px | width: 750px | 满屏宽 |
| 375px | width: 375px | 半屏宽 |
| 28px | font-size: 28px | 约 14pt 正文 |
| 32px | font-size: 32px | 约 16pt 标题 |
| 24px | font-size: 24px | 约 12pt 辅助文字 |
Scss 使用
// 变量
$primary-color: #6366f1;
$text-color: #1a1a1a;
$text-secondary: #666;
$border-radius: 16px;
$spacing: 24px;
// Mixin
@mixin flex-center {
display: flex;
align-items: center;
justify-content: center;
}
@mixin ellipsis($lines: 1) {
display: -webkit-box;
-webkit-line-clamp: $lines;
-webkit-box-orient: vertical;
overflow: hidden;
text-overflow: ellipsis;
}
// 使用
.card-title {
@include ellipsis(2);
font-size: 32px;
color: $text-color;
}
.centered-box {
@include flex-center;
width: 200px;
height: 200px;
background: $primary-color;
border-radius: $border-radius;
}
🍞 2. NutUI — Taro 官方 UI 组件库
安装
npm install @nutui/nutui-react-taro @nutui/icons-react-taro
// config/index.ts 配置
const config = {
// ... 其他配置
sass: {
data: `@import "@nutui/nutui-react-taro/dist/styles/variables.scss";`,
},
}
按钮
import { Button } from '@nutui/nutui-react-taro'
// 按钮类型
<Button type="primary">主要按钮</Button>
<Button type="success">成功按钮</Button>
<Button type="warning">警告按钮</Button>
<Button type="danger">危险按钮</Button>
<Button type="default">默认按钮</Button>
// 按钮形态
<Button type="primary" fill="outline">描边按钮</Button>
<Button type="primary" fill="none">文字按钮</Button>
<Button type="primary" size="large" block>大号全宽按钮</Button>
<Button type="primary" size="small">小按钮</Button>
<Button type="primary" loading>加载中</Button>
<Button type="primary" disabled>禁用</Button>
// 图标按钮
import { IconFont } from '@nutui/icons-react-taro'
<Button type="primary" icon={<IconFont name="star-fill" />}>收藏</Button>
表单组件
import { Input, Form, TextArea, Radio, Checkbox, Switch, Picker } from '@nutui/nutui-react-taro'
function ProfileForm() {
return (
<Form
labelPosition="left"
onFinish={(values) => console.log('表单提交:', values)}
>
<Form.Item label="姓名" name="name" rules={[{ required: true, message: '请填写姓名' }]}>
<Input placeholder="请输入姓名" />
</Form.Item>
<Form.Item label="简介" name="bio">
<TextArea placeholder="介绍一下自己" maxLength={200} showCount />
</Form.Item>
<Form.Item label="性别" name="gender">
<Radio.Group direction="horizontal">
<Radio value="male">男</Radio>
<Radio value="female">女</Radio>
</Radio.Group>
</Form.Item>
<Form.Item label="通知" name="notify">
<Switch />
</Form.Item>
<Form.Item>
<Button type="primary" formType="submit" block>
保存
</Button>
</Form.Item>
</Form>
)
}
反馈组件
import { Dialog, Toast, ActionSheet, Popup } from '@nutui/nutui-react-taro'
// Toast 轻提示
Toast.show('操作成功')
Toast.show({ content: '加载中', icon: 'loading', duration: 0 })
// Dialog 对话框
<Dialog
title="确认删除"
visible={showDialog}
onConfirm={() => handleDelete()}
onCancel={() => setShowDialog(false)}
>
删除后无法恢复,确定要删除吗?
</Dialog>
// ActionSheet 操作面板
<ActionSheet
visible={showAction}
options={[
{ name: '拍照' },
{ name: '从相册选择' },
{ name: '取消', danger: true },
]}
onSelect={(item, index) => handleAction(index)}
onCancel={() => setShowAction(false)}
/>
// Popup 弹出层
<Popup
visible={showPopup}
position="bottom"
round
onClose={() => setShowPopup(false)}
>
<View style={{ padding: '32px' }}>
自定义弹出内容
</View>
</Popup>
数据展示
import { Cell, Tag, Avatar, Empty, NoticeBar, Skeleton } from '@nutui/nutui-react-taro'
// Cell 列表项
<Cell title="我的订单" extra="查看全部" onClick={() => {}} />
<Cell title="收货地址" description="北京市朝阳区xxx" />
<Cell title="账号安全" extra={<Tag type="success">已验证</Tag>} />
// 空状态
<Empty description="暂无数据" />
// 公告栏
<NoticeBar content="欢迎使用 Taro + NutUI 开发小程序" scrollable />
// 骨架屏
<Skeleton rows={3} title animated visible={loading}>
<View>实际内容</View>
</Skeleton>
📱 3. 实战:商城首页布局
import { View, Text } from '@tarojs/components'
import { Swiper, SwiperItem, Image } from '@tarojs/components'
import { SearchBar, Grid, GridItem, NoticeBar } from '@nutui/nutui-react-taro'
import { IconFont } from '@nutui/icons-react-taro'
function HomePage() {
return (
<View className="home">
{/* 搜索栏 */}
<SearchBar
placeholder="搜索商品"
shape="round"
disabled
onClickInput={() => Taro.navigateTo({ url: '/pages/search/index' })}
/>
{/* 轮播图 */}
<Swiper className="banner" autoplay circular indicatorDots>
{banners.map(b => (
<SwiperItem key={b.id}>
<Image src={b.image} mode="aspectFill" className="banner-img" />
</SwiperItem>
))}
</Swiper>
{/* 公告 */}
<NoticeBar content="新用户专享:注册即送20元优惠券 🎉" scrollable />
{/* 分类宫格 */}
<Grid columns={5} className="categories">
{categories.map(cat => (
<GridItem
key={cat.id}
icon={<Image src={cat.icon} className="cat-icon" />}
text={cat.name}
onClick={() => Taro.navigateTo({ url: `/pages/list/index?category=${cat.id}` })}
/>
))}
</Grid>
{/* 热门商品 */}
<View className="section">
<View className="section-header">
<Text className="section-title">🔥 热门推荐</Text>
<Text className="section-more" onClick={() => {}}>查看更多 →</Text>
</View>
<View className="product-grid">
{products.map(p => (
<ProductCard key={p.id} product={p} />
))}
</View>
</View>
</View>
)
}
.home {
background: #f5f5f5;
.banner {
height: 320px;
.banner-img { width: 100%; height: 320px; }
}
.categories {
background: #fff;
padding: 20px 0;
margin-bottom: 16px;
.cat-icon { width: 48px; height: 48px; }
}
.section {
background: #fff;
padding: 24px;
margin-bottom: 16px;
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 20px;
.section-title { font-size: 32px; font-weight: bold; }
.section-more { font-size: 24px; color: #999; }
}
.product-grid {
display: flex;
flex-wrap: wrap;
gap: 16px;
}
}
}
✅ 本篇小结 Checklist
- 理解 Taro 的 px → rpx 自动转换机制
- 掌握 Scss 变量和 Mixin 复用
- 安装并配置 NutUI 组件库
- 掌握常用 NutUI 组件(Button / Form / Dialog / Cell)
- 能搭建商城首页布局
下一篇预告:《状态管理(Zustand)》
本文是「Taro 从零到一」系列第 5 篇,共 10 篇。