构建 Next.js Markdown 博客。
图像
注意:这是一个高级主题,所以我假设您已经熟悉 React、JavaScript 和 Web 开发的基础知识。
Next.Js
Nextjs 是一个React框架。它是最受欢迎的框架,因为它易于使用、非常灵活,并且拥有强大的基于文件的路由系统。它提供开箱即用的服务器端渲染功能。
让我们深入探讨
如果你不想一起写代码,只想看代码,请查看源代码
我不得不为我的个人作品集网站创建博客。网上虽然有一些文章,但我找不到任何简单的解决方案。所以我决定写一篇关于这个问题的简单文章。让我们开始吧
要创建 nextjs 应用程序,请在终端中运行以下命令
npm init next-app
# or
yarn create next-app
您可以使用npm
或yarn
包管理器,但我将使用yarn
给你的项目命名。包管理器将安装所有必要的包。
运行此命令
cd YOUR_PROJECT_NAME
启动项目
yarn dev
您的项目应该在端口3000上线。您应该看到类似这样的内容
太棒了。pages/index.js
删除所有内容并粘贴以下代码
import React from "react";
const Index = () => {
return <h1>My First Blog ✍ </h1>;
};
export default Index;
在文件夹根目录创建一个文件config.json
,并提供网站标题和描述。(此步骤用于 SEO 目的)。
{
"title": "Nextjs Blog Site",
"description": "A Simple Markdown Blog build with Nextjs."
}
在根目录中创建一个名为 的文件夹。我们的文件将存放content
在这里。.md
现在你的文件夹结构应该如下所示
components目录将包含我们的博客逻辑
内容目录将包含我们的 markdown 文件
pages目录包含我们的页面(路线)
用于提供静态文件(资产)的公共目录
让我们打开pages/index.js
并导入网站标题和描述config.json
import React from "react";
const Index = (props) => {
console.log("Index -> props", props);
return <h1>My First Blog ✍ </h1>;
};
export default Index;
export async function getStaticProps() {
const siteData = await import(`../config.json`);
return {
props: {
title: siteData.default.title,
description: siteData.default.description,
},
};
}
保存此页面后,您应该在浏览器的控制台中看到类似这样的内容。
Index -> props {title: "Nextjs Blog Site", description: "A Simple Markdown Blog build with Nextjs."}
。
好吧,刚才发生了什么?我们来分析一下
getStaticProps
getStaticProps是 Nextjs 函数,我们可以从中调用它page
。它会将 props 返回给我们的组件。就像props
我们index
我们稍后将使用此方法来获取我们的帖子。
内容将在构建时生成。如果您不明白这是什么意思,不用担心,只需记住,内容在构建前即可使用,我们不会每次用户访问我们的网站时都获取帖子。很酷吧?
我们正在导入我们的文件并返回组件的config.json
标题和描述props
index
Next.js 还为我们提供了Head
可以将元素附加到head
页面的组件,例如网站标题、meta
标签links
等。
import Head from 'next/head'
<Head>
<title>My page title</title>
<meta name="viewport" content="initial-scale=1.0, width=device-width" />
</Head>
让我们将其添加到我们的Index
页面
import React from "react";
import Head from "next/head";
const Index = (props) => {
return (
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<meta name="Description" content={props.description}></meta>
<title>{props.title}</title>
</Head>
<h1>My First Blog ✍ </h1>;
</>
);
};
export default Index;
export async function getStaticProps() {
const siteData = await import(`../config.json`);
return {
props: {
title: siteData.default.title,
description: siteData.default.description,
},
};
}
添加后Head
,查看浏览器的标签页,你看到了什么?网站标题已更新。
理想情况下,您可能希望将其放入布局组件,但在我们的例子中,我认为这很好。
现在回到我们的博客。我们需要在项目中添加一些包。运行以下命令yarn add react-markdown gray-matter raw-loader
或者
npm install react-markdown gray-matter raw-loader
react-markdown将帮助我们解析和渲染 markdown 文件
gray-matter将解析我们博客的前言。(文件顶部之间的部分---
)
我们需要这些元数据,包括、title
和。您可以在此处添加任何您喜欢的内容(例如英雄图片的 URL)。data
description
slug
raw-loader将帮助我们导入 markdown 文件。
安装完成后,我们需要进行一些 web pack 配置。next.config.js
在根目录中创建一个文件
并粘贴以下代码。
module.exports = {
webpack: function(config) {
config.module.rules.push({
test: /\.md$/,
use: 'raw-loader',
})
return config
}
}
注意:创建此文件后,您必须重新启动开发服务器。
在content
目录中创建两个 markdown 文件
content/blog-one.md
---
slug: blog-one
title: My First Blog
description: This Description Of My First Blog.
date: 25-September-2020
---
# h1
## h2
### h3
Normal text
content/blog-two.md
---
slug: blog-two
title: My Second Blog
description: This Description Of My Second Blog.
date: 25-September-2020
---
# h1
## h2
### h3
Normal text
首先,我们将呈现带有标题和描述的博客列表。
在我们的index.js
替换getStaticProps
函数中
export async function getStaticProps() {
const siteData = await import(`../config.json`);
const fs = require("fs");
const files = fs.readdirSync(`${process.cwd()}/content`, "utf-8");
const blogs = files.filter((fn) => fn.endsWith(".md"));
const data = blogs.map((blog) => {
const path = `${process.cwd()}/content/${blog}`;
const rawContent = fs.readFileSync(path, {
encoding: "utf-8",
});
return rawContent;
});
return {
props: {
data: data,
title: siteData.default.title,
description: siteData.default.description,
},
};
}
fs
是nodejs
帮助我们读写文件的模块。我们将用它fs.readdirSync
来读取文件。
process.cwd()
这将返回 Next.js 的执行目录。我们需要从当前目录(根目录)读取/content
所有文件,并将它们存储在变量中。files
endsWith
endsWith是一个 JavaScript 字符串方法,它确定字符串是否以指定字符串的字符结尾,并根据需要返回true
或。false
我们将绘制博客地图并path
获取rawContent
现在我们的index
组件将接收data
prop。
import React from "react";
import Head from "next/head";
import matter from "gray-matter";
import Link from "next/link";
const Index = ({ data, title, description }) => {
const RealData = data.map((blog) => matter(blog));
const ListItems = RealData.map((listItem) => listItem.data);
return (
<>
<Head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta charSet="utf-8" />
<meta name="Description" content={description}></meta>
<title>{title}</title>
</Head>
<h1>My First Blog ✍ </h1>;
<div>
<ul>
{ListItems.map((blog, i) => (
<li key={i}>
<Link href={`/${blog.slug}`}>
<a>{blog.title}</a>
</Link>
<p>{blog.description}</p>
</li>
))}
</ul>
</div>
</>
);
};
我们正在映射data
和格式化每个博客gray-matter
;
此时,你应该看到类似这样的内容
如果您点击“我的第一个博客”,它将带您到/blog-one
您为博客命名的任何位置
动态路线
我们可能有五十个不同的博客。我们不想为每个博客都创建一个页面。如果我们在 pages 目录中创建一个文件,blog
我们就可以导航到localhost:3000/blog
。但是,如果像这样在 blog(文件名)两边添加方括号,[blog].js
我们就有了一个动态路由。
路线最终将到达localhost:3000/:blog
[blog].js
在 pages 目录中创建新页面
import react from "react";
const Blog = () => {
return <h1>Blog</h1>;
};
export default Blog;
content
现在让我们从目录中获取文件
Blog.getInitialProps = async (context) => {
const { blog } = context.query;
const content = await import(`../content/${blog}.md`);
const data = matter(content.default);
return { ...data };
};
您应该在组件中content
提供data
Blog
import react from "react";
import matter from "gray-matter";
import ReactMarkdown from "react-markdown";
const Blog = ({ content, data }) => {
const frontmatter = data;
return (
<>
<h1>{frontmatter.title}</h1>
<h3>{frontmatter.description}</h3>
<ReactMarkdown escapeHtml={true} source={content} />
</>
);
};
export default Blog;
Blog.getInitialProps = async (context) => {
const { blog } = context.query;
// Import our .md file using the `slug` from the URL
const content = await import(`../content/${blog}.md`);
const data = matter(content.default);
return { ...data };
};
天哪!它起作用了。
那么代码呢
对于代码格式化,我们将使用react-syntax-highlighter
包
yarn add react-syntax-highlighter
现在创建一个代码块[blog].js
并将其传递给ReactMarkdown
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
const CodeBlock = ({ language, value }) => {
return (
<SyntaxHighlighter showLineNumbers={true} language={language}>
{value}
</SyntaxHighlighter>
);
};
现在你[blog].js
应该看起来像这样
import react from "react";
import matter from "gray-matter";
import ReactMarkdown from "react-markdown";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
const CodeBlock = ({ language, value }) => {
return (
<SyntaxHighlighter showLineNumbers={true} language={language}>
{value}
</SyntaxHighlighter>
);
};
const Blog = ({ content, data }) => {
const frontmatter = data;
return (
<>
<h1>{frontmatter.title}</h1>
<h3>{frontmatter.description}</h3>
<ReactMarkdown
escapeHtml={true}
source={content}
renderers={{ code: CodeBlock }}
/>
</>
);
};
export default Blog;
Blog.getInitialProps = async (context) => {
const { blog } = context.query;
// Import our .md file using the `slug` from the URL
const content = await import(`../content/${blog}.md`);
const data = matter(content.default);
return { ...data };
};
在内容目录中创建一个新文件conding-blog.md
---
slug: coding-blog
title: Coding blog
author: Imran Irshad
description: Coding Post For Beautiful Code
date: 30-September-2020
---
# React Functional Component
```
jsx
import React from "react";
const CoolComponent = () => <div>I'm a cool component!!</div>;
export default CoolComponent;
Now If Click `coding-blog`

## Images
Create a new file in `content` named `image-blog`
降价
slug:image-blog
标题:图片博客
描述:查看图片在我们的博客中的样子
日期:2020年9月30日
图像
结论
Nextjs 非常棒,而且非常灵活。你可以用它创建非常酷的东西。希望你从这篇文章中有所收获。
鏂囩珷鏉ユ簮锛�https://dev.to/imranib/build-a-next-js-markdown-blog-5777