使用 tailwind css 重构组件
在上一节课中,我们学习了如何使用 Props 让 PracticeCard
组件接收动态数据,从而展示不同的练习内容。现在,我们的卡片虽然功能完善,但在视觉上还比较朴素。
本节课,我们将引入强大的 Tailwind CSS 框架,对我们的“作业展示首页”进行全面的样式优化。我们将从重新初始化一个支持 Tailwind 的 Next.js 项目开始,然后逐步重构全局导航条和 PracticeCard
组件。整个过程,我们会特别关注认知负荷理论 (CLT) 的应用,力求让学习过程更平滑,成就感更强。
学习目标:
- 学会在创建 Next.js 项目时集成 Tailwind CSS。
- 理解如何将现有组件和页面结构迁移到新的 Tailwind 项目中。
- 应用认知负荷理论的原则,有条理地使用 Tailwind CSS 重构UI组件。
- 掌握使用 Tailwind 工具类构建响应式导航条。
- 能够用 Tailwind CSS 美化
PracticeCard
组件,并根据 Props(如completed
状态)应用条件样式。 - 体验 Tailwind CSS 带来的开发效率和样式一致性的提升。
1. 重新初始化项目并集成 Tailwind CSS
为了确保 Tailwind CSS 从一开始就正确配置,我们将创建一个新的 Next.js 项目,并在创建过程中选择集成 Tailwind CSS。
步骤 1: 创建新项目
打开你的终端,运行以下命令:
npx create-next-app@latest nextjs-tailwind-homework
在安装过程中,create-next-app
会询问你一系列问题。请确保:
- Would you like to use TypeScript? (No,本示例将使用 JavaScript)
- Would you like to use ESLint? (No)
- Would you like to use Tailwind CSS? 务必选择 Yes!
- Would you like to use
src/
directory? (Yes,推荐) - Would you like to use App Router? (recommended) (Yes)
- Would you like to customize the default import alias? (No,或根据你的喜好设置)
完成后,进入新项目目录:
cd nextjs-tailwind-homework
步骤 2: 迁移现有代码
现在,我们需要将上节课的 PracticeCard
组件和 app/page.js
的内容复制到新项目中。
-
创建
PracticeCard
组件: 在nextjs-tailwind-homework/src/components/
目录下创建一个新文件PracticeCard.js
。 将上节课PracticeCard.js
的代码(包含 Props 解构的版本)复制粘贴到这个新文件中。 目前,它可能还包含一些内联样式或者简单的className
。// src/components/PracticeCard.js (初始版本,可能带内联样式)export default function PracticeCard({ title, description, date, completed }) {return (<div className="practice-card" style={{ border: `2px solid ${completed ? 'green' : '#eee'}`, padding: '16px', margin: '10px 0', borderRadius: '8px' }}><h2>{title}</h2><p>{description}</p><p>日期: {date}</p><p>状态: {completed ? '已完成' : '未完成'}</p></div>);} -
更新首页
app/page.js
: 打开nextjs-tailwind-homework/src/app/page.js
。 用上节课app/page.js
的内容替换它,确保导入PracticeCard
的路径正确 (@/components/PracticeCard
)。// src/app/page.js (初始版本)import PracticeCard from '@/components/PracticeCard';export default function HomePage() {const weekOneTitle = "第一周:组件基础";const practiceDate = new Date().toLocaleDateString();return (<div> {/* 稍后我们会用 Tailwind 优化这个外层 div */}<h1>每周练习</h1> {/* 稍后我们会用 Tailwind 优化这个标题 */}<PracticeCardtitle={weekOneTitle}description="学习 React 组件的核心概念和 JSX 基础。"date={practiceDate}completed={false}/><PracticeCardtitle="第二周:Props 与 State"description="掌握组件间数据传递和内部状态管理。"date={practiceDate}completed={true}/><PracticeCardtitle="第三周:事件处理"description="让你的组件响应用户交互。"date={practiceDate}completed={false}/></div>);}
步骤 3: 检查 Tailwind 配置
Next.js 已经为我们配置好了 Tailwind:
tailwind.config.js
: Tailwind 的主配置文件。tailwind.config.js /** @type {import('tailwindcss').Config} */module.exports = {content: ['./src/pages/**/*.{js,ts,jsx,tsx,mdx}','./src/components/**/*.{js,ts,jsx,tsx,mdx}','./src/app/**/*.{js,ts,jsx,tsx,mdx}',],theme: {extend: {// 可以在这里扩展主题},},plugins: [],}src/app/globals.css
: 包含了 Tailwind 的基础指令。src/app/globals.css @tailwind base;@tailwind components;@tailwind utilities;/* 你也可以在这里添加一些全局基础样式 */body {/* 例如,设置一个默认的背景色 *//* background-color: #f3f4f6; /* 这是一个浅灰色 */}
步骤 4: 启动开发服务器
npm run dev
访问 http://localhost:3000
。你会看到 Next.js 的默认欢迎页(因为我们还没修改 layout.js
)或者一个非常朴素的、带有我们卡片内容的页面。我们的目标就是用 Tailwind 来美化它。
2. 应用认知负荷理论 (CLT) 进行重构
在开始用 Tailwind CSS 重构之前,我们回顾一下如何应用认知负荷理论来优化学习:
- 分解任务 (Intrinsic Load Management): 我们不会一次性重构整个页面。而是先处理导航条,再处理卡片,最后调整整体布局。每个组件的样式也会分步进行。
- 减少无关干扰 (Extraneous Load Minimization): Tailwind 的工具类直接应用于 JSX,减少了在 CSS 文件和 JS 文件间切换的认知成本。我们将专注于理解和应用这些类,而不是复杂的 CSS 规则或命名。
- 促进知识构建 (Germane Load Optimization): 每应用一组 Tailwind 类,我们都会立即观察其效果,并将这些原子化的类与它们产生的视觉变化联系起来,从而构建对 Tailwind 工作方式的理解。
3. 重构全局导航条 (Layout)
通常,一个应用会有一个全局导航条。我们将在 src/app/layout.js
中添加一个简单的导航条,并用 Tailwind 美化它。
步骤 1: 修改 src/app/layout.js
import './globals.css' // 确保 Tailwind 指令被引入import { Inter } from 'next/font/google'
const inter = Inter({ subsets: ['latin'] })
export const metadata = { title: '每周作业平台', description: '使用 Next.js 和 Tailwind CSS 构建',}
// 简单的导航条组件function Navbar() { return ( <nav className="bg-slate-800 text-white p-4 shadow-md"> <div className="container mx-auto flex justify-between items-center"> <a href="/" className="text-xl font-bold hover:text-slate-300 transition-colors"> 作业平台 </a> <div className="space-x-4"> <a href="/" className="hover:text-slate-300 transition-colors">首页</a> <a href="/archive" className="hover:text-slate-300 transition-colors">归档</a> {/* 可以添加更多链接 */} </div> </div> </nav> );}
export default function RootLayout({ children }) { return ( <html lang="zh-CN"> <body className={`${inter.className} bg-slate-50`}> {/* 给 body 添加一个背景色 */} <Navbar /> {/* 添加导航条 */} <main> {/* children 会被渲染在这里 */} {children} </main> {/* 你也可以在这里添加 Footer */} </body> </html> )}
代码讲解 (Navbar):
<nav className="bg-slate-800 text-white p-4 shadow-md">
:bg-slate-800
: 设置导航条背景为深蓝灰色 (Slate 800)。text-white
: 设置默认文字颜色为白色。p-4
: 内边距 1rem (16px)。shadow-md
: 添加一个中等大小的阴影。
<div className="container mx-auto flex justify-between items-center">
:container
: 一个 Tailwind 提供的类,用于设置最大宽度并居中内容,响应式。mx-auto
: 水平居中(当container
类不完全适用或需要自定义断点时)。flex
: 启用 Flexbox 布局。justify-between
: 项目在主轴上两端对齐(Logo 和链接分开)。items-center
: 项目在交叉轴上居中对齐。
<a href="/" className="text-xl font-bold hover:text-slate-300 transition-colors">
:text-xl
: 字体大小。font-bold
: 字体加粗。hover:text-slate-300
: 鼠标悬停时文字颜色变为浅灰色。transition-colors
: 为颜色变化添加平滑过渡效果。
<div className="space-x-4">
:space-x-4
: 为直接子元素之间添加水平间距 (1rem)。这是一个非常方便的布局工具。
保存文件,现在你的页面顶部应该出现了一个美观的导航条了!
强烈建议在你的代码编辑器 (如 VS Code) 中安装 “Tailwind CSS IntelliSense” 插件。它会提供类名自动补全、预览等功能,极大提升开发效率。
4. 用 Tailwind 重构 PracticeCard
组件
现在轮到我们的核心组件 PracticeCard.js
了。我们将移除之前的内联样式,完全用 Tailwind 工具类来替代。
修改 src/components/PracticeCard.js
:
export default function PracticeCard({ title, description, date, completed }) { // 根据 completed 状态决定边框和一些文本颜色 const cardClasses = ` bg-white rounded-lg shadow-lg p-6 m-4 transition-all duration-300 ease-in-out hover:shadow-xl border-2 ${completed ? 'border-green-500' : 'border-slate-200'} `;
const titleClasses = ` text-xl font-semibold mb-2 ${completed ? 'text-green-700' : 'text-slate-800'} `;
const statusTextClasses = ` text-sm font-medium ${completed ? 'text-green-600' : 'text-orange-500'} `;
return ( <div className={cardClasses}> <h2 className={titleClasses}>{title}</h2> <p className="text-slate-600 text-sm mb-3 min-h-[40px]">{description}</p> <p className="text-xs text-slate-400 mb-3">日期: {date}</p> <p className={statusTextClasses}> 状态: {completed ? '已完成 ✔' : '未完成 ⏳'} </p> </div> );}
代码讲解 (PracticeCard.js
):
cardClasses
(卡片容器div
):bg-white
: 白色背景。rounded-lg
: 圆角。shadow-lg
: 较大的阴影。p-6
: 内边距 (1.5rem)。m-4
: 外边距 (1rem)。transition-all duration-300 ease-in-out
: 为所有可过渡属性添加 300ms 的缓动过渡。hover:shadow-xl
: 鼠标悬停时阴影变大。border-2
: 2px 边框宽度。${completed ? 'border-green-500' : 'border-slate-200'}
: 条件样式! 如果completed
为true
,边框为绿色;否则为浅灰色。这是 Tailwind 结合 JavaScript 动态性的强大之处。
titleClasses
(标题h2
):text-xl font-semibold mb-2
: 字体大小、加粗、底部外边距。${completed ? 'text-green-700' : 'text-slate-800'}
: 标题颜色也根据completed
状态变化。
- 描述 (
p
):text-slate-600 text-sm mb-3
: 文字颜色、大小、底部外边距。min-h-[40px]
: 设置一个最小高度,让卡片在描述内容长短不一时也能大致对齐。[]
允许你使用任意值。
- 日期 (
p
):text-xs text-slate-400 mb-3
: 更小的字体,浅灰色,底部外边距。
statusTextClasses
(状态p
):text-sm font-medium
: 字体大小、中等粗细。${completed ? 'text-green-600' : 'text-orange-500'}
: 状态文字颜色,已完成为绿色,未完成为橙色,并添加了小图标增强视觉。
将动态类名(如基于 completed
状态的类)单独提取到变量中,或者直接在 className
中使用模板字符串,可以使 JSX 更整洁。对于复杂的条件,前者更易读。
5. 优化首页布局 (app/page.js
)
最后,我们来调整 app/page.js
的整体布局,让卡片排列更美观。
修改 src/app/page.js
:
import PracticeCard from '@/components/PracticeCard';
export default function HomePage() { const weekOneTitle = "第一周:组件基础"; const practiceDate = new Date().toLocaleDateString();
const practices = [ { id: 1, title: weekOneTitle, description: "学习 React 组件的核心概念和 JSX 基础。", date: practiceDate, completed: false, }, { id: 2, title: "第二周:Props 与 State", description: "掌握组件间数据传递和内部状态管理。", date: practiceDate, completed: true, }, { id: 3, title: "第三周:事件处理与列表渲染", description: "让你的组件响应用户交互,并学习如何高效渲染列表数据。", date: practiceDate, completed: false, }, { id: 4, title: "第四周:Tailwind CSS 集成", description: "使用 Tailwind CSS 快速美化你的 Next.js 应用。", date: practiceDate, completed: true, } ];
return ( // 使用 Flexbox 居中内容,并添加上下内边距 <div className="container mx-auto p-4 sm:p-6 lg:p-8"> <h1 className="text-3xl sm:text-4xl font-bold text-slate-800 mb-8 text-center"> 每周练习进度 </h1>
{/* 使用 Grid 布局来排列卡片 */} <div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6"> {practices.map((practice) => ( <PracticeCard key={practice.id} // 列表渲染时,key 是必须的 title={practice.title} description={practice.description} date={practice.date} completed={practice.completed} /> ))} </div> </div> );}
代码讲解 (app/page.js
):
- 数据结构化: 我们将练习数据放到了一个
practices
数组中,这更符合实际应用场景,便于使用map
方法渲染。 - 最外层
div
:container mx-auto
: 限制内容最大宽度并居中。p-4 sm:p-6 lg:p-8
: 响应式的内边距。在小屏幕上是p-4
,在中等屏幕 (sm) 及以上是p-6
,在大屏幕 (lg) 及以上是p-8
。
- 标题
h1
:text-3xl sm:text-4xl
: 响应式的字体大小。font-bold text-slate-800 mb-8 text-center
: 加粗、颜色、下边距、文本居中。
- 卡片容器
div
(Grid 布局):grid
: 启用 Grid 布局。grid-cols-1 sm:grid-cols-2 lg:grid-cols-3
: 响应式列数!- 默认(移动端优先):1 列。
sm:
(small breakpoint, 640px+): 2 列。lg:
(large breakpoint, 1024px+): 3 列。
gap-6
: 设置网格项之间的间距 (1.5rem)。
.map()
渲染:- 我们使用
practices.map()
来遍历数据并为每个练习渲染一个PracticeCard
组件。 key={practice.id}
: 在 React 中进行列表渲染时,为每个列表项提供一个唯一的key
prop 至关重要,有助于 React 高效更新 DOM。
- 我们使用
现在,刷新你的浏览器。你应该能看到一个包含导航条、居中标题和响应式网格布局卡片的漂亮页面了!尝试调整浏览器窗口大小,观察卡片是如何根据屏幕宽度自动调整列数的。
6. 总结与回顾
通过本节课的实践,我们完成了:
- 项目初始化: 成功创建了一个集成了 Tailwind CSS 的 Next.js 项目。
- 组件迁移与重构: 将已有的
PracticeCard
组件和首页内容迁移,并使用 Tailwind CSS 进行了彻底的样式重构。 - 布局实现: 构建了全局导航条和响应式的卡片网格布局。
- 条件样式: 学会了根据 Props 动态改变 Tailwind 类,实现条件化样式。
- CLT 应用: 通过分解任务、减少上下文切换、即时反馈等方式,力求学习过程更高效。
Tailwind CSS 的核心优势在于其原子化和工具优先的理念。它让我们能够直接在 JSX 中快速构建复杂界面,而无需编写大量自定义 CSS。这不仅提升了开发效率,还有助于保持项目样式的一致性和可维护性。
下一步:
- 继续探索 Tailwind CSS 官方文档,了解更多工具类。
- 尝试为你的组件添加更多交互效果,如
:focus
,:active
状态。 - 研究如何在
tailwind.config.js
中自定义主题(颜色、间距、字体等)以满足特定设计需求。
希望你享受使用 Tailwind CSS 的过程!