用Sanity CMS在Next.js中进行内容管理

2,270 阅读8分钟

Next.js是一个React框架,旨在通过路由、TypeScript支持以及网站和应用程序的静态和服务器混合渲染等内置功能来扩展React的功能。

虽然Next.js应用程序中的内容可以是硬编码,从JSON文件中读取,或从数据库中消费,但从Sanity CMS等内容管理系统(CMS)中获取内容可以为开发人员提供更灵活的创作。

在这篇文章中,我们将看看如何使用Next.js和Sanity CMS(一种无头的CMS),在Jamstack驱动的Web应用或网站中提供更好的内容管理。

什么是Sanity CMS?

Sanity CMS是一个无头CMS,它是一个与表现层或客户端脱钩的内容创作后端系统。它通过API将存储的内容作为数据公开,以便在网站、移动应用和物联网等不同平台上消费,它与WordPress等传统CMS截然不同。

Sanity CMS采取了结构化的内容创作方法,提供了简洁的功能,通过其图像管道管理图像,通过Portable Text管理文字,以及设计。它还提供了Sanity Studio,一个用React构建的功能齐全、可定制、可扩展的编辑器。

无头CMS的好处

无头CMS尽职尽责地体现了Jamstack架构的解耦方法,其优点如下。

安全性

由于后端系统与表现层分离,因此暴露在安全威胁面前的区域较小。

数据可重复使用

无头内容通过API以数据形式暴露,因此与平台无关。

结构化内容

内容建模是无头CMS的核心,要求DeepL 内容是一流的公民,这意味着它们可以根据任何应用程序的具体需求进行结构化。

更容易编辑

内容和代码之间的分离使内容编辑更容易专注于内容编辑,开发人员更容易专注于代码。

开发者体验

在无头CMS生态系统中,被称为头部,是内容在头部被消费的地方。它与正文无关,正文是内容编写和存储的地方,可以由开发人员酌情挑选。

改进构建时间

众所周知,当Jamstack网站包含相当大的内容时,其构建时间很长。虽然有一些解决方案,如增量静态再生(ISR)增量构建分布式持久化渲染(DPR),但在无头CMS中,将内容与头部分离可以解决构建时间长的问题。

用Sanity CMS管理内容

为了说明在Jamstack架构中使用Next.js和Sanity CMS,我们将建立一个简单的登陆页面,以了解Sanity的基本内容管理流程,包括在Next.js中使用其命令行界面(CLI)和操作其模式。

跟随这个提交,包括样式和默认的登陆页面内容是硬编码的。我们的最终产品将看起来像下面的图片。

Next.js And Sanity CMS Final Project Page

我们疯狂的方法将是。

  1. 在Sanity Studio上创建和发布内容
  2. 在我们的Next.js应用程序中获取无头内容作为动态内容的数据

创建并运行Next.js应用程序

首先,创建一个Next.js应用程序,并对其内容进行硬编码。

首先用create-next-app 创建一个Next.js应用程序,并运行此命令。

yarn create next-app client

接下来,用以下命令运行Next.js应用程序。

cd client
yarn dev

访问位于http://localhost:3000 的Next.js应用程序。

Default Next.js App running On http://localhost:3000

http://localhost:3000 上运行的默认 Next.js 应用程序。

现在我们已经成功创建了一个带有硬编码内容的Next.js应用程序,现在是时候在Sanity Studio中创建和运行一个项目了。

创建并运行一个Sanity Studio项目

在进行下一步之前,请确保你有一个Sanity账户,如果你没有,请创建一个。

接下来,用下面的命令全局安装Sanity CLI

yarn add @sanity/cli --global

这将安装必要的工具,以便通过CLI与Sanity一起工作。

在Sanity CLI安装完毕后,用以下命令创建一个新的Sanity项目。

sanity init

当这个命令运行时,会出现一个交互式的问答环节,用于创建一个Sanity Studio项目。我们可以为这个项目的相应问题输入以下答案。

  • 对于Select project to use 输入Create new project
  • 对于Your project name: 输入cms
  • 对于Use the default dataset configuration? 输入Yes
  • 对于Select project template 的输入Clean project with no predefined schemas

Answers to the question and answer prompt when setting up a Sanity Studio project

设置Sanity Studio项目时的问题和答案提示的答案

虽然我们用Sanity CLI创建了一个Sanity Studio项目,但我们也可以选择使用其中的一个启动器

为了运行Sanity项目,在3333端口用以下命令启动Sanity Studio。

cd cms
sanity start -p 3333

然后访问Next.js应用程序 [http://localhost:3333](http://localhost:3333)并根据提示登录到Sanity.io。

Visiting the Next.js app page

我们目前有一个空的模式,它必须被更新。

编辑Sanity.io的模式

模式是Sanity.io中结构化内容建模的核心,指的是文档由哪些字段类型组成,如文档图像对象引用。这些类型设计了Sanity.io上的内容与元数据属性,如nametypedescription

我们可以按照这个提交来添加所需的模式。

着陆页需要两个模式,所以我们将在Sanity.io项目根部的schemas 目录中创建模式homepage.jssiteheader.js

用以下内容更新schemas/homepage.js

// sanity/schemas/homepage.js
export default {
  name: 'homepage',
  title: 'Homepage',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Homepage title',
      type: 'string',
      description: "What's the title of the homepage hero?",
    },
    {
      name: 'subtitle',
      title: 'Homepage subtitle',
      type: 'string',
      description: "What's the subtitle of the homepage hero?",
    },
    {
      name: 'image',
      title: 'Homepage image',
      type: 'image',
    },
    {
      name: 'cta',
      description: "What's URL for the homepage CTA?",
      title: 'CTA',
      type: 'slug',
      options: {
        maxLength: 200,
      },
      validation: (Rule) => [Rule.required().error('Field cannot be empty')],
    },
  ],
};

