👁 #13 页面访问计数(PV)
每篇文章自动记录页面访问量。文章页头部显示 👁 128 次阅读 徽章。每次访问自动 +1,先显示占位符,POST 返回真值后替换。
👍 #17 文章 Emoji 反应
文章底部展示 6 个 emoji 按钮:👍 ❤️ 🚀 😂 🤔 👏。读者点击投票,带弹跳动画。同一 IP 每篇文章每天限投 1 次(6 选 1)。重复点击提示「今天已经投过票啦」。
🗂 文件清单
文件分类操作功能lib/viewStore.ts存储****新增#13lib/reactionStore.ts存储****新增#17lib/githubStore.ts存储****新增共用lib/redis.ts存储****新增共用app/api/views/route.tsAPI****新增#13app/api/reactions/route.tsAPI****新增#17components/ViewCounter.tsxUI****新增#13components/EmojiReactions.tsxUI****新增#17app/posts/[slug]/page.tsx页面****修改共用app/chatter/[slug]/page.tsx页面****修改共用
10 个文件:8 新增 + 2 修改。仅前端,后端无变更。
⚡ 存储架构
优先级
优先级后端触发条件速度1Upstash Redis设 UPSTASH_REDIS_URL + UPSTASH_REDIS_TOKEN~10ms2GitHub API设 GITHUB_STORAGE_TOKEN~1s3本地 JSON 文件都不设(自动降级)瞬间(仅 dev)
Redis 数据结构(Upstash)
PV 计数(Hash) blog:views { "post_xxx": 128, "chatter_xxx": 42 } # Emoji 计数(Hash,每 slug 一个 key) blog:reactions:post_xxx { "👍": 5, "❤️": 3, "🚀": 2 } # IP 投票记录(String,24h 自动过期) blog:reaction:ip:1.2.3.4:post_xxx = "2026-07-13" TTL: 86400s
🌐 API 接口
方法路径说明POST/api/viewsBody {"slug":"xxx"},递增 +1,返回 {"slug":"xxx","views":129}GET/api/views?slug=xxx查询单篇阅读量GET/api/views查询全部阅读量POST/api/reactionsBody {"slug":"xxx","emoji":"👍"},投票 +1GET/api/reactions?slug=xxx查询单篇所有 emoji 计数
⚙️ Vercel 环境变量
变量名必填说明GITHUB_STORAGE_TOKEN可选GitHub Personal Access Token (classic),勾 repoUPSTASH_REDIS_URL推荐Upstash Redis HTTPS 地址UPSTASH_REDIS_TOKEN推荐Upstash Redis Token
**⚡ 最佳配置:**加 UPSTASH_REDIS_URL + UPSTASH_REDIS_TOKEN 走 Redis(10ms 级延迟)。GITHUB_STORAGE_TOKEN 留着当备份,Redis 故障自动降级到 GitHub。
❓ 常见问题
现象原因解决部署后 PV 不变未配置任何存储 Token加 Redis 或 GitHub 环境变量并 Redeploy点击 Emoji 数字回弹同 IP 今天已投过正常行为,明天可再投本地 dev 所有人共享一个 IP127.0.0.1Vercel 部署后每个访客独立 IPUpstash Dashboard 显示 0 命令Redeploy 时未取消缓存Redeploy 时取消勾选 "Use existing Build Cache"

