Next.js与Supabase实时聊天:构建即时通讯应用
【免费下载链接】next.js The React Framework 项目地址: https://gitcode.com/GitHub_Trending/next/next.js
你是否正在寻找一种简单高效的方式来构建实时聊天功能?Next.js作为React框架提供了强大的服务端渲染和客户端交互能力,而Supabase则提供了实时数据库和身份验证功能。本文将带你一步步实现一个功能完整的实时聊天应用,无需复杂的后端知识,只需基本的JavaScript和React基础。
准备工作
在开始之前,我们需要准备以下环境和工具:
- Node.js 18.x或更高版本
- npm、yarn或pnpm包管理器
- 一个Supabase账号(可以在Supabase官网免费注册)
- Git(用于克隆项目模板)
首先,我们需要获取Next.js与Supabase集成的基础项目。可以通过以下命令克隆官方示例项目:
git clone https://gitcode.com/GitHub_Trending/next/next.js.git
cd next.js/examples/with-supabase
这个示例项目包含了Next.js与Supabase集成的基本配置,我们将在此基础上构建聊天功能。项目结构详情可参考examples/with-supabase目录。
项目设置
安装依赖
进入项目目录后,安装所需的依赖包:
npm install
# 或
yarn install
# 或
pnpm install
配置环境变量
复制环境变量示例文件并修改为你自己的Supabase项目信息:
cp .env.example .env.local
打开.env.local文件,添加你的Supabase项目URL和匿名密钥,这些信息可以在Supabase项目控制台的设置 > API中找到:
NEXT_PUBLIC_SUPABASE_URL=你的Supabase项目URL
NEXT_PUBLIC_SUPABASE_ANON_KEY=你的Supabase匿名密钥
数据库设计
在Supabase控制台中,我们需要创建一个用于存储聊天消息的表。登录Supabase控制台,进入SQL编辑器,执行以下SQL语句:
CREATE TABLE messages (
id UUID DEFAULT uuid_generate_v4() PRIMARY KEY,
content TEXT NOT NULL,
user_id UUID NOT NULL REFERENCES auth.users(id),
username TEXT NOT NULL,
created_at TIMESTAMP WITH TIME ZONE DEFAULT NOW() NOT NULL
);
-- 启用实时订阅
ALTER PUBLICATION supabase_realtime ADD TABLE messages;
-- 创建索引以提高查询性能
CREATE INDEX idx_messages_created_at ON messages(created_at);
这段SQL创建了一个messages表,包含消息内容、发送用户ID、用户名和创建时间等字段,并启用了实时订阅功能,这是实现实时聊天的关键。
构建聊天界面
创建聊天页面
在app目录下创建一个chat目录,并添加page.tsx文件:
'use client';
import { useState, useEffect } from 'react';
import { createClientComponentClient } from '@supabase/auth-helpers-nextjs';
import { AuthButton } from '@/components/auth-button';
export default function ChatPage() {
const [messages, setMessages] = useState([]);
const [newMessage, setNewMessage] = useState('');
const [user, setUser] = useState(null);
const supabase = createClientComponentClient();
// 获取当前用户信息
useEffect(() => {
const getCurrentUser = async () => {
const { data: { session } } = await supabase.auth.getSession();
if (session) {
setUser(session.user);
}
};
getCurrentUser();
}, [supabase.auth]);
// 加载历史消息
useEffect(() => {
const fetchMessages = async () => {
const { data } = await supabase
.from('messages')
.select('*')
.order('created_at', { ascending: true });
if (data) setMessages(data);
};
fetchMessages();
}, [supabase]);
// 订阅新消息
useEffect(() => {
if (!user) return;
const subscription = supabase
.channel('public:messages')
.on(
'postgres_changes',
{ event: 'INSERT', schema: 'public', table: 'messages' },
(payload) => {
setMessages((prev) => [...prev, payload.new]);
}
)
.subscribe();
return () => {
supabase.removeChannel(subscription);
};
}, [supabase, user]);
// 发送消息
const handleSendMessage = async (e) => {
e.preventDefault();
if (!newMessage.trim() || !user) return;
await supabase
.from('messages')
.insert([
{
content: newMessage,
user_id: user.id,
username: user.email.split('@')[0] // 使用邮箱前缀作为用户名
}
]);
setNewMessage('');
};
return (
<div className="max-w-2xl mx-auto p-4">
<div className="flex justify-between items-center mb-6">
<h1 className="text-2xl font-bold">实时聊天</h1>
<AuthButton />
</div>
{!user ? (
<div className="text-center py-10">
<p>请先登录以参与聊天</p>
</div>
) : (
<>
<div className="border border-gray-200 rounded-lg h-[500px] overflow-y-auto p-4 mb-4 bg-white">
{messages.map((message) => (
<div
key={message.id}
className={`mb-4 p-3 rounded-lg ${
message.user_id === user.id ? 'bg-blue-50 ml-auto' : 'bg-gray-50'
} max-w-[80%]`}
>
<div className="text-sm font-medium text-gray-500 mb-1">
{message.username}
</div>
<p className="text-gray-800">{message.content}</p>
<div className="text-xs text-gray-400 mt-1 text-right">
{new Date(message.created_at).toLocaleTimeString()}
</div>
</div>
))}
</div>
<form onSubmit={handleSendMessage} className="flex gap-2">
<input
type="text"
value={newMessage}
onChange={(e) => setNewMessage(e.target.value)}
placeholder="输入消息..."
className="flex-1 p-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-blue-500"
/>
<button
type="submit"
className="bg-blue-500 text-white px-4 py-2 rounded-lg hover:bg-blue-600 transition-colors"
>
发送
</button>
</form>
</>
)}
</div>
);
}
关键功能解析
上面的代码实现了几个核心功能:
-
用户认证:通过
AuthButton组件实现用户登录/注销功能,项目中已有此组件,无需额外编写。 -
消息加载:页面加载时从Supabase数据库获取历史消息。
-
实时订阅:使用Supabase的实时订阅功能,当有新消息插入时自动更新消息列表。
-
消息发送:将用户输入的消息发送到Supabase数据库。
-
消息显示:根据消息发送者显示不同的样式,区分自己和他人的消息。
运行应用
完成以上步骤后,启动开发服务器:
npm run dev
# 或
yarn dev
# 或
pnpm dev
在浏览器中访问http://localhost:3000/chat,你应该能看到聊天界面了。使用Supabase的认证功能登录后,就可以与其他用户进行实时聊天了。
功能扩展建议
这个基础聊天应用可以进一步扩展,添加更多功能:
-
私聊功能:添加用户列表和私聊功能,只需添加一个
chat_rooms表和修改实时订阅逻辑。 -
消息删除/编辑:添加消息管理功能,需要更新表结构和权限设置。
-
文件/图片发送:利用Supabase Storage实现文件上传功能。
-
消息通知:添加浏览器通知功能,当收到新消息时提醒用户。
-
在线状态指示:添加用户在线状态显示,可通过Supabase的Presence功能实现。
总结
通过本文的教程,你已经学会了如何使用Next.js和Supabase构建一个简单但功能完整的实时聊天应用。关键技术点包括:
- Next.js的App Router和客户端组件
- Supabase的实时数据库订阅功能
- 用户认证与权限控制
- React状态管理与副作用处理
这个应用虽然简单,但展示了现代Web应用开发的核心概念和最佳实践。你可以基于这个基础继续扩展,构建更复杂的通讯系统。
官方文档和更多示例可以参考:
- Next.js官方文档:docs/
- Supabase集成示例:examples/with-supabase
- 项目源代码:README.md
【免费下载链接】next.js The React Framework 项目地址: https://gitcode.com/GitHub_Trending/next/next.js
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考



