稀土掘金技术社区

【AI 编程实战】第 4 篇:一次完美 vs 五轮对话 - UnoCSS 配置的正确姿势

img_v3_02t9_51022880-c5f3-4f3c-8892-b6f6ca1ba8eg.png(✨年终技术征文火热进行中,速戳上图了解详情🔍)

❝

很多教程告诉你"写好提示词就能让 AI 一次给出正确答案",但现实是:「你不可能一开始就知道所有细节」。真正高效的 AI 协作,是在对话中逐步明确需求。这篇文章通过一个完整的 UnoCSS 配置案例,展示我和 TRAE SOLO 的「真实交互过程」——从模糊想法到完整方案,中间经历了哪些沟通和调整。

这是《AI 编程实战:TRAE SOLO 全栈开发指南》专栏的第四篇文章,带你学会如何与 AI 正确的沟通协作。

❞

一、开篇:别被"完美提示词"骗了

1.1 一个常见的误区

网上很多 AI 教程会给你看这样的"完美提示词":

帮我配置 UnoCSS,项目环境和需求如下:【项目环境】UniApp + Vue3 + Vite,目标平台微信小程序【功能需求】支持 attributify、@apply、图标、安全区适配【已知问题】小程序 transform 类需要预设 CSS 变量【输出要求】完整的 uno.config.ts 配置文件

然后告诉你:"看,一次就能得到完美答案!"

「但问题是」:

  • 你一开始怎么知道需要"安全区适配"?
  • 你怎么知道"transform 类需要预设 CSS 变量"?
  • 这些都是「踩坑之后才知道的」

1.2 真实的 AI 协作是什么样的

真实的 AI 协作是一个「渐进式对话」的过程:

第 1 轮:我有个模糊的需求    ↓AI:给出初步方案 + 执行计划    ↓第 2 轮:我发现问题 / 补充细节    ↓AI:调整方案    ↓第 3 轮:遇到报错 / 样式不生效    ↓AI:排查问题 + 修复    ↓最终:得到完整可用的方案

接下来,我会完整展示和 TRAE SOLO 配置 UnoCSS 的「真实对话过程」。

二、第一轮对话:从模糊需求开始

2.1 我的第一句话

刚开始,我对 UnoCSS 的了解很有限,只知道它是"原子化 CSS"。我的第一句话很简单:

我:项目需要用 UnoCSS,帮我配置一下

2.2 TRAE SOLO 的响应:先给执行计划

TRAE SOLO 没有直接开始写代码,而是先给出了「执行计划」:

TRAE SOLO:好的,我来帮你配置 UnoCSS。在开始之前,我需要先了解一些信息:
📋 执行计划:1. 分析项目结构,确定技术栈2. 选择合适的 UnoCSS 预设3. 生成配置文件4. 配置 Vite 插件
🤔 我有几个问题:1. 这是什么类型的项目?(Vue/React/UniApp)2. 目标运行平台是什么?(Web/小程序/App)3. 有没有特别想要的功能?(图标/attributify 模式等)
请告诉我这些信息,或者我可以先分析你的项目结构来判断。

「关键点」:TRAE SOLO 主动「询问上下文」,而不是瞎猜。

2.3 我补充信息

我:UniApp + Vue3 项目,主要跑微信小程序,也要支持 H5。    功能的话,我也不太清楚,你看着配吧,常用的都加上。

2.4 TRAE SOLO 分析项目后给出方案

TRAE SOLO:我分析了你的项目结构,发现:- 框架:UniApp + Vue3 + Vite- UI 库:已安装 wot-design-uni- 目标:微信小程序 + H5
📋 更新执行计划:
1. 安装依赖   - unocss   - @uni-helper/unocss-preset-uni (UniApp 专用预设,必须)   - @iconify-json/carbon (图标库,可选)
2. 创建 uno.config.ts,包含:   - presetUni:小程序适配   - presetAttributify:支持属性化写法   - presetIcons:图标支持   - transformerDirectives:支持 @apply 指令
3. 修改 vite.config.ts,添加 UnoCSS 插件
4. 在 main.ts 中引入 uno.css
是否按这个计划执行?或者你想调整什么?

2.5 我确认执行

我:可以,就按这个来
TRAE SOLO:好的,开始执行...
✅ 正在安装依赖...✅ 创建 uno.config.ts...✅ 修改 vite.config.ts...✅ 修改 main.ts...
配置完成!你可以这样使用:
<viewclass="flex items-center gap-4 p-4 bg-white rounded-xl">  <textclass="text-lg font-bold">Hello UnoCSS</text></view>
运行 pnpm dev:mp-weixin 试试看效果。

三、第二轮对话:遇到问题,继续沟通

3.1 我发现样式不生效

运行项目后,我发现部分样式不生效:

我:样式大部分生效了,但是 translate-x-2 这个类没效果,    元素没有移动

3.2 TRAE SOLO 排查问题

TRAE SOLO:让我检查一下...
我查看了生成的 CSS,发现 translate-x-2 生成的代码是:
transform: translateX(var(--un-translate-x));
问题在于:小程序环境没有预设 --un-translate-x 这个 CSS 变量。
在浏览器中,UnoCSS 会通过 :root 选择器预设这些变量,但小程序不支持 :root 选择器。
🔧 修复方案:在 uno.config.ts 中添加 preflights,手动为小程序元素预设变量。
是否需要我帮你修复?

3.3 我让它修复

我:帮我修复吧
TRAE SOLO:好的,我来修改 uno.config.ts...
添加了以下配置:
preflights: [  {    layer: 'unocss-transform-fix',    getCSS: () => `page, view, text, image {  --un-translate-x: 0;  --un-translate-y: 0;  --un-rotate: 0;  --un-scale-x: 1;  --un-scale-y: 1;  /* ... 其他变量 */}`,  },],
✅ 已修复,重新运行项目试试。

3.4 我验证修复成功

我:好了,translate 生效了

四、第三轮对话:追加新需求

4.1 我想添加安全区适配

开发过程中,我发现 iPhone 底部有安全区域遮挡内容:

我:iPhone 底部有个横条会挡住内容,UnoCSS 能处理这个吗?

4.2 TRAE SOLO 给出方案

TRAE SOLO:这是 iPhone 的安全区域问题。可以通过自定义 UnoCSS 规则来解决。
📋 方案:添加以下自定义规则:- pb-safe:底部安全区内边距- pt-safe:顶部安全区内边距(刘海屏)- p-safe:四边安全区内边距
使用方式:<view class="pb-safe">底部内容会避开安全区</view>
是否需要我添加这些规则?
我:加上吧
TRAE SOLO:好的,在 uno.config.ts 的 rules 中添加:
rules: [  ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],  ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],  ['p-safe', {    padding: 'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',  }],],
✅ 已添加,现在可以使用 pb-safe 类了。

五、第四轮对话:优化开发体验

5.1 我觉得类名太长了

我:flex items-center justify-between 每次都要写这么长,    有没有办法简化?

5.2 TRAE SOLO 介绍 shortcuts

TRAE SOLO:可以用 UnoCSS 的 shortcuts 功能,把常用组合定义成短类名。
比如:- center = flex justify-center items-center- between = flex justify-between items-center
你想定义哪些快捷方式?或者我根据你的代码分析常用组合?
我:你分析一下吧,看哪些用得多

5.3 TRAE SOLO 分析代码后给出建议

TRAE SOLO:我扫描了你的代码,发现以下组合出现频率较高:
📊 使用频率分析:1. flex items-center (出现 23 次)2. flex items-center justify-between (出现 15 次)3. flex items-center justify-center (出现 12 次)4. flex flex-col (出现 8 次)5. bg-white rounded-xl p-4 (出现 7 次)
🔧 建议添加的 shortcuts:
shortcuts: [  { 'center': 'flex justify-center items-center' },  { 'between': 'flex justify-between items-center' },  { 'col': 'flex flex-col' },  { 'card': 'bg-white rounded-xl p-4' },],
是否按这个配置?或者你想调整?
我:可以,就这样

六、第五轮对话:处理动态类名问题

6.1 我遇到动态类名不生效

我:我有个地方根据状态切换颜色:
    <text :class="`text-${status}-500`">状态</text>
    status 可能是 red、green、blue,但样式不生效

6.2 TRAE SOLO 解释原因并给方案

TRAE SOLO:这是 UnoCSS 的一个限制。
🔍 原因:UnoCSS 在构建时静态扫描代码,`text-${status}-500` 这种动态拼接的类名无法被识别,所以不会生成对应的 CSS。
🔧 解决方案有三种:
【方案一】对象语法(推荐)<text :class="{  'text-red-500': status === 'red',  'text-green-500': status === 'green',  'text-blue-500': status === 'blue',}">状态</text>
【方案二】safelist 预声明在 uno.config.ts 中添加:safelist: ['text-red-500', 'text-green-500', 'text-blue-500']
【方案三】CSS 变量<text class="text-[var(--status-color)]"      :style="{ '--status-color': statusColor }">  状态</text>
你的场景用哪种方案比较合适?
我:颜色就这三种,用方案二吧,简单
TRAE SOLO:好的,已添加到 safelist:
safelist: [  'text-red-500',  'text-green-500',  'text-blue-500',  'i-carbon-code', // 之前已有的图标],
✅ 现在动态类名可以正常工作了。

七、最终配置:多轮对话的成果

经过 5 轮对话,最终的 uno.config.ts 是这样的:

// uno.config.ts// 这个配置是通过多轮对话逐步完善的,不是一次性写出来的
import { presetUni } from '@uni-helper/unocss-preset-uni'import {  defineConfig,  presetAttributify,  presetIcons,  transformerDirectives,  transformerVariantGroup,} from 'unocss'
export default defineConfig({  // 【第 1 轮】基础预设  presets: [    presetUni({      attributify: {        prefixedOnly: true,      },    }),    presetIcons({      scale: 1.2,      warn: true,      extraProperties: {        'display': 'inline-block',        'vertical-align': 'middle',      },    }),    presetAttributify(),  ],
  // 【第 1 轮】转换器  transformers: [    transformerDirectives(),    transformerVariantGroup(),  ],
  // 【第 2 轮】修复小程序 transform 不生效  preflights: [    {      layer: 'unocss-transform-fix',      getCSS: () => `page, view, text, image {  --un-translate-x: 0;  --un-translate-y: 0;  --un-translate-z: 0;  --un-rotate: 0;  --un-rotate-x: 0;  --un-rotate-y: 0;  --un-rotate-z: 0;  --un-skew-x: 0;  --un-skew-y: 0;  --un-scale-x: 1;  --un-scale-y: 1;  --un-scale-z: 1;}`,    },  ],
  // 【第 3 轮】安全区适配  rules: [    ['p-safe', {      padding: 'env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)',    }],    ['pt-safe', { 'padding-top': 'env(safe-area-inset-top)' }],    ['pb-safe', { 'padding-bottom': 'env(safe-area-inset-bottom)' }],  ],
  // 【第 4 轮】常用快捷方式  shortcuts: [    { 'center': 'flex justify-center items-center' },    { 'between': 'flex justify-between items-center' },    { 'col': 'flex flex-col' },    { 'card': 'bg-white rounded-xl p-4' },  ],
  // 【第 5 轮】动态类名预声明  safelist: [    'text-red-500',    'text-green-500',    'text-blue-500',    'i-carbon-code',  ],
  // 【第 1 轮】主题配置  theme: {    colors: {      primary: 'var(--wot-color-theme, #0957DE)',    },    fontSize: {      '2xs': ['20rpx', '28rpx'],      '3xs': ['18rpx', '26rpx'],    },  },})

八、组件开发:同样是对话式协作

8.1 开发回复卡片组件

配置完成后,我开始开发业务组件。同样是对话式协作:

我:帮我写一个回复卡片组件,用来展示 AI 生成的回复。    左边是语气标签(比如"高情商"),中间是回复文字,右边是复制按钮。
TRAE SOLO:好的,我来帮你开发这个组件。
📋 执行计划:1. 创建 ReplyCard.vue 组件2. 定义 Props:tone(语气)、text(文本)3. 定义 Events:copy(复制)4. 使用 UnoCSS 原子类实现样式
🤔 几个问题:1. 语气标签的颜色是固定的还是根据内容变化?2. 复制按钮是图标还是文字?3. 有没有设计稿可以参考?
我:标签颜色根据语气类型自动变,用哈希算法分配吧。    复制按钮用图标。设计稿没有,你自己设计一个好看的。
TRAE SOLO:明白了,我来实现:
【设计方案】- 卡片:白色背景、圆角、内边距- 标签:小圆角、根据文字哈希分配 5 种颜色- 文本:深灰色、支持多行- 按钮:圆形点击区域、图标居中
开始生成代码...

8.2 TRAE SOLO 生成的组件

<!-- components/ReplyCard.vue --><template>  <viewclass="flex items-center gap-2.5 bg-white rounded-xl p-4 pr-2">    <!-- 语气标签 -->    <view      v-show="!hideTag"      class="text-xs font-bold rounded-sm px-2 py-1 flex-shrink-0"      :style="{ backgroundColor: tagStyle.bg, color: tagStyle.text }"    >      {{ tone }}    </view>
    <!-- 回复文本 -->    <viewclass="text-sm text-#1F2937 flex-1">      {{ text }}    </view>
    <!-- 复制按钮 -->    <view      class="w-9 h-9 center cursor-pointer"      @click="$emit('copy', text)"    >      <imagesrc="/static/images/home/copy.svg"mode="aspectFit"class="w-5 h-5" />    </view>  </view></template>
<scriptsetuplang="ts">import { computed } from 'vue';
interface Props {  tone: string;  text: string;  hideTag?: boolean;}
const props = withDefaults(defineProps<Props>(), {  hideTag: false,});
defineEmits<{  copy: [text: string];}>();
// 5 种预设颜色const COLORS = [  { bg: '#F3E8FF', text: '#8B5CF6' },  // 紫色  { bg: '#E6FFFA', text: '#319795' },  // 青色  { bg: '#FFF5E6', text: '#F56500' },  // 橙色  { bg: '#FFE4E6', text: '#E11D48' },  // 粉色  { bg: '#E0F2FE', text: '#0284C7' },  // 蓝色];
// 根据语气文字哈希分配颜色const tagStyle = computed(() => {  let hash = 0;  for (let i = 0; i < props.tone.length; i++) {    hash = (hash + props.tone.charCodeAt(i)) % COLORS.length;  }  return COLORS[hash];});</script>

8.3 我提出修改意见

我:不错,但我想加一个功能:有时候不需要显示标签,能不能加个 prop 控制?
TRAE SOLO:已经有了,就是 hideTag 这个 prop。
使用方式:<ReplyCard tone="高情商" text="回复内容" :hide-tag="true" />
设置 hide-tag 为 true 就会隐藏标签。
我:哦对,我没注意看。那再加个需求:复制成功后给个反馈
TRAE SOLO:复制反馈建议在父组件处理,因为:1. 卡片组件应该保持简单,只负责 UI2. 复制逻辑和 toast 提示是业务逻辑,放父组件更合适
父组件这样用:
<ReplyCard  tone="高情商"  text="回复内容"  @copy="handleCopy"/>
const handleCopy = async (text: string) => {  await copyText(text);  toast.success('已复制');};
你觉得这样合理吗?还是希望把 toast 放在组件内部?
我:你说得对,放父组件更好

九、核心经验:与 AI 协作的正确姿势

9.1 不要追求"一次完美"

错误做法
正确做法
花 30 分钟写一个"完美提示词"
花 1 分钟说出需求,让 AI 提问
一次性想清楚所有细节
边做边发现问题,逐步完善
把 AI 当执行工具
把 AI 当协作伙伴,可以对话

9.2 让 AI 先给执行计划

每次提需求后,让 AI 先给出「执行计划」:

我:[需求描述]
AI:📋 执行计划:    1. xxx    2. xxx    3. xxx
    🤔 有几个问题:    1. xxx?    2. xxx?
    确认后我开始执行。

「好处」:

  • 确保 AI 理解正确
  • 发现遗漏的需求
  • 可以在执行前调整方向

9.3 遇到问题时提供上下文

「❌ 错误方式」:

我:样式不生效

「✅ 正确方式」:

我:translate-x-2 这个类在小程序里不生效,但在 H5 正常。    我看了生成的 CSS,是 transform: translateX(var(--un-translate-x))。    是不是变量没定义?

9.4 让 AI 解释它的方案

当 AI 给出方案时,追问原理:

我:为什么要用 preflights?直接写 CSS 不行吗?
TRAE SOLO:preflights 是 UnoCSS 的预设样式注入机制,和直接写 CSS 的区别:
1. preflights 会被注入到所有生成的 CSS 之前,确保优先级正确2. 可以通过 layer 控制加载顺序3. 统一管理,修改时只需要改配置文件
直接写 CSS 也可以,但需要确保加载顺序正确,否则可能被覆盖。

「好处」:理解原理后,下次遇到类似问题可以自己判断。

十、总结:对话式协作 > 完美提示词

10.1 本文的核心观点

  1. 「不要追求"一次完美的提示词"」,真实的 AI 协作是多轮对话
  2. 「让 AI 先给执行计划」,确认后再执行
  3. 「遇到问题时提供上下文」,帮助 AI 定位问题
  4. 「追问原理」,理解 AI 的方案,下次才能举一反三

10.2 5 轮对话完成的工作

轮次
需求
成果
第 1 轮
基础配置
presets、transformers、theme
第 2 轮
修复 transform 问题
preflights
第 3 轮
安全区适配
自定义 rules
第 4 轮
简化常用类名
shortcuts
第 5 轮
动态类名支持
safelist

10.3 下一篇预告

「《【AI 编程实战】第 5 篇:Pinia 状态管理不会写?让 AI 手把手教你最佳实践》」

下一篇继续展示对话式协作,教你:

  • 如何让 AI 设计 Store 结构
  • 状态持久化的对话过程
  • 用户登录态管理的多轮沟通

❝

与 AI 协作的关键不是"写出完美提示词",而是「学会和 AI 对话」。

如果这篇文章对你有帮助,请点赞、收藏!

❞
图片
点击"阅读原文"了解详情~