从 v4 迁移到 v5
v5 最大的变化不是 API 名字,而是职责边界变了。
在 v4 里,很多项目会先让 Tailwind 官方插件生成 CSS,再由 weapp-tailwindcss 做小程序转义。v5 改成由 WeappTailwindcss 接管 Tailwind CSS 生成链路:同一份 Tailwind 输入,在小程序端生成小程序可用的选择器和样式;在 H5/Web 端生成浏览器可用的 Tailwind CSS。
所以迁移时别急着到处改类名。先把构建链路理顺。
先看结论
大多数项目要做这几件事:
- 升级
weapp-tailwindcss到 v5。 - 删除
postinstall: "weapp-tw patch"。 - 小程序构建里移除 Tailwind 官方生成插件。
- 注册
WeappTailwindcss。 - 检查 Tailwind CSS 3/4 的入口和扫描范围。
- H5/Web 构建不要再简单禁用
WeappTailwindcss;Taro 项目要同时覆盖 H5 和小程序构建。 - 跑一次小程序构建,再测一行新增任意值 class。
不用强制一起升级到 Tailwind CSS 4。weapp-tailwindcss@5 同时支持 Tailwind CSS 3 和 4。存量项目想稳一点,可以先保留 Tailwind CSS 3,只迁移 weapp-tailwindcss。
1. 升级依赖
继续使用 Tailwind CSS 3:
pnpm add -D weapp-tailwindcss@5 tailwindcss@3
使用 Tailwind CSS 4:
pnpm add -D weapp-tailwindcss@5 tailwindcss@4
如果这些包只服务小程序构建,可以删掉:
pnpm remove tailwindcss-patch @tailwindcss/vite @tailwindcss/postcss
如果同一个仓库里还有独立的 Web 应用,Web 应用可以继续使用 @tailwindcss/vite 或 @tailwindcss/postcss。限制只针对同一次小程序构建:不要让官方 Tailwind 插件和 WeappTailwindcss 同时生成 Tailwind CSS。
2. 删除安装后 patch
删掉 package.json 里的旧脚本:
{
"scripts": {
// 删除
"postinstall": "weapp-tw patch"
}
}
v5 的生成链路会在构建时处理 Tailwind 运行时,不需要安装后 patch。保留这个脚本通常不会带来收益,排查问题时反而会多一个干扰项。
3. 清理 Tailwind 官方生成插件
小程序构建里不要再注册这些插件:
module.exports = {
plugins: {
tailwindcss: {},
'@tailwindcss/postcss': {},
},
}
import tailwindcss from '@tailwindcss/vite'
export default defineConfig({
plugins: [
tailwindcss(),
],
})
业务 PostCSS 插件可以留,比如 autoprefixer、框架自己的 PostCSS 插件、压缩插件等。要删的是 Tailwind CSS 的生成入口。
4. 检查 Tailwind CSS 入口
Tailwind CSS 3 继续使用 @tailwind 指令:
@tailwind base;
@tailwind components;
@tailwind utilities;
tailwind.config.* 里要覆盖模板和脚本文件:
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./src/**/*.{html,js,ts,jsx,tsx,vue,wxml,axml,ttml,mpx,uts,uvue}',
'!./src/uni_modules/**/*',
'!./node_modules/**/*',
'!./dist/**/*',
'!./unpackage/**/*',
],
}
Tailwind CSS 4 使用 CSS-first 入口:
@import "tailwindcss";
@source "./**/*.{html,js,ts,jsx,tsx,vue,wxml,axml,ttml,mpx,uts,uvue}";
@source not "./uni_modules";
@source not "../node_modules";
@source not "../dist";
@source not "../unpackage";
Tailwind CSS 4 的入口请放在纯 .css 文件里。不要把 @import "tailwindcss" 直接写到 scss、less、sass 文件中。业务预处理样式可以引入这个 CSS 文件,但 Tailwind 入口本身最好保持简单。
5. 注册 WeappTailwindcss
Vite 项目:
import { defineConfig } from 'vite'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
export default defineConfig({
plugins: [
WeappTailwindcss({
rem2rpx: true,
}),
],
})
uni-app Vite 项目里,放在 uni() 后面:
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
export default defineConfig({
plugins: [
uni(),
WeappTailwindcss({
rem2rpx: true,
}),
],
})
Webpack 项目:
const { WeappTailwindcss } = require('weapp-tailwindcss/webpack')
module.exports = {
plugins: [
new WeappTailwindcss({
rem2rpx: true,
}),
],
}
Taro、Mpx、uni-app Webpack 这类项目的挂载位置各不相同,按对应框架页复制完整写法即可。迁移时要抓住一个点:Tailwind 生成交给 WeappTailwindcss,不要再从 PostCSS 或官方 Vite 插件生成第二份 Tailwind CSS。
Taro 迁移要覆盖 H5 和小程序
Taro 迁移时不要只在小程序链路注册插件。v5 的 WeappTailwindcss 会根据 TARO_ENV=h5 自动切到 Web 目标,所以 H5 构建也应该保留插件。
Webpack 项目里,mini.webpackChain 和 h5.webpackChain 都注册一次:
const path = require('node:path')
const { WeappTailwindcss } = require('weapp-tailwindcss/webpack')
const weappTailwindcssOptions = {
rem2rpx: true,
cssEntries: [
path.resolve(__dirname, '../src/app.css'),
],
}
function registerWeappTailwindcss(chain) {
chain.merge({
plugin: {
install: {
plugin: WeappTailwindcss,
args: [weappTailwindcssOptions],
},
},
})
}
module.exports = {
mini: {
webpackChain(chain) {
registerWeappTailwindcss(chain)
},
},
h5: {
webpackChain(chain) {
registerWeappTailwindcss(chain)
},
},
}
Vite 项目里,把插件放在 config/index 的 compiler.vitePlugins。不要只写单独的 vite.config.ts,因为它通常只在小程序运行时被加载,H5 不会走这份配置。
import path from 'node:path'
import type { Plugin } from 'vite'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
export default {
compiler: {
type: 'vite',
vitePlugins: [
WeappTailwindcss({
rem2rpx: true,
cssEntries: [
path.resolve(__dirname, '../src/app.css'),
],
}),
] as Plugin[],
},
}
6. 什么时候写 cssEntries
普通 Vite 项目通常不用写。入口 CSS 被 Vite 引入后,v5 会尝试自动识别。
这些情况建议显式写:
- Tailwind CSS 4 项目里有多个 CSS-first 入口。
- 入口 CSS 没有被框架直接引入。
- Webpack、Gulp、自定义构建。
- 构建日志提示没有找到 Tailwind CSS 入口。
- uni-app x + Tailwind CSS 4,尤其是 HBuilderX 项目。
import path from 'node:path'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
WeappTailwindcss({
rem2rpx: true,
cssEntries: [
path.resolve(__dirname, 'src/app.css'),
],
})
cssEntries 写绝对路径,指向包含 @import "tailwindcss" 或 @tailwind 指令的 CSS 入口。多个入口就都写进去。
7. H5/Web 构建不要一刀切禁用
v4 项目里经常有这段:
const isH5 = process.env.UNI_PLATFORM === 'h5'
WeappTailwindcss({
disabled: isH5,
})
迁移到 v5 后,先删掉这种 H5/Web 禁用逻辑:
WeappTailwindcss({
rem2rpx: true,
})
v5 会根据常见环境变量自动切换目标。例如 UNI_PLATFORM=h5/app/app-plus、UNI_UTS_PLATFORM=h5/web/web-*、TARO_ENV=h5、MPX_CLI_MODE=web、MPX_CURRENT_TARGET_MODE=web 会走 Web 目标,输出浏览器可用的 Tailwind CSS,而不是小程序转义后的选择器。UNI_UTS_PLATFORM=app-android/app-ios/app-harmony 这类 uni-app x 原生 App 目标不会被当成 Web,也不需要新增 target: 'app'。
如果你在自定义构建里需要明确指定,也可以写:
WeappTailwindcss({
generator: {
target: 'web',
},
})
disabled 仍然有用,但它适合“完全跳过插件”的构建,例如某些 RN、Harmony 或独立原生构建。对 uni-app、uni-app x、Taro、Mpx 的 H5/Web 目标,通常不需要禁用。
8. uni-app x 和 HBuilderX
uni-app x 建议使用 uniAppX 预设:
import path from 'node:path'
import { defineConfig } from 'vite'
import uni from '@dcloudio/vite-plugin-uni'
import { uniAppX } from 'weapp-tailwindcss/presets'
import { WeappTailwindcss } from 'weapp-tailwindcss/vite'
export default defineConfig({
plugins: [
uni(),
WeappTailwindcss(
uniAppX({
base: __dirname,
cssEntries: [
path.resolve(__dirname, 'main.css'),
],
rem2rpx: true,
}),
),
],
})
Tailwind CSS 3 的 uni-app x 项目可以不写 cssEntries,前提是入口能被构建链路识别。Tailwind CSS 4 建议显式写 cssEntries,指向纯 CSS 入口。
HBuilderX 本地运行时可以按目标分别验证:
pnpm dev:mp-weixin
pnpm dev:android:emulator
pnpm dev:ios:simulator
如果用仓库里的本地 e2e,则是:
pnpm e2e:hbuilderx:local:app
pnpm e2e:hbuilderx:local:android
pnpm e2e:hbuilderx:local:ios
iOS 模拟器需要完整 Xcode。xcode-select -p 应指向 /Applications/Xcode.app/Contents/Developer,xcodebuild -checkFirstLaunchStatus 应返回 0。
9. 验证迁移是否真的成功
只看构建成功还不够。迁移后至少测一条新增类。
在页面里临时写:
<view class="mt-[11px] w-[173px] rounded-[13px] bg-[#102938] p-4 text-[#f7fbff]">
v5 check
</view>
然后跑对应目标:
pnpm dev:mp-weixin
H5/Web 项目再跑一次 HMR:启动 dev server 后,连续改几次任意值 class,确认 CSS 会刷新。例如把 bg-[#102938] 依次改成 bg-[#0f5132]、bg-[#7c2d12]、bg-[#4338ca]。如果第一遍有样式,后续新增任意值 class 没有样式,通常是入口扫描或 HMR 依赖没有接上。
App 端不要只看 manifest.json。uni-app x 的 Android 产物可以检查 .uvue/app-android/** 里是否出现转换后的类名;iOS 在 HBuilderX 不同版本下输出位置会不同,可以检查 app-ios/app-service.js 或 unpackage/cache/.app-ios/sourcemap/app-service.js.map。例如:
bg-[#102938] -> bg-_b_h102938_B
text-[#f7fbff] -> text-_b_hf7fbff_B
w-[173px] -> w-_b173px_B
常见问题
| 现象 | 优先检查 |
|---|---|
| 完全没有样式 | CSS 入口是否被构建器引入,或是否需要配置 cssEntries |
| Tailwind CSS 3 类名没生成 | tailwind.config.* 的 content 是否包含页面、组件和脚本 |
| Tailwind CSS 4 类名没生成 | CSS 入口里的 @source 是否覆盖源码,是否排除了 dist / unpackage |
| 样式重复或顺序怪 | 小程序构建里是否还注册了 tailwindcss / @tailwindcss/postcss / @tailwindcss/vite |
| 安装后还在 patch | package.json 是否还保留 postinstall: "weapp-tw patch" |
| JS 字符串里的类名没转 | 这个类是否先被 Tailwind 扫描到了;v5 不会猜普通字符串 |
| H5 样式变成小程序转义类 | 检查 Web 目标环境变量,必要时显式设置 generator.target: 'web' |
| uni-app x App 样式缺失 | 检查是否启用 uniAppX 预设,Tailwind CSS 4 是否配置了 cssEntries |
迁移后可以删掉的东西
tailwindcss-patchpostinstall: "weapp-tw patch"- 小程序构建里的
@tailwindcss/vite - 小程序构建里的
@tailwindcss/postcss - 小程序构建里的
tailwindcssPostCSS 插件 - 只为 H5/Web 写的
disabled: isH5逻辑
不要急着删业务 PostCSS 插件、框架插件、Tailwind 配置文件。它们是否保留,取决于项目本身。