📖 文章概述
TailwindCSS 是一个原子化 CSS 框架,提供低级功能类来构建任意设计。本文深入讲解其核心理念、高级用法、响应式设计和实战应用。
🎯 TailwindCSS 核心理念
传统 CSS vs 原子化 CSS
| 方面 | 传统 CSS | 原子化 CSS |
|---|---|---|
| 选择器 | .button { } | class="px-4 py-2 bg-blue-500" |
| 维护 | 容易重复样式 | 逻辑清晰 |
| 体积 | 大(混合不用代码) | 小(只包含使用的类) |
| 学习曲线 | 平缓 | 需熟悉类名 |
| 性能 | 受限 | 优秀 |
为什么选择 TailwindCSS?
<!-- 传统方式:需要写 CSS -->
<style>
.button {
padding: 0.5rem 1rem;
background-color: #3b82f6;
color: white;
border-radius: 0.375rem;
}
.button:hover {
background-color: #2563eb;
}
</style>
<button class="button">Click Me</button>
<!-- TailwindCSS 方式:直接用类 -->
<button class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">
Click Me
</button>
🚀 快速开始
1. 安装和配置
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
tailwind.config.js 配置
/** @type {import('tailwindcss').Config} */
export default {
content: [
"./index.html",
"./src/**/*.{js,ts,jsx,tsx,vue}",
],
theme: {
extend: {
colors: {
primary: '#3b82f6',
secondary: '#8b5cf6',
},
spacing: {
'7xl': '80rem',
},
fontSize: {
'sm': '0.875rem',
'base': '1rem',
}
},
},
plugins: [],
}
在 CSS 中导入
/* main.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer components {
.btn {
@apply px-4 py-2 rounded font-semibold transition-colors;
}
}
📐 响应式设计
2. 断点系统
<!-- 移动端优先(Mobile First) -->
<!-- 默认样式适用于所有屏幕 -->
<div class="text-sm">
<!-- sm 及以上屏幕 -->
<div class="sm:text-base md:text-lg lg:text-xl xl:text-2xl">
响应式文字大小
</div>
</div>
默认断点表
| 断点 | 最小宽度 | CSS 媒体查询 |
|---|---|---|
| sm | 640px | @media (min-width: 640px) |
| md | 768px | @media (min-width: 768px) |
| lg | 1024px | @media (min-width: 1024px) |
| xl | 1280px | @media (min-width: 1280px) |
| 2xl | 1536px | @media (min-width: 1536px) |
3. 高级响应式模式
<!-- 响应式布局 -->
<div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4">
<div class="bg-white rounded shadow">Card 1</div>
<div class="bg-white rounded shadow">Card 2</div>
<div class="bg-white rounded shadow">Card 3</div>
<div class="bg-white rounded shadow">Card 4</div>
</div>
<!-- 响应式图片 -->
<img
class="w-full h-auto sm:w-1/2 md:w-1/3 lg:w-1/4"
src="image.jpg"
alt="Responsive image"
/>
<!-- 响应式导航 -->
<nav class="flex flex-col md:flex-row justify-between items-center">
<div class="text-2xl font-bold">Logo</div>
<div class="flex flex-col md:flex-row gap-4 mt-4 md:mt-0">
<a href="#" class="hover:text-blue-500">Home</a>
<a href="#" class="hover:text-blue-500">About</a>
<a href="#" class="hover:text-blue-500">Contact</a>
</div>
</nav>
🎨 高级特性
4. 状态变体
<!-- 悬停、焦点、活跃等状态 -->
<button
class="
bg-blue-500 text-white
hover:bg-blue-600
active:bg-blue-700
focus:ring-2 focus:ring-blue-400
disabled:opacity-50 disabled:cursor-not-allowed
transition-colors duration-200
"
>
Interactive Button
</button>
<!-- 暗色模式 -->
<div class="bg-white dark:bg-slate-900 text-black dark:text-white">
Content adapts to dark mode
</div>
5. 群组变体(组件状态传播)
<template>
<!-- 当 group 悬停时,所有 group-hover: 的类都会激活 -->
<div class="group cursor-pointer">
<img class="group-hover:opacity-75" src="image.jpg" />
<p class="group-hover:text-blue-500 group-hover:font-bold">
Hover over me
</p>
</div>
</template>
6. 任意值支持
<!-- 使用方括号传递任意值 -->
<div class="w-[280px] h-[400px]">
Custom dimensions
</div>
<!-- 任意颜色 -->
<div class="bg-[#f472b6] text-[rgb(59, 130, 246)]">
Custom colors
</div>
<!-- 任意 CSS -->
<div class="[transform:rotateX(37deg)_rotateZ(-25deg)]">
3D Transform
</div>
🏗️ 组件化策略
7. @apply 提取组件
/* components.css */
@layer components {
/* 按钮组件 */
.btn {
@apply px-4 py-2 rounded-lg font-semibold transition-all duration-200;
}
.btn-primary {
@apply btn bg-blue-500 text-white hover:bg-blue-600;
}
.btn-secondary {
@apply btn bg-gray-200 text-gray-900 hover:bg-gray-300;
}
.btn-danger {
@apply btn bg-red-500 text-white hover:bg-red-600;
}
/* 卡片组件 */
.card {
@apply bg-white rounded-lg shadow p-6;
}
/* 表单输入 */
.input {
@apply w-full px-3 py-2 border border-gray-300 rounded-md
focus:outline-none focus:ring-2 focus:ring-blue-500
transition-colors;
}
}
使用组件类
<template>
<div>
<button class="btn btn-primary">Primary Button</button>
<button class="btn btn-secondary">Secondary Button</button>
<div class="card">
<h2 class="text-xl font-bold mb-2">Card Title</h2>
<p class="text-gray-600">Card content goes here.</p>
</div>
<input type="text" class="input" placeholder="Enter text..." />
</div>
</template>
8. Vue 组件包装
<!-- Button.vue -->
<script setup lang="ts">
interface Props {
variant?: 'primary' | 'secondary' | 'danger'
size?: 'sm' | 'md' | 'lg'
disabled?: boolean
}
const props = withDefaults(defineProps<Props>(), {
variant: 'primary',
size: 'md'
})
const sizeClasses = {
sm: 'px-2 py-1 text-sm',
md: 'px-4 py-2 text-base',
lg: 'px-6 py-3 text-lg'
}
const variantClasses = {
primary: 'bg-blue-500 hover:bg-blue-600 text-white',
secondary: 'bg-gray-200 hover:bg-gray-300 text-gray-900',
danger: 'bg-red-500 hover:bg-red-600 text-white'
}
</script>
<template>
<button
:class="[
'rounded-lg font-semibold transition-all duration-200',
'disabled:opacity-50 disabled:cursor-not-allowed',
sizeClasses[size],
variantClasses[variant]
]"
:disabled="disabled"
>
<slot></slot>
</button>
</template>
<!-- 使用 -->
<Button variant="primary" size="lg">Large Primary Button</Button>
<Button variant="danger" size="sm" :disabled="true">Disabled</Button>
🎯 实战:完整登录页面
<template>
<div class="min-h-screen bg-gradient-to-br from-blue-500 to-purple-600 flex items-center justify-center p-4">
<div class="w-full max-w-md bg-white rounded-lg shadow-2xl p-8">
<!-- 标题 -->
<h1 class="text-3xl font-bold text-center text-gray-900 mb-2">
欢迎登录
</h1>
<p class="text-center text-gray-500 mb-8">
输入您的凭证以继续
</p>
<!-- 表单 -->
<form @submit.prevent="handleLogin" class="space-y-4">
<!-- 邮箱字段 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
邮箱地址
</label>
<input
v-model="email"
type="email"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500
focus:border-transparent transition-colors"
placeholder="your@email.com"
/>
</div>
<!-- 密码字段 -->
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
密码
</label>
<input
v-model="password"
type="password"
class="w-full px-4 py-2 border border-gray-300 rounded-lg
focus:outline-none focus:ring-2 focus:ring-blue-500
focus:border-transparent transition-colors"
placeholder="••••••••"
/>
</div>
<!-- 记住我 + 忘记密码 -->
<div class="flex items-center justify-between text-sm">
<label class="flex items-center">
<input v-model="rememberMe" type="checkbox" class="w-4 h-4" />
<span class="ml-2 text-gray-600">记住我</span>
</label>
<a href="#" class="text-blue-500 hover:text-blue-600">
忘记密码?
</a>
</div>
<!-- 提交按钮 -->
<button
type="submit"
:disabled="isLoading"
class="w-full py-2 px-4 bg-gradient-to-r from-blue-500 to-purple-600
text-white font-semibold rounded-lg
hover:from-blue-600 hover:to-purple-700
disabled:opacity-50 disabled:cursor-not-allowed
transition-all duration-200"
>
{{ isLoading ? '登录中...' : '登录' }}
</button>
</form>
<!-- 分隔线 -->
<div class="relative my-6">
<div class="absolute inset-0 flex items-center">
<div class="w-full border-t border-gray-300"></div>
</div>
<div class="relative flex justify-center text-sm">
<span class="px-2 bg-white text-gray-500">或</span>
</div>
</div>
<!-- 社交登录 -->
<div class="grid grid-cols-2 gap-4">
<button
type="button"
class="py-2 px-4 border border-gray-300 rounded-lg
hover:bg-gray-50 transition-colors text-sm font-medium"
>
Google
</button>
<button
type="button"
class="py-2 px-4 border border-gray-300 rounded-lg
hover:bg-gray-50 transition-colors text-sm font-medium"
>
WeChat
</button>
</div>
<!-- 注册链接 -->
<p class="text-center text-gray-600 mt-6">
还没有账号?
<a href="#" class="text-blue-500 hover:text-blue-600 font-semibold">
立即注册
</a>
</p>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
const email = ref('')
const password = ref('')
const rememberMe = ref(false)
const isLoading = ref(false)
const handleLogin = async () => {
isLoading.value = true
try {
// 模拟 API 调用
await new Promise(resolve => setTimeout(resolve, 2000))
console.log('Login successful', { email: email.value })
} finally {
isLoading.value = false
}
}
</script>
⚡ 性能优化
9. 生产优化
// tailwind.config.js
export default {
content: [
"./src/**/*.{js,ts,jsx,tsx,vue}",
// ✅ 只扫描需要的文件
// ❌ 避免扫描 node_modules
],
// 只包含使用的颜色
theme: {
colors: {
white: '#ffffff',
black: '#000000',
blue: {
50: '#eff6ff',
500: '#3b82f6',
900: '#1e3a8a',
}
}
}
}
10. 文件大小优化
/* 生产 CSS 大小对比 */
/* 无优化: 800KB (所有 Tailwind 类) */
/* 智能扫描: 45KB (只包含使用的类) */
/* 压缩后: 8KB (gzip 压缩) */
/* 优化技巧 */
@tailwind base;
@tailwind components;
@tailwind utilities;
/* 删除未使用的动画 */
@layer utilities {
@keyframes none { }
}
🐛 常见问题解决
问题 1:类名没有生效
// ❌ 错误:类名是动态的
const variant = 'primary'
const className = `btn-${variant}` // Tailwind 扫描不到
// ✅ 正确:使用静态字符串
const classList = {
'btn-primary': 'btn bg-blue-500',
'btn-secondary': 'btn bg-gray-500'
}
const className = classList[variant]
问题 2:样式优先级冲突
<!-- ❌ 问题:两个类冲突 -->
<div class="bg-blue-500 bg-red-500">
Will use bg-red-500
</div>
<!-- ✅ 解决:使用 !important 或重新组织 -->
<div class="bg-blue-500" :class="dynamicBg">
<!-- dynamicBg 可控制背景 -->
</div>
<!-- 或使用 @layer 管理优先级 -->
<style>
@layer utilities {
.bg-custom {
@apply bg-blue-500 !important;
}
}
</style>
问题 3:自定义样式与 Tailwind 冲突
/* ❌ 问题:重置了 Tailwind */
body {
margin: 0;
padding: 0;
}
/* ✅ 解决:在 @layer base 中定义 */
@layer base {
body {
@apply m-0 p-0;
}
}
📊 性能对比
| 场景 | 传统 CSS | TailwindCSS |
|---|---|---|
| 构建时间 | 2-5s | 1-3s |
| CSS 文件大小 | 50-200KB | 8-15KB |
| 开发速度 | 中等 | 高 |
| 维护难度 | 中等 | 低 |
| 学习曲线 | 平缓 | 陡峭 |
🎓 最佳实践总结
DO ✅
<!-- 1. 使用组件提取重复模式 -->
<button class="btn btn-primary">Click</button>
<!-- 2. 合理使用响应式前缀 -->
<div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4">
<!-- 3. 利用群组变体 -->
<div class="group hover:bg-gray-100">
<p class="group-hover:text-blue-500">Content</p>
</div>
<!-- 4. 使用任意值处理特殊需求 -->
<div class="w-[280px] h-[400px]">
<!-- 5. 在 @layer 中组织代码 -->
@layer components { .card { ... } }
DON'T ❌
<!-- 1. 避免动态类名 -->
<div :class="`col-${span}`"> <!-- 扫描不到 -->
<!-- 2. 避免过度使用 @apply -->
<!-- 应该只用于真正可复用的模式 -->
<!-- 3. 避免在 HTML 中过度嵌套 -->
<div class="flex flex-col md:flex-row lg:flex-row">
<!-- 过度复杂 -->
<!-- 4. 不要禁用 PurgeCSS/Scaning -->
<!-- 这会导致包体积巨大 -->
📚 扩展资源
总结
TailwindCSS 通过原子化 CSS 理念提供了:
- 快速开发:无需切换到 CSS 文件
- 小包体积:只包含使用的样式
- 易于维护:样式与 HTML 紧密关联
- 强大功能:响应式、状态变体、任意值等
掌握 TailwindCSS,能够显著提升前端开发效率和项目质量!