普通视图

发现新文章,点击刷新页面。
昨天 — 2026年4月22日首页

uni-app 组件库 Wot UI 2.0 发布了,我们带来了这些改变!

2026年4月22日 12:49

大家好,我是不如摸鱼去,好久不见。

进入 2026 以来,大家可以感受到 wot-ui 的迭代速度明显放缓了,我们最近也接到了无数催更的消息。很多人以为我们是在偷懒或者放弃更新了,其实不是,我们是在偷偷写代码然后准备惊艳所有人!(其实是苦苦码了好几个月,来点点赞吧)

接下来看看我们带来了哪些东西吧!

V2

如今已经是 AI 编程时代了,Wot UI 的 slogan 也调整成了「轻量、美观、AI 友好」。我们的目标很直接,就是为大家带来更高效、更易用,也更适合和 AI 协作的 uni-app 开发实践。

主要变化

对比 v1,v2 这一版我们带来了不少更新:

  1. 全新的设计系统。 在 v2 版本,我们基于基础变量、语义变量和组件变量三层 design token 搭建全新的设计系统,使修改组件样式和自定义主题变得随心所欲,同时升级了 UI 视觉体验。
  2. 简化 form 及相关组件 我们简化了 form 相关组件的用法,提供基于 zod 的校验引擎以及支持自定义校验引擎,使用 form-item 替换 cell 作为表单项,优化各表单组件与非表单组件结合 form-item 使用的写法,不再区分「表单组件」和「非表单组件」。
  3. 优化文档体验 重新整理文档结构,统一组件文档结构,将 @wot-ui/vitepress-theme 提取后发布为 vitepress 主题,统一多个 wot-ui 多个库的文档 UI,同时在每个组件文档中增加 css 变量的展示。
  4. 优化 AI 支持
  • 提供 cli 工具 @wot-ui/cli,其内部提供 cli 与 mcp,以优化 wot-ui 组件库的 AI 编程体验。
  • 提供多个 skills 与 LLMs.txt。
  1. 提供 Unocss 预设 提供了 Unocss 预设 @wot-ui/unocss-preset,内置主题变量、语义色、间距、圆角、字重、透明度、描边和排版相关原子类规则,把 wot-ui 的设计 token 和主题变量映射成可直接使用的原子类。

CLI

很多同学在用 AI 写 wot-ui 页面时,问题其实不在于模型不会写,而在于它总爱“凭感觉”写。比如 props 名字记错了、事件猜错了、slot 用法写得像对的,结果一跑就炸。

所以在 v2 里,我们专门提供了 @wot-ui/cli。它不是一个单纯的脚手架工具,而是把 wot-ui v2 的组件知识整理成一套可查询、可校验、可给 AI 调用的能力。

你可以把它理解成一个本地离线知识库,既能给开发者自己查,也能给 AI 客户端通过 MCP 调用。常见能力包括:

  • 查询组件的 props、events、slots 和 CSS 变量
  • 查看组件 demo,减少 AI 瞎猜用法
  • 扫描本地项目,检查不合理或错误的组件写法
  • 通过 MCP 接入 AI 工具链,让 Agent 先查再写

比如以前你让 AI 写一个表单页,它可能先吐出一份“看起来很像 wot-ui”的代码。现在更合理的流程是,先查组件约束,再生成代码,最后再跑一遍检查。这样出来的结果会稳很多。

简单来说,@wot-ui/cli 想解决的不是“怎么让 AI 更会猜”,而是“怎么让 AI 少猜一点”。这也是我们这次把「AI 友好」放进 v2 里的一个重点。

Starter

如果说组件库解决的是“页面怎么写”,那 Starter 解决的就是“项目怎么开”。所以这次我们也没有把它当成一个单纯的 demo 仓库来维护,而是持续把它往一套更适合真实开发、也更适合 AI 协作的 uni-app 起手方案去打磨。

1.5 之后,Starter 先补上了 skills,开始把项目里常用的开发约定、页面结构和组件使用方式整理出来,让 AI 在这个模板里写代码时不再完全靠猜。到了 2.0,Starter 进一步完成了对 wot-ui v2 的适配,示例、主题能力、反馈组件文档以及整体开发链路也一起升级。

你可以把现在的 Starter 理解成:它不只是“集成了 wot-ui 的模板”,而是一个默认就站在 v2 体系上的起点。新项目拉下来之后,从主题定制、页面组织到后续和 AI 配合开发,整个体验都会比 1.x 时代更顺手一些。

CSS 插件

再往下一层看,组件和模板之外,样式这一层我们也往前推了一步,提供了 @wot-ui/unocss-preset。它本质上是一个基于 UnoCSS 的预设,用来把 wot-ui v2 的设计 token 直接映射成可用的原子类。

这件事的价值在于,你在写页面时不需要一边翻设计变量、一边手写一堆样式映射了。像颜色、间距、圆角、字重、排版这些能力,现在都可以直接通过统一的 wot- 前缀类名来组织,主题切换时也能更自然地跟着整套 token 体系走。对于喜欢原子化 CSS 的同学来说,这一层会让 wot-ui v2 真正从“组件好用”变成“整套样式开发都更顺手”。

VSCode 插件

如果说前面这些更多是在补工具链和工程体验,那再落回到日常写代码,我们也补上了 VS Code 插件这一层,也就是 VS Code 插件 wot-ui-intellisense

这个插件主要解决的是写页面时那些很碎、但又很烦的事情。比如组件名记不全、属性名老要回头翻文档、事件到底叫什么总要试一下。现在在 .vue.html 文件里,输入 <wd-、空格、:@ 这些常见场景时,都可以直接拿到补全提示。

除了补全之外,它还支持组件、属性、事件的悬停文档展示,以及一部分属性值校验和错误诊断。也就是说,很多以前要切出去查文档、或者运行后才发现的问题,现在在编辑器里就能先拦一层。

如果说 CLI 更像是给 AI 和工程化链路准备的,那 VS Code 插件就是给开发者日常写代码准备的。一个负责让模型少猜,一个负责让人少翻文档,配合起来,整个 wot-ui v2 的开发体验就会完整很多。

最后

回过头来看,这次 wot-ui v2 对我们来说并不只是一次常规升级。

它一边在补齐设计系统、表单体系、文档体验这些基础能力,一边也在认真回应这两年越来越明显的变化:大家写代码的方式,确实已经和以前不太一样了。

所以你会看到,这一版里不只有组件本身,也有 Starter、CLI、UnoCSS 预设、VS Code 插件这些围绕开发体验的配套。我们想做的,不只是一个“能用”的组件库,而是一套更顺手、更现代,也更适合和 AI 一起协作的 uni-app 开发方案。

v2 还有很多东西会在后面陆续展开,这篇文章先带大家看一个整体。如果你也在关注 wot-ui v2,或者也在想组件库怎么更好地拥抱 AI 编程,欢迎继续关注我们后面的更新。

参考资料

昨天以前首页

独立开发复盘:我用 Uni-app + Strapi v5 肝了一个“会上瘾”的打卡小程序

作者 知航驿站
2026年4月21日 20:06

大家好,我是一名独立开发者。最近利用业余时间,我从零到一开发并上线了一款目标打卡/习惯养成类的小程序。

今天这篇文章,不仅是想向大家推荐一下我的心血之作,更想从创作灵感核心技术实现代码细节以及无数次踩坑的角度,和大家深度复盘一下整个项目的历程。如果你也想尝试用 Uni-app + Strapi 搞全栈独立开发,这篇“避坑指南 + 技术解析”绝对不容错过!


307c9942d27117ec00e7781976431a56.jpg

1fa52da0afde72c0faf8e72dc49c1c29.jpg

💡 创作灵感与产品心得:为什么还要做一个打卡应用?

市面上的打卡应用多如牛毛,为什么我还要自己造轮子? 其实原因很简单:我觉得现有的工具太“冷冰冰”了,缺乏足够的情绪反馈。

打卡/坚持习惯本身就是一件反人性的事情,如果工具只是一个无情的“待办列表”,那用户很容易就会放弃。因此,在产品设计之初,我定下了几个核心基调:

  1. 克制与聚焦:我限制了每天最多只能创建 12 个任务,到达 10 个时会温馨警告。目标泛滥等于没有目标。
  2. 正向反馈拉满:任务完成不能只是打个勾,必须要有“爽感”。我加入了物理震动、纸屑爆裂动画(撒花)、以及 3D 翻转的徽章解锁系统。
  3. 互助与抄作业:很多时候我们不知道该养成什么习惯,所以我做了一个“社区广场”(瀑布流布局),看到别人优秀的习惯,可以直接“一键 Copy”到自己的计划中。

🛠 技术选型:单兵作战的效率最优解

作为独立开发者,开发效率是第一生产力。我选择了这套组合拳:

  • 前端:Uni-app (Vue 3) + Tailwind CSS
    • Vue 3 的 Composition API 逻辑复用非常爽。
    • 结合原子化 CSS(如 Tailwind/UnoCSS),极大提升了切图速度,摆脱了起 class 名字的内耗。
  • 后端:Strapi v5 (Headless CMS)
    • 绝对的效率神器!不用手写繁琐的 CRUD 接口,建好模型直接生成 RESTful API。
    • 自带强大的 Admin 后台,数据管理极度舒适,让我能把 80% 的精力全放在前端交互和产品体验上。

💻 核心技术点与代码实现

1. 极致的微交互:让打卡“爽”起来

为了让用户点下“完成”的那一刻有真实的成就感,我结合了 CSS 动画和原生的触觉反馈:

// 核心打卡逻辑片段
const handleCheckIn = async (task) => {
  // 1. 触发 Haptic 震动反馈 (重震动带来物理按压感)
  uni.vibrateShort({ type: 'heavy' });
  
  // 2. 触发微动效:按钮自身的弹跳 + 全局撒花特效
  task.isBouncing = true; 
  uni.$emit('trigger-particle-confetti'); // 呼叫全局纸屑动画组件
  
  try {
    await api.completeTask(task.id);
    // 3. 检查是否触发徽章解锁
    checkBadgeUnlock(task);
  } catch (e) {
    // 错误处理...
  }
}

在徽章解锁时,我还写了一个 3D 翻牌效果(利用 CSS transform: rotateY 配合 animate-flip-y),让徽章展示更有仪式感。

2. Strapi 关系模型 Hack:如何优雅地记录“徽章解锁时间”?

在后端的开发中,我遇到了一个经典问题:多对多关联表的额外字段怎么存? User 和 Badge 是多对多关系,但在 Strapi 原生模型中,中间表无法轻易添加像 unlockedAt 这样的字段。

我的解法: 直接在 User Schema 中扩展一个轻量级的 JSON 字段 badge_unlock_records

// apps/api/src/extensions/users-permissions/strapi-server.ts
// 扩展 Strapi 默认的 User Schema
export default (plugin) => {
  plugin.contentTypes.user.attributes = {
    ...plugin.contentTypes.user.attributes,
    // 原生多对多关联
    badges: {
      type: 'relation',
      relation: 'manyToMany',
      target: 'api::badge.badge',
    },
    // 💡 Hack: 用 JSON 字段记录具体的解锁元数据
    badge_unlock_records: {
      type: 'json',
      // 数据结构示例: { "badge_id_1": "2023-10-01T12:00:00Z" }
    }
  };
  return plugin;
};

这样既保留了原生关系(方便在 Admin 面板查看),又解决了业务上的元数据存储需求。

3. 社区广场的“真”瀑布流与分页

社区页面的卡片高度是不固定的,传统的 Grid 布局会留下大片空白。我通过维护左右两列的数据数组,实现了原生的瀑布流效果:

// 瀑布流计算核心逻辑
const leftColumn = ref([]);
const rightColumn = ref([]);
let leftHeight = 0;
let rightHeight = 0;

const appendToMasonry = (items) => {
  items.forEach(item => {
    // 估算卡片高度 (基于内容长度)
    const estimatedHeight = calculateHeight(item);
    
    // 哪边矮往哪边塞
    if (leftHeight <= rightHeight) {
      leftColumn.value.push(item);
      leftHeight += estimatedHeight;
    } else {
      rightColumn.value.push(item);
      rightHeight += estimatedHeight;
    }
  });
};

配合 onReachBottom 触底事件,以及自己封装的 wd-loadmore 状态组件,整个信息流刷起来非常丝滑。


🚧 吐血踩坑录:那些让我熬夜的 Bug

全栈开发最怕的就是遇到莫名其妙的兼容性和环境问题。以下这几个坑,价值好几百根头发:

坑一:iOS 13 下 Swiper 圆角失效问题

症状:在旧版 iOS 中,给 <swiper> 设了 border-radiusoverflow: hidden,但里面的图片滑动时依然会无视圆角溢出。 解法:这是 transform 堆叠上下文导致的渲染 Bug。不仅要给 swiper 和 image 都加上圆角类名,还必须强制加上 transform: translateY(0);

<!-- 💡 注意 style 中的 transform 是精髓 -->
<swiper class="rounded-[5px]" style="transform: translateY(0);">
  <swiper-item>
    <image class="rounded-[5px]" src="..." />
  </swiper-item>
</swiper>

坑二:小程序下渐变文字(bg-clip-text)直接消失

症状:想用 Tailwind 的 bg-clip-text text-transparent 做炫酷的渐变文字,结果在微信小程序/iOS上文字直接隐身了。 解法:小程序对 <text> 标签的背景裁剪支持极差。如果要用,必须把 <text> 换成 <view> 标签来写文字,或者老老实实退回到纯色文本。

坑三:Strapi v5 生产环境部署大坑

  1. 插件报错:初始化 v5 时,报 Middleware plugin::email.rateLimit not found解法:手动执行 pnpm add @strapi/email 安装缺失依赖。
  2. RTK Query 压缩报错:打包上线后 Admin 面板报 Cannot read properties of undefined (reading 'merge')。原因是 Vite 压缩把 RTK Query 的方法名压没了。 解法:在 src/admin/vite.config.ts 中关闭 minify,并清理缓存!
// src/admin/vite.config.ts
export default {
  build: {
    minify: false, // 💡 必须设为 false
  },
};

(执行 npx rimraf .strapi build 清除缓存后再 build)


结语

从一行代码都没有,到完整的前后端链路打通;从构思微交互,到处理数据备份(云端同步 + 剪贴板文本导出);这个过程虽然辛苦,但当看到产品真正跑起来,有人开始用它记录生活时,一切都值了。

目前小程序已经上线,欢迎大家在微信搜索 简行一周 体验!

如果你对文章中的技术点感兴趣,或者在用 Uni-app / Strapi 的时候也遇到了头疼的问题,欢迎在评论区留言交流,我一定知无不言!

最后,如果你觉得这篇文章对你有启发,求个点赞 + 收藏,这对我这个独立开发者是莫大的鼓励!🚀

6e051cfd4b1574ab6dc9e48938b739d7.png

uni-app 运行时揭秘:styleIsolation 的转化

2026年4月18日 14:16

背景

大家好,我是 uni-app 的核心开发 前端笨笨狗。本篇是 uni-app 源码分析的第三篇文章,欢迎关注!

前两天有开发者在群里面问我 uni-app 中如何配置 styleIsolation,我告诉了他正确的配置方案,也计划写篇文章揭秘 uni-app 是如何通过运行时将开发者的配置转化为原生微信小程序的配置。

指南

选项式

uni-app 中,开发者可以通过在页面组件中添加 options 配置项来设置 styleIsolation,示例如下:

<script>
export default {
  name: 'MyComp',
  options: {
    styleIsolation: 'isolated'
  },  
}
</script>
<script>
import { defineComponent } from "vue";

export default defineComponent({
  name: "MyComp",
  options: {
    styleIsolation: "isolated",
  },
});
</script>

组合式

在使用组合式 API 的页面组件中,开发者同样可以通过 defineOptions 来设置 styleIsolation,示例如下:

<script setup>
defineOptions({
  name: 'MyComp',
  options: {
    styleIsolation: 'isolated'
  }
})
</script>

原理

createComponent 这个函数大家如果看过 vue 文件的 js 编译产物就一定不会陌生,比如

<script setup>
defineOptions({
  options: {
    styleIsolation: "shared",
  },
});
</script>

会被编译为

const _sfc_main = {
  __name: "comp",
  options: {
    styleIsolation: "shared"
  }
  setup(__props) {
    return (_ctx, _cache) => {
      return {};
    };
  }
};
wx.createComponent(_sfc_main);

也就是 script 中写的代码会被编译成一个对象,这个对象就是 vue 组件的配置项,而微信小程序又不认识 vue 组件的配置项,那么怎么把 vue 组件的配置项转化为微信小程序的配置项呢?这就要靠 uni-app 的运行时了,在 common/vendor.js 中,createComponent 函数会调用 parseComponent 函数来解析 vue 组件的配置项,parseComponent 的返回值就是微信小程序组件的配置项,也就是 Component 构造器 的参数,可以用来构造小程序原生组件。

function initCreateComponent() {
  return function createComponent(vueComponentOptions) {
    return Component(parseComponent(vueComponentOptions));
  };
}

const createComponent = initCreateComponent();
wx.createComponent = createComponent;

parseComponent 解析到页面组件时,会检查组件的 options 配置项,如果发现 styleIsolation,就会将其转化为微信小程序的配置项。

function parseComponent(vueOptions) {
  vueOptions = vueOptions.default || vueOptions;
  const options = {
    multipleSlots: true,
    // styleIsolation: 'apply-shared',
    addGlobalClass: true,
    pureDataPattern: /^uP$/
  };
  // 将开发者在 options 中设置的配置项转化为微信小程序的配置项
  if (vueOptions.options) {
    Object.assign(options, vueOptions.options);
  }
  const mpComponentOptions = {
    options,
    // 省略其他配置项
  };
  return mpComponentOptions;
}

这样一来,开发者在页面组件中设置的 styleIsolation 就会被正确地转化为微信小程序的配置项,从而自由控制样式隔离。

❌
❌