Next.js API
目前的页面
- index和posts/indext都是HTML
- 但实际开发中我们需要请求/user/shops等API
- 返回的内容是JSON格式的字符串
使用 Next.js API
- 路径为/api/v1/posts以便与/posts区分开来
- 默认导出的函数的类型为 NextApiHandler
- 该代码只运行在Node.js里,不运行在浏览器中
pages/api/v1/posts.tsx
import {NextPage} from "next";
import {usePosts} from "../../hooks/usePosts";
const PostIndex: NextPage = ()=> {
const {isLoading, empty, postData} = usePosts()
return (
<div>
<h1>文章列表</h1>
{isLoading ? <div>加载中...</div> :
empty? <div>没有数据</div> : postData.map(item => {
return <div key={item.id}>
{item.id}
</div>
})}
</div>
)
}
export default PostIndex
lib/posts.tsx
import path from "path";
import fs, {promises as fsPromise} from "fs";
import matter from 'gray-matter';
export const getPosts = async ()=> {
const markdownDir = path.join(process.cwd(), 'markdown')
const fileNames = await fsPromise.readdir(markdownDir)
const postData = fileNames.map(fileName=> {
const fullPath = path.join(markdownDir, fileName)
const id = fileName.replace(/\.md$/g, '')
console.log(fullPath);
const text = fs.readFileSync(fullPath,'utf-8');
const {data: {title, date}, content} = matter(text);
return {
title,
date,
id
}
})
return postData
}
Next.js三种渲染
客户端渲染
- 只在浏览器上执行的渲染
静态页面生成(SSG)
- Static Site Generation,解决白屏问题、SEO问题
- 无法生成用户相关内容(所有用户请求的结果都一样)
服务端渲染(SSR)
- 解决白屏问题、SEO问题
- 可以生成用户相关内容(不同用户结果不同)
注意:SSR
和SSG
都属于预渲染Pre-rendering
客户端渲染
文件列表完全又前端渲染的,我们称之为客户端渲染
客户端渲染的缺点
白屏
在AJAX得到响应之前,页面中之后Loading
SEO 不友好
- 搜索引擎访问页面,看不到posts数据
- 因为搜索引擎默认不会执行JS,只能看到HTML
静态页面生成(SSG)
背景
- 你有没有想过,其实每个人看到的文章列表都是一样的
- 那么为什么还需要在每个人的浏览器上渲染一次
- 为什么不在后端渲染好,然后发给每个人
- N次渲染变成了1次渲染
- N次客户端渲染变成了1次静态页面生成
- 这个过程叫做动态内容静态化
思考
- 显然,后端最好不要通过AJAX来获取 posts(为什么)
- 那么,应该如何获取posts呢?
getStaticProps获取posts
声明位置
- 每个page不是默认导出一个函数么?
- 把getStaticProps声明在这个函数旁边即可
- 别忘了加export
写法
import {NextPage} from "next";
import {getPosts} from "../../lib/posts";
type Props = {
posts: Post[];
}
const PostIndex: NextPage<Props> = (props)=> {
const {posts} = props
return (
<div>
<h1>文章列表</h1>
{posts.map(p => <div key={p.id}>
{p.id}
</div>)}
</div>
)
}
export default PostIndex
export const getStaticProps = async ()=> {
const posts = await getPosts();
return {
props: {
posts: JSON.parse(JSON.stringify(posts))
}
}
}
getStaticProps
如何使用 props
export default function PostsIndex =(props)=> {...}
- 默认导出的函数的第一个参数就是
props
如何给 props 添加类型
const PostsIndex:NextPage<{ posts:Post[] }>=(props)=> {...}
- 把 function 改成 const + 箭头函数
- 类型声明为 NextPage
- 用泛型给 NextPage 传个参数<Props>
- Props就是props 的类型
静态化的时机
环境
- 在
开发环境
,每次请求都会运行一次getStaticProps
这是为了方便你修改代码重新运行 - 在
生产环境
,getStaticProps
只在build
时运行一次这样可以提供一份HTML给所有用户下载
如何体验生产环境
关掉yarn dev
yarn build
yarn start
生产环境
解读
-
λ
-(Server)SSR 不能自动创建HTML(等会再说) -
◯
-(Static)自动创建 HTML(发现你没用到props) -
●
-(SSG) 自动创建HTM、 JS、 JSON(发现你用到了props)
三种文件类型
- posts.html含有静态内容,用于用户直接访问
- posts.js也含有静态内容,用于快速导航(与HTML对应)
- posts.json含有数据,跟posts.js结合得到界面
为什么不直接把数据放入posts.js呢?
显然,是为了让posts.js接受不同的数据(下文解释)
当然,目前只能接受一个数据(来自getStaticProps)
小结
动态内容静态化
- 如果动态内容与用户无关,那么可以提前静态化
- 通过
getStaticProps
可以获取数据 -
静态内容+数据(本地获取)
就得到了完整页面 - 代替了之前的
静态内容+动态内容(AJAX 获取)
时机
- 静态化是在yarn build的时候实现的
优点
- 生产环境中直接给出完整页面
- 首屏不会白屏
- 搜索引擎能看到页面内容(方便 SEO)
渲染方式:SSR
getServerSideProps
运行时机
- 无论是开发环境还是生产环境
- 都是在
请求到来之后
运行getServerSideProps
回顾一下 getStaticProps
- 开发环境,每次请求到来后运行,方便开发
- 生产环境,
build时运行一次
参数
- context,类型为NextPageContext
- context.req/context.res 可以获取请求和响应一般只需要用到context.req
const Home: NextPage<Props> = (props) => {
const {browser} = props
const [width, setWidth] = useState(0)
useEffect(()=> {
const w = document.documentElement.clientWidth
setWidth(w)
}, [])
return (
<div>
<p>你的浏览器是{browser.name}</p>
<p>你的浏览器窗口大小 {width} px</p>
</div>
)
}
- 展示了当前用户的浏览器
- 这些信息不可能在请求之前知道
- 思考:如果我要在页面上展示当前窗口大小,可以吗答案:只能用客户端渲染做到
总结
静态内容
- 直接输出HTML,没有术语
动态内容
- 术语:客户端渲染,通过AJAX请求,渲染成HTML
动态内容静态化
- 术语:SSG,通过getStaticProps获取用户无关内容
用户相关动态内容静态化
- 术语:SSR,通过 getServerSideProps获取请求
- 缺点:无法获取客户端信息,如浏览器窗口大小
还差一个功能
** 点击posts列表查看文章**
<Link href="/posts/[id]" as={`/posts/${p.id}`}>
<a> {p.title}</a>
</Link>
新建的文件名应该叫做什
pages/posts/[id].tsx
- 你没有看错,文件名就是
[id].tsx
/pages/posts/[id].tsx的作用
- 既声明了路由/posts/:id
- 又是/posts/:id的页面实现程序
[id].tsx
步骤
- 实现
PostsShow
,从props接收post数据 - 实现
getStaticProps
,从第一个参数接受params.id - 实现
getStaticPaths
,返回id列表
优化
- 使用 marked 得到markdown的HTML内容
yarn add marked
build
- 中断
yarn dev
-
yarn build
然后看一下.next/server
目录 -
yarn start
fallback:false
的作用
- 是否自动兜底
- false 表示如果请求的id不在getStaticPaths的结果里,则直接返回404页面
true表示自动兜底,id找不到依然渲染页面 - 注意id不在结果里不代表id不存在,比如大型项目无法讲所有产品页面都静态化,只静态化部分id对应的页面
[id].tsx
import {NextPage} from "next";
import {Post} from "../../type";
import {getPostById, getPostIds} from "../../lib/posts";
type Props = {
post: Post
}
const PostShow: NextPage<Props>= (props)=> {
const {post} = props
return (
<div>
<h1>{post.title}</h1>
<article dangerouslySetInnerHTML={{__html: post.htmlContent}}/>
</div>
)
}
export default PostShow
export const getStaticPaths = async () => {
const idList = await getPostIds()
return {
paths: idList.map(id => ({params: {id: id}})),
fallback: false
}
}
export const getStaticProps = async (x: any)=> {
const id = x.params.id
const post = await getPostById(id)
return {
props: {
post: post
}
}
}