
Sanity CMS 指南:我們如何用它發布 10 種語言的內容
透過 Sanity CMS,我們在 4 個網站和 10 種語言中發布了超過 400 篇內容。以下是我們從 Schema 設計到自動化多語言發布所學到的經驗。
Sanity CMS 是一個無頭內容平台,圍繞著結構化內容、即時 Content Lake(內容湖)以及名為 Sanity Studio 的可客製化 React 編輯器建構而成。它使用 GROQ 進行查詢、Portable Text 處理豐富內容,並採用 schema-as-code(程式碼即結構定義)的方式進行內容建模。本指南將涵蓋設定、Schema 設計、GROQ、Portable Text、多語言架構以及定價方案。
什麼是 Sanity CMS?
Sanity 是一個結構化內容平台,Sanity.io 團隊稱之為「內容作業系統」。與傳統 CMS 將 HTML 區塊儲存在資料庫中不同,Sanity 將每一段內容作為結構化的 JSON 儲存在名為 Content Lake 的託管後端中。你可以使用 GROQ 或 GraphQL 對其進行查詢,並將內容渲染到你想要的任何前端:Next.js、React Native、Svelte、行動應用程式、CLI 工具,皆可。
使用它的公司涵蓋各種規模。Nike、Figma、Puma 和 Cloudflare 都在企業級規模上運行 Sanity。新創公司使用它是因為其免費方案確實可用(稍後會詳細介紹定價)。我們使用它是因為沒有其他工具能給予我們足夠的靈活性,來建構完全自動化的 10 語言發布管線。
Content Lake 架構
Content Lake 是 Sanity 的託管後端。你可以將其視為一個託管的文件儲存庫,能在所有連接的客戶端之間即時同步。當編輯者在 Sanity Studio 中修改一段文字時,另一位編輯者會立即看到變更,無需儲存按鈕、無合併衝突、也無需資料庫遷移。
在底層,文件以帶有型別欄位的結構化 JSON 形式儲存。每個變更都透過交易日誌進行追蹤,因此預設情況下你擁有完整的版本歷史記錄。即時同步使用基於監聽器的架構(在 Sanity 的 GitHub 架構文件 中有描述),透過 RxJS observables 將變更推送到所有訂閱者。
這與例如帶有 REST API 的 PostgreSQL 資料庫有何不同?Content Lake 將內容建模、存取控制、CDN 快取、圖片轉換和即時協作作為單一託管服務處理。你不需要執行遷移,也不需要管理複本。你只需定義 Schema 並查詢內容。
Sanity Studio:你的可客製化編輯器
Sanity Studio 是一個開源的 React 應用程式,作為你的編輯介面。它不是託管的後台面板,而是一個存在於你程式碼庫中的 React 應用程式。你可以客製化它的每一個方面:自訂輸入元件、條件式欄位、文件動作、結構建構器模式以及外掛。
即時協作已內建其中。多位編輯者可以同時處理同一份文件,並具備在線狀態指示器和即時更新。如果你曾使用過 Google Docs,體驗非常相似,你可以即時看到其他人的游標和變更。
我們使用 npx sanity deploy 部署我們的 Studio,這會將其託管在 Sanity 的 CDN 上,並使用自訂子網域。由於它只是一個 React 應用程式,你也可以自行託管。在很大程度上,我們將 Sanity 在 無頭 CMS 比較 中排名靠前,正是因為 Studio 的靈活性。
如何設定 Sanity 專案
要設定 Sanity CMS,請使用 npm create sanity@latest 安裝 CLI,選擇專案範本,設定你的 Schema 檔案,然後執行 npx sanity dev 在本地啟動 Studio。整個過程耗時不到 5 分鐘。
先決條件與安裝
你需要 Node.js 18+ 和 npm(或 pnpm)。僅此而已。執行初始化命令:
npm create sanity@latest
# You'll be prompted for:
# - Login method (Google, GitHub, email)
# - Project name
# - Dataset name (default: "production")
# - Project template (blog, ecommerce, clean)
# - TypeScript? (recommended: yes)CLI 會為你搭建好所需的一切專案結構。以下是專案結構的樣子:
專案結構解析
my-sanity-project/
├── schemas/ # Your content schemas (this is where you'll spend time)
│ ├── index.ts # Schema registry -- imports and exports all types
│ ├── post.ts # Document type definitions
│ └── blockContent.ts # Rich text / Portable Text config
├── sanity.config.ts # Main config -- plugins, Studio structure, dataset
├── sanity.cli.ts # CLI config -- project ID, dataset
├── package.json
└── tsconfig.jsonsanity.config.ts 檔案是你的入口點。以下是一個最小的配置:
// sanity.config.ts
import { defineConfig } from 'sanity'
import { structureTool } from 'sanity/structure'
import { visionTool } from '@sanity/vision'
import { schemaTypes } from './schemas'
export default defineConfig({
name: 'default',
title: 'My Blog',
projectId: 'your-project-id',
dataset: 'production',
plugins: [structureTool(), visionTool()],
schema: { types: schemaTypes },
})visionTool() 外掛提供了一個 Studio 內的 GROQ 遊樂場,你在開發過程中會頻繁使用它。
部署你的 Studio
使用 npx sanity dev 在本地啟動(運行於 localhost:3333)。當你準備好與編輯者分享時,將其部署到 Sanity 的 CDN:
npx sanity deploy
# Prompts for a hostname, e.g., "my-blog"
# Deploys to https://my-blog.sanity.studio專業提示:在任何 Schema 變更後,執行 npx sanity@latest schema deploy。這會將你的 Schema 上傳到 Sanity 的 API,從而啟用諸如 GraphQL API 和感知 Schema 的工具(包括我們稍後將介紹的 MCP 伺服器)等功能。
Sanity CMS 中的 Schema 設計
Sanity Schema 在你的程式碼庫中定義為 JavaScript 或 TypeScript 物件。每個 Schema 指定一個帶有欄位、驗證規則和自訂輸入元件的文件類型。對 Schema 的更改是即時的,無需資料庫遷移。這就是「schema-as-code」方法,也是讓我們放棄 Contentful 轉而選擇 Sanity 的關鍵因素。
欄位類型與驗證
Sanity 提供了豐富的欄位類型。以下是我們最常用的幾種:
| 欄位類型 | 使用案例 | 範例 |
|---|---|---|
string | 短文字、標題、Slug | 文章標題、作者名稱 |
text | 多行純文字 | 摘要、描述 |
number | 整數、浮點數 | 閱讀時間、排序順序 |
boolean | 開關 | 精選標誌、草稿狀態 |
array | 列表、豐富文字(Portable Text) | 正文內容、標籤 |
reference | 連結到其他文件 | 作者、分類 |
image | 帶有元數據的圖片 | 帶有替代文字的封面圖片 |
slug | URL 友好字串 | 從標題自動生成 |
object | 巢狀欄位群組 | SEO 欄位(metaTitle + metaDescription) |
date / datetime | 日期 | 發布日期 |
每個欄位都支援透過 validation 回呼函數進行驗證。你可以強制要求必填欄位、最小/最大值、正規表達式模式和自訂規則:
defineField({
name: 'seoDescription',
title: 'Meta Description',
type: 'string',
validation: (Rule) =>
Rule.required()
.min(145)
.max(160)
.warning('Meta description should be 145-160 characters'),
})自訂區塊類型(我們的生產環境範例)
這裡是 Sanity 變得有趣的地方,也是其他 6 篇競爭指南中沒有一篇展示程式碼的地方。在我們的生產環境 Schema 中,我們在 body 陣列內定義了五種自訂區塊類型:block(標準文字)、table、codeBlock、chartBlock 和 inlineImage。
這是我們的 codeBlock 定義:
// schemas/objects/codeBlock.ts
import { defineType } from 'sanity'
export const codeBlock = defineType({
name: 'codeBlock',
title: 'Code Block',
type: 'object',
fields: [
{
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'JavaScript', value: 'javascript' },
{ title: 'TypeScript', value: 'typescript' },
{ title: 'Python', value: 'python' },
{ title: 'Bash', value: 'bash' },
{ title: 'JSON', value: 'json' },
{ title: 'GROQ', value: 'groq' },
],
},
},
{
name: 'code',
title: 'Code',
type: 'text',
},
],
})以下是 body 欄位如何引用所有自訂類型:
// schemas/fields/body.ts
defineField({
name: 'body',
title: 'Body',
type: 'array',
of: [
{ type: 'block' }, // Standard Portable Text (paragraphs, headings, lists)
{ type: 'table' }, // @sanity/table plugin
{ type: 'codeBlock' }, // Our custom code block
{ type: 'chartBlock' }, // Data visualization (bar, line, pie)
{ type: 'inlineImage' }, // Images with alt text and captions
],
})這為我們的編輯者提供了豐富的内容工具包,同時保持每個元素都有型別且可查詢。chartBlock 不僅僅是一個不透明的 HTML 嵌入,它是具有 chartType、title、dataPoints 和 dataLabels 欄位的結構化數據。當你需要在網頁、電子郵件和行動裝置上渲染相同內容時,這一點至關重要。
Schema 組織最佳實踐
保持 Schema 模組化。我們按類型將其拆分到不同文件中:schemas/documents/post.ts、schemas/objects/codeBlock.ts、schemas/objects/chartBlock.ts。在 schemas/index.ts 中匯入它們:
// schemas/index.ts
import { post } from './documents/post'
import { codeBlock } from './objects/codeBlock'
import { chartBlock } from './objects/chartBlock'
import { inlineImage } from './objects/inlineImage'
export const schemaTypes = [post, codeBlock, chartBlock, inlineImage]我們從處理結構化內容中獲得的關鍵見解是:你的 Schema 就是你的內容模型。如果你將其視為內容團隊的情境工程,你會做出更好的設計決策。你添加的每個欄位都應有其目的,無論是為了編輯者、渲染還是查詢。
GROQ:Sanity 的查詢語言
GROQ(Graph-Relational Object Queries)是 Sanity 的開源查詢語言,用於過濾、連接和投影 JSON 文件。基本語法是 *[filter]{projection},選擇所有符合過濾條件的文件,然後塑造輸出格式。對於 Sanity 特定的查詢,它比 GraphQL 更簡潔,而且根據我們的經驗,學習速度更快。
基本查詢:過濾與投影
最簡單的查詢獲取特定類型的所有文件:
// Fetch all posts -- just title and slug
*[_type == "post"]{
title,
"slug": slug.current
}
// Filter by language, expand author reference
*[_type == "post" && language == "en"]{
title,
"slug": slug.current,
"authorName": author->name,
"authorImage": author->image,
"categoryTitle": category->title,
publishedAt
}-> 運算子遵循引用。author->name 意味著「跟随 author 引用並返回 name 欄位」。無需單獨查詢、無 N+1 問題、無 JOIN,全部包含在一個表達式中。
連接、排序與分頁
對於我們的部落格索引頁面,我們需要帶有擴展引用的有序、分頁文章:
// Paginated posts with full metadata
*[_type == "post" && language == "en"] | order(publishedAt desc) [0...10] {
title,
"slug": slug.current,
excerpt,
publishedAt,
readTime,
"author": author->{name, image},
"category": category->{title, "slug": slug.current},
"coverImage": coverImage{
"src": asset->url,
alt
}
}[0...10] 給你前 10 個結果(從 0 開始索引,結束不包含)。| order(publishedAt desc) 按最新優先排序。投影塑造輸出以包含前端確切需要的內容,不多不少。
你可以使用 Sanity Studio 內的 Vision 外掛 互動式測試所有這些查詢。這在開發期間非常有價值。如需更多模式,請查看 GROQ 速查表。
GROQ 與 GraphQL
Sanity 同時支援 GROQ 和 GraphQL。何時該使用哪一個?
GROQ 是 Sanity 的原生語言。它在單個查詢字串中處理連接、投影和計算欄位。這是 Content Lake 優化的方向。
GraphQL 在你部署 Schema 後可用(npx sanity@latest schema deploy)。當你需要標準化工具時使用它,例如,如果你的前端已經使用 Apollo Client,或者你的團隊熟悉 GraphQL 但不熟悉 GROQ。
我們 exclusively 使用 GROQ。它對於 Sanity 數據更具表現力,而且 Vision 外掛使調試查詢變得輕而易舉。
Portable Text:正確處理豐富內容
Portable Text 是 Sanity 針對結構化豐富文字的規範。它不是將內容儲存為 HTML 字串,而是儲存一個由型別區塊组成的陣列,段落、標題、圖片、程式碼片段、表格,每個都作為一個 JSON 物件。這使得內容可以在任何框架、任何平台、任何格式中渲染。
數據結構
以下是段落和程式碼區塊作為 Portable Text JSON 的樣子:
[
{
"_type": "block",
"_key": "a1b2c3",
"style": "normal",
"markDefs": [],
"children": [
{
"_type": "span",
"_key": "d4e5f6",
"text": "Here's an example of our pipeline config:",
"marks": []
}
]
},
{
"_type": "codeBlock",
"_key": "g7h8i9",
"language": "typescript",
"code": "export default defineConfig({ ... })"
}
]每個區塊都有 _type 和 _key。標準文字區塊使用 "block" 並帶有子跨度(支援粗體、斜體和連結等標記)。自訂區塊,如我們的 codeBlock、chartBlock、table 和 inlineImage,使用它們自己的 _type 並攜帶結構化欄位。
為什麼這很重要?因為 HTML 是一種渲染格式,而不是儲存格式。如果你在資料庫中儲存 <h2>Title</h2><p>Some <strong>text</strong></p>,你就將自己鎖定在網頁渲染中。你無法乾淨地將其提取用於行動應用程式、電子郵件通訊、PDF 或 AI 代理的情境窗口。Portable Text 將內容與呈現分離。Portable Text 規範 是開源的,並非 Sanity 專屬綁定。
生產環境中的自訂區塊
我們的管線使用 Python 腳本(scripts/md_to_portable_text.py)將 Markdown 轉換為 Portable Text。轉換器處理標準區塊,以及我們的四種自訂類型:
table,使用@sanity/table外掛 Schema。行和單元格作為結構化數據儲存。codeBlock,語言和程式碼作為單獨的欄位, enabling 渲染時的語法高亮。chartBlock,圖表類型、標題、軸標籤、系列名稱和數據點作為結構化 JSON。前端使用 Chart.js 渲染這些內容。inlineImage,替代文字、來源和可選標題作為單獨的欄位。
這種結構意味著我們可以查詢部落格中的所有程式碼範例(*[body[]._type == "codeBlock"]),查找帶有圖表的文章,或提取所有缺少替代文字的圖片,全部透過 GROQ 完成。
渲染 Portable Text
在前端,使用 @portabletext/react(或 Svelte/Vue 等效庫)。你為每種區塊類型註冊自訂元件:
import { PortableText } from '@portabletext/react'
const components = {
types: {
codeBlock: ({ value }) => (
<pre className={`language-${value.language}`}>
<code>{value.code}</code>
</pre>
),
chartBlock: ({ value }) => <Chart data={value} />,
inlineImage: ({ value }) => (
<figure>
<img src={value.src} alt={value.alt} />
{value.caption && <figcaption>{value.caption}</figcaption>}
</figure>
),
},
}
// In your component:
<PortableText value={post.body} components={components} />這就是完整的渲染管線。PortableText 元件自動處理標準區塊(段落、標題、列表、標記)。你只需為自訂類型定義自訂元件。
使用 Sanity CMS 進行多語言內容
Sanity 透過文件級本地化(每種語言單獨的文件,通過規範引用連結)或欄位級本地化(一個文件內的翻譯欄位)來支援多語言內容。文件級本地化更適合 SEO 和大規模發布,這也是我們在 10 語言管線中使用的方法。
文件級與欄位級本地化
| 方面 | 文件級 | 欄位級 |
|---|---|---|
| 方法 | 每種語言單獨的文件 | 所有翻譯集中在一個文件中 |
| SEO | 每個文件有自己的 URL/Slug | 單一 URL,難以提供每種語言的頁面 |
| 查詢複雜度 | 簡單過濾:language == "de" | 巢狀欄位訪問:title.de |
| 內容大小 | 小而專注的文件 | 包含所有語言的大型文件 |
| 最適合 | 部落格文章、頁面、SEO 驅動的內容 | 小型 UI 字串、標籤、元數據 |
| 我們的結論 | 我們在所有內容上使用此方法 | 僅用於共享的 UI 字串 |
我們選擇文件級本地化,因為每個翻譯都有自己的 slug、自己的 URL 和自己的元數據。一篇關於 Supabase vs Firebase 的文章的土耳其語版本獲得 slug supabase-firebase-karsilastirma,這是正確的土耳其語,而不是 URL 參數 hack。
我們的 10 語言管線架構
以下是我們的自動化管線工作方式:我們用英語撰寫一篇文章,然後將其翻譯成另外 9 種語言(德語、法語、荷蘭語、西班牙語、土耳其語、義大利語、瑞典語、挪威語、阿拉伯語)。每個翻譯都經過 Markdown 轉換、Portable Text 生成和 Sanity API 發布。
架構如下所示:
- 撰寫,帶有 YAML frontmatter 的英語 Markdown
- 翻譯,AI 翻譯成 9 種語言(驗證完整性和變音符號)
- 轉換,Python 腳本將每個
.md文件轉換為 Portable Text JSON - 發布,呼叫 Sanity API:建立文件、上傳圖片、修補引用
每個文件都有一個 language 欄位和一個指向英語原文的 canonicalPost 引用。以下是獲取一篇文章及其所有翻譯的 GROQ 查詢:
// Fetch a post and all its translations
*[_type == "post" && slug.current == "sanity-cms-guide" && language == "en"][0]{
title,
language,
"translations": *[
_type == "post" &&
canonicalPost._ref == ^._id
]{
title,
language,
"slug": slug.current
}
}Schema 方面很直接,一個 language 欄位帶有支援語言的枚舉:
defineField({
name: 'language',
title: 'Language',
type: 'string',
options: {
list: [
{ title: 'English', value: 'en' },
{ title: 'German', value: 'de' },
{ title: 'French', value: 'fr' },
{ title: 'Dutch', value: 'nl' },
{ title: 'Spanish', value: 'es' },
{ title: 'Turkish', value: 'tr' },
{ title: 'Italian', value: 'it' },
{ title: 'Swedish', value: 'sv' },
{ title: 'Norwegian', value: 'no' },
{ title: 'Arabic', value: 'ar' },
],
},
validation: (Rule) => Rule.required(),
})我們艱難學到的一個陷阱:先發布英語文件,然後使用已發布的文件 ID(而非 drafts. 前綴)修補翻譯上的 canonicalPost 引用。Sanity 在內部將草稿和已發布文件視為單獨的實體。
有關此管線如何連接到 Model Context Protocol 的更多詳情,請參閱下一節。
Sanity AI 功能:MCP、Canvas 和代理情境
Sanity 將自己定位為 AI 時代的內容作業系統。關鍵 AI 功能包括供 AI 代理讀寫內容的 MCP 伺服器、Studio 內 AI 輔助編輯的 Canvas,以及供生產級 AI 代理在意識到 Schema 的情況下查詢結構化內容的 Agent Context。
MCP 伺服器整合
Sanity MCP 伺服器讓 AI 代理(Claude Code、Cursor、Windsurf 等)以程式化方式與你的 Sanity 工作區互動。代理可以讀取 Schema、執行 GROQ 查詢、建立文件和管理內容,無需自訂 API 包裝器。
我們在日常內容管線中使用 Sanity MCP 伺服器。我們的 AI 代理查詢 Schema 以了解文件結構,獲取現有文章以尋找內部連結機會,並發布新文件。MCP 協議賦予代理 Schema 意識,它們知道存在哪些欄位、預期什麼類型以及適用哪些驗證規則。如果你正在為 商業 AI 代理 工作流程建構工具,這是一個強大的模式。
生產級 AI 的 Agent Context
Agent Context 是針對生產級 AI 整合的獨立功能。與專為開發者工具設計的 MCP 伺服器不同,Agent Context 為需要在運行時查詢內容的 AI 代理提供唯讀、範圍限定的訪問權限,想想聊天機器人、推薦引擎或內容個人化系統。
區別很重要:MCP 用於構建時和編輯工作流程(感知 Schema 的開發工具),而 Agent Context 用於具有適當身份驗證和速率限制的運行時內容訪問。
Sanity 的結構化內容在此處提供了真正的優勢。WordPress 網站將內容儲存為 HTML 區塊,AI 代理必須解析 HTML 才能理解內容。Sanity 儲存帶有定義 Schema 的型別 JSON 文件。代理可以查詢 *[_type == "product" && category == "electronics"]{name, price, features} 並獲得乾淨、結構化的數據。無需爬取、無需解析、無需猜測。
我們在 Techsy 如何使用 Sanity
這不是假設性的章節。我們在 4 個生產網站上運行 Sanity CMS,透過我們在過去一年建構的自動化管線以 10 種語言發布。以下是架構。
我們的內容管線架構
管線從研究到跨越所有 10 種語言的發布文章:
- 研究,關鍵字分析、競爭對手缺口識別、SERP 模式
- 簡報,帶有章節指導、字數、內部連結的結構化寫作規格
- 撰寫,產生帶有 YAML frontmatter 的英語 Markdown
- 轉換,Python 腳本將 Markdown 轉換為帶有我們 5 種自訂區塊類型的 Portable Text JSON
- 發布,呼叫 Sanity API:
createOrReplace文件、將圖片上傳到 Sanity CDN、修補作者/分類引用 - 翻譯,AI 翻譯成 9 種語言,驗證完整性
- 發布翻譯,每種語言執行相同的轉換/發布流程,並將
canonicalPost引用修補到英語原文
自訂 Schema 支援 block、table、codeBlock、chartBlock 和 inlineImage 類型,全部定義為帶有驗證規則的生產級 Sanity Schema 物件。在我們測試過的 新創公司 AI 工具 中,這個基於 Sanity 的管線在大規模結構化內容方面是最可靠的。
從 400+ 篇發布文章中汲取的教訓
幾件我們希望有人早告訴我們的事:
引用修補順序很重要。 Sanity 引用不能指向尚不存在的文件。先發布英語文章,然後建立翻譯,讓 canonicalPost 指向英語文件的已發布 ID。我們早期曾多次破壞這一點。
Schema 部署是按工作區進行的。 如果你運行多個 Sanity 專案(我們運行 4 個),你需要分別將 Schema 部署到每個專案:每個專案配置執行 npx sanity@latest schema deploy。
免費方案是真實的。 我們在免費方案上運行了四個網站中的兩個達數月之久。20 個用戶、每月 50 萬次 API 請求、10 萬次 CDN 請求,這足以支撐一個真正的生產網站,而不僅僅是玩具專案。
Portable Text 轉換是瓶頸。 Markdown 到 Portable Text 的轉換並不簡單。巢狀列表、塊引用內的表格、帶有特殊字元的程式碼區塊,到處都是邊緣情況。我們對轉換器腳本進行了數月的迭代。
需要幫助為你的專案設定 Sanity 嗎?我們已為 4 個生產網站建構了多語言內容管線。獲取免費諮詢
Sanity CMS 定價細目
Sanity 提供三種方案:Free(20 個用戶,每月 50 萬次 API 請求)、Growth(每位用戶每月 15 美元,帶有進階角色和排程草稿)和 Enterprise(自訂定價,帶有 SLA 和合規性功能)。免費方案是無頭 CMS 市場中最慷慨的。
| 功能 | Free | Growth ($15/用戶/月) | Enterprise |
|---|---|---|---|
| 用戶 | 20 | 50 | 無限 |
| API 請求 | 50 萬/月 | 250 萬/月 | 自訂 |
| CDN 請求 | 10 萬/月 | 50 萬/月 | 自訂 |
| 角色 | 僅 Admin | Admin, Developer, Editor, Contributor | 自訂角色 |
| 協作 | 即時編輯 | + 排程發布、草稿 | + 工作流程 |
| 支援 | 社群 | 電子郵件 | 專屬 + SLA |
| 合規性 | , | , | SOC 2, HIPAA |
在免費方案上,我們運行兩個網站而未達到限制。$15/用戶/月 的 Growth 方案增加了基於角色的訪問權限(當我們擁有非技術編輯者時這很重要)和排程發布。Growth 方案中的觀察者是免費的,這是一個很好的貼心設計,你不會因為給予利益相關者讀取權限而受到懲罰。
這與競爭對手相比如何?
| 功能 | Sanity Free | Contentful Free | Strapi Cloud Free | Payload Cloud |
|---|---|---|---|---|
| 用戶 | 20 | 1 | 1 | 1 |
| 內容類型 | 無限 | 48 | 無限 | 無限 |
| API 呼叫 | 50 萬/月 | 包含 | 包含 | 包含 |
| 自訂類型 | 是 | 有限 | 是 | 是 |
| 成長價格 | $15/用戶/月 | $300/月 | $29/月 | $50/月 |
Sanity 的 20 用戶免費方案非常出色。Contentful 在免費方案中限制你為 1 個用戶,並在其 Team 方案中跳躍至每月 300 美元。如果你是一家新創公司或小團隊,Sanity 的免費方案讓你可以在不花費任何資金的情況下運行真正的生產工作負載。
Sanity 還提供一個新創公司計畫,為符合条件的新創公司提供一年的免費 Growth 訪問權限。如果你符合資格,值得申請。
常見問題
什麼是 Sanity CMS,它是如何工作的?
Sanity CMS 是一個無頭內容平台,將結構化的 JSON 文件儲存在名為 Content Lake 的託管後端中。你透過 Sanity Studio(一個可客製化的 React 應用程式)編輯內容,使用 GROQ 或 GraphQL 查詢內容,並在任何前端框架中渲染它。內容在所有連接的客戶端之間即時同步。
Sanity CMS 是免費的嗎?
是的。Sanity 的免費方案包括 20 個用戶、每月 50 萬次 API 請求和 10 萬次 CDN 請求,這是無頭 CMS 平台中最慷慨的免費方案。Growth 方案每位用戶每月收費 15 美元,並增加基於角色的訪問權限、排程發布和更高的限制。Enterprise 定價為自訂。
Sanity 和 Contentful 有什麼區別?
Sanity 使用 schema-as-code(Schema 存在於你的程式碼庫中)、GROQ 進行查詢,以及完全可客製化的開源 Studio。Contentful 使用基於 GUI 的內容建模、GraphQL,以及客製化程度較低的託管編輯器。Sanity 的免費方案包括 20 個用戶,而 Contentful 僅限 1 個。Contentful 擁有更大的外掛市場。
Sanity CMS 適合初學者嗎?
Sanity Studio 對內容編輯者來說直觀易懂,編輯體驗無需技術知識。然而,設定 Schema 需要具備 JavaScript 或 TypeScript 能力。Sanity 提供出色的文件、專案範本和活躍支援的社群 Slack。從 npm create sanity@latest 和部落格範本開始。
我可以自行託管 Sanity 嗎?
Sanity Studio 完全可以自行託管,因為它是一個開源的 React 應用程式。你可以將其部署到 Vercel、Netlify 或任何靜態託管提供商。Content Lake 後端是一項託管服務,數據層沒有自行託管選項。這是一個權衡:你獲得零基礎設施管理,但沒有本地數據控制權。
Sanity 使用什麼類型的資料庫?
Sanity 的 Content Lake 不是傳統的 SQL 或 NoSQL 資料庫。它是一個託管的文件儲存庫,將內容儲存為結構化 JSON,頂部帶有 GROQ 查詢層。你不直接與底層資料庫互動,而是透過 Sanity 的 API 進行互動。文件具有完整的版本歷史記錄和內建的即時同步。
Sanity CMS 是開源的嗎?
Sanity Studio 在 MIT 許可證下是開源的,你可以分叉、客製化和自行託管它。Content Lake 後端是專有的 SaaS。GROQ 查詢語言規範也是開源的,發布在 GitHub 上。Portable Text 規範也是開源的,由 portabletext.org 維護。
Sanity 中的 Portable Text 是什麼?
Portable Text 是 Sanity 針對結構化豐富文字的規範。它不是將內容儲存為 HTML 字串,而是將段落、標題、圖片和自訂區塊表示為陣列中的型別 JSON 物件。這使得內容可以在框架和平台之間移植。你可以定義自訂區塊類型,如程式碼片段、圖表和表格,並帶有它們自己的結構化欄位。
什麼是 GROQ,它與 GraphQL 有何不同?
GROQ(Graph-Relational Object Queries)是 Sanity 的原生查詢語言。其語法 *[filter]{projection} 對於 Sanity 數據比 GraphQL 更簡潔,內建支援透過 -> 運算子進行連接和計算欄位。GraphQL 也可供偏好標準化工具或已經使用 Apollo Client 的團隊使用。
Sanity 如何處理多語言內容?
Sanity 支援文件級本地化(每種語言單獨的文件,通過規範引用連結)和欄位級本地化(一個文件內的翻譯欄位)。文件級本地化更適合 SEO,因為每個翻譯都有自己的 URL 和元數據。我們使用文件級本地化,透過自動化翻譯和發布管線跨越 10 種語言進行發布。