ByteNoteByteNote
小程序按钮换图标:用 Tailwind 打磨分段控件
字

字节笔记本

2026年10月7日 · 约 8 分钟读完

小程序按钮换图标:用 Tailwind 打磨分段控件

API中转
¥120

移动端列表页的质感,往往取决于最不起眼的那些小控件。一个典型的场景:单词学习页要把列表项里的「播放」文字按钮换成图标,顺手把整页手写 CSS 迁到 Tailwind CSS,再给列表加一组「全部 / 收藏 / 已学」筛选——也就是分段控件(Segmented Control)。

分段控件并不是新发明,它源自 iOS 的 UISegmentedControl:一条胶囊或圆角容器里并排放几个互斥选项,选中的那格反色高亮。它和标签栏(Tab Bar)的区别在于,标签栏负责页面级导航,分段控件只负责同一块内容的视图切换,粒度更小、上下文不跳走。Web 前端常年用按钮组模拟它,小程序与跨端框架里也没有现成组件,几乎人人都要手写一次。本文就把这条路径完整复盘:从图标替换、样式迁移,到三版分段控件的取舍。

状态管道

一、文字按钮换图标:一次零成本的替换

在单词列表这种高频操作场景里,每个词条右侧放一个「播放」文字按钮,占面积、视觉噪音也大,换成图标是标准做法。而在 uni-app 里,这个替换几乎零成本:button 和 image 都是能绑定 @tap 的普通组件,事件处理函数原样保留,直接把图标放进原位置,再把为文字按钮写的宽度、行高、背景色样式一并删掉即可。

html
<image class="w-6 h-6" src="/static/images/play.svg" @tap="playPronunciation(word.en)" />

两个提醒:其一,静态资源路径必须真实存在,放在项目的 static 目录下随包发布,别引用占位路径;其二,跨端项目里 SVG 的表现并非处处一致,部分平台真机渲染与开发者工具不吻合,稳妥起见优先用 PNG,或至少真机验证一遍。

另外 24px 见方的图标低于移动端常见的 44px 最小可点击标准,误触与点不中都会发生。外面包一层更大的 view 分摊点击区域,或者干脆给 image 加大 padding、用负 margin 收回视觉尺寸,都是常用的补救。

二、手写 CSS 迁到 Tailwind:删掉的不只是样式

原页面有一百多行手写 rpx 样式。迁到 Tailwind 后,style 块整个删掉,类名直接落在模板上。收益有三:样式与结构同处一地,改起来不用来回跳;删组件时对应样式随之清空,不留死代码;间距、圆角、字号统一走工具类,多个页面之间更容易对齐。在 uni-app 里接入 Tailwind 通常还要借助 weapp-tailwindcss 这类方案做小程序类名转义,工具类才能真正落地到 WXML。

但在小程序语境下,有几个坑值得记下:

  1. hover: 是死代码。 小程序没有鼠标悬停,hover:bg-blue-600 永远不会生效。要么删掉,要么用 button 自带的 hover-class 表达按压态。迭代中「搜索按钮重写、去掉 hover」的需求正来自这里——工具类抄自 Web 模板时,最容易把这类只在浏览器里有效的类一并带过来。
  2. 警惕敲错的类名。 比如 rounded-0 在 Tailwind 里并不存在,正确写法是 rounded-none。写错了构建不报错、静默失效,只有发现圆角没生效时才可能倒查回来。同类陷阱还有把 rounded-r-lg 叠在不存在的类后面,指望前一个「清零」圆角。
  3. 任意值里的魔法数字。 scroll-view 高度写成 h-[calc(100vh-200px)],顶部结构一变——比如加了一条筛选栏——就要手工把 200px 改成 300px。改漏一次就是底部被裁切。更稳的做法是父容器纵向 flex、scroll-view 取 flex-1,把高度交给布局而不是算术。
  4. 默认样式与自定义导航。 小程序 button 自带边框与内边距,跨端项目里还常被 ::after 伪元素画出的边框困扰,重置样式要有意识;路由配置里 navigationStyle 设为 custom 后系统导航栏消失,页面顶部需要自行补白,模板根节点的 pt-20 干的就是这件事。

三、分段控件的三种做法与取舍

加「全部 / 收藏 / 已学」筛选时,自然的选型就是分段控件。实现迭代了三轮,每轮都是一个常见的社区方案:

分段控件方案对比

方案 A:静态高亮。 三个按钮排一行,选中项用动态 class 加 bg-blue-500 text-white。实现最简,状态一目了然,作为基线没有任何问题。缺点是选中块生硬地「跳」过去,缺少连续感。

方案 B:滑动指示器。 胶囊容器设 relative,内部放一个 absolute 定位的蓝色背景块,宽度绑定为 100/n%,left 绑定为 (100/n)×index,再配 transition-all duration-300,切换时背景「划」过去:

html
<view class="absolute h-8 bg-blue-500 rounded-full transition-all duration-300"
  :style="{ width: `${100 / tabs.length}%`, left: `${(100 / tabs.length) * activeTabIndex}%` }" />

这是设计稿里最常见的形态,落地细节却不少:文字要 z-10 垫在背景之上,否则会被色块盖住;按百分比等分只对等宽标签成立,文字长短不一时背景与文字会错位;transition 作用于 left 和 width 会持续触发重排,性能上不如 transform 位移。这一版拿到的直接反馈就是「效果不好」——动效说到底是在为状态服务,观感不对就全盘不对。

方案 C:下划线指示器。 选中项底部用 absolute 定位一条极细蓝线,v-if 控制显隐。这是经典 Web 模式,桌面端导航条上无处不在。但 v-if 的出现与消失只是切换而非位移,动效感很弱,放在本就小巧的分段控件里存在感更低。

最后的落点反而朴素:保留胶囊造型、去掉动效——rounded-full 容器配 p-1,每个选项 rounded-full,选中 bg-blue-500 text-white,未选中 text-gray-600,切换即时生效。回头看三版方案:要快速交付选 A,要有「产品感」且标签等宽选 B,内容型导航选 C;而对一个工具型小页面的筛选栏,最终选了 A 的胶囊变体。分段控件的核心诉求是「当前在哪、还能去哪」,状态清晰比动画讨喜更重要;动效只在语义有增益时,才值得付出实现与性能成本。

四、状态模型先于样式

三种样式方案能来回横跳,前提是状态模型干净:一个 ref 存当前标签,两个计算属性串成过滤管道——搜索词先筛出 filteredWords,再按当前标签二次筛出 displayedWords;每个词条挂 isFavorite、isLearned 两个标记,收藏图标用 :src 三元表达式在实心与空心之间切换。

这条管道的好处在于关注点分离:搜索词变了只重算一次过滤,切标签只重算第二级筛选,切换 tab 不会丢掉搜索词,反之亦然。列表页的搜索、筛选、收藏,套这个结构基本都够用;将来要加「已学」的标记逻辑,也不过是给词条多挂一个布尔值、多加一个切换函数。样式迭代再多次,管道一行不用改。

结语

这次改动没有一个「高级」技巧:组件替换、类名迁移、三版控件对比。但它演示了一个朴素的节奏——先对齐状态模型,再动样式;样式方案要选允许快速试错的,Tailwind 工具类恰好是,三版分段控件一个下午就能全部试完,最终留下最不花哨的那版。小程序 UI 打磨大多如此:决定成败的不是动效,而是点击热区、状态清晰度,和那些没人注意的死代码。

相关文章

分享: