使用Next.js创建Blog

Next.js 已经成为 React 应用程序最重要的框架之一 。它可以帮助开发人员在没有模板的情况下构建更好的服务器端渲染 React 应用程序 。
Next.js 之所以能成为目前最好的 React 框架之一,与其很多特性离不开,比如打包构建、路由预取、TypeScript、seo 等 。
对于那些想要拥有一个简单但功能强大的博客的人来说,使用 Next.js 创建博客是当今的最佳选择 。
SEO(搜索引擎优化)是改进应用程序在搜索引擎排名的过程 。对于任何想要在搜索引擎上获得更好排名并带来更多流量的博客来说,这都是非常重要的 。
我们将在本文中使用 Next.js 来构建博客 。我们将介绍 SSG(静态站点生成)的工作原理,并完成 SEO 友好的博客 。
入门使用官方推荐的Create Next App创建项目
npx create-next-app@latest --typescript# oryarn create next-app --typescript# orpnpm create next-app --typescript复制代码为什么要使用Create Next App

  • 交互式体验:不带任何参数运行npx create-next-app@latest,将会开启交互模式,引导创建项目
  • 零依赖:Create Next App没有依赖,毫秒级创建项目
  • 离线支持:Create Next App侦测网络状态,无网状态将使用本地依赖缓存
  • 支持模板:通过加入--example参数,可以拉取官方仓库任何模板
  • 集成测试:集成测试功能
创建完成后项目目录构造如下:
.├── README.md├── next-env.d.ts├── next.config.js├── node_modules├── package.json├── pages├── pnpm-lock.yaml├── public├── styles└── tsconfig.json复制代码安装依赖【使用Next.js创建Blog】pnpm install globby gray-matter dayjs @chakra-ui/react prismjs @emotion/react @emotion/styled framer-motion next-mdx-remote remark-gfm复制代码创建文章根目录新增_posts目录,在_posts目录下创建两个mdx文件(_posts/js/helloWorld.mdx,_posts/demo.mdx),为什么是mdx文件呢?mdx支持渲染组件,支持引入导出组件,详细文档参考MDX
创建公共函数目录根目录新增utils目录,在utils目录下创建getAllPosts.js并写入如下函数
import fs from 'fs'import {globby} from 'globby'import matter from 'gray-matter'const dayjs = require('dayjs')const relativeTime = require('dayjs/plugin/relativeTime')dayjs.extend(relativeTime)//获取所有文章const GetAllPosts = async () => {const posts = await globby(['_posts'])return posts.reduce((prev, next) => {const fileContents = fs.readFileSync(next, 'utf8')const {data, content} = matter(fileContents)const postData = https://www.isolves.com/it/cxkf/kj/2022-11-25/{...data,group: dayjs(data.date).format('MMM/YYYY'),date: dayjs(data.date).format('MMM DD, YYYY'),fromNow: dayjs(data.date).fromNow(),modified: dayjs(data.modified).format('MMM DD, YYYY'),content,slug: next.replace(/^_posts//, '').replace(/.mdx$/, '')}!data.draft && prev.push(postData)return prev}, []).sort((a, b) => dayjs(b.date) - dayjs(a.date))}// 根据slug导出文章const GetPostBySlug = (slug) => {// eslint-disable-next-line no-undefreturn new Promise((resolve, reject) => {GetAllPosts().then((posts) => {const post = posts.find((post) =>post.slug.includes(`${slug.join('/')}`))resolve(post)}).catch(() => {reject({})})})}export {GetAllPosts, GetPostBySlug}复制代码创建组件根目录新增components目录
  1. 创建PostPage.tsx组件,内容如下:
import React, {useEffect} from 'react'import Prism from 'prismjs'import {Box} from '@chakra-ui/react'// 以下按需引入require('prismjs/components/prism-go')require('prismjs/components/prism-Python/ target=_blank class=infotextkey>Python')require('prismjs/components/prism-JAVAscript')require('prismjs/components/prism-css')require('prismjs/components/prism-bash')require('prismjs/components/prism-swift')require('prismjs/components/prism-tsx')require('prismjs/components/prism-jsx')require('prismjs/components/prism-typescript')require('prismjs/components/prism-sql')require('prismjs/themes/prism-okaidia.min.css')const PostPage = ({children}) => {useEffect(() => {const highlight = async () => {await Prism.highlightAll()}highlight().then(() => {})}, [children])return (<Box position="relative" w="2/3" fontSize="text.sm">{children}</Box>)}export default PostPage复制代码
  1. 创建pages/index.tsx
import NextLink from 'next/link'import {Fragment} from 'react'import {List,LinkOverlay,ListItem,Container,Heading,Image} from '@chakra-ui/react'const IndexPage = ({groupByMonthPosts}) => {return (<Container>{Object.keys(groupByMonthPosts).map((group) => {return (<Fragment key={group}><Heading as="h3" mt={12} mb={4}>{group}</Heading><List spacing={3}>{groupByMonthPosts[group].map((post) => {return (<ListItemposition="relative"display="flex"gap={2}alignItems="center"key={post.title}><NextLinklegacyBehaviorhref=https://www.isolves.com/it/cxkf/kj/2022-11-25/{`/${post.slug}`}passHref>


推荐阅读