开源CMS对比:Ghost vs Strapi vs Directus选型
内容管理系统(CMS)是构建网站和应用的基础设施。随着 Headless 架构的流行,传统CMS正在向 API 优先的方向演进。Ghost、Strapi 和 Directus 是当前最受关注的三个开源CMS方案,它们各有侧重,适用于不同场景。本文将全面对比这三个方案。
三大CMS概览
Ghost:极致的博客出版平台
Ghost 专注于博客和出版领域,由 WordPress 前团队成员创建,以出色的写作体验和邮件订阅功能著称。
# 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核心特点:
Strapi:灵活的Headless CMS
Strapi 是最流行的开源 Headless CMS,开发者可以自由定义内容类型和API结构。
# 快速创建 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核心特点:
Directus:数据优先的无头CMS
Directus 的理念是"包裹你的数据库",它不会创建自己的数据表结构,而是直接在你现有的数据库上提供API和管理界面。
# 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核心特点:
核心维度对比
1. 内容建模
# 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"}
}
}-- 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 能力
# 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"]# 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 } } }
}
}
}
}
"""# 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. 认证与权限
// 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();# 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. 扩展性
// 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',
}
]
};// 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 的场景
选择 Directus 的场景
综合对比表
| 维度 | 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)
暂无评论,快来抢沙发吧!