第 7 章:框架集成
2025/9/1大约 11 分钟
第 7 章:框架集成
学习目标
- 在 React 项目中集成 Tailwind CSS
- 在 Vue.js 项目中使用 Tailwind CSS
- 与 Next.js 和 Nuxt.js 的集成配置
- 学会在不同构建工具中的最佳实践
React 项目集成
Create React App 集成
# 创建 React 应用
npx create-react-app my-tailwind-app
cd my-tailwind-app
# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./src/**/*.{js,jsx,ts,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
/* src/index.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
Vite + React 集成
# 创建 Vite React 应用
npm create vite@latest my-tailwind-app -- --template react
cd my-tailwind-app
npm install
# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
export default defineConfig({
plugins: [react()],
css: {
postcss: './postcss.config.js',
},
})
React 组件示例
// components/Button.jsx
import React from 'react'
import { cn } from '../utils/cn'
const Button = React.forwardRef(({
className,
variant = 'default',
size = 'default',
asChild = false,
...props
}, ref) => {
const Comp = asChild ? "span" : "button"
return (
<Comp
className={cn(
"inline-flex items-center justify-center rounded-md text-sm font-medium transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:pointer-events-none disabled:opacity-50",
{
"bg-primary text-primary-foreground shadow hover:bg-primary/90": variant === 'default',
"bg-destructive text-destructive-foreground shadow-sm hover:bg-destructive/90": variant === 'destructive',
"border border-input bg-background shadow-sm hover:bg-accent hover:text-accent-foreground": variant === 'outline',
"bg-secondary text-secondary-foreground shadow-sm hover:bg-secondary/80": variant === 'secondary',
"hover:bg-accent hover:text-accent-foreground": variant === 'ghost',
"text-primary underline-offset-4 hover:underline": variant === 'link',
},
{
"h-9 px-4 py-2": size === 'default',
"h-8 rounded-md px-3 text-xs": size === 'sm',
"h-10 rounded-md px-8": size === 'lg',
"h-9 w-9": size === 'icon',
},
className
)}
ref={ref}
{...props}
/>
)
})
Button.displayName = "Button"
export { Button }
// components/Card.jsx
import React from 'react'
import { cn } from '../utils/cn'
const Card = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn(
"rounded-xl border bg-card text-card-foreground shadow",
className
)}
{...props}
/>
))
Card.displayName = "Card"
const CardHeader = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex flex-col space-y-1.5 p-6", className)}
{...props}
/>
))
CardHeader.displayName = "CardHeader"
const CardTitle = React.forwardRef(({ className, ...props }, ref) => (
<h3
ref={ref}
className={cn("font-semibold leading-none tracking-tight", className)}
{...props}
/>
))
CardTitle.displayName = "CardTitle"
const CardDescription = React.forwardRef(({ className, ...props }, ref) => (
<p
ref={ref}
className={cn("text-sm text-muted-foreground", className)}
{...props}
/>
))
CardDescription.displayName = "CardDescription"
const CardContent = React.forwardRef(({ className, ...props }, ref) => (
<div ref={ref} className={cn("p-6 pt-0", className)} {...props} />
))
CardContent.displayName = "CardContent"
const CardFooter = React.forwardRef(({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex items-center p-6 pt-0", className)}
{...props}
/>
))
CardFooter.displayName = "CardFooter"
export { Card, CardHeader, CardFooter, CardTitle, CardDescription, CardContent }
// App.jsx - 使用示例
import React from 'react'
import { Button } from './components/Button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from './components/Card'
function App() {
return (
<div className="min-h-screen bg-gray-50 p-8">
<div className="max-w-2xl mx-auto space-y-8">
<h1 className="text-3xl font-bold text-center text-gray-900">
React + Tailwind CSS
</h1>
{/* 按钮示例 */}
<div className="flex flex-wrap gap-4">
<Button>默认按钮</Button>
<Button variant="outline">轮廓按钮</Button>
<Button variant="destructive">危险按钮</Button>
<Button variant="ghost">幽灵按钮</Button>
<Button variant="link">链接按钮</Button>
</div>
{/* 卡片示例 */}
<Card>
<CardHeader>
<CardTitle>项目标题</CardTitle>
<CardDescription>
这是一个使用 Tailwind CSS 构建的 React 组件示例
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-gray-600">
卡片内容区域,展示主要信息...
</p>
</CardContent>
</Card>
</div>
</div>
)
}
export default App
工具函数
// utils/cn.js
import { clsx } from "clsx"
import { twMerge } from "tailwind-merge"
export function cn(...inputs) {
return twMerge(clsx(inputs))
}
Vue.js 项目集成
Vue CLI 集成
# 创建 Vue 应用
vue create my-tailwind-app
cd my-tailwind-app
# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
Vite + Vue 集成
# 创建 Vite Vue 应用
npm create vue@latest my-tailwind-app
cd my-tailwind-app
npm install
# 安装 Tailwind CSS
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
"./index.html",
"./src/**/*.{vue,js,ts,jsx,tsx}",
],
theme: {
extend: {},
},
plugins: [],
}
Vue 组件示例
<!-- components/BaseButton.vue -->
<template>
<button
:class="buttonClasses"
:disabled="disabled"
@click="handleClick"
>
<slot />
</button>
</template>
<script>
import { computed } from 'vue'
export default {
name: 'BaseButton',
props: {
variant: {
type: String,
default: 'primary',
validator: (value) => ['primary', 'secondary', 'outline', 'ghost'].includes(value)
},
size: {
type: String,
default: 'md',
validator: (value) => ['sm', 'md', 'lg'].includes(value)
},
disabled: {
type: Boolean,
default: false
}
},
emits: ['click'],
setup(props, { emit }) {
const buttonClasses = computed(() => {
const baseClasses = [
'inline-flex',
'items-center',
'justify-center',
'font-medium',
'rounded-lg',
'transition-all',
'duration-200',
'focus:outline-none',
'focus:ring-2',
'focus:ring-offset-2',
'disabled:opacity-50',
'disabled:cursor-not-allowed'
]
// 变体样式
const variantClasses = {
primary: [
'bg-blue-600',
'text-white',
'hover:bg-blue-700',
'focus:ring-blue-500'
],
secondary: [
'bg-gray-200',
'text-gray-900',
'hover:bg-gray-300',
'focus:ring-gray-500'
],
outline: [
'border',
'border-gray-300',
'text-gray-700',
'bg-white',
'hover:bg-gray-50',
'focus:ring-blue-500'
],
ghost: [
'text-gray-700',
'hover:bg-gray-100',
'focus:ring-gray-500'
]
}
// 尺寸样式
const sizeClasses = {
sm: ['px-3', 'py-1.5', 'text-sm'],
md: ['px-4', 'py-2', 'text-base'],
lg: ['px-6', 'py-3', 'text-lg']
}
return [
...baseClasses,
...variantClasses[props.variant],
...sizeClasses[props.size]
]
})
const handleClick = (event) => {
if (!props.disabled) {
emit('click', event)
}
}
return {
buttonClasses,
handleClick
}
}
}
</script>
<!-- components/BaseCard.vue -->
<template>
<div class="bg-white rounded-lg shadow-md border border-gray-200 overflow-hidden">
<div v-if="$slots.header" class="px-6 py-4 border-b border-gray-200 bg-gray-50">
<slot name="header" />
</div>
<div class="p-6">
<slot />
</div>
<div v-if="$slots.footer" class="px-6 py-4 bg-gray-50 border-t border-gray-200">
<slot name="footer" />
</div>
</div>
</template>
<script>
export default {
name: 'BaseCard'
}
</script>
<!-- App.vue -->
<template>
<div class="min-h-screen bg-gray-50 p-8">
<div class="max-w-2xl mx-auto space-y-8">
<h1 class="text-3xl font-bold text-center text-gray-900">
Vue.js + Tailwind CSS
</h1>
<!-- 按钮示例 -->
<div class="flex flex-wrap gap-4">
<BaseButton @click="handleClick">默认按钮</BaseButton>
<BaseButton variant="secondary" @click="handleClick">次要按钮</BaseButton>
<BaseButton variant="outline" @click="handleClick">轮廓按钮</BaseButton>
<BaseButton variant="ghost" @click="handleClick">幽灵按钮</BaseButton>
</div>
<!-- 卡片示例 -->
<BaseCard>
<template #header>
<h3 class="text-lg font-semibold text-gray-900">项目标题</h3>
</template>
<p class="text-gray-600">
这是一个使用 Tailwind CSS 构建的 Vue.js 组件示例。
</p>
<template #footer>
<div class="flex justify-end space-x-3">
<BaseButton variant="outline" size="sm">取消</BaseButton>
<BaseButton size="sm">确认</BaseButton>
</div>
</template>
</BaseCard>
<!-- 表单示例 -->
<form @submit.prevent="handleSubmit" class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
邮箱地址
</label>
<input
v-model="form.email"
type="email"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入邮箱"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">
消息内容
</label>
<textarea
v-model="form.message"
rows="4"
class="block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-blue-500 focus:border-blue-500"
placeholder="请输入消息内容"
></textarea>
</div>
<BaseButton type="submit" class="w-full">
发送消息
</BaseButton>
</form>
</div>
</div>
</template>
<script>
import { reactive } from 'vue'
import BaseButton from './components/BaseButton.vue'
import BaseCard from './components/BaseCard.vue'
export default {
name: 'App',
components: {
BaseButton,
BaseCard
},
setup() {
const form = reactive({
email: '',
message: ''
})
const handleClick = () => {
console.log('按钮被点击')
}
const handleSubmit = () => {
console.log('表单提交', form)
}
return {
form,
handleClick,
handleSubmit
}
}
}
</script>
Next.js 集成
安装和配置
# 创建 Next.js 应用
npx create-next-app@latest my-tailwind-app
cd my-tailwind-app
# Next.js 13+ 默认支持 Tailwind CSS
# 如果没有,手动安装
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
// tailwind.config.js
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
extend: {},
},
plugins: [],
}
/* globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;
@layer base {
:root {
--background: 0 0% 100%;
--foreground: 222.2 84% 4.9%;
--primary: 221.2 83.2% 53.3%;
--primary-foreground: 210 40% 98%;
}
.dark {
--background: 222.2 84% 4.9%;
--foreground: 210 40% 98%;
--primary: 217.2 91.2% 59.8%;
--primary-foreground: 222.2 84% 4.9%;
}
}
Next.js App Router 示例
// app/layout.js
import './globals.css'
export const metadata = {
title: 'Next.js + Tailwind CSS',
description: '使用 Tailwind CSS 构建的 Next.js 应用',
}
export default function RootLayout({ children }) {
return (
<html lang="zh-CN">
<body className="min-h-screen bg-background font-sans antialiased">
{children}
</body>
</html>
)
}
// app/page.js
import Link from 'next/link'
import { Button } from '@/components/ui/button'
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card'
export default function Home() {
return (
<main className="container mx-auto px-4 py-8">
<div className="max-w-4xl mx-auto space-y-8">
<div className="text-center space-y-4">
<h1 className="text-4xl font-bold tracking-tight">
欢迎使用 Next.js + Tailwind CSS
</h1>
<p className="text-xl text-muted-foreground">
构建现代化的 Web 应用程序
</p>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
<Card>
<CardHeader>
<CardTitle>快速开始</CardTitle>
<CardDescription>
了解如何在 Next.js 中使用 Tailwind CSS
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
Tailwind CSS 提供了丰富的工具类...
</p>
<Button asChild>
<Link href="/docs">查看文档</Link>
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>组件库</CardTitle>
<CardDescription>
预构建的组件帮助快速开发
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
使用我们的组件库快速构建界面...
</p>
<Button variant="outline" asChild>
<Link href="/components">浏览组件</Link>
</Button>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>示例项目</CardTitle>
<CardDescription>
学习最佳实践和设计模式
</CardDescription>
</CardHeader>
<CardContent>
<p className="text-sm text-muted-foreground mb-4">
查看实际项目中的应用示例...
</p>
<Button variant="secondary" asChild>
<Link href="/examples">查看示例</Link>
</Button>
</CardContent>
</Card>
</div>
</div>
</main>
)
}
Next.js API 路由与 Tailwind
// app/dashboard/page.js
import { Suspense } from 'react'
async function getData() {
const res = await fetch('https://jsonplaceholder.typicode.com/posts')
if (!res.ok) {
throw new Error('Failed to fetch data')
}
return res.json()
}
function Loading() {
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{[...Array(6)].map((_, i) => (
<div key={i} className="animate-pulse">
<div className="bg-gray-200 h-48 rounded-lg mb-4"></div>
<div className="space-y-2">
<div className="h-4 bg-gray-200 rounded w-3/4"></div>
<div className="h-4 bg-gray-200 rounded w-1/2"></div>
</div>
</div>
))}
</div>
)
}
async function PostList() {
const posts = await getData()
return (
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
{posts.slice(0, 6).map((post) => (
<Card key={post.id} className="hover:shadow-lg transition-shadow">
<CardHeader>
<CardTitle className="line-clamp-2">{post.title}</CardTitle>
</CardHeader>
<CardContent>
<p className="text-muted-foreground line-clamp-3">
{post.body}
</p>
</CardContent>
</Card>
))}
</div>
)
}
export default function Dashboard() {
return (
<div className="container mx-auto px-4 py-8">
<div className="space-y-8">
<div>
<h1 className="text-3xl font-bold tracking-tight">仪表板</h1>
<p className="text-muted-foreground">
管理您的内容和数据
</p>
</div>
<Suspense fallback={<Loading />}>
<PostList />
</Suspense>
</div>
</div>
)
}
Nuxt.js 集成
安装和配置
# 创建 Nuxt 应用
npx nuxi@latest init my-tailwind-app
cd my-tailwind-app
npm install
# 安装 Tailwind CSS 模块
npm install -D @nuxtjs/tailwindcss
// nuxt.config.ts
export default defineNuxtConfig({
devtools: { enabled: true },
modules: [
'@nuxtjs/tailwindcss'
],
tailwindcss: {
exposeConfig: true,
viewer: true,
}
})
Nuxt 组件示例
<!-- components/AppHeader.vue -->
<template>
<header class="bg-white shadow-sm border-b">
<nav class="container mx-auto px-4">
<div class="flex items-center justify-between h-16">
<NuxtLink to="/" class="text-xl font-bold text-gray-900">
My App
</NuxtLink>
<div class="hidden md:flex items-center space-x-6">
<NuxtLink
to="/"
class="text-gray-600 hover:text-gray-900 transition-colors"
active-class="text-blue-600 font-medium"
>
首页
</NuxtLink>
<NuxtLink
to="/about"
class="text-gray-600 hover:text-gray-900 transition-colors"
active-class="text-blue-600 font-medium"
>
关于
</NuxtLink>
<NuxtLink
to="/contact"
class="text-gray-600 hover:text-gray-900 transition-colors"
active-class="text-blue-600 font-medium"
>
联系
</NuxtLink>
</div>
<button class="md:hidden p-2 text-gray-600">
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"/>
</svg>
</button>
</div>
</nav>
</header>
</template>
<!-- pages/index.vue -->
<template>
<div>
<AppHeader />
<main class="container mx-auto px-4 py-8">
<section class="text-center space-y-8 py-20">
<h1 class="text-5xl font-bold text-gray-900">
欢迎使用
<span class="bg-gradient-to-r from-blue-600 to-purple-600 bg-clip-text text-transparent">
Nuxt.js
</span>
</h1>
<p class="text-xl text-gray-600 max-w-2xl mx-auto">
使用 Tailwind CSS 构建美观、响应式的用户界面
</p>
<div class="flex flex-col sm:flex-row gap-4 justify-center">
<NuxtLink
to="/docs"
class="inline-flex items-center px-6 py-3 bg-blue-600 text-white font-medium rounded-lg hover:bg-blue-700 transition-colors"
>
开始使用
</NuxtLink>
<NuxtLink
to="/examples"
class="inline-flex items-center px-6 py-3 border border-gray-300 text-gray-700 font-medium rounded-lg hover:bg-gray-50 transition-colors"
>
查看示例
</NuxtLink>
</div>
</section>
<section class="py-20">
<div class="text-center mb-16">
<h2 class="text-3xl font-bold text-gray-900 mb-4">特性介绍</h2>
<p class="text-lg text-gray-600">了解我们提供的核心功能</p>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<div class="text-center p-6">
<div class="w-12 h-12 bg-blue-100 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-6 h-6 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M13 10V3L4 14h7v7l9-11h-7z"/>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">快速开发</h3>
<p class="text-gray-600">使用工具类快速构建用户界面</p>
</div>
<div class="text-center p-6">
<div class="w-12 h-12 bg-green-100 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-6 h-6 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 5a1 1 0 011-1h14a1 1 0 011 1v2a1 1 0 01-1 1H5a1 1 0 01-1-1V5zM4 13a1 1 0 011-1h6a1 1 0 011 1v6a1 1 0 01-1 1H5a1 1 0 01-1-1v-6zM16 13a1 1 0 011-1h2a1 1 0 011 1v6a1 1 0 01-1 1h-2a1 1 0 01-1-1v-6z"/>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">响应式设计</h3>
<p class="text-gray-600">内置响应式工具类支持</p>
</div>
<div class="text-center p-6">
<div class="w-12 h-12 bg-purple-100 rounded-lg flex items-center justify-center mx-auto mb-4">
<svg class="w-6 h-6 text-purple-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M7 21a4 4 0 01-4-4V5a2 2 0 012-2h4a2 2 0 012 2v12a4 4 0 01-4 4zm0 0h12a2 2 0 002-2v-4a2 2 0 00-2-2h-2.343M11 7.343l1.657-1.657a2 2 0 012.828 0l2.829 2.829a2 2 0 010 2.828l-8.486 8.485M7 17h4a2 2 0 012-2v-1a2 2 0 00-2-2H7m-2-4h2m8 2v2"/>
</svg>
</div>
<h3 class="text-xl font-semibold text-gray-900 mb-2">高度定制</h3>
<p class="text-gray-600">完全可定制的设计系统</p>
</div>
</div>
</section>
</main>
</div>
</template>
构建工具最佳实践
PostCSS 配置优化
// postcss.config.js
module.exports = {
plugins: {
'tailwindcss': {},
'autoprefixer': {},
...(process.env.NODE_ENV === 'production'
? {
'cssnano': {
preset: ['default', {
discardComments: { removeAll: true },
}]
}
}
: {}
)
}
}
Webpack 配置优化
// webpack.config.js
const path = require('path')
module.exports = {
// 其他配置...
resolve: {
alias: {
'@': path.resolve(__dirname, 'src'),
'@components': path.resolve(__dirname, 'src/components'),
'@utils': path.resolve(__dirname, 'src/utils'),
}
},
module: {
rules: [
{
test: /\.css$/,
use: [
'style-loader',
'css-loader',
'postcss-loader'
]
}
]
}
}
Vite 配置优化
// vite.config.js
import { defineConfig } from 'vite'
import path from 'path'
export default defineConfig({
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
'@components': path.resolve(__dirname, './src/components'),
'@utils': path.resolve(__dirname, './src/utils'),
}
},
css: {
postcss: './postcss.config.js',
devSourcemap: true
},
build: {
cssCodeSplit: true,
rollupOptions: {
output: {
assetFileNames: (assetInfo) => {
if (assetInfo.name === 'style.css') return 'assets/main.[hash].css'
return 'assets/[name].[hash].[ext]'
}
}
}
}
})
小结
通过本章学习,你应该掌握了:
- React 集成:在不同 React 项目中配置和使用 Tailwind CSS
- Vue.js 集成:Vue 项目的 Tailwind CSS 最佳实践
- Next.js 集成:利用 Next.js 特性优化 Tailwind CSS 使用
- Nuxt.js 集成:在 Nuxt 项目中无缝集成 Tailwind CSS
- 构建优化:不同构建工具的配置和优化策略
这些技能让你能够在任何现代前端框架中高效地使用 Tailwind CSS。