样式
VPT 可同时使用 Tailwind CSS v4、CSS Modules 和普通 CSS。样式也支持热更新,修改后立刻在开发者工具中能看到效果,不会丢失页面状态。
| 写法 | 适合场景 | 作用范围 |
|---|---|---|
| Tailwind CSS | 直接在 JSX 中组合样式 | 全局工具类 |
| CSS Modules | 组件内部的复杂样式、动画和伪元素 | 类名自动隔离 |
| 普通 CSS | 页面基础样式、重置样式和共享规则 | 全局 |
Tailwind CSS v4
Section titled “Tailwind CSS v4”create-vite-taro 默认启用 Tailwind CSS v4。该功能由 weapp-tailwindcss 支持,感谢作者开源。
默认的 src/app.css 包含:
@import "tailwindcss/theme.css";@import "tailwindcss/preflight.css";@import "tailwindcss/utilities.css";
@source "./";- 三个
@import分别引入主题、基础规则和工具类。 @source "./"扫描src中使用的 Tailwind 类名。- vpt 为微信、支付宝、抖音和 Web 分别生成可用的目标样式。
在 JSX 中直接使用 className:
import { Text, View } from 'virtual:taro/components'
export function AccountSummary() { return ( <View className="rounded-[24px] bg-emerald-950 p-5 shadow-xl"> <Text className="text-sm font-medium text-emerald-100">本月余额</Text> <Text className="mt-2 block text-3xl font-bold text-white">¥ 8,260</Text> </View> )}任意值、颜色透明度和常见状态变体会经过同一套目标转换。
保持类名完整
Section titled “保持类名完整”Tailwind 根据源码中出现的完整类名生成样式。不要用字符串片段拼出类名:
// 不要这样写:源码中不存在完整的 bg-*-500 类名。const className = `bg-${tone}-500`将所有可能的类名写成完整字符串:
const toneClass = active ? 'bg-emerald-600' : 'bg-slate-300'
return <View className={`rounded-xl p-4 ${toneClass}`} />从 @source "./" 以外的位置读取类名时,使用 Tailwind v4 的 @source 指令显式添加扫描目录。动态尺寸不能写成 `w-[${width}px]`:Tailwind 扫描不到完整类名;请使用动态行内样式。
推荐通过 @theme 设定项目的设计令牌(tokens):
@theme { --color-brand-500: #2a9d5b; --color-brand-950: #123a25; --font-display: Georgia, serif;}之后可以使用 bg-brand-500、text-brand-950 和 font-display。随着项目扩大,相同工具类会在不同组件中复用,样式体积主要随实际使用的规则增长;统一的颜色、间距、字号和圆角令牌也能减少任意值与重复 CSS。
CSS Modules
Section titled “CSS Modules”若遇到复杂样式,推荐使用 CSS Modules。CSS Modules 会生成唯一的 class name,杜绝样式冲突和覆盖。
文件名使用 Vite 的 *.module.css 约定:
.card { padding: 24px; border: 1px solid rgba(23, 103, 61, 0.16); border-radius: 20px; background: white;}
.title { color: #17673d; font-size: 28px; font-weight: 700;}小程序构建都会把普通 CSS 中的 px 和 rem 转换为 rpx。
在组件中导入生成的类名映射:
import { Text, View } from 'virtual:taro/components'import styles from './profile-card.module.css'
export function ProfileCard() { return ( <View className={styles.card}> <Text className={styles.title}>个人资料</Text> </View> )}与 Tailwind 一起使用
Section titled “与 Tailwind 一起使用”Tailwind 工具类可以与 CSS Modules 生成的类名组合:
<View className={`${styles.card} flex items-center gap-3 p-4`}> <Text className={styles.title}>个人资料</Text></View>Sass、Less 和 Stylus 也支持 Modules 文件约定,例如 profile-card.module.scss。
普通 CSS
Section titled “普通 CSS”普通 CSS 不会隔离选择器,适合放置全局重置、字体和根元素规则。建议将这些规则集中在 app.css 中。
*,*::before,*::after { box-sizing: border-box;}
page,body { margin: 0; background: #f8faf7; color: #173e29;}page 是小程序页面根元素,body 是 Web 文档根元素。
目标专用规则可以使用条件指令:
/* #ifdef wx */.wx-only-surface { padding-bottom: env(safe-area-inset-bottom);}/* #endif */
/* #ifdef h5 */.web-only-surface { min-height: 100dvh;}/* #endif */Sass、Less 和 Stylus
Section titled “Sass、Less 和 Stylus”VPT 仍支持 .scss、.sass、.less 和 .styl,但不推荐新项目使用。新项目请优先使用 Tailwind,复杂组件样式使用 CSS Modules。迁移现有预处理器样式时,需要安装对应的依赖,例如:
npm install --save-dev sass然后像普通 CSS 一样导入:
import './profile-card.scss'PostCSS 配置使用 Vite 的 css.postcss 或项目根目录中的 PostCSS 配置文件。
运行 npm run dev:... 时,以下修改会触发样式热更新:
- 修改已导入的 CSS、CSS Modules 或预处理器文件。
- 在 JSX 中新增、替换或删除 Tailwind 类名。
- 添加或删除组件对样式文件的导入。
样式如何进入应用
Section titled “样式如何进入应用”样式文件必须由应用模块图导入。全局样式通常从 src/app.tsx 导入:
import type { PropsWithChildren } from 'react'import './app.css'
function App({ children }: PropsWithChildren) { return children}
export default App页面或组件也可以导入自己的样式;未被任何应用、页面或组件导入的样式不会进入构建结果。
小程序与 Web 的区别
Section titled “小程序与 Web 的区别”Web 目标沿用 Vite 的 CSS 行为。小程序通过 build.cssCodeSplit 控制样式范围,开发和生产构建使用同一设置:
| 设置 | 效果 |
|---|---|
true(默认) | App 导入的样式全局生效;页面及其组件导入的其他样式仅在使用它们的页面生效。 |
false | 应用导入的所有样式全局生效,普通选择器可能影响其他页面。 |
需要全局共享的样式,建议从 src/app.tsx 导入。也可以在 vite.config.ts 中关闭拆分:
import { defineConfig } from 'vite'
export default defineConfig({ build: { cssCodeSplit: false }})两种设置都支持普通 CSS、CSS Modules、预处理器和 Tailwind;CSS Modules 的类名隔离不变。从 App 导入的 Tailwind 工具类始终全局可用。
完整示例见 page-styles-demo。
尺寸单位与动态样式
Section titled “尺寸单位与动态样式”CSS 中的尺寸
Section titled “CSS 中的尺寸”小程序(微信、支付宝、抖音)默认按 750px 设计稿转换应用 CSS:100px → 100rpx、1rem → 32rpx。已有的 rpx 保持不变;原生组件的样式不参与转换。
动态行内样式
Section titled “动态行内样式”行内样式不经过 CSS 转换。需要固定的 CSS 像素时,在 style 中使用带 px 单位的字符串:
<View style={{ width: '100px', borderBottom: '1px solid #ddd' }} />需要按设计稿换算尺寸时,小程序中可以使用 Taro.pxTransform(),默认 100 → '100rpx':
import Taro from 'virtual:taro/api'import { View } from 'virtual:taro/components'
export function SizedView({ width }: { width: number }) { return <View style={{ width: Taro.pxTransform(width) }} />}Web 目标
Section titled “Web 目标”Web 默认保留 CSS 中的 px,不会自动将其转换为 rem。若要在 Web 中使用 Taro.pxTransform(),须先初始化,并让 CSS 与动态样式使用相同单位。rem 的实际尺寸取决于根字号;这里推荐使用直接按视口宽度计算的 vw。
使用其他设计稿宽度
Section titled “使用其他设计稿宽度”以 375px 设计稿为例,为小程序和 Web 配置同一套自适应尺寸:
-
安装转换插件:
终端窗口 pnpm add -D postcss-rule-unit-converter -
在
vite.config.ts现有的defineConfig回调中,取得target后加入css字段,保留其他配置:import unitConverter, { presets } from 'postcss-rule-unit-converter'const css = {postcss: {plugins: [unitConverter({rules: target === 'h5'? [presets.pxToVw({ viewportWidth: 375 }),presets.rpxToVw({ viewportWidth: 375, ratio: 1 })]: [presets.pxToRpx({ ratio: 2 })]})]}}// 将 css 加入原有的 return 对象。 -
在首次调用
Taro.pxTransform()前,用相同的设计宽度初始化运行时:import Taro from 'virtual:taro/api'Taro.initPxTransform({designWidth: 375,deviceRatio: { 375: 2 },targetUnit: import.meta.env.VITE_VPT_TARGET === 'h5' ? 'vw' : 'rpx'})
H5 中已有的 rpx 按相同设计稿比例换算为 vw。100px 在小程序中成为 200rpx,在 Web 中成为 26.66667vw;Taro.pxTransform(100) 也会返回对应单位。两者在 375px 宽的屏幕上均相当于 100px。
Tailwind 类名没有生成
Section titled “Tailwind 类名没有生成”确认引入 Tailwind 的 CSS 文件仍由 app.tsx 导入,并保留 @source "./"。同时检查类名是否以完整字符串出现在扫描范围内。
CSS Modules 导入没有类型
Section titled “CSS Modules 导入没有类型”确认应用的 TypeScript 配置包含:
{ "compilerOptions": { "types": ["vite/client", "vite-plugin-taro/client"] }}小程序与 Web 显示不同
Section titled “小程序与 Web 显示不同”检查所用属性和选择器是否受当前 WXSS、ACSS 或 TTSS 方言支持,并确认尺寸换算、默认元素样式和 Tailwind Preflight。不要依赖开发者工具的 PostCSS;样式转换由 vpt 完成。