我们现在已经创建了一个document 模式,其中包括字段title,subtitle,image, 和行动呼吁(CTA)。CTA 字段还包括一个validation 函数,确保该字段不能为空**。**

我们可以看到name,title, 和type 的重复模式来描述内容。type 决定了在Sanity Studio编辑器中会生成什么样的字段,如下图所示。

Generating Fields in Sanity Studio Editor

同时,更新schemas/siteheader.js

// schemas/siteheader.js
export default {
  name: 'siteheader',
  title: 'Site Header',
  type: 'document',
  fields: [
    {
      name: 'title',
      title: 'Site header title',
      type: 'string',
    },
    {
      name: 'repoURL',
      title: 'Repo URL',
      type: 'slug',
    },
  ],
};

接下来,导入homepagesiteheader 架构,并将其添加到schema.js 的架构列表中。

// schemas/schema.js
import createSchema from 'part:@sanity/base/schema-creator';
import schemaTypes from 'all:part:@sanity/base/schema-type';

// Import both schemas
import homepage from './homepage';
import siteheader from './siteheader';

export default createSchema({
  // We name our schema
  name: 'default',
  types: schemaTypes.concat([
    /* Append to the list of schemas */
    homepage,
    siteheader,
  ]),
});

Sanity.io项目应该更新以反映这些模式,这时可以为homepagesiteheader 模式创建和发布内容。

Create and Publish The Homepage and Siteheader Schemas

现在我们已经在Sanity Studio上创建并发布了内容,现在是在Next.js中消费数据的时候了。

在Next.js中从Sanity CMS获取数据

这就是我们在无头内容和表现层(也分别称为头部和主体)之间架起的桥梁**。**

通过从Sanity.io获取以数据形式暴露的自创内容,我们可以用它来动态填充登陆页面的相关部分。我们可以按照这个提交

通过@sanity/client包连接Next.js应用,我们可以在Sanity.io上获取发布的数据。让我们用以下命令在我们的Next.js应用中安装@sanity/client

yarn add @sanity/client

有了这个包,我们就可以从Next.js与Sanity.io对接。但首先,我们必须给它提供一个配置属性的对象。

在Next.js项目中,创建一个新的lib 文件夹,里面有一个新文件sanity.js

// /lib/sanity.js
import sanityClient from '@sanity/client';

// See the image above on how to get your projectId and add a new API token
// I added one called "landing page"
const client = sanityClient({
  projectId: 'your-project-id',
  dataset: 'production',
  token: 'api-token', // or leave blank to be anonymous user
  useCdn: false, // `false` if you want to ensure fresh data
  ignoreBrowserTokenWarning: true,
});

export default client;

这些配置值应作为环境变量存储和访问。

Configuration Values Of Environmental Variables

用GROQ在Next.js中获取Sanity.io数据

现在,我们必须通过@sanity/client ,以获取数据。 [getStaticProps](https://nextjs.org/docs/basic-features/data-fetching#getstaticprops-static-generation)使用Sanity的开源查询语言Graph-Relational Object Queries(GROQ)。我们可以通过这个提交来进行跟踪。

要继续下去,我们必须在pages/index.js 中进行两项更新。

首先,在lib/sanity.js 中导入由sanityClient 返回的client 对象。

import client from '../lib/sanity';

接下来,将下面的代码附加到pages/index.js

// Create a query called siteHeaderQuery
const siteHeaderQuery = `*\[_type == "siteheader"\][0] {
  title,
  repoURL {
    current
  }
}`;

// Create a query called homepageQuery
const homepageQuery = `*\[_type == "homepage"\][0] {
  title,
  subtitle,
  "ctaUrl": cta {
    current
        },
  image {
    ...asset->
  }
}`;

export async function getStaticProps() {
  const homepageData = await client.fetch(homepageQuery);
  const siteHeaderData = await client.fetch(siteHeaderQuery);

  const data = { homepageData, siteHeaderData };

  return {
    props: {
      data,
    },
    revalidate: 1,
  };
}

getStaticProps 返回的data 将作为Home 组件上的一个道具可用。

export default function Home({ data }) {
  const { siteHeaderData, homepageData } = data;

  // Check your browser console for output
  console.log({siteHeaderData, homepageData});

  // rest of component
}

如果一切顺利,我们应该准备好动态更新Home 组件中的登陆页面内容。

登陆页面的图片最初是作为静态资产public 文件夹中获取的。

<img className="homepage-img" src="/glass-building-at-the-end-of-a-shadowed-street.jpg" alt="" />

但现在它已被更新为从Sanity.io获取数据。

<img className="homepage-img" src={homepageData.image.url} alt={homepageData.subtitle} />

利用Sanity.io和Next.js向前迈进

本文所分享的主题和观点是使用Sanity.io进行结构化内容管理的基础,以及它如何与Next.js整合。在这篇文章中,完整的存储库被分成独立的提交,但你可以在GitHub上查看完整的最终演示存储库。

Sanity.io还提供了比本帖所展示的更多的特性和功能,官方文档是一个开始学习更多内容的好地方。

The postContent management in Next.js with Sanity CMSappeared first onLogRocket Blog.