05-样式与UI组件库

2 阅读3分钟

🎨 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 写法实际效果
750pxwidth: 750px满屏宽
375pxwidth: 375px半屏宽
28pxfont-size: 28px约 14pt 正文
32pxfont-size: 32px约 16pt 标题
24pxfont-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 篇。