基于PocketBase创建项目

By | 2026-01-11

创建一个基于 PocketBase 的博客项目非常简单,因为它将数据库、文件存储和后台管理全部集成到了一个只有 10MB左右 的单文件中。


🛠️ 第一阶段:PocketBase 后端部署

1. 下载与运行

  1. 前往 PocketBase 官网 下载适合你系统的版本(Windows, Linux, macOS)。
  2. 将下载的压缩包解压,你会得到一个 pocketbase.exe (Windows) 或 pocketbase (Linux/Mac) 的文件。
  3. 启动服务:在终端/命令行中进入该目录,运行:
    ./pocketbase serve
  1. 此时终端会显示:
  • REST API: http://127.0.0.1:8090/api/
  • Admin UI: http://127.0.0.1:8090/_/

2. 初始化管理员

  1. 打开浏览器,访问 http://127.0.0.1:8090/_/
  2. 第一次进入会要求设置管理员邮箱密码

3. 创建数据表 (Collections)

  1. 点击左侧的 “Collections”,点击 “New collection”
  2. Name: 输入 posts
  3. Fields (字段设置)
  • title: 类型 Plain text (选中 Non-empty)。
  • content: 类型 Plain text (用于存储 Markdown 内容)。
  1. API Rules (最关键的一步)
  • 点击 “API Rules” 选项卡。
  • “List/Search Rule”“View/Read Rule” 的输入框清空(点击右侧的锁图标使其变亮)。
  • 这允许任何人在不登录的情况下读取文章列表。
  1. 点击 “Save Changes”

🌐 第二阶段:前端页面开发

pocketbase 可执行文件的同级目录下创建一个名为 pb_public 的文件夹。在这个文件夹内创建 index.html,并将以下代码粘贴进去:

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <title>PocketBase 快捷博客</title>
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/sakura.css/css/sakura.css">
    <script src="https://cdn.jsdelivr.net/npm/pocketbase/dist/pocketbase.umd.js"></script>
    <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
    <style>
        .key-hint { display: none; background: #ff4444; color: white; padding: 2px 8px; border-radius: 4px; margin-right: 10px; font-family: monospace; }
        .show-hints .key-hint { display: inline-block; }
        .post-item { margin-bottom: 20px; display: flex; align-items: center; }
        #help-modal { display:none; position:fixed; top:0; left:0; width:100%; height:100%; background:rgba(0,0,0,0.8); z-index:99; justify-content:center; align-items:center; }
        #help-modal .box { background:white; padding:20px; border-radius:8px; color:#333; max-width:400px; }
    </style>
</head>
<body>
    <nav><a href="#" onclick="router.list()">🏠 首页</a> | <small>按 <b>Ctrl + /</b> 查看帮助</small></nav>
    <hr>
    <input type="text" id="search-box" placeholder="搜索... (快捷键: /)" style="width:100%;">
    <main id="app"></main>

    <div id="help-modal"><div class="box"><h3>⌨️ 快捷键</h3><p><b>Space</b>: 开关数字显示<br><b>Ctrl+1~9</b>: 跳转文章<br><b>Ctrl+H</b>: 回首页<br><b>/</b>: 搜索</p><button onclick="this.parentElement.parentElement.style.display='none'">关闭</button></div></div>

    <script>
        const pb = new PocketBase('/'); // 自动连接当前服务器
        const app = document.getElementById('app');

        const router = {
            async list(q = '') {
                app.innerHTML = '加载中...';
                const filter = q ? `title ~ "${q}" || content ~ "${q}"` : '';
                const records = await pb.collection('posts').getFullList({ sort: '-created', filter });

                let html = '<h2>文章列表</h2>';
                records.forEach((r, i) => {
                    const hint = (i + 1) < 10 ? (i + 1) : 0;
                    html += `<div class="post-item">
                        <span class="key-hint">Ctrl+${hint}</span>
                        <h3 style="display:inline"><a href="#" class="post-link" onclick="router.detail('${r.id}')">${r.title}</a></h3>
                    </div>`;
                });
                app.innerHTML = html || '<p>无数据</p>';
            },
            async detail(id) {
                const r = await pb.collection('posts').getOne(id);
                app.innerHTML = `<h1>${r.title}</h1><hr><div>${marked.parse(r.content)}</div>`;
            }
        };

        // 快捷键拦截
        window.addEventListener('keydown', (e) => {
            const ctrl = e.ctrlKey || e.metaKey;
            if (ctrl && e.key === '/') { e.preventDefault(); const m = document.getElementById('help-modal'); m.style.display = m.style.display === 'flex' ? 'none' : 'flex'; }
            if (ctrl && e.key === 'h') { e.preventDefault(); router.list(); }
            if (document.activeElement.tagName !== 'INPUT') {
                if (e.key === '/') { e.preventDefault(); document.getElementById('search-box').focus(); }
                if (e.code === 'Space') { e.preventDefault(); document.body.classList.toggle('show-hints'); }
                if (ctrl && e.key >= '0' && e.key <= '9') {
                    const idx = (e.key === '0') ? 9 : parseInt(e.key) - 1;
                    const links = document.querySelectorAll('.post-link');
                    if(links[idx]) { e.preventDefault(); links[idx].click(); }
                }
            }
        }, true);

        document.getElementById('search-box').oninput = (e) => router.list(e.target.value);
        router.list();
    </script>
</body>
</html>

🚀 第三阶段:运行与发布

  1. 访问项目
    由于你把 index.html 放到了 pb_public 文件夹中,PocketBase 会自动托管它。直接访问:
    http://127.0.0.1:8090
  2. 发布文章
    前往 Admin UI (http://127.0.0.1:8090/_/) 手动添加几条数据到 posts 表,首页就会实时更新。

📝 维护指南

  • 数据迁移:如果你要更换服务器,只需拷贝整个文件夹(包含 pocketbase 程序和 pb_data 文件夹)。pb_data 存放了所有的文章内容和配置。
  • 静态资源:所有的图片上传(如果你在 Admin UI 上传图片)都会存放在 pb_data/storage 中。