【React】使用Next.js构建并部署个人博客

网友投稿 550 2022-11-05


【React】使用Next.js构建并部署个人博客

前言

关于博客系统,相信大家早已驾轻就熟,网上有很多以​​markdown​​​驱动的博客框架,如​​vuepress​​​,​​hexo​​等,这类框架的本质是生成静态站点,而个人开发的博客系统大多是使用数据库的全栈项目,这两种方式各有各的好处,这里就不做比较了

这篇文章我们将自己独立去开发并部署一个以​​markdown​​驱动的静态站点博客,所用技术栈如下:

​​React​​​​TypeScript​​​​Next.js​​​​tailwindcss​​​​Vercel​​部署

注意: 本文只是演示使用​​Next.js​​从0到1构建并部署一个个人博客项目,不会对项目构建过程中所用到的技术做详细的讲解,不过不用担心,只要跟着文章一步一步来,小白都能成功部署自己的个人博客!

项目仓库地址:​​最终效果可见:​​12.13.0​​ 或更高版本。

文章目录

​​前言​​​​1、创建Next.js项目​​​​2、安装tailwindcss​​​​3、添加布局页面​​​​4、新建markdown文章​​​​5、解析markdown内容​​​​6、添加首页​​​​7、添加文章详情页面​​​​8、Vercel部署​​​​结语​​

1、创建Next.js项目

要创建 ​​Next.js​​​ 应用程序,请打开终端,​​cd​​进入到要在其中创建应用程序的目录,然后运行以下命令:

npx create-next-app@latest --typescript ailjx-blog

上述代码表示:通过​​create-next-app​​​创建名为​​ailjx-blog​​​的​​TypeScript​​​版本的​​Next.js​​应用程序

用​​vscode​​​打开​​ailjx-blog​​项目,目录结构如下:

在项目根目录终端运行以下命令启动项目:

npm

打开​​install

生成​​tailwindcss​​配置文件:

npx tailwindcss init -p

此时项目里会多出两个文件:​​tailwind.config.js​​​和​​postcss.config.js​​

修改​​tailwind.config.js​​​文件里的​​content​​为:

: [ "./pages/**/*.{js,ts,jsx,tsx}", "./components/**/*.{js,ts,jsx,tsx}", "./styles/**/*.css", ],

在​​pages​​​文件夹下的​​_app.tsx​​文件的第一行添加:

import "tailwindcss/tailwind.css";

之后重新启动项目

3、添加布局页面

准备一张自己的头像(建议比例为1:1,这里演示用的头像文件名为​​author.jpg​​)

在​​public​​​文件夹下新建​​images​​​文件夹,将你的头像图片放入其中,并删除​​public​​​文件夹下的​​svg​​文件

​​public​​​文件为项目的静态文件,可直接通过地址访问,如访问演示所用头像:​​Head from "next/head";import Image from "next/image";import Link from "next/link";const name = "Ailjx"; // 名称,根据需要修改export const siteTitle = "Ailjx Blog"; // 网站标题,根据需要修改interface Props { children: React.ReactNode; home?: boolean;}export default function Layout({ children, home }: Props) { return (

{home ? ( <> {name}

{name}

) : ( <> {name}

{name}

)}
{children}
{!home && ( )}
);}

这里使用了几个​​Next​​自带的组件:

​​Head​​​:向​​Html​​​页面的​​head​​内添加内容,里面内容自己根据需要修改​​Image​​​:渲染图像的组件,​​src​​地址修改为自己头像的地址​​Link​​ :页面间跳转组件

4、新建markdown文章

项目根目录下新建​​posts​​​文件夹,添加一个​​markdown​​文件,如:

​​欢迎来到我的博客.md​​

---title: "欢迎来到我的博客"date: "2022-08-08"---## 欢迎你!

注意: 需要在每个​​markdown​​​文件的顶部通过​​---​​添加元数据,元数据需要有​​title​​​字段表示文章标题,​​date​​​字段表示日期,如上面​​欢迎来到我的博客.md​​的元数据为:

---title: "欢迎来到我的博客"date: "2022-08-08"

这些数据在我们渲染​​markdown​​内容时需要用到

5、解析markdown内容

需要安装以下插件:

​​remark-prism​​:代码高亮插件​​date-fns​​:处理日期​​gray-matter​​:获取元数据​​next-mdx-remote​​​:用于解析和渲染​​markdown​​内容​​remark-external-links​​​:对​​markdown​​​内的链接添加​​rel​​​和​​target​​,使其能够在新页面打开

在项目根目录终端运行以下命令安装上述插件:

npm

npm

在项目根目录新建存放工具函数的​​utils​​​文件夹,里面新建处理​​markdown​​​文件的​​posts.ts​​:

