使用 Next.js 和 Contentlayer 构建多语言博客系统
7 min read
·在这篇文章中,我将分享如何使用 Next.js 和 Contentlayer 构建多语言博客系统。我们将从项目设置开始,一直到实现自动日期管理和动态站点地图生成。
为什么选择 Contentlayer?
在使用 Next.js 构建博客时,有几种处理 MDX 内容的方法:
- 直接使用 
@next/mdx - 使用无头 CMS
 - 使用 Contentlayer
 
我选择 Contentlayer 是因为它提供:
- 类型安全的内容
 - 出色的 MDX 支持
 - 快速的构建时间
 - 与 Next.js 的简单集成
 
项目设置
首先,我们需要安装必要的依赖:
pnpm add contentlayer next-contentlayer date-fns
然后,更新 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],
});
这个配置的主要特点:
- 多语言支持:博客文章按语言组织在 
content/blog/{language}/目录中 - 自动日期管理:使用文件系统时间戳作为创建和修改日期
 - 类型安全字段:所有字段都有适当的类型定义,提供更好的开发体验
 
TypeScript 配置
为了获得更好的类型安全性和开发体验,我们需要配置 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 内容,我们创建一个客户端 MDX 组件:
'use client';
import Image from 'next/image';
import { useMDXComponent } from 'next-contentlayer/hooks';
// 定义在 MDX 文件中使用的自定义组件
const components = {
  Image, // 使用 Next.js 的 Image 组件来优化图片
  // 在这里添加更多自定义组件
};
interface MdxProps {
  code: string;
}
export function Mdx({ code }: MdxProps) {
  const Component = useMDXComponent(code);
  return <Component components={components} />;
}
这个实现允许我们:
- 在 MDX 文件中使用自定义组件
 - 利用 Next.js 优化的组件(如 Image)
 - 在需要时添加客户端交互功能
 
要在 MDX 文件中使用自定义组件,只需将它们作为 JSX 元素使用:
<Image src="/path/to/image.jpg" alt="我的图片" width={800} height={400} />
博客文章结构
博客文章以 MDX 文件存储,使用简单的 frontmatter:
---
title: 文章标题
description: 简短描述
slug: article-slug
type: Blog
createdAt: '2024-11-18 21:31:58'
updatedAt: '2024-11-18 21:31:58'
---
注意我们不需要手动管理日期 - 它们由文件系统自动处理!
渲染博客文章
以下是我们在 Next.js 页面中渲染博客文章的方式:
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';
import { MetadataRoute } from 'next';
export default function sitemap(): MetadataRoute.Sitemap {
  const baseUrl = 'https://yuanzhixiang.com';
  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];
}
这种方法的优势
- 类型安全:Contentlayer 为我们的内容生成 TypeScript 类型
 - 自动日期管理:无需手动管理日期
 - 易于维护:简单的文件结构和配置
 - SEO 友好:带有适当时间戳的自动站点地图生成
 - 开发者体验:通过类型提示和自动完成提供出色的 IDE 支持
 
总结
使用 Contentlayer 和 Next.js 构建多语言博客系统是一次愉快的体验。自动日期管理和类型安全功能显著减少了维护负担,而与 Next.js 的 App Router 的集成使系统快速且对 SEO 友好。