Firebase v9 Firestore如何实现每个用户对单条帖子仅点赞一次
Firestore帖子点赞防重复实现方案
你当前的代码没有存储点赞用户的关联记录,也没有做服务端校验,所以无法限制单用户重复点赞,按以下步骤修改即可:
1. 调整帖子数据结构
原有帖子文档仅存储点赞总数无法判断用户是否已点赞,需要新增数组字段存储所有点赞用户的ID,单条帖子文档结构参考:
content:帖子文案imageUrl:帖子配图地址noLikes:点赞总数字段likedBy:数组类型,存储所有已点赞用户的唯一uid
2. 替换前端点赞逻辑
不要在前端手动计算点赞数累加,用Firestore提供的原子操作避免并发计数错误,同时配合安全规则做拦截。
首先引入需要的Firebase方法:
import { doc, updateDoc, arrayUnion, arrayRemove, increment } from "firebase/firestore"; import { auth, db } from "./你的firebase初始化配置文件路径";
修改点赞处理函数:
const likeHandle = async () => { // 未登录用户直接拦截 const currentUser = auth.currentUser; if (!currentUser) { alert("请登录后再点赞"); return; } const userId = currentUser.uid; const postDocRef = doc(db, "posts", postId); await updateDoc(postDocRef, { likedBy: arrayUnion(userId), // increment是原子自增操作,多用户同时点赞不会出现计数错误 noLikes: increment(1) }); }
3. 配置Firestore安全规则(必做,否则无法防刷)
仅靠前端逻辑无法阻止恶意用户重复刷赞,所有客户端代码都可以被篡改,必须在Firestore服务端配置强制校验规则。
打开Firestore控制台的「安全规则」面板,替换为以下规则(可根据你的业务需求调整读、写帖子的权限,以下仅包含点赞相关的校验逻辑):
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /posts/{postId} { // 点赞操作更新权限校验 allow update: if // 必须是登录用户发起操作 request.auth != null // 点赞操作仅允许修改likedBy、noLikes两个字段,不能篡改帖子其他内容 && request.resource.data.keys().hasOnly(["likedBy", "noLikes"]) // 校验用户之前没有点过赞 && !(request.auth.uid in resource.data.likedBy) // 校验操作后点赞列表包含当前用户ID && request.auth.uid in request.resource.data.likedBy // 校验点赞数仅累加1,不能恶意修改数值 && request.resource.data.noLikes == resource.data.noLikes + 1 // 校验点赞列表仅新增当前用户1条记录,不能批量加其他用户 && request.resource.data.likedBy.size() == resource.data.likedBy.size() + 1; // 以下权限按你的业务需求调整,示例为所有用户可读帖子、登录用户可发帖子 allow read: if true; allow create: if request.auth != null; } } }
配置完规则后,如果已点赞用户重复发起点赞请求,会被Firestore直接拒绝,不会修改任何数据。
4. 前端体验优化(可选)
加载帖子数据时,可以提前判断当前登录用户是否在likedBy列表中,给已点赞的帖子切换高亮样式,同时支持取消点赞功能:
// 判断当前用户是否已点赞 const currentUser = auth.currentUser; const isLiked = post.likedBy?.includes(currentUser?.uid); // 取消点赞逻辑参考 const unlikeHandle = async () => { const currentUser = auth.currentUser; if (!currentUser) return; const postDocRef = doc(db, "posts", postId); await updateDoc(postDocRef, { likedBy: arrayRemove(currentUser.uid), noLikes: increment(-1) }) }
内容的提问来源于stack exchange,提问作者Santi
相关产品推荐
相关产品推荐