import fs from "fs";import path from "path";// gray-matter:获取元数据import matter from "gray-matter";// date-fns:处理日期import { parseISO } from "date-fns";import { serialize } from "next-mdx-remote/serialize";// remark-prism:markdown代码高亮import prism from "remark-prism";// externalLinks:使markdown的链接是在新页面打开链接import externalLinks from "remark-external-links";interface MatterMark { data: { date: string; title: string }; content: string; [key: string]: unknown;}// posts目录的路径const postsDirectory = path.join(process.cwd(), "posts");// 获取posts目录下的所有文件名(带后缀)const fileNames = fs.readdirSync(postsDirectory);// 获取所有文章用于展示首页列表的数据export function getSortedPostsData() { // 获取所有md文件用于展示首页列表的数据,包含id,元数据(标题,时间) const allPostsData = fileNames.map((fileName) => { // 去除文件名的md后缀,使其作为文章id使用 const id = fileName.replace(/\.md$/, ""); // 获取md文件路径 const fullPath = path.join(postsDirectory, fileName); // 读取md文件内容 const fileContents = fs.readFileSync(fullPath, "utf8"); // 使用matter提取md文件元数据:{data:{//元数据},content:'内容'} const matterResult = matter(fileContents); return { id, ...(matterResult.data as MatterMark["data"]), }; }); // 按照日期从进到远排序 return allPostsData.sort(({ date: a }, { date: b }) => // parseISO:字符串转日期 parseISO(a) < parseISO(b) ? 1 : -1 );}// 获取格式化后的所有文章id(文件名)export function getAllPostIds() { // 这是返回的格式: // [ // { // params: { // id: '......' // } // }, // { // params: { // id: '......' // } // } // ] return fileNames.map((fileName) => { return { params: { id: fileName.replace(/\.md$/, ""), }, }; });}// 获取指定文章内容export async function getPostData(id: string) { // 文章路径 const fullPath = path.join(postsDirectory, `${id}.md`); // 读取文章内容 const fileContents = fs.readFileSync(fullPath, "utf8"); // 使用matter解析markdown元数据和内容 const matterResult = matter(fileContents); return { content: await serialize(matterResult.content, { mdxOptions: { remarkPlugins: [prism, externalLinks] }, }), ...(matterResult.data as MatterMark["data"]), };}

​​posts.ts​​里有三个主要的函数:

​​getSortedPostsData​​:在首页用于展示文章列表​​getAllPostIds​​​:获取指定格式的所有文章​​id​​(文件名),这个格式是​​Next​​所要求的

因为我们在写文章详情页面时需要使用动态路由,每个文章的​​id​​​就是一个路由,并且我们使用的​​Next​​静态站点生成会在项目打包构建时直接生成所有的​​html​​​文件,需要把每一个路由对应的页面都构建出来,​​Next​​​会根据​​getAllPostIds​​​函数返回的这种格式的数据去构建每一个​​html​​页面

​​getPostData​​:获取文章详情,在文章详情页面会用到

6、添加首页

首页会展示文章列表,会用到一个日期渲染组件,我们先创建一下

在​​components​​​文件夹下新建​​date.tsx​​文件:

import { parseISO, format } from "date-fns";interface Props { dateString: string;}export default function Date({ dateString }: Props) { const date = parseISO(dateString); return ( );}

修改​​pages​​​文件夹下的​​index.tsx​​文件如下:

import type { NextPage, GetStaticProps } from "next";import Head from "next/head";import Layout, { siteTitle } from "../components/layout";import Link from "next/link";import Date from "../components/date";import { getSortedPostsData } from "../utils/posts";interface Props { allPostsData: { date: string; title: string; id: string; }[];}const Home: NextPage = ({ allPostsData }) => { return (

{siteTitle}

你好,我是 Ailjx

一个又菜又爱玩的前端小白,欢迎来到我的博客!

Blog

    {allPostsData.map(({ id, date, title }) => (
  • {title}
  • ))}
);};export const getStaticProps: GetStaticProps = async () => { // 获取文章列表 const allPostsData = getSortedPostsData(); return { props: { allPostsData, }, };};export default Home;

修改​​styles​​​文件夹下的​​globals.css​​如下:

a { color: #0070f3; text-decoration: none;}a:hover { text-decoration: underline;}img { max-width: 100%; display: block;}::-webkit-scrollbar { width: 5px; height: 5px; position: absolute;}::-webkit-scrollbar-thumb { background-color: #0070f3;}::-webkit-scrollbar-track { background-color: #ddd;}

删除​​style​​​文件夹下的​​Home.module.css​​

此时运行项目,打开​​type { GetStaticProps, GetStaticPaths } from "next";import Layout from "../../components/layout";import { getAllPostIds, getPostData } from "../../utils/posts";import Head from "next/head";import Date from "../../components/date";import { MDXRemote, MDXRemoteProps } from "next-mdx-remote";// 引入代码高亮cssimport "prismjs/themes/prism-okaidia.min.css";interface Props { postData: { title: string; date: string; content: MDXRemoteProps; };}export default function Post({ postData }: Props) { return ( {postData.title}

{postData.title}

);}// getStaticProps和getStaticPaths只在服务器端运行,永远不会在客户端运行export const getStaticPaths: GetStaticPaths = async () => { // 获取所有文章id,即所有路由 const paths = getAllPostIds(); return { paths, fallback: false, };};export const getStaticProps: GetStaticProps = async ({ params }) => { // 获取文章内容 const postData = await getPostData(params!.id as string); return { props: { postData, }, };};

到此一个简单的博客项目就写好了

8、Vercel部署

没有​​Github​​账号的先去注册一个账号

在​​Github​​​上新建一个名为​​next-blog​​的仓库(名称自己根据需要修改):

仓库权限公共私有都可,并且不需要使用​​README​​ 或其他文件对其进行初始化

在我们的博客项目根目录下运行以下命令推送代码到​​Github​​仓库里:

git remote add origin branch -M maingit

请将上述第一行命令​​origin​​后面的地址替换成你的仓库地址,一般是将​​​​​替换为你​​Gitub​​​的用户名,​​next-blog​​替换成你仓库的名称

之后刷新仓库查看代码:

项目仓库地址:​​最终效果可见:​​https://next-blog-eosin-six.vercel.app/​​

参考资料:​​Next.js官网​​​​tailwindcss中文文档​​​​date-fns文档​​​​next-mdx-remote仓库​​​​remark文档​​


版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:安装mysql报错处理
下一篇:毕业生档案查询API(毕业生档案查询入口)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~