Next。js已经成为React应用程序最重要的框架之一。它可以帮助开发人员在没有模板的情况下构建更好的服务器端渲染React应用程序。 Next。js之所以能成为目前最好的React框架之一,与其很多特性离不开,比如打包构建、路由预取、TypeScript、SEO等。 对于那些想要拥有一个简单但功能强大的博客的人来说,使用Next。js创建博客是当今的最佳选择。 SEO(搜索引擎优化)是改进应用程序在搜索引擎排名的过程。对于任何想要在搜索引擎上获得更好排名并带来更多流量的博客来说,这都是非常重要的。 我们将在本文中使用Next。js来构建博客。我们将介绍SSG(静态站点生成)的工作原理,并完成SEO友好的博客。入门 使用官方推荐的CreateNextApp创建项目npxcreatenextapplatesttypescriptoryarncreatenextapptypescriptorpnpmcreatenextapptypescript复制代码 为什么要使用CreateNextApp交互式体验:不带任何参数运行npxcreatenextapplatest,将会开启交互模式,引导创建项目零依赖:CreateNextApp没有依赖,毫秒级创建项目离线支持:CreateNextApp侦测网络状态,无网状态将使用本地依赖缓存支持模板:通过加入example参数,可以拉取官方仓库任何模板集成测试:集成测试功能 创建完成后项目目录构造如下:。README。mdnextenv。d。tsnext。config。jsnodemodulespackage。jsonpagespnpmlock。yamlpublicstylestsconfig。json复制代码安装依赖pnpminstallglobbygraymatterdayjschakrauireactprismjsemotionreactemotionstyledframermotionnextmdxremoteremarkgfm复制代码创建文章 根目录新增posts目录,在posts目录下创建两个mdx文件(postsjshelloWorld。mdx,postsdemo。mdx),为什么是mdx文件呢?mdx支持渲染组件,支持引入导出组件,详细文档参考MDX创建公共函数目录 根目录新增utils目录,在utils目录下创建getAllPosts。js并写入如下函数importfsfromfsimport{globby}fromglobbyimportmatterfromgraymatterconstdayjsrequire(dayjs)constrelativeTimerequire(dayjspluginrelativeTime)dayjs。extend(relativeTime)获取所有文章constGetAllPostsasync(){constpostsawaitglobby(〔posts〕)returnposts。reduce((prev,next){constfileContentsfs。readFileSync(next,utf8)const{data,content}matter(fileContents)constpostData{。。。data,group:dayjs(data。date)。format(MMMYYYY),date:dayjs(data。date)。format(MMMDD,YYYY),fromNow:dayjs(data。date)。fromNow(),modified:dayjs(data。modified)。format(MMMDD,YYYY),content,slug:next。replace(posts,)。replace(。mdx,)}!data。draftprev。push(postData)returnprev},〔〕)。sort((a,b)dayjs(b。date)dayjs(a。date))}根据slug导出文章constGetPostBySlug(slug){eslintdisablenextlinenoundefreturnnewPromise((resolve,reject){GetAllPosts()。then((posts){constpostposts。find((post)post。slug。includes({slug。join()}))resolve(post)})。catch((){reject({})})})}export{GetAllPosts,GetPostBySlug}复制代码创建组件 根目录新增components目录创建PostPage。tsx组件,内容如下:importReact,{useEffect}fromreactimportPrismfromprismjsimport{Box}fromchakrauireact以下按需引入require(prismjscomponentsprismgo)require(prismjscomponentsprismpython)require(prismjscomponentsprismjavascript)require(prismjscomponentsprismcss)require(prismjscomponentsprismbash)require(prismjscomponentsprismswift)require(prismjscomponentsprismtsx)require(prismjscomponentsprismjsx)require(prismjscomponentsprismtypescript)require(prismjscomponentsprismsql)require(prismjsthemesprismokaidia。min。css)constPostPage({children}){useEffect((){consthighlightasync(){awaitPrism。highlightAll()}highlight()。then((){})},〔children〕)return(Boxpositionrelativew23fontSizetext。sm{children}Box)}exportdefaultPostPage复制代码创建pagesindex。tsximportNextLinkfromnextlinkimport{Fragment}fromreactimport{List,LinkOverlay,ListItem,Container,Heading,Image}fromchakrauireactconstIndexPage({groupByMonthPosts}){return(Container{Object。keys(groupByMonthPosts)。map((group){return(Fragmentkey{group}Headingash3mt{12}mb{4}{group}HeadingListspacing{3}{groupByMonthPosts〔group〕。map((post){return(ListItempositionrelativedisplayflexgap{2}alignItemscenterkey{post。title}NextLinklegacyBehaviorhref{{post。slug}}passHrefLinkOverlay{post。title}LinkOverlayNextLink{post。tags。map((tag){return(Imagekey{tag}boxSize{4}objectFitcoveralt{tag}srca2020imgdataimg。jpgdatasrc{https:picsrust。vercel。appuPicicons{tag}。svg})})}ListItem)})}ListFragment)})}Container)}exportdefaultIndexPageexportasyncfunctiongetStaticProps(){const{GetAllPosts}awaitimport(utilsgetAllPosts)constpostsawaitGetAllPosts()constgroupByMonthPostsposts。reduce((prev,next){if(Array。isArray(prev〔next。group〕)){prev〔next。group〕。push(next)}else{prev〔next。group〕〔〕prev〔next。group〕。push(next)}returnprev},{})return{props:{groupByMonthPosts}}}复制代码创建pages〔。。。slug〕。tsximport{MDXRemote}fromnextmdxremoteimport{serialize}fromnextmdxremoteserializeimportdynamicfromnextdynamicimportErrorPagefromnexterrorimportNextLinkfromnextlinkimport{useRouter}fromnextrouterimportReactfromreactimportremarkGfmfromremarkgfmimportcomponentsfromutilscomponentsimport{Container,Box,Heading,Text,Link,Image,Center}fromchakrauireactconstPostPagedynamic(()import(componentsPostPage))constPost({title,description,date,originalUrl,mdxSource,cover}){constrouteruseRouter()if(!router。isFallback!mdxSource){returnErrorPagestatusCode{404}}return(Containermt{20}maxW{{sm:container。sm,md:container。md,lg:container。2xl,xl:container。xl}}classNamepostNextSeotitle{title}description{description}openGraph{{title,description}}BoxashgroupTexttextAligncentercolorgray。500fontSizexsaspPublished{date}TextHeadingtextAligncenterash1mt{4}mb{2}{title}Heading{originalUrl(Centercolorgray。500fontSizesmmb{8}本文翻译自:NextLinklegacyBehaviorhref{originalUrl}passHrefLink{originalUrl}LinkNextLinkCenter)}BoxImageboxSize100srca2020imgdataimg。jpgdatasrc{cover??https:cdn。jsdelivr。netghmanonicupicsmasteruPicNhSU3O。jpg}alt{title}PostPageMDXRemote{。。。mdxSource}components{components}PostPageContainer)}exportconstgetStaticPathsasync(){const{GetAllPosts}awaitimport(utilsgetAllPosts)constallPostsawaitGetAllPosts()constpathsallPosts。map((post)({params:{slug:post。slug。split()}}))return{paths,fallback:false}}exportconstgetStaticPropsasync({params}){const{GetPostBySlug}awaitimport(utilsgetAllPosts)const{content,。。。data}awaitGetPostBySlug(params。slug)constmdxSourceawaitserialize(content,{mdxOptions:{remarkPlugins:〔〔remarkGfm〕〕,rehypePlugins:〔〕},scope:data})return{props:{。。。data,mdxSource}}}exportdefaultPost复制代码 至此,基本框架搭建完成,接下来调整样式及组件的引入,以及mdx渲染修正。调整样式 可选 引入tailwind。css,执行pnpminstallDtailwindcsspostcssautoprefixernpxtailwindcssinitp 修改tailwind。config。js,如下:type{import(tailwindcss)。Config}module。exports{content:〔。pages。{js,ts,jsx,tsx},。components。{js,ts,jsx,tsx}〕,theme:{extend:{}},plugins:〔〕}复制代码 修改全局样式stylesglobals。复制代码 必须 修改pagesapp。tsx,引入chakraui的配置pagesapp。jsimport{ChakraProvider}fromchakrauireactfunctionMyApp({Component,pageProps}){return(ChakraProviderComponent{。。。pageProps}ChakraProvider)}exportdefaultMyApp复制代码 到这里,不出意外,你的界面应该是长这样 点击链接,应该会报错,未引入utilscomponents,这个是配置mdx内元素渲染的组件,参考MDXComponents,mdx提供默认的渲染组件,所以,这个是非必须的,不需要删除即可 个人比较喜欢chakraui,所以将组件都转成了chakraui提供的组件,配置如下:importCanIUsefromcomponentsCanIUseimport{Heading,Link,Box}fromchakrauireactimport{FiExternalLink}fromreacticonsficonstcomponents{CanIUse,h2:(props)(Headingash2mb{4}{props。children}Heading),h3:(props)(Headingash3mb{4}{props。children}Heading),h4:(props)(Headingash4mb{4}{props。children}Heading),h5:(props)(Headingash5mb{4}{props。children}Heading),p:(props)(Boxaspmb{4}{props。children}Box),p:(props)Boxmb{4}{props。children}Box,a:(props){return(LinkdisplayinlineflexalignItemscenterhref{props。href}gap{2}isExternal{props。children}FiExternalLinkLink)}}exportdefaultcomponents复制代码 好了,到这里基本完成了基于Next。js的博客搭建。部署到Vercel Next。js部署到Vercel无需更改和配置,无缝衔接。 【SourceCode】 也可以参考我的个人网站Manon。icuHome