接着,创建 ./pages/post/[id].js 文件,这个文件对应的,就是路由形如 /post/{id} 的所有页面,而 id 的作用就是匹配文章的 _id。这样通过访问 URL:/post/{id1}.js,就能访问到文章id等于 id1 的文章对应页面。
我们先往 [id].js 文件中填入以下的内容:
import Head from 'next/head'; import Link from 'next/link'; import styles from '../../styles/Post.module.css'; export default function Post({ title, image, author, avator, contentHtml }) { return ( <div className={styles.container}> <Head> <title>{title}</title> <link href="http://www.likecs.com/favicon.png" /> </Head> <main className={styles.main}> <div className={styles.info_container}> <img className={styles.image} src=http://www.likecs.com/{image}></img> <div className={styles.info}> <h1 className={styles.title}>{title}</h1> <div className={styles.author_info}> <div className={styles.avator} style={{ backgroundImage: `url(${avator})` }}></div> <h2>{author}</h2> </div> </div> </div> <div className={styles.markdown} dangerouslySetInnerHTML={{ __html: contentHtml }} /> <Link href='http://www.likecs.com/'><h3 className={styles.back}>返回</h3></Link> </main> <footer className={styles.footer}> <a href="http://www.cloudbase.net/" target="_blank" > Powered by{' '} <img src="http://main.qcloudimg.com/raw/3b942431a6ef465d3b8369969e861c0f/favicon.png" alt="TCB Logo" className={styles.logo} /> </a> </footer> </div> ) } export async function getStaticPaths() { return { paths: undefined, fallback: false }; } export async function getStaticProps({ params }) { return { props: undefined }; }可以看到,相比 index.js,[id].js 多出了一个 getStaticProps 函数,getStaticProps 也多了一个 parms 参数。getStaticProps 函数暂时不用管,而 param.id 就是在路由中匹配到的 id,可以借助它,执行获取对应文章内容的逻辑。
在 api.js 中,添加以下内容:
// 对于 image 类型的字段,直接取得的 id 需要转换为可用的 URL const dealWithUrl = url => 'https://' + url .replace(`cloud://${tcbConfig.env}.`, '') .replace('/cloudbase-cms', '.tcb.qcloud.la/cloudbase-cms'); export const getPost = async (id) => { const post = (await Article.where({ _id: id }).get()).data[0]; const { name, avator } = (await Author .where({ _id: post.author }) .get()).data[0]; post.author = name; post.avator = dealWithUrl(avator); post.image = dealWithUrl(post.image); return post; };然后安装 remark 以及 remark-html 两个库,我们将用它们把 markdown 转化为 html,然后修改 [id].js 文件中的 getStaticProps 为
import Head from 'next/head'; import Link from 'next/link'; import styles from '../../styles/Post.module.css'; import { getPost } from '../../lib/api'; import remark from 'remark' import html from 'remark-html' ... export async function getStaticProps({ params }) { const post = await getPost(params.id); const processedContent = await remark() .use(html) .process(post.content) post.contentHtml = processedContent.toString() return { props: post }; } 拉取所有的文章id以渲染所有文章页面只到这一步还不够,我们需要知道所有的路由可能匹配到的 id 值,Next.js 才能渲染出全部的文章页面。[id].js 多出的 getStaticPaths 函数正是用来返回 id 所有可能的匹配值的。
这就是我们只需要编写一次拉取文章数据逻辑,编写一次文章页面 UI,就能让 Next.js 生成出无数文章的静态页面的奥秘。因为,可以让 Next.js 知道所有的文章 id,然后 Next.js 就能对每个文章页面执行一次生成了。
修改往 api.js 中添加获取所有文章 id 的函数:
export const getAllPostId = async () => { let posts = (await Article.where({}).get()).data; return posts.map(value => ({ params: { id: value._id } })) };然后修改 getStaticPaths 函数:
export async function getStaticPaths() { return { paths: await getAllPostId(), fallback: false }; }然后,访问首页,随便进入一篇文章,就可以看到如下效果:
到这里,我们就成功完成 Next.js 项目的构建啦!
部署使用腾讯云云开发,你可以轻易地将应用部署到公共网络上。
我们先修改 package.json 中依赖库的配置,因为云开发环境对于依赖版本有一定限制:
"dependencies": { "@cloudbase/node-sdk": "^2.5.1", "next": "9.5.4", "react": "16.13.1", "react-dom": "16.13.1", "remark": "^13.0.0", "remark-html": "^13.0.1" }