Next.js(下)

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 (
        

文章列表

{isLoading ?
加载中...
: empty?
没有数据
: postData.map(item => { return
{item.id}
})}
) } 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 Api

Next.js三种渲染

客户端渲染

  • 只在浏览器上执行的渲染

静态页面生成(SSG)

  • Static Site Generation,解决白屏问题、SEO问题
  • 无法生成用户相关内容(所有用户请求的结果都一样)

服务端渲染(SSR)

  • 解决白屏问题、SEO问题
  • 可以生成用户相关内容(不同用户结果不同)

注意:SSRSSG都属于预渲染Pre-rendering

客户端渲染

文件列表完全又前端渲染的,我们称之为客户端渲染

客户端渲染的缺点

白屏
在AJAX得到响应之前,页面中之后Loading

SEO 不友好

  • 搜索引擎访问页面,看不到posts数据
  • 因为搜索引擎默认不会执行JS,只能看到HTML
image.png

静态页面生成(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)=> {
    const {posts} = props
    return (
        

文章列表

{posts.map(p =>
{p.id}
)}
) } 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 的类型
同构

静态化的时机

环境

  • 开发环境,每次请求都会运行一次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) => {
    const {browser} = props
    const [width, setWidth] = useState(0)
    useEffect(()=> {
        const w = document.documentElement.clientWidth
        setWidth(w)
    }, [])
    return (
    

你的浏览器是{browser.name}

你的浏览器窗口大小 {width} px

) }
  • 展示了当前用户的浏览器
  • 这些信息不可能在请求之前知道
  • 思考:如果我要在页面上展示当前窗口大小,可以吗答案:只能用客户端渲染做到
流程图

总结

静态内容

  • 直接输出HTML,没有术语

动态内容

  • 术语:客户端渲染,通过AJAX请求,渲染成HTML

动态内容静态化

  • 术语:SSG,通过getStaticProps获取用户无关内容

用户相关动态内容静态化

  • 术语:SSR,通过 getServerSideProps获取请求
  • 缺点:无法获取客户端信息,如浏览器窗口大小

还差一个功能

** 点击posts列表查看文章**


       {p.title}

新建的文件名应该叫做什

  • 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)=> {
    const {post} = props
    return (
        

{post.title}

) } 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 } } }

你可能感兴趣的:(Next.js(下))