diff --git a/README.md b/README.md index bce36afa..1068ebf0 100644 --- a/README.md +++ b/README.md @@ -84,6 +84,7 @@ Quick Reference [HTML](./docs/html.md) [JavaScript](./docs/javascript.md) [Less.js](./docs/lessjs.md) +[Next.js](./docs/nextjs.md) [React](./docs/react.md) [RegEx 正则表达式](./docs/regex.md) [TypeScript](./docs/typescript.md) diff --git a/assets/nextjs.svg b/assets/nextjs.svg new file mode 100644 index 00000000..cc3a23ad --- /dev/null +++ b/assets/nextjs.svg @@ -0,0 +1,3 @@ + + + diff --git a/docs/nextjs.md b/docs/nextjs.md new file mode 100644 index 00000000..e72b90c4 --- /dev/null +++ b/docs/nextjs.md @@ -0,0 +1,1585 @@ +Next.js 备忘清单 +=== + +这是一份快速参考备忘单,包含 Next.js 的 API 参考列表和一些示例 + +入门 +---- + +### 创建项目 + +```shell +npx create-next-app@latest +# or +yarn create next-app +# or +pnpm create next-app +``` + +或者创建 [TypeScript](./typescript.md) 项目 + +```shell +npx create-next-app@latest --typescript +# or +yarn create next-app --typescript +# or +pnpm create next-app --typescript +``` + +运行 `npm run dev` 或 `yarn dev` 或 `pnpm dev` 以在 上启动开发服务器 + +### 添加首页 + +使用以下内容填充 `pages/index.js`: + +```jsx +function HomePage() { + return
Welcome to Next.js!
+} + +export default HomePage +``` + +`Next.js` 是围绕页面的概念构建的。 页面是从 `pages` 目录中的 `.js`、`.jsx`、`.ts` 或 `.tsx` 文件导出的 `React` 组件 + +### getServerSideProps + + +```jsx +function Page({ data }) { + // 渲染数据... +} + +// 每个请求都会调用它 +export async function getServerSideProps() { + // 从外部 API 获取数据 + const res = await fetch(`https://.../data`) + const data = await res.json() + + // 通过 props 向页面传递数据 + return { props: { data } } +} + +export default Page +``` + +如果您从页面导出一个名为 `getServerSideProps`(服务器端渲染)的函数,`Next.js` 将使用 `getServerSideProps` 返回的数据在每个请求上预渲染该页面 + +- 当您直接请求此页面时,`getServerSideProps` 在请求时运行,此页面将使用返回的 props 进行预渲染 +- 当您通过 `next/link` 或 `next/router` 在客户端页面转换上请求此页面时,`Next.js` 会向服务器发送 API 请求,服务器运行 `getServerSideProps` + +### getStaticPaths + + +```jsx +// pages/posts/[id].js +export async function getStaticPaths() { + // 当这是真的时(在预览环境中)不要预呈现任何静态页面(更快的构建,但更慢的初始页面加载) + if (process.env.SKIP_BUILD_STATIC_GENERATION) { + return { + paths: [], + fallback: 'blocking', + } + } + + // 调用外部 API 端点以获取帖子 + const res = await fetch('https://.../posts') + const posts = await res.json() + + // 根据帖子获取我们要预渲染的路径 在生产环境中,预渲染所有页面 + // (构建速度较慢,但初始页面加载速度较快) + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // { fallback: false } 表示其他路由应该 404 + return { paths, fallback: false } +} +``` + +如果页面具有动态路由并使用 `getStaticProps`,则需要定义要静态生成的路径列表 + +- 数据来自无头 CMS +- 数据来自数据库 +- 数据来自文件系统 +- 数据可以公开缓存(非用户特定) +- 页面必须预渲染(用于 SEO)并且速度非常快 —— `getStaticProps` 生成 HTML 和 JSON 文件,这两种文件都可以由 CDN 缓存以提高性能 + +### getStaticProps + + +```jsx +// 帖子将在构建时由 getStaticProps() 填充 +function Blog({ posts }) { + return ( + + ) +} + +// 这个函数在服务器端的构建时被调用。 +// 它不会在客户端调用,因此您甚至可以直接进行数据库查询。 +export async function getStaticProps() { + // 调用外部 API 端点以获取帖子。 您可以使用任何数据获取库 + const res = await fetch('https://.../posts') + const posts = await res.json() + + // 通过返回 { props: { posts } },Blog 组件将在构建时接收 `posts` 作为 prop + return { + props: { + posts, + }, + } +} + +export default Blog +``` + +在服务器端的构建时被调用 + +### 增量静态再生 + + +```jsx +function Blog({ posts }) { + return ( + + ) +} + +// 这个函数在服务器端的构建时被调用 +// 如果启用了重新验证并且有新请求进入,它可能会在无服务器功能上再次调用 +export async function getStaticProps() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + return { + props: { + posts, + }, + // Next.js 将尝试重新生成页面: + // - 当请求进来时 + // - 最多每 10 秒一次 + revalidate: 10, // 片刻之间 + } +} + +// 这个函数在服务器端的构建时被调用 +// 如果尚未生成路径,则可能会在无服务器函数上再次调用它 +export async function getStaticPaths() { + const res = await fetch('https://.../posts') + const posts = await res.json() + + // 根据帖子获取我们要预渲染的路径 + const paths = posts.map((post) => ({ + params: { id: post.id }, + })) + + // 我们将在构建时仅预渲染这些路径 + // { fallback: blocking } 如果路径不存在,服务器将按需呈现页面 + return { paths, fallback: 'blocking' } +} + +export default Blog +``` + +- 在初始请求之后和 10 秒之前对页面的任何请求也会被缓存和即时 +- 在 10 秒窗口之后,下一个请求仍将显示缓存的(陈旧的)页面 +- Next.js 在后台触发页面的重新生成 +- 一旦页面生成成功,Next.js 将使缓存失效并显示更新后的页面。如果后台重新生成失败,旧页面仍将保持不变 + +### 使用 useeffect 客户端数据获取 + +```jsx +import { useState, useEffect } from 'react' + +function Profile() { + const [data, setData] = useState(null) + const [isLoading, setLoading] = useState(false) + + useEffect(() => { + setLoading(true) + fetch('/api/profile-data') + .then((res) => res.json()) + .then((data) => { + setData(data) + setLoading(false) + }) + }, []) + + if (isLoading) return

Loading...

+ if (!data) return

No profile data

+ + return ( +
+

{data.name}

+

{data.bio}

+
+ ) +} +``` + +### 使用 SWR 获取客户端数据 + +```jsx +import useSWR from 'swr' + +const fetcher = (...args) => fetch(...args).then((res) => res.json()) + +function Profile() { + const { data, error } = useSWR('/api/profile-data', fetcher) + + if (error) return
Failed to load
+ if (!data) return
Loading...
+ + return ( +
+

{data.name}

+

{data.bio}

+
+ ) +} +``` + +### 静态文件服务 + +Next.js 可以在根目录中名为 `public` 的文件夹下提供静态文件,如图像。 然后,您的代码可以从基本 URL (`/`) 开始引用 `public` 中的文件 + +```jsx +import Image from 'next/image' + +function Avatar() { + return ( + me + ) +} + +export default Avatar +``` + +### 支持的浏览器和功能 + +Next.js 支持零配置的现代浏览器 + +- Chrome 64+ +- Edge 79+ +- Firefox 67+ +- Opera 51+ +- Safari 12+ + + +Next.js 支持在 `package.json` 文件中配置 `Browserslist` + +```js +{ + "browserslist": [ + "chrome 64", + "edge 79", + "firefox 67", + "opera 51", + "safari 12" + ] +} +``` + +内置 CSS 支持 +--- + +### 添加全局样式表 + + +如果不存在,请创建一个 `pages/_app.js` 文件。 然后,导入 `styles.css` 文件 + +```jsx +import '../styles.css'; + +// 在新的“pages/_app.js”文件中需要此默认导出 +export default function MyApp({ + Component, pageProps +}) { + return +} +``` + +例如,考虑以下名为 `styles.css` 的样式表 + +```css +body { + font-family: + 'SF Pro Text', 'SF Pro Icons', + 'Helvetica Neue', 'Helvetica', + 'Arial', sans-serif; + margin: 0 auto; +} +``` + +### 从 node_modules 导入样式 + +对于全局样式表,如 `bootstrap` 或 `nprogress`,您应该在 `pages/_app.js` 中导入文件 + +```jsx +// pages/_app.js +import 'bootstrap/dist/css/bootstrap.css' + +export default function MyApp({ + Component, pageProps +}) { + return +} +``` + +从 Next.js 9.5.4 开始,您的应用程序中的任何地方都允许从 `node_modules` 导入 CSS 文件 + +### 添加组件级 CSS (CSS Modules) + + +您无需担心 .error {} 与任何其他 `.css` 或 `.module.css` 文件!他将被生成 `hash` 名称 + +```css +.error { + color: white; + background-color: red; +} +``` + +然后,创建 `components/Button.js`,导入并使用上面的 CSS 文件: + +```jsx +import styles from './Button.module.css' + +export function Button() { + return ( + + ) +} +``` + +### Sass 支持 + +Next.js 允许您使用 `.scss` 和 `.sass` 扩展名导入 Sass,可以通过 CSS 模块和 `.module.scss` 或 `.module.sass` 扩展名使用组件级 `Sass` + +```shell +$ npm install --save-dev sass +``` + +在使用 Next.js 的内置 `Sass` 支持之前,请务必安装 `sass` + +### 自定义 Sass 选项 + + +通过在 `next.config.js` 中使用 `sassOptions` 来实现配置 `Sass` 编译器。例如添加 `includePaths`: + +```js +const path = require('path') + +module.exports = { + sassOptions: { + includePaths: + [path.join(__dirname, 'styles')], + }, +} +``` + +#### Sass 变量 + +```sass +/* variables.module.scss */ +$primary-color: #64ff00; + +:export { + primaryColor: $primary-color; +} +``` + +在 `pages/_app.js` 中导入 `variables.module.scss` + +```jsx +import variables from '../styles/variables.module.scss' + +export default function MyApp({ Component, pageProps }) { + return ( + + + + ) +} +``` + +### CSS-in-JS + +最简单的一种是内联样式: + +```jsx +function HiThere() { + return ( +

hi 这里

+ ) +} + +export default HiThere +``` + +使用 [styled-jsx](https://github.com/vercel/styled-jsx) 的组件如下所示: + +```jsx +function HelloWorld() { + return ( +
+ Hello world +

scoped!

+ + +
+ ) +} + +export default HelloWorld +``` + +当然,你也可以使用 [styled-components](./styled-components.md) + +Layouts +--- + +### 基础示例 + +```jsx +// components/layout.js +import Navbar from './navbar' +import Footer from './footer' + +export default function Layout({ children }) { + return ( + <> + +
{children}
+