给博客添加计数器

前言 博客计数器是博客一项基本的统计功能,至少从表面上能大概看出有多少人多少次浏览过自己的博客。于是就想为自己的博客添加该项功能,期间踩了许多坑,最后决定直接用cloudflare自己的数据库来保存页面次数即可,然后借助gemini来帮写相关的js文件,多次调试后通过,以下记录一下步骤。 创建数据库 在cloudflare的面板中,选择以下D1数据库,创建一个免费的SQLite数据库,我创建的是my-blog-db, 点击数据库 my-blog-db,切换到 控制台 (Console) 标签页。 执行以下 SQL 语句,创建用于存放阅读量的表格 pv_table: CREATE TABLE pv_table ( url TEXT PRIMARY KEY, pv INTEGER DEFAULT 0 ) Cloudflare 后台关键绑定提醒: 进入 Cloudflare Pages 项目设置 -> 函数 (Functions) -> D1 数据库绑定。 添加绑定,变量名称必须严格填写全大写的 DB,数据库选择你的 my-blog-db。 编写 Pages Functions 后端接口 在 Hugo 博客根目录下,创建一个名为 functions 的文件夹,并在其中创建 pv.js 文件,写入以下后端逻辑: // functions/pv.js export async function onRequest(context) { const { request, env } = context; // 统一配置跨域头(即使同源也加上,确保万无一失) const corsHeaders = { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Methods": "GET, POST, OPTIONS", "Access-Control-Allow-Headers": "Content-Type", "Content-Type": "application/json", }; if (request.method === "OPTIONS") { return new Response(null, { headers: corsHeaders }); } try { const url = new URL(request.url); let pageUrl = url.searchParams.get("url"); const action = url.searchParams.get("action"); if (!pageUrl) { return new Response(JSON.stringify({ error: "Missing url" }), { status: 400, headers: corsHeaders }); } // 解码前端传过来的路径(处理中文路径和全局标识符) pageUrl = decodeURIComponent(pageUrl); // ========================================================== // 核心逻辑:无论是普通文章,还是全局总暗号 "__TOTAL_SITE_PV__",一视同仁 // ========================================================== if (action !== "get") { // 只有不是 "get"(即新访客详情页)时,才触发“无则插入1,有则累加1” await env.DB.prepare(` INSERT INTO pv_table (url, pv) VALUES (?, 1) ON CONFLICT(url) DO UPDATE SET pv = pv + 1 `).bind(pageUrl).run(); } else { // 如果是列表页、冷却期、或者是主页拉取总访问量(带有 action=get) // 确保这条记录在数据库存在(无则初始化为0,有则什么都不动),做到绝对“只读” await env.DB.prepare(` INSERT OR IGNORE INTO pv_table (url, pv) VALUES (?, 0) `).bind(pageUrl).run(); } // 统一读取最新数据并返回 const row = await env.DB.prepare("SELECT pv FROM pv_table WHERE url = ?").bind(pageUrl).first(); const currentPv = row ? row.pv : 0; return new Response(JSON.stringify({ pv: currentPv }), { status: 200, headers: corsHeaders }); } catch (err) { // 线上如果后端报错,直接把错误吐给前端,方便在页面上直观排查 return new Response(JSON.stringify({ pv: "后端报错了: " + err.message }), { status: 200, headers: corsHeaders }); } } 修改前端 post_meta.html 坑位 为了让列表页和详情页都能认准同一条“文章路径”,我们让 Hugo 在编译时直接把文章相对路径硬编码进 HTML 中。 ...