目录

为什么选择 Contentlayer?

在使用 Next.js 构建博客时,你可能会考虑以下几种方式来处理 MDX 内容:

  • 直接使用 **@next/mdx**:简单但缺乏扩展性。
  • 无头 CMS:强大但可能带来复杂的依赖管理。
  • Contentlayer:提供高性能和类型安全的解决方案。

其中,Contentlayer 脱颖而出,原因如下:

  1. 类型安全:自动生成 TypeScript 类型,减少代码错误。
  2. MDX 支持优秀:可轻松集成自定义组件。
  3. 构建速度快:优化了内容处理。
  4. 与 Next.js 的无缝集成:减少配置工作量。

快速启动:项目配置

安装依赖

首先,安装必要的依赖包:

pnpm add contentlayer next-contentlayer date-fns

配置 Next.js

更新 next.config.js,引入 Contentlayer:

import { withContentlayer } from 'next-contentlayer';

/** @type {import('next').NextConfig} */
const nextConfig = {};

export default withContentlayer(nextConfig);

定义 Contentlayer 配置

Contentlayer 是这个系统的核心。以下是完整的配置:

import { defineDocumentType, makeSource } from 'contentlayer/source-files';
import { statSync } from 'fs';
import { join } from 'path';

const computedFields = {
  language: {
    type: 'string',
    resolve: (doc) => doc._raw.flattenedPath.split('/')[1],
  },
  createdAt: {
    type: 'date',
    resolve: (doc) => {
      const stats = statSync(join('./content', doc._raw.sourceFilePath));
      return stats.birthtime;
    },
  },
  updatedAt: {
    type: 'date',
    resolve: (doc) => {
      const stats = statSync(join('./content', doc._raw.sourceFilePath));
      return stats.mtime;
    },
  },
};

export const Blog = defineDocumentType(() => ({
  name: 'Blog',
  filePathPattern: 'blog/**/*.mdx',
  contentType: 'mdx',
  fields: {
    title: { type: 'string', required: true },
    description: { type: 'string' },
    slug: { type: 'string', required: true },
  },
  computedFields,
}));

export default makeSource({
  contentDirPath: './content',
  documentTypes: [Blog],
});

配置亮点

  1. 多语言支持:按语言组织文件,例如 content/blog/{language}/
  2. 自动日期管理:基于文件系统时间戳自动生成创建和修改时间。
  3. 类型安全字段:通过严格的类型定义提升开发效率。

TypeScript 加持

为了提升类型安全性和开发体验,更新 tsconfig.json

{
  "compilerOptions": {
    "baseUrl": ".",
    "paths": {
      "@/*": ["./*"],
      "contentlayer/generated": ["./.contentlayer/generated"]
    }
  },
  "include": [
    "next-env.d.ts",
    "**/*.ts",
    "**/*.tsx",
    ".next/types/**/*.ts",
    ".contentlayer/generated"
  ]
}

这确保 TypeScript 能正确识别 Contentlayer 生成的类型。

实现自定义 MDX 组件

为了渲染 MDX 内容,我们创建了一个支持自定义组件的客户端组件:

'use client';

import Image from 'next/image';
import { useMDXComponent } from 'next-contentlayer/hooks';

const components = {
  Image, // 使用 Next.js 的 Image 优化图片
};

interface MdxProps {
  code: string;
}

export function Mdx({ code }: MdxProps) {
  const Component = useMDXComponent(code);
  return <Component components={components} />;
}

通过这种方式:

  1. 在 MDX 中使用自定义组件变得简单。
  2. 利用 Next.js 的内置功能(如图片优化)。
  3. 提供扩展性,可随时添加更多组件。

例如,在 MDX 文件中:

<Image src="/path/to/image.jpg" alt="图片" width={800} height={400} />

博客文章的组织结构

所有文章以 MDX 文件存储,并包含简单的 frontmatter:

---
title: 示例标题
description: 示例描述
slug: example
type: Blog
---

文件系统会自动处理创建和修改日期,无需手动管理。

渲染博客文章

通过以下方式在页面中展示博客内容:

import { allBlogs } from 'contentlayer/generated';
import { Mdx } from '@/components/mdx-components';

export default function BlogPost({ params }) {
  const post = allBlogs.find(
    (post) => post.language === params.language && post.slug === params.slug
  );

  if (!post) notFound();

  return (
    <article className="prose dark:prose-invert max-w-none">
      <h1>{post.title}</h1>
      <Mdx code={post.body.code} />
    </article>
  );
}

动态站点地图生成

我们还支持基于博客时间戳的动态站点地图生成:

import { allBlogs } from 'contentlayer/generated';

export default function sitemap() {
  const baseUrl = 'https://notemi.cn';

  const blogUrls = allBlogs.map((blog) => ({
    url: `${baseUrl}/${blog.language}/blog/${blog.slug}`,
    lastModified: blog.updatedAt,
    changeFrequency: 'weekly',
    priority: 0.7,
  }));

  const staticUrls = ['en', 'zh'].map((lang) => ({
    url: `${baseUrl}/${lang}`,
    lastModified: new Date(),
    changeFrequency: 'daily',
    priority: 1,
  }));

  return [...staticUrls, ...blogUrls];
}

方法优势

  1. 类型安全:自动生成 TypeScript 类型,降低错误率。
  2. 自动化管理:利用文件系统时间戳,简化日期管理。
  3. 易维护:文件结构清晰,配置简单。
  4. SEO 优化:动态站点地图提升搜索引擎友好度。
  5. 开发体验:类型提示和自动补全提升效率。

总结

使用 Contentlayer 和 Next.js 构建多语言博客系统,不仅高效,还极大提升了开发者体验。从自动化日期管理到类型安全支持,再到 SEO 优化,整个系统无缝集成,值得一试!