关于 tinacms
这是用于将 TinaCMS 集成到内容型 Web 应用的实用技能,涵盖多种框架的配置、Schema 设计和故障排查,适合构建支持可视化编辑的 Git 内容网站。
主要功能
- 01各框架配置指南
- 02Schema 与集合建模
- 03可视化编辑集成
- 04配置与错误排查
适用场景
- 01为 Next.js 网站添加 CMS
- 02建模博客或文档内容
- 03为编辑者启用实时预览
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ jackspace/ claudeskillz --skill tinacms安装后在 Claude Code 中输入/tinacms确认已加载。
附带文件README.mdSKILL.json
详细介绍
TinaCMS 技能
用于将 TinaCMS 集成到现代 Web 应用中的完整技能。
什么是 TinaCMS?
TinaCMS 是一个开源、由 Git 支持的无头内容管理系统(CMS),可让开发者和内容创作者在内容丰富的网站上无缝协作。
主要功能
-
由 Git 支持的存储
- 内容以 Markdown、MDX 或 JSON 文件的形式存储在 Git 仓库中
- 完整的版本控制和变更历史
- 不受供应商锁定——内容保存在你的仓库中
-
可视化/上下文编辑
- 并排编辑体验
- 输入时实时预览更改
- 针对 Markdown 内容的类所见即所得编辑
-
由架构驱动的内容建模
- 在代码中定义内容结构(
tina/config.ts) - 根据架构自动生成类型安全的 GraphQL API
- 用于组织内容的集合和字段系统
- 在代码中定义内容结构(
-
灵活部署
- TinaCloud:托管服务(最简单,提供免费层级)
- 自托管:Cloudflare Workers、Vercel Functions、Netlify Functions、AWS Lambda
- 多种身份验证选项(Auth.js、自定义、本地开发)
-
框架支持
- 最佳:Next.js(App Router + Pages Router)
- 良好:React、Astro(实验性的可视化编辑)、Gatsby、Hugo、Jekyll、Remix、11ty
- 框架无关:适用于任何框架(可视化编辑仅限 React)
当前版本
- tinacms:2.9.0(2025 年 9 月)
- @tinacms/cli:1.11.0(2025 年 10 月)
- React 支持:19.x(>=18.3.1 <20.0.0)
何时使用此技能
✅ 适合使用 TinaCMS 的场景:
-
构建内容丰富的网站
- 博客和个人网站
- 文档网站
- 营销网站
- 作品集网站
-
非技术编辑人员需要访问权限
- 不具备编程知识的内容团队
- 管理页面的营销团队
- 撰写博客文章的作者
-
需要基于 Git 的工作流
- 希望通过 Git 对内容进行版本管理
- 需要通过拉取请求审核内容
- 希望将内容与代码一起保存在仓库中
-
需要可视化编辑
- 编辑人员希望实时查看更改
- 偏好所见即所得的体验
- 并排编辑工作流
❌ 不适合使用 TinaCMS 的场景:
-
需要实时协作
- 多个用户同时编辑(类似 Google Docs)
- 改用 Sanity、Contentful 或 Firebase
-
数据高度动态
- 库存频繁变动的电商产品目录
- 实时仪表板
- 改用传统数据库(D1、PostgreSQL)
-
不需要内容管理
- 应用由数据驱动,而非内容驱动
- 硬编码内容已足够
按框架划分的设置模式
根据所选框架使用相应的设置模式。
1. Next.js 设置(推荐)
App Router(Next.js 13+)
步骤:
-
初始化 TinaCMS:
npx @tinacms/cli@latest init- 当提示输入公共资源目录时,输入
public
- 当提示输入公共资源目录时,输入
-
更新 package.json 脚本:
{ "scripts": { "dev": "tinacms dev -c \"next dev\"", "build": "tinacms build && next build", "start": "tinacms build && next start" } } -
设置环境变量:
# .env.local NEXT_PUBLIC_TINA_CLIENT_ID=your_client_id TINA_TOKEN=your_read_only_token -
启动开发服务器:
npm run dev -
访问管理界面:
http://localhost:3000/admin/index.html
创建的关键文件:
tina/config.ts- 架构配置app/admin/[[...index]]/page.tsx- 管理界面路由(如果使用 App Router)
模板:参见 templates/nextjs/tina-config-app-router.ts
Pages Router(Next.js 12 及以下版本)
设置完全相同,但管理路由为:
- 使用
pages/admin/[[...index]].tsx,而不是 app 目录
数据获取模式:
// pages/posts/[slug].tsx
import { client } from '../../tina/__generated__/client'
import { useTina } from 'tinacms/dist/react'
export default function BlogPost(props) {
// Hydrate for visual editing
const { data } = useTina({
query: props.query,
variables: props.variables,
data: props.data
})
return (
<article>
<h1>{data.post.title}</h1>
<div dangerouslySetInnerHTML={{ __html: data.post.body }} />
</article>
)
}
export async function getStaticProps({ params }) {
const response = await client.queries.post({
relativePath: `${params.slug}.md`
})
return {
props: {
data: response.data,
query: response.query,
variables: response.variables
}
}
}
export async function getStaticPaths() {
const response = await client.queries.postConnection()
const paths = response.data.postConnection.edges.map((edge) => ({
params: { slug: edge.node._sys.filename }
}))
return { paths, fallback: 'blocking' }
}
模板:参见 templates/nextjs/tina-config-pages-router.ts
2. Vite + React 设置
步骤:
-
安装依赖:
npm install react@^19 react-dom@^19 tinacms -
初始化 TinaCMS:
npx @tinacms/cli@latest init- 将公共资源目录设置为
public
- 将公共资源目录设置为
-
更新 vite.config.ts:
import { defineConfig } from 'vite' import react from '@vitejs/plugin-react' export default defineConfig({ plugins: [react()], server: { port: 3000 // TinaCMS default } }) -
更新 package.json 脚本:
{ "scripts": { "dev": "tinacms dev -c \"vite\"", "build": "tinacms build && vite build", "preview": "vite preview" } } -
创建管理界面:
选项 A:手动路由(React Router)
// src/pages/Admin.tsx import TinaCMS from 'tinacms' export default function Admin() { return <div id="tina-admin" /> }选项 B:直接使用 HTML
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>Tina CMS</title> <meta name="viewport" content="width=device-width, initial-scale=1" /> </head> <body> <div id="root"></div> <script type="module" src="/@fs/[path-to-tina-admin]"></script> </body> </html> -
使用 useTina hook 进行可视化编辑:
import { useTina } from 'tinacms/dist/react' import { client } from '../tina/__generated__/client' function BlogPost({ initialData }) { const { data } = useTina({ query: initialData.query, variables: initialData.variables, data: initialData.data }) return ( <article> <h1>{data.post.title}</h1> <div>{/* render body */}</div> </article> ) } -
设置环境变量:
# .env VITE_TINA_CLIENT_ID=your_client_id VITE_TINA_TOKEN=your_read_only_token
模板:参见 templates/vite-react/
3. Astro 设置
步骤:
-
使用官方入门模板(推荐):
npx create-tina-app@latest --template tina-astro-starter或手动初始化:
npx @tinacms/cli@latest init -
更新 package.json 脚本:
{ "scripts": { "dev": "tinacms dev -c \"astro dev\"", "build": "tinacms build && astro build", "preview": "astro preview" } } -
配置 Astro:
// astro.config.mjs import { defineConfig } from 'astro/config' import react from '@astro/react' export default defineConfig({ integrations: [react()] // Required for Tina admin }) -
可视化编辑(实验性):
- 需要 React 组件
- 使用
client:tinaDirective进行交互式编辑 - 截至 2025 年 10 月,可视化编辑仍处于实验阶段
-
设置环境变量:
# .env PUBLIC_TINA_CLIENT_ID=your_client_id TINA_TOKEN=your_read_only_token
适用场景:以内容为主的静态网站、文档、博客
模板:参见 templates/astro/
4. 框架无关的设置
适用范围:Hugo、Jekyll、Eleventy、Gatsby、Remix 或任何框架
步骤:
-
初始化 TinaCMS:
npx @tinacms/cli@latest init -
手动配置构建脚本:
{ "scripts": { "dev": "tinacms dev -c \"<your-dev-command>\"", "build": "tinacms build && <your-build-command>" } } -
管理界面:
- 自动创建于
http://localhost:<port>/admin/index.html - 端口取决于所用框架
- 自动创建于
-
数据获取:
- 不支持可视化编辑(仅侧边栏)
- 通过由 Git 提供支持的界面编辑内容
- 更改会直接保存到文件
-
设置环境变量:
TINA_CLIENT_ID=your_client_id TINA_TOKEN=your_read_only_token
限制:
- 不支持可视化编辑(仅限 React 的功能)
- 需要手动集成
- 仅支持基于侧边栏的编辑
Schema 建模最佳实践
在 tina/config.ts 中定义内容结构。
基本配置结构
import { defineConfig } from 'tinacms'
export default defineConfig({
// Branch configuration
branch: process.env.GITHUB_BRANCH ||
process.env.VERCEL_GIT_COMMIT_REF ||
'main',
// TinaCloud credentials (if using managed service)
clientId: process.env.NEXT_PUBLIC_TINA_CLIENT_ID,
token: process.env.TINA_TOKEN,
// Build configuration
build: {
outputFolder: 'admin',
publicFolder: 'public',
},
// Media configuration
media: {
tina: {
mediaRoot: '',
publicFolder: 'public',
},
},
// Content schema
schema: {
collections: [
// Define collections here
],
},
})
集合
集合 = 内容类型 + 目录映射
{
name: 'post', // Singular, internal name (used in API)
label: 'Blog Posts', // Plural, display name (shown in admin)
path: 'content/posts', // Directory where files are stored
format: 'mdx', // File format: md, mdx, markdown, json, yaml, toml
fields: [/* ... */] // Array of field definitions
}
关键属性:
name:内部标识符(仅限字母数字字符和下划线)label:管理界面中显示的易读名称path:相对于项目根目录的文件路径format:文件扩展名(默认为 'md')fields:内容结构定义
字段类型参考
| 类型 | 用途 | 示例 |
|---|---|---|
string | 短文本(单行) | 标题、slug、作者姓名 |
rich-text | 长格式内容 | 博客正文、页面内容 |
number | 数值 | 价格、数量、评分 |
datetime | 日期/时间值 | 发布日期、活动时间 |
boolean | 真/假开关 | 草稿状态、精选标记 |
image | 图片上传 | 主图、缩略图、头像 |
reference | 链接到其他文档 | 作者、类别、相关文章 |
object | 嵌套字段组 | SEO 元数据、社交链接 |
完整参考:参见 references/field-types-reference.md
集合模板
博客文章集合
{
name: 'post',
label: 'Blog Posts',
path: 'content/posts',
format: 'mdx',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true, // Shows in content list
required: true
},
{
type: 'string',
name: 'excerpt',
label: 'Excerpt',
ui: {
component: 'textarea' // Multi-line input
}
},
{
type: 'image',
name: 'coverImage',
label: 'Cover Image'
},
{
type: 'datetime',
name: 'date',
label: 'Published Date',
required: true
},
{
type: 'reference',
name: 'author',
label: 'Author',
collections: ['author'] // References author collection
},
{
type: 'boolean',
name: 'draft',
label: 'Draft',
description: 'If checked, post will not be published',
required: true
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true // Main content area
}
],
ui: {
router: ({ document }) => `/blog/${document._sys.filename}`
}
}
模板:参见 templates/collections/blog-post.ts
文档页面集合
{
name: 'doc',
label: 'Documentation',
path: 'content/docs',
format: 'mdx',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true,
required: true
},
{
type: 'string',
name: 'description',
label: 'Description',
ui: {
component: 'textarea'
}
},
{
type: 'number',
name: 'order',
label: 'Order',
description: 'Sort order in sidebar'
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true,
templates: [
// MDX components can be defined here
]
}
],
ui: {
router: ({ document }) => {
const breadcrumbs = document._sys.breadcrumbs.join('/')
return `/docs/${breadcrumbs}`
}
}
}
模板:参见 templates/collections/doc-page.ts
作者集合(引用目标)
{
name: 'author',
label: 'Authors',
path: 'content/authors',
format: 'json', // Use JSON for structured data
fields: [
{
type: 'string',
name: 'name',
label: 'Name',
isTitle: true,
required: true
},
{
type: 'string',
name: 'email',
label: 'Email',
ui: {
validate: (value) => {
if (!value?.includes('@')) {
return 'Invalid email address'
}
}
}
},
{
type: 'image',
name: 'avatar',
label: 'Avatar'
},
{
type: 'string',
name: 'bio',
label: 'Bio',
ui: {
component: 'textarea'
}
},
{
type: 'object',
name: 'social',
label: 'Social Links',
fields: [
{
type: 'string',
name: 'twitter',
label: 'Twitter'
},
{
type: 'string',
name: 'github',
label: 'GitHub'
}
]
}
]
}
模板:参见 templates/collections/author.ts
着陆页集合(多个模板)
{
name: 'page',
label: 'Pages',
path: 'content/pages',
format: 'mdx',
templates: [ // Multiple templates for different page types
{
name: 'basic',
label: 'Basic Page',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true,
required: true
},
{
type: 'rich-text',
name: 'body',
label: 'Body',
isBody: true
}
]
},
{
name: 'landing',
label: 'Landing Page',
fields: [
{
type: 'string',
name: 'title',
label: 'Title',
isTitle: true,
required: true
},
{
type: 'object',
name: 'hero',
label: 'Hero Section',
fields: [
{
type: 'string',
name: 'headline',
label: 'Headline'
},
{
type: 'string',
name: 'subheadline',
label: 'Subheadline',
ui: { component: 'textarea' }
},
{
type: 'image',
name: 'image',
label: 'Hero Image'
}
]
},
{
type: 'object',
name: 'cta',
label: 'Call to Action',
fields: [
{
type: 'string',
name: 'text',
label: 'Button Text'
},
{
type: 'string',
name: 'url',
label: 'Button URL'
}
]
}
]
}
]
}
使用模板时:文档的 frontmatter 中必须包含 _template 字段:
---
_template: landing
title: My Landing Page
---
模板:参见 templates/collections/landing-page.ts
常见错误与解决方案
1. ❌ ESbuild 编译错误
错误消息:
ERROR: Schema Not Successfully Built
ERROR: Config Not Successfully Executed
原因:
- 导入使用自定义加载器(webpack、babel 插件、esbuild 加载器)的代码
- 导入仅供前端使用的代码(使用
window、DOM API、React 钩子) - 导入整个组件库,而不是特定模块
解决方案:
仅导入所需内容:
// ❌ Bad - Imports entire component directory
import { HeroComponent } from '../components/'
// ✅ Good - Import specific file
import { HeroComponent } from '../components/blocks/hero'
预防建议:
- 尽量减少
tina/config.ts中的导入 - 仅导入类型定义和简单的工具函数
- 避免直接导入 UI 组件
- 如有需要,创建单独的
.schema.ts文件
参考:请参阅 references/common-errors.md#esbuild
2. ❌ 模块解析:“Could not resolve 'tinacms'”
错误消息:
Error: Could not resolve "tinacms"
原因:
- 安装损坏或不完整
- 依赖项之间的版本不匹配
- 缺少对等依赖项
解决方案:
# Clear cache and reinstall
rm -rf node_modules package-lock.json
npm install
# Or with pnpm
rm -rf node_modules pnpm-lock.yaml
pnpm install
# Or with yarn
rm -rf node_modules yarn.lock
yarn install
预防措施:
- 使用锁文件(
package-lock.json、pnpm-lock.yaml、yarn.lock) - 不要使用
--no-optional或--omit=optional标志 - 确保已安装
react和react-dom(即使使用非 React 框架)
3. ❌ 字段命名限制
错误消息:
Field name contains invalid characters
原因:
- TinaCMS 字段名称只能包含字母、数字和下划线
- 不允许使用连字符、空格和特殊字符
解决方案:
// ❌ Bad - Uses hyphens
{
name: 'hero-image',
label: 'Hero Image',
type: 'image'
}
// ❌ Bad - Uses spaces
{
name: 'hero image',
label: 'Hero Image',
type: 'image'
}
// ✅ Good - Uses underscores
{
name: 'hero_image',
label: 'Hero Image',
type: 'image'
}
// ✅ Good - CamelCase also works
{
name: 'heroImage',
label: 'Hero Image',
type: 'image'
}
注意:这是从 Forestry.io 迁移时的破坏性变更
4. ❌ Docker 绑定问题
错误:
- 无法从 Docker 容器外部访问 TinaCMS 管理界面
原因:
- TinaCMS 默认绑定到
127.0.0.1(仅限本地主机) - Docker 容器需要绑定到
0.0.0.0才能接受外部连接
解决方案:
# Ensure framework dev server listens on all interfaces
tinacms dev -c "next dev --hostname 0.0.0.0"
tinacms dev -c "vite --host 0.0.0.0"
tinacms dev -c "astro dev --host 0.0.0.0"
Docker Compose 示例:
services:
app:
build: .
ports:
- "3000:3000"
command: npm run dev # Which runs: tinacms dev -c "next dev --hostname 0.0.0.0"
5. ❌ 缺少 _template 键错误
错误消息:
GetCollection failed: Unable to fetch
template name was not provided
原因:
- 集合使用
templates数组(多个架构) - 文档的 frontmatter 中缺少
_template字段 - 从
templates迁移到fields,但文档尚未更新
解决方案:
选项 1:改用 fields(单个模板时推荐)
{
name: 'post',
path: 'content/posts',
fields: [/* ... */] // No _template needed
}
选项 2:确保 frontmatter 中存在 _template
---
_template: article # ← Required when using templates array
title: My Post
---
迁移脚本(从 templates 转换为 fields 时):
# Remove _template from all files in content/posts/
find content/posts -name "*.md" -exec sed -i '/_template:/d' {} +
6. ❌ 路径不匹配问题
错误:
- 文件未显示在 Tina 管理界面中
- 保存时出现“找不到文件”错误
- GraphQL 查询返回空结果
原因:
- 集合配置中的
path与实际文件目录不匹配 - 相对路径与绝对路径混淆
- 末尾斜杠问题
解决方案:
// Files located at: content/posts/hello.md
// ✅ Correct
{
name: 'post',
path: 'content/posts', // Matches file location
fields: [/* ... */]
}
// ❌ Wrong - Missing 'content/'
{
name: 'post',
path: 'posts', // Files won't be found
fields: [/* ... */]
}
// ❌ Wrong - Trailing slash
{
name: 'post',
path: 'content/posts/', // May cause issues
fields: [/* ... */]
}
调试:
- 运行
npx @tinacms/cli@latest audit检查路径 - 确认文件存在于指定目录中
- 检查文件扩展名是否与
format字段匹配
7. ❌ 构建脚本顺序问题
错误消息:
ERROR: Cannot find module '../tina/__generated__/client'
ERROR: Property 'queries' does not exist on type '{}'
原因:
- 在
tinacms build之前运行框架构建 - TypeScript 编译前未生成 Tina 类型
- CI/CD 流水线顺序不正确
解决方案:
{
"scripts": {
"build": "tinacms build && next build" // ✅ Tina FIRST
// NOT: "build": "next build && tinacms build" // ❌ Wrong order
}
}
CI/CD 示例(GitHub Actions):
- name: Build
run: |
npx @tinacms/cli@latest build # Generate types first
npm run build # Then build framework
原因说明:
tinacms build会在tina/__generated__/中生成 TypeScript 类型- 框架构建需要这些类型才能成功编译
- 顺序错误会导致类型错误
8. ❌ TinaCMS 资源加载失败
错误消息:
Failed to load resource: net::ERR_CONNECTION_REFUSED
http://localhost:4001/...
原因:
- 将开发环境的
admin/index.html推送到生产环境(会从 localhost 加载资源) - 网站部署在子目录中,但未配置
basePath
解决方案:
生产环境部署:
{
"scripts": {
"build": "tinacms build && next build" // ✅ Always build
// NOT: "build": "tinacms dev" // ❌ Never dev in production
}
}
子目录部署:
// tina/config.ts
export default defineConfig({
build: {
outputFolder: 'admin',
publicFolder: 'public',
basePath: 'your-subdirectory' // ← Set if site not at domain root
}
})
CI/CD 修复:
# GitHub Actions / Vercel / Netlify
- run: npx @tinacms/cli@latest build # Always use build, not dev
9. ❌ 引用字段出现 503 Service Unavailable
错误:
- 引用字段下拉菜单超时并返回 503 错误
- 加载引用字段时,管理界面变得无响应
原因:
- 被引用的集合中项目过多(数百或数千个)
- 目前引用字段不支持分页
解决方案:
选项 1:拆分集合
// Instead of one huge "authors" collection
// Split by active status or alphabetically
{
name: 'active_author',
label: 'Active Authors',
path: 'content/authors/active',
fields: [/* ... */]
}
{
name: 'archived_author',
label: 'Archived Authors',
path: 'content/authors/archived',
fields: [/* ... */]
}
选项 2:使用带验证的字符串字段
// Instead of reference
{
type: 'string',
name: 'authorId',
label: 'Author ID',
ui: {
component: 'select',
options: ['author-1', 'author-2', 'author-3'] // Curated list
}
}
选项 3:自定义字段组件(高级)
- 在自定义组件中实现分页
- 请参阅 TinaCMS 文档:https://tina.io/docs/extending-tina/custom-field-components/
部署模式
选择适合你需求的部署方式。
选项 1:TinaCloud(托管)- 最简单 ⭐
适用于:快速设置、免费套餐、托管基础设施
步骤:
- 注册:https://app.tina.io
- 创建项目,获取 Client ID 和 Read Only Token
- 设置环境变量:
NEXT_PUBLIC_TINA_CLIENT_ID=your_client_id TINA_TOKEN=your_read_only_token - 初始化后端:
npx @tinacms/cli@latest init backend - 部署到托管服务商(Vercel、Netlify、Cloudflare Pages)
- 在 Tina 控制面板中设置 GitHub 集成
优点:
- ✅ 无需配置后端
- ✅ 自动提供 GraphQL API
- ✅ 内置身份验证
- ✅ 自动处理 Git 集成
- ✅ 免费套餐额度充足(每月 10k 次请求)
缺点:
- ❌ 超出免费套餐后需要付费
- ❌ 依赖服务商(不过内容仍保存在 Git 中)
参考:参阅 references/deployment-guide.md#tinacloud
选项 2:在 Cloudflare Workers 上自托管 🔥
适用于:完全掌控、Cloudflare 生态、边缘部署
步骤:
-
安装依赖:
npm install @tinacms/datalayer tinacms-authjs -
初始化后端:
npx @tinacms/cli@latest init backend -
创建 Workers 端点:
// workers/src/index.ts import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer' import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs' import databaseClient from '../../tina/__generated__/databaseClient' const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true' export default { async fetch(request: Request, env: Env) { const handler = TinaNodeBackend({ authProvider: isLocal ? LocalBackendAuthProvider() : AuthJsBackendAuthProvider({ authOptions: TinaAuthJSOptions({ databaseClient, secret: env.NEXTAUTH_SECRET, }), }), databaseClient, }) return handler(request) } } -
更新
tina/config.ts:export default defineConfig({ contentApiUrlOverride: '/api/tina/gql', // Your Workers endpoint // ... rest of config }) -
配置
wrangler.jsonc:{ "name": "tina-backend", "main": "workers/src/index.ts", "compatibility_date": "2025-10-24", "vars": { "TINA_PUBLIC_IS_LOCAL": "false" }, "env": { "production": { "vars": { "NEXTAUTH_SECRET": "your-secret-here" } } } } -
部署:
npx wrangler deploy
优点:
- ✅ 完全掌控后端
- ✅ 免费套餐额度充足(每天 100k 次请求)
- ✅ 全球边缘网络(全球访问速度快)
- ✅ 不受服务商锁定
缺点:
- ❌ 设置较复杂
- ❌ 需要配置身份验证
- ❌ 需要了解 Cloudflare Workers
完整指南:参阅 references/self-hosting-cloudflare.md
模板:参阅 templates/cloudflare-worker-backend/
选项 3:在 Vercel Functions 上自托管
适用于:Next.js 项目、Vercel 生态
步骤:
-
安装依赖:
npm install @tinacms/datalayer tinacms-authjs -
创建 API 路由:
// api/tina/backend.ts import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer' import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs' import databaseClient from '../../../tina/__generated__/databaseClient' const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true' const handler = TinaNodeBackend({ authProvider: isLocal ? LocalBackendAuthProvider() : AuthJsBackendAuthProvider({ authOptions: TinaAuthJSOptions({ databaseClient, secret: process.env.NEXTAUTH_SECRET, }), }), databaseClient, }) export default handler -
创建
vercel.json重写规则:{ "rewrites": [ { "source": "/api/tina/:path*", "destination": "/api/tina/backend" } ] } -
更新开发脚本:
{ "scripts": { "dev": "TINA_PUBLIC_IS_LOCAL=true tinacms dev -c \"next dev --port $PORT\"" } } -
在 Vercel 控制面板中设置环境变量:
NEXTAUTH_SECRET=your-secret TINA_PUBLIC_IS_LOCAL=false -
部署:
vercel deploy
优点:
- ✅ 与 Next.js 原生集成
- ✅ Vercel 部署简单
- ✅ 无服务器(自动扩缩)
缺点:
- ❌ 仅适用于 Vercel
- ❌ 函数限制(10s 超时、50MB 大小)
参考:参阅 references/deployment-guide.md#vercel
选项 4:在 Netlify Functions 上自托管
步骤:
-
安装依赖:
npm install express serverless-http @tinacms/datalayer tinacms-authjs -
创建函数:
// netlify/functions/tina.ts import express from 'express' import ServerlessHttp from 'serverless-http' import { TinaNodeBackend, LocalBackendAuthProvider } from '@tinacms/datalayer' import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs' import databaseClient from '../../tina/__generated__/databaseClient' const app = express() app.use(express.json()) const tinaBackend = TinaNodeBackend({ authProvider: AuthJsBackendAuthProvider({ authOptions: TinaAuthJSOptions({ databaseClient, secret: process.env.NEXTAUTH_SECRET, }), }), databaseClient, }) app.post('/api/tina/*', tinaBackend) app.get('/api/tina/*', tinaBackend) export const handler = ServerlessHttp(app) -
创建
netlify.toml:[functions] node_bundler = "esbuild" [[redirects]] from = "/api/tina/*" to = "/.netlify/functions/tina" status = 200 force = true -
部署:
netlify deploy --prod
参考:参阅 references/deployment-guide.md#netlify
身份验证设置
选项 1:本地开发(默认)
用途:本地开发,不部署到生产环境
// tina/__generated__/databaseClient or backend config
const isLocal = process.env.TINA_PUBLIC_IS_LOCAL === 'true'
authProvider: isLocal ? LocalBackendAuthProvider() : /* ... */
环境变量:
TINA_PUBLIC_IS_LOCAL=true
安全性:没有身份验证——仅在本地使用!
选项 2:Auth.js(自托管推荐)
用途:搭配 OAuth 提供商(GitHub、Discord、Google 等)进行自托管
安装:
npm install next-auth tinacms-authjs
配置:
import { AuthJsBackendAuthProvider, TinaAuthJSOptions } from 'tinacms-authjs'
import DiscordProvider from 'next-auth/providers/discord'
export const AuthOptions = TinaAuthJSOptions({
databaseClient,
secret: process.env.NEXTAUTH_SECRET,
providers: [
DiscordProvider({
clientId: process.env.DISCORD_CLIENT_ID,
clientSecret: process.env.DISCORD_CLIENT_SECRET,
}),
// Add GitHub, Google, etc.
],
})
const handler = TinaNodeBackend({
authProvider: AuthJsBackendAuthProvider({
authOptions: AuthOptions,
}),
databaseClient,
})
支持的提供商:GitHub、Discord、Google、Twitter、Facebook、Email 等
参考:https://next-auth.js.org/providers/
选项 3:TinaCloud Auth(托管)
用途:TinaCloud 托管服务
import { TinaCloudBackendAuthProvider } from '@tinacms/auth'
authProvider: TinaCloudBackendAuthProvider()
设置:
- 在 https://app.tina.io 注册
- 创建项目
- 在控制面板中管理用户
- 自动处理身份验证
选项 4:自定义身份验证提供商
用途:已有身份验证系统、自定义需求
const CustomBackendAuth = () => {
return {
isAuthorized: async (req, res) => {
const token = req.headers.authorization
// Your validation logic
const user = await validateToken(token)
if (user && user.canEdit) {
return { isAuthorized: true }
}
return {
isAuthorized: false,
errorMessage: 'Unauthorized',
errorCode: 401
}
},
}
}
authProvider: CustomBackendAuth()
GraphQL API 用法
TinaCMS 会自动生成类型安全的 GraphQL 客户端。
查询数据
TinaCloud:
import client from '../tina/__generated__/client'
// Single document
const post = await client.queries.post({
relativePath: 'hello-world.md'
})
// Multiple documents
const posts = await client.queries.postConnection()
自托管:
import client from '../tina/__generated__/databaseClient'
// Same API as TinaCloud client
const post = await client.queries.post({
relativePath: 'hello-world.md'
})
使用 useTina Hook 进行可视化编辑
Next.js 示例:
import { useTina } from 'tinacms/dist/react'
import { client } from '../../tina/__generated__/client'
export default function BlogPost(props) {
// Hydrate data for visual editing
const { data } = useTina({
query: props.query,
variables: props.variables,
data: props.data
})
return (
<article>
<h1>{data.post.title}</h1>
<p>{data.post.excerpt}</p>
<div dangerouslySetInnerHTML={{ __html: data.post.body }} />
</article>
)
}
export async function getStaticProps({ params }) {
const response = await client.queries.post({
relativePath: `${params.slug}.md`
})
return {
props: {
data: response.data,
query: response.query,
variables: response.variables
}
}
}
工作原理:
- 在生产环境中:
useTina返回初始数据(无额外开销) - 在编辑模式下:
useTina连接到 GraphQL 并实时更新 - 更改会立即显示在预览中
更多资源
模板
templates/nextjs/- Next.js App Router + Pages Router 配置templates/vite-react/- Vite + React 设置templates/astro/- Astro 集成templates/collections/- 预构建的集合架构templates/cloudflare-worker-backend/- Cloudflare Workers 自托管
参考资料
references/schema-patterns.md- 高级架构建模模式references/field-types-reference.md- 完整的字段类型文档references/deployment-guide.md- 适用于所有平台的部署指南references/self-hosting-cloudflare.md- 完整的 Cloudflare Workers 指南references/common-errors.md- 扩展错误排查指南references/migration-guide.md- 从 Forestry.io 迁移
脚本
scripts/init-nextjs.sh- 自动设置 Next.jsscripts/init-vite-react.sh- 自动设置 Vite + Reactscripts/init-astro.sh- 自动设置 Astroscripts/check-versions.sh- 验证软件包版本
官方文档
- 网站:https://tina.io
- 文档:https://tina.io/docs
- GitHub:https://github.com/tinacms/tinacms
- Discord:https://discord.gg/zumN63Ybpf
Token 效率
预计节省:65-70%(节省 10,900 个 token)
不使用技能(约 16,000 个 token):
- 初始研究和探索:3,000 个 token
- 框架设置试错:2,500 个 token
- 架构建模尝试:2,000 个 token
- 错误排查:4,000 个 token
- 部署配置:2,500 个 token
- 身份验证设置:2,000 个 token
使用技能(约 5,100 个 token):
- 技能发现:100 个 token
- 加载技能(SKILL.md):3,000 个 token
- 模板选择:500 个 token
- 少量项目特定调整:1,500 个 token
可预防的错误
此技能可预防 9 种常见错误(预防率为 100%):
- ✅ ESbuild 编译错误(导入问题)
- ✅ 模块解析问题
- ✅ 字段命名约束违规
- ✅ Docker 绑定问题
- ✅ 缺少
_template键错误 - ✅ 路径不匹配问题
- ✅ 构建脚本顺序错误
- ✅ 生产环境中的资源加载错误
- ✅ 引用字段 503 超时
快速入门示例
示例 1:使用 Next.js + TinaCloud 构建博客
# 1. Create Next.js app
npx create-next-app@latest my-blog --typescript --app
# 2. Initialize TinaCMS
cd my-blog
npx @tinacms/cli@latest init
# 3. Set environment variables
echo "NEXT_PUBLIC_TINA_CLIENT_ID=your_client_id" >> .env.local
echo "TINA_TOKEN=your_token" >> .env.local
# 4. Start dev server
npm run dev
# 5. Access admin
open http://localhost:3000/admin/index.html
示例 2:使用 Astro 构建文档站点
# 1. Use official starter
npx create-tina-app@latest my-docs --template tina-astro-starter
# 2. Install dependencies
cd my-docs
npm install
# 3. Start dev server
npm run dev
# 4. Access admin
open http://localhost:4321/admin/index.html
示例 3:在 Cloudflare Workers 上自托管
# 1. Initialize project
npm create cloudflare@latest my-app
# 2. Add TinaCMS
npx @tinacms/cli@latest init
npx @tinacms/cli@latest init backend
# 3. Install dependencies
npm install @tinacms/datalayer tinacms-authjs
# 4. Copy Cloudflare Workers backend template
cp -r [path-to-skill]/templates/cloudflare-worker-backend/* workers/
# 5. Configure and deploy
npx wrangler deploy
生产环境示例
- TinaCMS 网站:https://tina.io(使用自家产品)
- Astro 起始模板:https://github.com/tinacms/tina-astro-starter
- Next.js 起始模板:https://github.com/tinacms/tina-starter-alpaca
支持
遇到问题? 请先查看 references/common-errors.md
仍然无法解决?
- Discord:https://discord.gg/zumN63Ybpf
- GitHub Issues:https://github.com/tinacms/tinacms/issues
- 官方文档:https://tina.io/docs
最后更新:2025-10-24 技能版本:1.0.0 TinaCMS 版本:2.9.0 CLI 版本:1.11.0