Tailwind CSS实战:构建仿ChatGPT聊天页面(失败了)
参考文章:https://nextjs.org/docs/app/getting-started/css#tailwind-css
参考文章:How to install Tailwind CSS v3 in your Next.js application
注意:
- globals.css只能放在app/globals.css 或 src/styles/globals.css 这类约定俗成的位置,保持路径清晰;移动后别忘了更新 layout.tsx 里的 import。
- layout.tsx也只能放在以下位置之一(取决于项目配置):app/layout.tsx、src/app/layout.tsx
文章目录
- 1. 环境准备
- 2. 项目结构
- 3. 设置Tailwind CSS
- Add the PostCSS plugin to your postcss.config.mjs file:
- Import Tailwind in your global CSS file:
- Import the CSS file in your root layout:
- 打开`tailwind.config.js`并配置content字段,告诉Tailwind需要扫描哪些文件来生成CSS类:
- 在`src/app/globals.css`中添加Tailwind指令:
- 4. 创建状态管理(Zustand)(尴尬,语法看不太懂,回头再研究,先把代码跑通😳)(src/store/chatStore.ts)
- 5. 实现聊天消息组件(src/components/Chat/Message.tsx)
- 6. 实现消息列表组件(src/components/Chat/MessageList.tsx)
- 7. 实现输入框组件(看不太懂代码,先跑通)(src/components/InputBox.tsx)
- 8. 创建主页面(src/app/page.tsx)
- 9. 添加全局样式(src/app/globals.css)
- 10. 运行项目
- 11. 代码解释
- Tailwind CSS的实用类设计
- Zustand状态管理
- 交互逻辑
- 12. 扩展建议
- 13. 总结
- 14. 最终效果
- 15. 下一步学习
在现代前端开发中,Tailwind CSS凭借其实用类(utility classes)和高度可定制性,成为了构建响应式UI的热门选择。今天,我将带你一步步使用Tailwind CSS、React、TypeScript、Next.js和Zustand,实现一个仿ChatGPT的聊天界面。我们将使用假接口模拟后端交互,专注于前端实现。
1. 环境准备
首先,创建一个新的Next.js项目:
npx create-next-app@latest chatgpt-clone --typescript --tailwind --eslint --src-dir --app --import-alias "@/*"
命令解释:npx create-next-app@latest 是Next.js官方提供的脚手架命令,用于快速创建Next.js项目。--typescript 表示使用TypeScript,--tailwind 表示集成Tailwind CSS,--eslint 表示集成ESLint,--src-dir 表示将源代码放在src目录下,--app 表示使用App Router,--import-alias "@/*" 表示设置路径别名。

cd chatgpt-clone
命令解释:cd 是切换当前工作目录的命令,进入刚刚创建的项目目录。
npm install zustand
命令解释:npm install 是安装Node.js包的命令。这里安装了Zustand状态管理库。
注意:
@types/zustand已被弃用:
zustand 库从 v4.0.0 开始(2022 年)将类型定义直接打包到主包中(zustand 包内包含 index.d.ts)。 因此,无需单独安装@types/zustand,npm 官方仓库已移除该包。
2. 项目结构
我们的项目结构将如下:
src/
├── app/
│ ├── layout.tsx
│ ├── page.tsx
├── components/
│ ├── Chat/
│ │ ├── Message.tsx
│ │ └── MessageList.tsx
│ └── InputBox.tsx
├── store/
│ └── chatStore.ts
└── styles/└── globals.css
3. 设置Tailwind CSS
Add the PostCSS plugin to your postcss.config.mjs file:
export default {plugins: {'@tailwindcss/postcss': {},},
}
参考文章:PostCSS介绍(一个用JavaScript工具和插件生态系统来转换CSS代码的工具)CSS后处理器,用于支持Tailwind CSS

我打开文件看,似乎已经配置好了?(虽然语法不太一样)
Import Tailwind in your global CSS file:
在src/app/globals.css中增加:
@import 'tailwindcss';

好像已经加了,懵,跟官方给的有点不一样,我是通过create-next-app工具装的,可能有所不同。
Import the CSS file in your root layout:
将以下内容添加到app/layout.tsx:
import './globals.css'export default function RootLayout({children,
}: {children: React.ReactNode
}) {return (<html lang="en"><body>{children}</body></html>)
}
似乎也已经添加了,可能版本不一样,代码不同:

打开tailwind.config.js并配置content字段,告诉Tailwind需要扫描哪些文件来生成CSS类:
// tailwind.config.js
module.exports = {// 指定Tailwind需要扫描的文件路径,以生成对应的CSS类// 这里包括了app目录下的所有JS/TS/JSX/TSX文件content: ["./src/app/**/*.{js,ts,jsx,tsx}",// 也包括components目录下的所有JS/TS/JSX/TSX文件"./src/components/**/*.{js,ts,jsx,tsx}",],theme: {// 扩展Tailwind的默认主题,添加自定义颜色extend: {colors: {// 定义一个名为primary的颜色,用于主要按钮和强调元素primary: '#4F46E5',// 定义一个名为secondary的颜色,用于次要按钮和强调元素secondary: '#10B981',}},},// 插件扩展,目前没有使用任何插件plugins: [],
}
我没看到有tailwind.config.js文件,只能创建一个了。

在src/app/globals.css中添加Tailwind指令:
/* 从Tailwind的base样式开始,这是Tailwind的基础重置 */
@tailwind base;/* 从Tailwind的组件样式开始,这是Tailwind的组件样式 */
@tailwind components;/* 从Tailwind的实用类样式开始,这是Tailwind的核心实用类 */
@tailwind utilities;

4. 创建状态管理(Zustand)(尴尬,语法看不太懂,回头再研究,先把代码跑通😳)(src/store/chatStore.ts)
我们使用Zustand管理聊天状态:
// src/store/chatStore.ts
// 从zustand库导入create函数,用于创建状态管理
import { create } from 'zustand';// 定义聊天消息的类型,包含id、文本、是否是用户发送、时间戳
type ChatMessage = {// 消息的唯一标识符,使用时间戳生成id: string;// 消息的文本内容text: string;// 标记消息是否是用户发送的(true表示用户,false表示AI)isUser: boolean;// 消息发送的时间戳timestamp: number;
};// 定义ChatState的状态类型,包含消息数组、添加消息函数、清除消息函数
type ChatState = {// 存储所有聊天消息的数组messages: ChatMessage[];// 添加新消息到状态的函数addMessage: (message: ChatMessage) => void;// 清空所有聊天消息的函数clearMessages: () => void;
};// 使用zustand的create函数创建状态管理
// 传入一个函数,该函数接收set函数,用于更新状态
export const useChatStore = create<ChatState>((set) => ({// 初始状态:消息数组为空messages: [],// 添加消息函数:将新消息添加到消息数组的末尾addMessage: (message) => set((state) => ({// 通过set函数更新状态,将新消息添加到现有消息数组的末尾messages: [...state.messages, message]})),// 清除消息函数:将消息数组重置为空数组clearMessages: () => set({ messages: [] }),
}));

5. 实现聊天消息组件(src/components/Chat/Message.tsx)
首先,创建消息组件:
// src/components/Chat/Message.tsx
// 从React导入React组件
import React from 'react';// 定义Message组件的Props类型,包含text和isUser
interface MessageProps {// 消息的文本内容text: string;// 标记消息是否是用户发送的isUser: boolean;
}// 创建Message组件,接收text和isUser作为props
const Message: React.FC<MessageProps> = ({ text, isUser }) => {// 返回一个div,根据isUser的值决定消息的对齐方式// 如果是用户发送的消息,右对齐;如果是AI发送的消息,左对齐return (<div className={`flex ${isUser ? 'justify-end' : 'justify-start'} mb-4`}>{/* 消息容器,根据isUser的值设置背景色和文字颜色 */}<div className={`max-w-[70%] rounded-lg p-3 ${isUser ? 'bg-primary text-white' : 'bg-gray-200'}`}>{/* 消息文本,设置字体大小 */}<p className="text-sm">{text}</p></div></div>);
};// 导出Message组件,使其可以在其他地方使用
export default Message;

6. 实现消息列表组件(src/components/Chat/MessageList.tsx)
// src/components/Chat/MessageList.tsx
// 从React导入React组件
import React from 'react';// 从store/chatStore导入useChatStore,用于访问聊天状态
import Message from './Message';
import { useChatStore } from '@/store/chatStore';// 定义MessageList组件,没有接收任何props
const MessageList: React.FC = () => {// 使用useChatStore获取消息状态const { messages } = useChatStore();// 返回消息列表,使用div包裹,设置高度、溢出滚动和间距return (<div className="h-[calc(100vh-120px)] overflow-y-auto p-4 space-y-4">{/* 使用map遍历messages数组,为每条消息渲染Message组件 */}{messages.map((message) => (// 为每条消息提供唯一的key,确保React可以高效更新<Messagekey={message.id}text={message.text}isUser={message.isUser}/>))}</div>);
};// 导出MessageList组件
export default MessageList;

