- Published on
第四天 用户注册和匿名评论功能
- Authors
- Name
- 真的假的
Firebase实现用户注册和评论功能
当前静态托管(如 Vercel 的免费套餐)无法直接运行后端服务器和数据库,需要采用一种无服务器(Serverless)或第三方服务的方式来实现。前面第二天已经尝试采用Github自带的Giscus评论系统,虽然可用但是需要github账号登录才能评论,不便于快速评论,下面尝试采用Firebase免费authentication和firestore database实现用户注册和匿名评论功能:
实现步骤:
用户注册:
匿名评论
- 登录谷歌账号新建一个firebase项目:https://firebase.google.com/ ,名称自拟;
- 启用匿名访问:进入项目,Authentication板块中登录方法添加“匿名”;
- 为项目添加一个web应用用于评论功能:如web_comments;
- 进入应用后可查看firebase安装和配置信息:
const firebaseConfig = {
apiKey: '',
authDomain: '',
databaseURL: '',
projectId: '',
storageBucket: '',
messagingSenderId: '',
appId: '',
measurementId: '',
}
- 为项目添加数据库用于储存评论内容:进入项目,Firestore database点击添加数据库,可免费创建一个;
- 本地IDE终端安装firebase SDK,以pycharm为例:
npm install firebase
修改博客前端文件,将firebase评论功能添加到博客页面:
- 根目录创建firebase配置文件,如firebase.js;
import { initializeApp } from 'firebase/app' import { getAuth } from 'firebase/auth' import { getFirestore } from 'firebase/firestore' // 填入你的 Firebase 配置信息 const firebaseConfig = { apiKey: '', authDomain: '', projectId: '', storageBucket: '', messagingSenderId: '', appId: '', measurementId: '', } // 初始化 Firebase const app = initializeApp(firebaseConfig) const auth = getAuth(app) const db = getFirestore(app) // 导出实例,以便在其他文件中使用 export { app, auth, db }
- components目录下添加Firebasecomments组件(如Firebasecomments.tsx),从firebase.js导入配置信息;
import { useState, useEffect } from 'react'; import { getAuth, signInAnonymously } from "firebase/auth"; import { getFirestore, collection, addDoc, serverTimestamp, query, orderBy, onSnapshot, where } from "firebase/firestore"; import { app, auth, db } from '../firebase'; // firebase.js 文件在根目录 interface Comment { id: string; comment: string; timestamp: Date; } export default function FirebaseComments({ slug }: { slug: string }) { // **接收 slug 属性** const [commentText, setCommentText] = useState(''); const [comments, setComments] = useState<Comment[]>([]); const [loading, setLoading] = useState(true); // 匿名登录并获取评论 useEffect(() => { // 匿名登录 signInAnonymously(auth).catch(error => { console.error("Anonymous login failed:", error); }); // 监听评论数据 const q = query( collection(db, "comments"), where("slug", "==", slug), // **新增筛选条件** orderBy("timestamp", "desc") ); const unsubscribe = onSnapshot(q, (querySnapshot) => { const fetchedComments: Comment[] = []; querySnapshot.forEach((doc) => { const data = doc.data(); fetchedComments.push({ id: doc.id, comment: data.comment, timestamp: data.timestamp?.toDate() || new Date(), }); }); setComments(fetchedComments); setLoading(false); }); return () => unsubscribe(); }, [slug]); // **添加 slug 到依赖数组,确保 slug 变化时重新获取评论** const handleCommentSubmit = async () => { if (!commentText.trim()) { alert('评论内容不能为空!'); return; } try { // 检查用户是否已匿名登录 if (!auth.currentUser) { await signInAnonymously(auth); } // 添加评论到 Firestore await addDoc(collection(db, "comments"), { slug: slug, // **新增此行** username: '匿名用户', // 或 'Guest' comment: commentText, timestamp: serverTimestamp(), }); setCommentText(''); alert('评论已成功提交!'); } catch (error) { console.error("提交评论失败:", error); alert('提交评论失败,请稍后再试。'); } }; }
- 修改原有comments.tsx文件,启用上述Firebasecomments组件;
import { Comments as CommentsComponent } from 'pliny/comments' import { useState } from 'react' import siteMetadata from '@/data/siteMetadata' import FirebaseComments from './FirebaseComments' // 导入新的组件 export default function Comments({ slug }: { slug: string }) { const [loadComments, setLoadComments] = useState(false) // 检查是否启用了 pliny 的评论提供商 const isPlinyProvider = siteMetadata.comments?.provider // 决定使用哪种评论组件 const CommentSection = () => { if (isPlinyProvider) { return <CommentsComponent commentsConfig={siteMetadata.comments} slug={slug} /> } else { // 如果没有配置 pliny,则使用 Firebase 评论 return <FirebaseComments slug={slug} /> } } // 默认使用 pliny 的加载按钮 if (!isPlinyProvider) { return <CommentSection /> } return ( <> {loadComments ? ( <CommentSection /> ) : ( <button onClick={() => setLoadComments(true)}>Load Comments</button> )} </> ) }
- 修改评论区显示格式(Firebasecomments.tsx),显示用户名为匿名用户:
<div className="comment-section mt-10"> <h3 className="mb-4 text-2xl font-bold">评论区</h3> <div className="mb-4"> <textarea value={commentText} onChange={(e) => setCommentText(e.target.value)} placeholder="在这里输入评论..." className="focus:ring-primary-500 h-24 w-full rounded-md border p-2 focus:ring-2 focus:outline-none dark:border-gray-600 dark:bg-gray-800" /> <button onClick={handleCommentSubmit} className="bg-primary-500 hover:bg-primary-600 mt-2 rounded-md px-4 py-2 text-white" > 提交 </button> </div> <div className="comments-list"> {loading ? ( <div>加载评论中...</div> ) : ( comments.map((comment) => ( <div key={comment.id} className="border-b p-4 dark:border-gray-700"> {/* 用户名单独在第一行,靠左 */} <p className="mb-1 text-left font-bold text-gray-800 dark:text-white"> {comment.username || '匿名用户'} </p> {/* 评论内容在下一行,靠左对齐(默认行为)*/} <p className="mb-1 text-left">{comment.comment}</p> {/* 时间在再下一行,靠右对齐 */} <p className="text-right text-sm text-gray-500 dark:text-gray-400"> {comment.timestamp.toLocaleString()} </p> </div> )) )} </div> </div>
- 启用firebase评论功能,替代原有Giscus评论系统: 修改data/blog目录下siteMetadata.js文件,将其中comments模块的provider设置为空:
provider: '', // comments.tsx 中直接调用了 FirebaseComments 组件,这里provider为空时启用备用评论组件(firebase),可以填写其他providers: giscus, utterances, disqus
- 通过这种方式,你成功地将 Firebase 评论功能添加到了你的博客中,并且保留了原有versel next.js模板的pliny评论系统的架构。