Next.js 学习笔记
Next.js 学习笔记
数据抓取
getServerSideProps
什么时候使用:
仅当需要呈现其数据必须在请求时获取的页面时,才应使用 getServerSideProps
。这可能是由于请求的数据或属性的性质(例如 authorization
标头或地理位置)。使用 getServerSideProps
的页面将在请求时在服务器端呈现,并且只有在配置了缓存控制标头时才会被缓存。不能与getStaticProps
同时使用
什么时候运行:
仅在服务器端运行,进入界面就会执行。
使用举例:
|
|
getStaticProps
什么时候使用:
用户请求之前数据是确定的。
创建项目
|
|
路由
1.可以使用目录直接导航
-
在
pages
目录下新建psots
文件夹 -
在
./pages/psots
下新建文件first-post.tsx
文件1 2 3 4 5 6 7
export default function FirstPost() { return ( <> <h1>First Post</h1> </> ) }
-
访问
http://localhost:3000/posts/first-post
即可
2.使用Link
导航
href中写明路径即可,以
pages
目录为跟路径。
1 2 3
import Link from "next/link"; <Link href="/posts/first-post">this page!</Link>
-
index.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
import Head from 'next/head' import Link from "next/link"; export default function Home() { return ( <> <Head> <title>Create Next App</title> <meta name="description" content="Generated by create next app"/> <meta name="viewport" content="width=device-width, initial-scale=1"/> <link rel="icon" href="/favicon.ico"/> </Head> Hello Next Read <Link href="/posts/first-post">this page!</Link> </> ) }
-
/psots/first-post.tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14
import Link from "next/link"; export default function FirstPost() { return ( <> <h1>First Post</h1> <h2> <Link href="/"> Back to home </Link> </h2> </> ) }
3.使用函数导航
|
|
静态资源
在顶级
public
公共目录下提供静态文件
在public
文件夹下有文件next.svg
index.tsx
,src以public
为跟路径
|
|
图像优化
图片组件
|
|
本地图片使用
|
|
远程图片必须填写width
和height
|
|
元数据
在pages/index.tsx
下Head
组件中编写
|
|
如果在其他界面中添加Head
组件则会出现覆盖效果,比如在/psots/first-post.tsx
中添加Head
组件
|
|
CSS Module
-
在跟路径下创建
components
文件夹用来存放组件 -
在
components
文件夹中创建layout.tsx
1 2 3 4 5 6 7 8 9 10 11
import React from "react"; type Props = { children: React.ReactNode } export default function Layout({children}: Props) { return ( <div>{children}</div> ) }
-
在
pages/posts/first-post.tsx
中,导入Layout
并使其成为最外层的组件1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
import Link from "next/link"; import Head from "next/head"; import Layout from "@/components/layout"; export default function FirstPost() { return ( <Layout> <Head> <title>ONE</title> </Head> <h1>First Post</h1> <h2> <Link href="/"> Back to home </Link> </h2> </Layout> ) }
-
在
components
文件夹中创建layout.module.css
1 2 3 4 5
.container { max-width: 36rem; padding: 0 1rem; margin: 3rem auto 6rem; }
-
在
layout.tsx
中使用css文件1 2 3 4 5 6 7 8 9 10 11 12
import React from "react"; import styles from './layout.module.css' type Props = { children: React.ReactNode } export default function Layout({children}: Props) { return ( <div className={styles.container}>{children}</div> ) }
-
观察界面效果
Global CSS
-
在
pages
下创建_app.tsx
,App
组件是顶级组件,它将在所有不同的页面中通用,在next.js中只能在pages/_app.tsx
中添加全局css1 2 3 4 5 6 7
import type {AppProps} from 'next/app' export default function App({Component, pageProps}: AppProps) { return ( <Component {...pageProps} /> ) }
-
在跟路径下创建
styles
文件夹 -
在
styles
文件夹下创建global.css
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
html, body { padding: 0; margin: 0; font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; line-height: 1.6; font-size: 18px; } * { box-sizing: border-box; } a { color: #0070f3; text-decoration: none; } a:hover { text-decoration: underline; } img { max-width: 100%; display: block; }
-
在
_app.tsx
中引入1
import '@/styles/globals.css'
-
观察页面
Link
变化
预渲染
可以带来更好的性能和 SEO
构建时实现带数据的静态生成
-
在跟路径下创建
posts
文件夹 -
在
posts
文件夹创建一个md文件1 2 3 4 5 6 7 8 9 10 11
--- title: 'Two Forms of Pre-rendering' date: '2020-01-01' --- Next.js has two forms of pre-rendering: **Static Generation** and **Server-side Rendering**. The difference is in **when** it generates the HTML for a page. - **Static Generation** is the pre-rendering method that generates the HTML at **build time**. The pre-rendered HTML is then _reused_ on each request. - **Server-side Rendering** is the pre-rendering method that generates the HTML on **each request**. Importantly, Next.js lets you **choose** which pre-rendering form to use for each page. You can create a "hybrid" Next.js app by using Static Generation for most pages and using Server-side Rendering for others.
-
实现解析文件获取
title
和date
-
安装
gray-matte
解析markdown 文件中的元数据1
npm install gray-matter
-
在跟路径下创建
lib
文件夹 -
创建
posts.js
文件1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35
import fs from 'fs' import path from 'path' import matter from 'gray-matter' const postsDirectory = path.join(process.cwd(), 'posts') export function getSortedPostsData() { // Get file names under /posts const fileNames = fs.readdirSync(postsDirectory) const allPostsData = fileNames.map(fileName => { // Remove ".md" from file name to get id const id = fileName.replace(/\.md$/, '') // Read markdown file as string const fullPath = path.join(postsDirectory, fileName) const fileContents = fs.readFileSync(fullPath, 'utf8') // Use gray-matter to parse the post metadata section const matterResult = matter(fileContents) // Combine the data with the id return { id, ...matterResult.data } }) // Sort posts by date return allPostsData.sort((a, b) => { if (a.date < b.date) { return 1 } else { return -1 } }) }
-
-
index.tsx
界面处理1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50
import Head from 'next/head' import Layout, {siteTitle} from '../components/layout' import utilStyles from '../styles/utils.module.css' import {getSortedPostsData} from '@/lib/posts' type PostsData = { id: string, date: string, title: string } type AllPostsData = { allPostsData: PostsData[] } export default function Home({allPostsData}: AllPostsData) { return ( <Layout home={true}> <Head> <title>{siteTitle}</title> </Head> <section className={utilStyles.headingMd}> <p>自我介绍</p> </section> <section className={`${utilStyles.headingMd} ${utilStyles.padding1px}`}> <h2 className={utilStyles.headingLg}>Blog</h2> <ul className={utilStyles.list}> {allPostsData.map(({id, date, title}) => ( <li className={utilStyles.listItem} key={id}> {title} <br/> {id} <br/> {date} </li> ))} </ul> </section> </Layout> ) } export async function getStaticProps() { const allPostsData = getSortedPostsData() return { props: { allPostsData } } }
请求时
使用getServerSideProps
动态路由
在目录中或文件中使用
[xx]
或[xx].tsx
即可通过url访问多级:
[...all].tsx
-
创建
pages/posts/[id].tsx
文件,getStaticPaths
方法需要返回所有可能有的id的列表getStaticProps
方法需要返回指定id的数据1 2 3 4 5 6 7 8 9 10 11 12 13
import Layout from '../../components/layout' export default function Post() { return <Layout>...</Layout> } export async function getStaticPaths() { } export async function getStaticProps({ params }) { }
-
在
lib/posts.js
中添加功能1 2 3 4 5 6 7 8 9 10
export function getAllPostIds() { const fileNames = fs.readdirSync(postsDirectory) return fileNames.map(fileName => { return { params: { id: fileName.replace(/\.md$/, '') } } }) }
-
完善
pages/posts/[id].tsx
中getStaticPaths
方法1 2 3 4 5 6 7 8 9
import { getAllPostIds } from '../../lib/posts' export async function getStaticPaths() { const paths = getAllPostIds() return { paths, fallback: false } }
-
在
lib/posts.js
中添加功能1
npm install remark remark-html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
import {remark} from 'remark' import html from 'remark-html' export async function getPostData(id) { const fullPath = path.join(postsDirectory, `${id}.md`) const fileContents = fs.readFileSync(fullPath, 'utf8') // Use gray-matter to parse the post metadata section const matterResult = matter(fileContents) // Use remark to convert markdown into HTML string const processedContent = await remark() .use(html) .process(matterResult.content) const contentHtml = processedContent.toString() // Combine the data with the id and contentHtml return { id, contentHtml, ...matterResult.data } }
-
完善
pages/posts/[id].tsx
中getStaticProps
方法1 2 3 4 5 6 7 8
export async function getStaticProps({params}: any) { const postData = await getPostData(params.id) return { props: { postData } } }
-
完善
pages/posts/[id].tsx
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
export default function Post({postData}: any) { return ( <Layout home={false}> <Head> <title>{postData.title}</title> </Head> <article> <h1 className={utilStyles.headingXl}>{postData.title}</h1> <div className={utilStyles.lightText}> {postData.date} </div> <div dangerouslySetInnerHTML={{ __html: postData.contentHtml }} /> </article> </Layout> ) }