7. 实现输入框组件(看不太懂代码,先跑通)(src/components/InputBox.tsx)
// src/components/InputBox.tsx
// 从React导入useState
import React, { useState } from 'react';// 从store/chatStore导入useChatStore,用于访问聊天状态
import { useChatStore } from '@/store/chatStore';// 定义InputBox组件,没有接收任何props
const InputBox: React.FC = () => {// 使用useState管理输入框的值const [input, setInput] = useState('');// 从useChatStore获取addMessage函数,用于添加新消息const { addMessage } = useChatStore();// 处理表单提交事件const handleSubmit = (e: React.FormEvent) => {// 阻止表单默认提交行为e.preventDefault();// 如果输入为空,直接返回,不处理if (!input.trim()) return;// 添加用户消息到状态addMessage({// 使用当前时间戳作为消息IDid: Date.now().toString(),// 用户输入的消息文本text: input,// 标记为用户发送isUser: true,// 记录消息发送时间timestamp: Date.now(),});// 清空输入框setInput('');// 模拟AI响应,1秒后添加AI消息setTimeout(() => {addMessage({// 使用当前时间戳+1作为AI消息IDid: (Date.now() + 1).toString(),// AI的模拟响应文本text: "这是一个模拟的AI响应。在实际应用中,这里会调用API获取真实响应。",// 标记为AI发送isUser: false,// 记录AI消息发送时间timestamp: Date.now() + 1000,});}, 1000);};// 返回表单,包含输入框和发送按钮return (<form onSubmit={handleSubmit} className="p-4 border-t border-gray-200"><div className="flex">{/* 输入框,绑定输入值和输入事件 */}<inputtype="text"value={input}// 当输入框内容变化时,更新input状态onChange={(e) => setInput(e.target.value)}// 输入框的占位符文本placeholder="请输入消息..."// 设置输入框的样式,包括边框、内边距、圆角和焦点样式className="flex-1 p-3 border border-gray-300 rounded-l-lg focus:outline-none focus:ring-2 focus:ring-primary"/>{/* 发送按钮,绑定提交事件,禁用状态根据输入是否为空决定 */}<buttontype="submit"// 设置按钮的背景色、文字颜色、内边距、圆角和悬停效果className="bg-primary text-white px-6 rounded-r-lg hover:bg-indigo-600 transition-colors"// 如果输入为空,禁用按钮disabled={!input.trim()}>发送</button></div></form>);
};// 导出InputBox组件
export default InputBox;

8. 创建主页面(src/app/page.tsx)
// src/app/page.tsx
// 从React导入React组件
import React from 'react';// 从components导入MessageList和InputBox组件
import MessageList from '@/components/Chat/MessageList';
import InputBox from '@/components/InputBox';// 定义Home组件,作为应用程序的主页面
export default function Home() {// 返回页面结构,包含头部、主内容区域return (<div className="min-h-screen bg-gray-50 flex flex-col">{/* 头部,包含标题 */}<header className="bg-white shadow-md p-4 text-center">{/* 标题,设置字体大小、粗细和颜色 */}<h1 className="text-xl font-bold text-primary">AI Chat</h1></header>{/* 主内容区域,包含消息列表和输入框 */}<main className="flex-1 flex flex-col">{/* 消息列表组件 */}<MessageList />{/* 输入框组件 */}<InputBox /></main></div>);
}
将以上内容替换page.tsx中的初始内容:

9. 添加全局样式(src/app/globals.css)
/* src/app/globals.css */
/* 从Google Fonts导入Inter字体,指定字体重量 */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600&display=swap');/* 设置页面的字体为Inter,如果没有则使用sans-serif */
body {font-family: 'Inter', sans-serif;
}

10. 运行项目
npm run dev
命令解释:npm run dev 是Next.js项目默认的开发服务器命令,它会启动一个开发服务器,自动检测代码变化并热重载。
11. 代码解释
Tailwind CSS的实用类设计
- 布局:
flex,justify-end,justify-start,flex-1用于实现响应式布局 - 间距:
p-4,mb-4,space-y-4用于控制元素间距 - 颜色:
bg-primary,text-white,bg-gray-200,border-gray-300用于设置颜色 - 圆角:
rounded-lg用于设置圆角 - 响应式:
max-w-[70%]用于限制消息宽度,确保在不同屏幕下显示良好
Zustand状态管理
create:Zustand的创建函数addMessage:添加新消息到状态clearMessages:清空聊天记录useChatStore:在组件中使用状态
交互逻辑
- 用户输入消息并提交
- 将用户消息添加到状态
- 模拟1秒后添加AI响应
- 消息列表自动滚动到最新消息
12. 扩展建议
- 添加加载状态:在AI响应时显示加载指示器
- 实现真正的API调用:替换假接口为实际的API
- 添加消息删除功能:允许用户删除消息
- 实现消息编辑:允许用户编辑已发送的消息
- 添加主题切换:实现深色/浅色模式
13. 总结
通过这个简单的示例,我们使用Tailwind CSS实现了仿ChatGPT的聊天界面。Tailwind CSS的实用类使我们能够快速构建响应式UI,而Zustand则提供了简洁的状态管理。
关键点:
- 使用Tailwind的实用类快速构建UI
- 通过Zustand管理应用状态
- 使用假接口模拟后端交互
- 确保界面在不同设备上的响应式体验
这个项目是学习Tailwind CSS和现代前端开发模式的绝佳起点。随着你对Tailwind的熟悉,可以尝试添加更多功能,如消息表情、图片上传等。
14. 最终效果

也太丑了吧。。。
(注:实际效果请运行代码查看)
15. 下一步学习
- 学习Tailwind的响应式设计:Tailwind官方响应式文档
- 探索Zustand的高级功能:Zustand官方文档
- 了解Next.js的API路由:Next.js API Routes
