读取文件

首先,我们创建一个posts文件夹,在里面创建.md后缀的markdown文本作为例子。

然后可以使用fs函数读取文件

1
2
3
4
5
6
7
8
9
import fs from "fs";

const getPostMetadata = () => {
const folder = "posts/";
const files = fs.readdirSync(folder);
const markdownPosts = files.filter((file) => file.endsWith('.md'));
const slugs = markdownPosts.map((file) => file.replace(".md", ""));
return slugs;
};

之后可以在对应的page.tsx文件中使用

1
2
3
4
5
6
7
8
const postMetadata = getPostMetadata();
const postPreviews = postMetadata.map((slug) => (
<div key={slug} className="text-white">
<Link href={`/design/posts/${encodeURIComponent(slug)}`}>
{slug}
</Link>
</div>
));

之后直接在div中包裹{postPreviews}即可

fs函数使用的是同步处理没有使用异步,如果需要处理大型文件还是需要使用异步处理。

(PS:这里注意一个点是我使用了encodeURIComponent(),这个函数的作用是用于解码,如果你的某些地方使用中文出现乱码的话需要使用这个函数进行解码)

因为使用Nextjs,所以我们使用动态路由就创建文件夹posts/[slug]/即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface PostPageProps {
params: {
slug: string;
};
}

const getPostContent = (slug: string) => {
const folder = "posts/";
const file = `${folder}${decodeURIComponent(slug)}.md`;
const content = fs.readFileSync(file, "utf8");
return content;
}

const PostPage:React.FC<PostPageProps> = async ({
params
}) => {
const { slug } = await params;
const content = getPostContent(slug);

这是一段示例代码(PS:Nextjs15中动态路由都设置为异步处理了,所以这里必须使用异步,如果使用的是14或之前版本则无此要求)

同理之后也是直接渲染即可

1
2
3
4
<div className="text-white">
This is Post: {decodeURIComponent(slug)}
<Markdown>{content}</Markdown>
</div>

解析markdow文本

可以使用第三方库来帮助解析

我随便使用了一个react-markdown

使用起来非常简单,只需要包裹即可

1
2
3
4
5
import Markdown from "react-markdown";

<article className="markdown">
<Markdown>{post.content}</Markdown>
</article>
  1. Markdown 转换

    • react-markdown 将 Markdown 文本转换为 React 可以渲染的真实 HTML 元素,例如 <h1><p><ul> 等。
  2. Tailwind CSS 的影响

    • Tailwind CSS 会使用 @apply@tailwind 等指令来设置各种样式,但它不会提供默认的标题、段落或列表的样式。因此,使用 Tailwind CSS 后,Markdown 转换后的文本元素可能会看起来很平淡或缺少样式。
  3. 解决方案

    • 为了解决这个问题,建议创建一个名为 .markdown 的 CSS 类,并在你的 index.css 文件中添加如下样式:

      1
      2
      3
      css.markdown > * {
      all: revert;
      }
    • 这段 CSS 的作用是将所有 .markdown 类下的直接子元素的样式恢复为浏览器的默认样式。通过使用 all: revert;,可以让这些元素恢复到没有被 Tailwind CSS 改变的状态。

  4. 实施步骤

    • 创建该 CSS 类后,在使用 ReactMarkdown 组件的地方,将该类添加到组件中,如下所示:

      1
      jsx<ReactMarkdown className="markdown">{content}</ReactMarkdown>

添加代码块和语法高亮

现在我目前使用的library有下面这几个

1
2
3
import remarkGfm from 'remark-gfm';
import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter';
import { solarizedlight } from 'react-syntax-highlighter/dist/esm/styles/prism';

然后在<ReacrMarkdown>标签中添加对应属性,因为我们知道ReactMarkdown的作用是把Markdown语法转换为React元素,所以我们要找到对应的标签进行处理(PS:这里我遇到了Nextjs告诉的

标签中不能包含

标签等一系列问题,如果遇到问题请思考解决)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<article className="markdown overflow-auto p-4 text-gray-200 prose-slate lg:prose-xl">
<ReactMarkdown
remarkPlugins={[remarkGfm]}
components={{
code({ inline, className, children } : any) {
// 检查是否为代码块
const match = /language-(\w+)/.exec(className || '');
return !inline && match ? (
<SyntaxHighlighter style={solarizedlight} language={match[1]}>
{String(children).replace(/\n$/, '')}
</SyntaxHighlighter>
) : (
// 对于内联代码,直接渲染code标签
<code className="bg-slate-400/50 text-orange-600">
{children}
</code>
);
}
}}
>
{post.content}
</ReactMarkdown>
</article>

这里还使用了tailwindcss中的一个插件用于使文本看起来更好看QAQ,位于Offical Plugins中的Typography插件。

对了,上面的那些获取代码可以抽出为文件让page.tsx更干净一些。


to be continued…

剩余研究问题:

1.代码块的展开关闭

2.代码块的复制按钮

3.现在是同步获取,之后也许需要修改为异步获取