为什么选择 Contentlayer?
在使用 Next.js 构建博客时,你可能会考虑以下几种方式来处理 MDX 内容:
- 直接使用
**@next/mdx**
:简单但缺乏扩展性。
- 无头 CMS:强大但可能带来复杂的依赖管理。
- Contentlayer:提供高性能和类型安全的解决方案。
其中,Contentlayer 脱颖而出,原因如下:
- 类型安全:自动生成 TypeScript 类型,减少代码错误。
- MDX 支持优秀:可轻松集成自定义组件。
- 构建速度快:优化了内容处理。
- 与 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],
});
配置亮点
- 多语言支持:按语言组织文件,例如
content/blog/{language}/
。
- 自动日期管理:基于文件系统时间戳自动生成创建和修改时间。
- 类型安全字段:通过严格的类型定义提升开发效率。
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} />;
}
通过这种方式:
- 在 MDX 中使用自定义组件变得简单。
- 利用 Next.js 的内置功能(如图片优化)。
- 提供扩展性,可随时添加更多组件。
例如,在 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];
}
方法优势
- 类型安全:自动生成 TypeScript 类型,降低错误率。
- 自动化管理:利用文件系统时间戳,简化日期管理。
- 易维护:文件结构清晰,配置简单。
- SEO 优化:动态站点地图提升搜索引擎友好度。
- 开发体验:类型提示和自动补全提升效率。
总结
使用 Contentlayer 和 Next.js 构建多语言博客系统,不仅高效,还极大提升了开发者体验。从自动化日期管理到类型安全支持,再到 SEO 优化,整个系统无缝集成,值得一试!
Comments (暂无评论)