开源CMS对比:Ghost vs Strapi vs Directus选型

开源CMS对比:Ghost vs Strapi vs Directus选型

内容管理系统(CMS)是构建网站和应用的基础设施。随着 Headless 架构的流行,传统CMS正在向 API 优先的方向演进。GhostStrapiDirectus 是当前最受关注的三个开源CMS方案,它们各有侧重,适用于不同场景。本文将全面对比这三个方案。

三大CMS概览

Ghost:极致的博客出版平台

Ghost 专注于博客和出版领域,由 WordPress 前团队成员创建,以出色的写作体验和邮件订阅功能著称。

bash
# Docker 部署 Ghost
docker run -d --name ghost \
  -p 2368:2368 \
  -e url=http://localhost:2368 \
  -e database__client=mysql \
  -e database__connection__host=db \
  -e database__connection__user=ghost \
  -e database__connection__password=secret \
  -e database__connection__database=ghost \
  ghost:latest

核心特点:

  • 基于 Node.js,性能优异

  • 内置会员系统和邮件订阅

  • 拥有精美的编辑器(基于Mobiledoc)

  • 支持 Headless 模式,提供 Content API

  • 内置 SEO 和社交分享功能
  • Strapi:灵活的Headless CMS

    Strapi 是最流行的开源 Headless CMS,开发者可以自由定义内容类型和API结构。

    bash
    # 快速创建 Strapi 项目
    npx create-strapi-app@latest my-project --quickstart
    
    # 或使用 Docker
    docker run -d --name strapi \
      -p 1337:1337 \
      -e DATABASE_CLIENT=postgres \
      -e DATABASE_HOST=db \
      -e DATABASE_PORT=5432 \
      -e DATABASE_NAME=strapi \
      -e DATABASE_USERNAME=strapi \
      -e DATABASE_PASSWORD=secret \
      strapi/strapi:latest

    核心特点:

  • 完全可自定义的内容类型构建器(Content-Type Builder)

  • 自动生成 REST 和 GraphQL API

  • 支持多种数据库(PostgreSQL、MySQL、SQLite)

  • 灵活的权限和角色管理

  • 丰富的插件生态系统
  • Directus:数据优先的无头CMS

    Directus 的理念是"包裹你的数据库",它不会创建自己的数据表结构,而是直接在你现有的数据库上提供API和管理界面。

    bash
    # Docker 部署 Directus
    docker run -d --name directus \
      -p 8055:8055 \
      -e KEY=your-key \
      -e SECRET=your-secret \
      -e ADMIN_EMAIL=admin@example.com \
      -e ADMIN_PASSWORD=admin123 \
      -e DB_CLIENT=postgres \
      -e DB_HOST=db \
      -e DB_PORT=5432 \
      -e DB_NAME=directus \
      -e DB_USER=directus \
      -e DB_PASSWORD=secret \
      directus/directus:latest

    核心特点:

  • 数据库优先,不侵入现有数据结构

  • 自动生成 REST 和 GraphQL API

  • 支持任意 SQL 数据库

  • 内置文件资产管理

  • 实时数据流(WebSocket)
  • 核心维度对比

    1. 内容建模

    python
    # Strapi: 通过 Content-Type Builder 可视化创建内容类型
    # 示例:创建"文章"内容类型
    {
        "kind": "collectionType",
        "collectionName": "articles",
        "attributes": {
            "title": {"type": "string", "required": true},
            "content": {"type": "richtext"},
            "cover_image": {"type": "media"},
            "author": {"type": "relation", "relation": "manyToOne",
                       "target": "plugin::users-permissions.user"},
            "tags": {"type": "relation", "relation": "oneToMany",
                     "target": "api::tag.tag"},
            "published_at": {"type": "datetime"}
        }
    }

    sql
    -- Directus: 直接在数据库中建表,Directus自动识别
    CREATE TABLE articles (
        id SERIAL PRIMARY KEY,
        title VARCHAR(255) NOT NULL,
        content TEXT,
        cover_image UUID,
        author_id INTEGER REFERENCES users(id),
        published_at TIMESTAMP,
        status VARCHAR(20) DEFAULT 'draft'
    );
    
    -- Directus 自动为此表生成 CRUD API
    -- GET    /items/articles
    -- POST   /items/articles
    -- PATCH  /items/articles/:id
    -- DELETE /items/articles/:id

    | 建模方式 | Ghost | Strapi | Directus |
    |---------|-------|--------|----------|
    | 建模自由度 | 低(固定结构) | 高(可视化构建) | 最高(原生SQL) |
    | 学习曲线 | 低 | 中 | 中 |
    | 现有数据库支持 | 否 | 否 | 是 |
    | 可视化界面 | 是 | 是 | 是 |

    2. API 能力

    python
    # Ghost Content API (只读)
    import requests
    
    # 获取文章列表
    response = requests.get(
        "http://localhost:2368/ghost/api/content/posts/",
        params={
            "key": "your-content-api-key",
            "limit": 10,
            "fields": "title,excerpt,published_at",
            "filter": "tag:technology",
        },
    )
    posts = response.json()["posts"]

    python
    # Strapi REST API
    # 获取文章(支持丰富的查询参数)
    response = requests.get(
        "http://localhost:1337/api/articles",
        params={
            "populate": "*",                    # 填充关联数据
            "filters[title][$contains]": "AI",  # 标题包含AI
            "sort": "published_at:desc",        # 按发布时间倒序
            "pagination[page]": 1,
            "pagination[pageSize]": 10,
        },
        headers={"Authorization": "Bearer your-jwt-token"},
    )
    
    # Strapi GraphQL
    query = """
    query {
      articles(pagination: {limit: 10}, sort: "published_at:desc") {
        data {
          id
          attributes {
            title
            content
            author { data { attributes { username } } }
            tags { data { attributes { name } } }
          }
        }
      }
    }
    """

    python
    # Directus REST API(支持深度过滤和聚合)
    response = requests.get(
        "http://localhost:8055/items/articles",
        params={
            "fields": "id,title,content,author.name,tags.name",
            "filter": '{"_and": [{"title": {"_contains": "AI"}}, {"status": {"_eq": "published"}}]}',
            "sort": "-published_at",
            "limit": 10,
        },
        headers={"Authorization": "Bearer your-token"},
    )
    
    # Directus 聚合查询
    response = requests.get(
        "http://localhost:8055/items/articles",
        params={
            "aggregate": '{"count": "*", "avg": "views"}',
            "groupBy": "author",
        },
    )

    3. 认证与权限

    javascript
    // Strapi: 基于角色的访问控制(RBAC)
    // 在管理界面中配置:
    // - Public: 仅可读取已发布文章
    // - Authenticated: 可创建和编辑自己的文章
    // - Editor: 可编辑所有文章
    // - Admin: 完全访问权限
    
    // 获取 JWT Token
    const loginResponse = await fetch('http://localhost:1337/api/auth/local', {
        method: 'POST',
        headers: { 'Content-Type': 'application/json' },
        body: JSON.stringify({
            identifier: 'user@example.com',
            password: 'password'
        })
    });
    const { jwt } = await loginResponse.json();

    python
    # Directus: 更细粒度的权限控制
    # 可以控制到字段级别的读写权限
    # 支持自定义权限规则
    
    # Directus 角色权限配置示例
    {
        "role": "editor",
        "collection": "articles",
        "permissions": {
            "create": {"fields": ["title", "content", "tags"], "presets": {"author": "$CURRENT_USER"}},
            "read": {"fields": ["*"], "filter": {"_or": [{"author": "$CURRENT_USER"}, {"status": "published"}]}},
            "update": {"fields": ["title", "content"], "filter": {"author": "$CURRENT_USER"}},
            "delete": {"filter": {"author": "$CURRENT_USER"}}
        }
    }

    4. 扩展性

    javascript
    // Strapi: 自定义控制器和路由
    // src/api/article/controllers/article.js
    module.exports = {
        async findPopular(ctx) {
            const entries = await strapi.db.query('api::article.article').findMany({
                where: { views: { $gt: 1000 } },
                orderBy: { views: 'desc' },
                limit: 10,
            });
            return entries;
        }
    };
    
    // 自定义路由
    // src/api/article/routes/article.js
    module.exports = {
        routes: [
            {
                method: 'GET',
                path: '/articles/popular',
                handler: 'article.findPopular',
            }
        ]
    };

    javascript
    // Directus: 使用 Extensions 自定义端点
    // extensions/endpoints/popular-articles/index.js
    export default ({ database }) => {
        return {
            id: 'popular-articles',
            handler: async (req, res) => {
                const articles = await database('articles')
                    .where('views', '>', 1000)
                    .orderBy('views', 'desc')
                    .limit(10);
                res.json(articles);
            }
        };
    };

    选型决策矩阵

    选择 Ghost 的场景


  • 需要一个开箱即用的博客平台

  • 重视写作体验和内容发布

  • 需要会员订阅和邮件营销功能

  • 团队没有太多开发资源做定制

  • 想要快速上线一个内容网站
  • 选择 Strapi 的场景


  • 需要高度自定义的内容结构

  • 前后端分离的Headless架构

  • 需要REST和GraphQL双API

  • 有开发团队进行定制开发

  • 构建企业级内容平台
  • 选择 Directus 的场景


  • 已有现有数据库需要快速提供API

  • 需要数据库优先的方案

  • 需要精细的字段级权限控制

  • 需要实时数据同步能力

  • 构建数据驱动的应用
  • 综合对比表

    | 维度 | Ghost | Strapi | Directus |
    |------|-------|--------|----------|
    | 定位 | 博客/出版平台 | Headless CMS | 数据API平台 |
    | 开发语言 | Node.js | Node.js | Node.js |
    | 数据库 | MySQL/SQLite | 多种SQL | 任意SQL |
    | API类型 | REST(只读) | REST+GraphQL | REST+GraphQL |
    | 内容建模 | 固定 | 可视化构建 | 数据库原生 |
    | 权限系统 | 基础 | RBAC | 字段级精细 |
    | 会员系统 | 内置 | 插件 | 无 |
    | 学习曲线 | 最低 | 中等 | 中等 |
    | GitHub Stars | 48k+ | 62k+ | 28k+ |
    | 适合团队 | 内容团队 | 开发团队 | 全栈团队 |

    总结

    三个CMS各有其最佳使用场景。如果你需要一个专注于内容创作的博客平台,Ghost 是最佳选择,它的写作体验和订阅功能无可替代。如果你需要构建一个灵活的 Headless CMS,Strapi 的可视化内容类型构建器和双API支持让开发效率大幅提升。如果你已有数据库并希望快速为其添加API和管理界面,Directus 的数据库优先理念是最自然的方案。选型的关键在于理解自己的需求:是"内容驱动"还是"数据驱动",这决定了你应该选择哪个工具。开源CMS的价值不仅在于免费,更在于你可以完全掌控自己的内容和数据。

    💬 评论区 (0)

    暂无评论,快来抢沙发吧!