Next
什么是Nextjs
Next.js 是一个流行的、轻量级的框架,用于配合 React 打造静态化和服务端渲染应用。 可以使用 React 构建您的 UI,然后逐步采用 Next.js 功能来解决常见的应用程序需求,例如路由、数据获取、集成 - 同时改善开发人员和最终用户的体验。
Next 官网地址 托管在vercel平台上
常见服务端渲染框架
常用的 CSR SSR SSG 几种渲染页面方式
CSR
CSR全称是 Client Side Rendering , 代表的是客户端渲染,渲染工作在客户端(浏览器)进行,比如Vue, React 等框架,都是先下载html (不是最终完整的html),然后下载js 来执行渲染出页面结果。
优点:
- 前后端分离。前端专注于界面开发,后端专注于api开发,且前端有更多的选择性,可以使用vue,react框架开发,而不需要遵循后端特定的模板。
- 服务器压力变轻了,渲染工作在客户端进行,服务器直接返回不加工的html
- 用户在后续访问操作体验好,(首屏渲染慢)可以将网站做成SPA,可以增量渲染
缺点:
- 不利于SEO,因为搜索引擎不执行JS相关操作,无法获取渲染后的最终html
- 首屏渲染时间比较长,因为需要页面执行ajax获取数据来渲染页面,如果请求接口多,不利于首屏渲染
SSR
SSR全称是 Server Side Rendering,代表的是服务端渲染。与客户端渲染不同的是,SSR输出的是一个渲染完成的html,整个渲染过程是在服务器端进行的。列如传统JSP, Node+Pug,PHP
优点:
- 有利于SEO,由于页面在服务器生成,搜索引擎直接抓取到最终页面结果。
- 有利于首屏渲染,html所需要的数据都在服务器处理好,直接生成html,首屏渲染时间变短。
缺点:
- 占用服务器资源,渲染工作都在服务端渲染
- 用户体验不好,每次跳转到新页面都需要在重新服务端渲染整个页面,不能只渲染可变区域
SSG
SSG全称是 Static Site Generation ,代表的是静态站点生成。在构建的时候直接把结果页面输出html到磁盘,每次访问直接把html返回给客户端,相当于一个静态资源
优点:
- 减轻服务器压力,可以把生成的静态资源(html)放到CDN上,合理利用缓存
- 有利于SEO,由于html已经提前生成好,不需要服务端和客户端去渲染
缺点:
- 只适用于静态数据,对于经常改动的数据,需要每次重新生成页面。
- 用户体验不好,每次打开新页面都需要重新渲染整个页面,不能只渲染可变区域
ISR
Next.js 推出的 ISR(Incremental Static Regeneration) 方案, 允许在应用运行时再重新生成每个页面 HTML,而不需要重新构建整个应用。
这样即使有海量页面,也能使用上 SSG 的特性。一般来说,使用 ISR 需要 getStaticPaths
和 getStaticProps
同时配合使用
// pages/posts/[id].jsfunction Post(props) {const { postData } = props;return <div>{postData.title}</div>}export async function getStaticPaths() {const paths = await fetch('https://.../posts');return {paths,// 页面请求的降级策略,这里是指不降级,等待页面生成后再返回,类似于 SSRfallback: 'blocking'}}export async function getStaticProps({ params }) {// 使用 params.id 获取对应的静态数据const postData = await getPostData(params.id)return {props: {postData},// 开启 ISR,最多每10s重新生成一次页面revalidate: 10,}}
表示开启 ISR。在上面的例子中,指定 revalidate = 10
,表示最多10秒内重新生成一次静态 HTML。当浏览器请求已在构建时渲染生成的页面时,首先返回的是缓存的 HTML,10s 后页面开始重新渲染,页面成功生成后,更新缓存,浏览器再次请求页面时就能拿到最新渲染的页面内容了。
Next.js 不仅支持 SSR、SSG、CSR、ISR,还支持渲染模式的混合使用。SSR + CSR(服务+客户), SSG + CSR(静态 + 客户),SSG + SSR (静态+服务)
安装
npx create-next-app@latest --ts
目录结构
|-- project|-- .babelrc.js|-- .eslintrc.json|-- .gitignore|-- README.md|-- config.json|-- next.config.js|-- package.json|-- server.js|-- yarn.lock|-- .vercel| |-- README.txt| |-- project.json| |-- cache|-- components # 组件目录| |-- Hello| |-- index.js|-- pages # 页面目录| |-- _app.js| |-- index.js| |-- index.module.less| |-- 404| | |-- index.js| |-- _error| | |-- index.jsx| |-- api| | |-- [id].js| | |-- detail.js| | |-- list.js| |-- home| | |-- home.module.css| | |-- home.module.less| | |-- index.js|-- public| |-- favicon.ico| |-- vercel.svg|-- static # 静态文件夹目录| |-- favicon.ico| |-- person.jpg| |-- sum.png| |-- vercel.svg|-- styles # 全局样式目录| |-- Home.module.css| |-- globals.css| |-- test.less|-- unitl|-- test.js
1.样式
-
添加全局样式
-
添加组件样式
-
Css-in-js 内联样式
-
less
全局样式
要将样式表添加到您的应用程序中,请在 pages/_app.js 文件中导入(import)CSS 文件。// 根路径 所有page 都会进到这个页面import '../styles/globals.css'export default ({Component, pageProps}) => {return <Component {...pageProps}/>}
添加组件样式
Next.js 通过 [name].module.css 文件命名约定来支持 CSS 模块 。import Link from "next/link"import Styles from './home.module.css'export default () => {return <div className={Styles.home}><div><Link href="/posts/12" ><a>link 跳转 id</a></Link><br/><Link href="/posts/12/12" ><a>link 跳转 name</a></Link></div></div>}
Css-in-js 内联样式
<Link href="/posts/12/12" ><a style={{fontSize: "30px"}}>link 跳转 name</a></Link>
styled-jsx
styled-jsx 支持作用域隔离(isolated scoped)的 CSSexport default () => {return <div className={Styles.home}><div><p>test home</p><style jsx>{`p{color: blue}`}</style><style global jsx>{`html, body {background: #ffffff}`}</style></div></div>}
支持作用域隔离
less
cnpm i less less-loader next-with-less -Snext.config.jsconst withLess = require("next-with-less");module.exports = withLess({lessLoaderOptions: {},})# 全局引入styles/test.lessimport '../styles/globals.css'import "../styles/test.less"export default ({Component, pageProps}) => {return <Component {...pageProps}/>}# 组件引入home.module.less #[name].module.lessimport Styless from './home.module.less'export default () => {return <div className={Styless.home}><div><p>test home</p></div></div>}
2. 获取数据
nextjs 两种预渲染
服务端渲染
也被称为ssr 或者动态渲染
访问XXX路由之前,向服务器要数据,把要回来的数据和Html加工直接返回前台显示
在 每次页面请求(request)时 重新生成 HTML。
缺点: 当访问服务器人数多的情况下,服务器压力比较大。
静态化
访问XXX路由之前,向服务器要数据,把要回来的数据和Html加工生成真正的XXX.html文件
HTML 在 构建时 生成,并在每次页面请求(request)时重用。
优点: 下次访问同一个路由地址的时候,直接返回静态压面,减少服务器压力,提高性能
缺点: 就是页面数据不变的情况下,如果页面数据经常变化,还是需要服务端渲染。
- getStaticProps
- getStaticPaths
- getServerSideProps
getServerSideProps
要对 page(页面)使用服务器端渲染,需要 export
一个名为 getServerSideProps
的 async
函数。服务器将在每次页面请求时调用此函数。
import fetch from 'node-fetch'// 页面function CaseList({ data }) {console.log(data, '用来接收http://10.60.104.23:3003/api/list')return (<div>has</div>)// Render data...}// node 层export async function getServerSideProps(context) {let res = await fetch('http://10.60.104.23:3003/api/list')const { result: { data } } = await res.json()console.log(data, 'res')// 不存在if (!data) {return {notFound: true,}}// 通过props 传递参数到CaseList页面return {props: {data}, // will be passed to the page component as props}}export default CaseList
getStaticProps
在构建时获取数据。
// 静态页面import fetch from "node-fetch"export default function({data}) {return <div>details {JSON.stringify(data)}</div>}export async function getStaticProps() {let res = await fetch('http://10.60.104.23:3003/api/detail')const { result: { data } } = await res.json()console.log('details')return {props: {data},}}
getStaticPaths
pages/[id].js// 动态页面import {useRouter} from "next/router"// 用于在使用动态路由时生成静态文件。export async function getStaticPaths() {let res = await fetch('http://10.60.104.23:3003/list')const { result: { data } } = await res.json()const paths = data.map((post) => ({params: { id: post.id },}))console.log(data, 'getStaticPaths--->res')return { paths: paths, fallback: "blocking" }}export async function getStaticProps({params: {id}}) {console.log(id, 'params')return {props: {id}, // will be passed to the page component as props}}export default function({id}) {const router = useRouter()return <div>listData {id}</div>}
如你所见,
getServerSideProps
类似于getStaticProps
,但两者的区别在于getServerSideProps
在每次页面请求时都会运行,而在构建时不运行;
getStaticProps 是在构建的时候,获取数据生成静态页面.html和一个.json文件。
3. API 能力
API 路由为使用 Next.js 构建你自己的 API 提供了一种解决方案。pages/api 目录下的任何文件都将作为 API 端点映射到 /api/*,而不是 page。 # 这些文件只会增加服务端文件包的体积,而不会增加客户端文件包的大小。
Api 路由都是在项目page/api 目录下
4. 图片
Nextjs 提供了一个next/image 组件
import Image from 'next/image'# 引入图片# 不支持 await import()或require()import Person from '../../public/person.jpg'# 在不知道图片具体大小的时候,可以设置父元素大小限制 子元素大小<div style={{width: '800px', display:'inline-block'}}><Image src={Car} alt="Car" placeholder="blur" quality={1} layout="responsive"></Image></div><img src={"../../static/sum.png"} style={{ width: '800px' }} alt="" /># 设置图片 宽高 占位用# placeholder 有个模糊效果,预先加载渲染质量模块,等全部加载完了,在显示全部图片(对大图特别友好)# 如果是要用到外部的图片需要配置 next.config.js 中配置 images domains<div><Image src={Person} alt="beautiful of the person" layout="fixed" width={400} height={500} placeholder="blur" ></Image><Image src="http://si1.go2yd.com/get-image/0bVwH1EStDU" width={500} height={500}></Image></div>
images: {domains: ['si1.go2yd.com'],}
5. Script
Next.js 脚本组件next/script
是 HTML<script>
元素的扩展。它使开发人员可以在其应用程序中的任何位置设置第三方脚本的加载优先级,而无需直接附加到next/head
,节省开发人员时间的同时提高加载性能。
import Script from "next/script"<Script src="https://connect.facebook.net/en_US/sdk.js" strategy="beforeInteractive" onLoad={() => {console.log('load ok')}} onError={() => {console.log('err')}} />
beforeInteractive 在页面交互之前加载
beforeInteractive (默认): 页面变为交互式后立即加载
lazyOnload 在空闲时间加载
6. 自定义server.js
Nextjs 会带有自己的server, 如果你想自定义server, 需要确保所有情况都正常
const express = require('express')const next = require('next')console.log(process.env.PORT, '--->')const port = parseInt(process.env.PORT, 10) || 3000const dev = process.env.NODE_ENV !== 'production'const app = next({ dev })const handle = app.getRequestHandler()app.prepare().then(() => {const server = express()server.all('*', (req, res) => {return handle(req, res)})server.listen(port, () => {console.log(`> Ready on http://localhost:${port}`)})})
package.json"dev": "node server.js",
7. 自定义 _document.js
nextjs 本身自带了一套document ,如果想覆盖,如:
import Document, { Html, Head, Main, NextScript } from 'next/document'class MyDocument extends Document {static async getInitialProps(ctx) {const initialProps = await Document.getInitialProps(ctx)return { ...initialProps }}render() {return (<Html><Head><title>The First Project</title><meta name="viewport" content="initial-scale=1.0, width=device-width" /><meta name="author" content="lucky-泽" /><meta name="description" content="lucky-泽-recode"></meta></Head><body><Main /><NextScript /></body></Html>)}}export default MyDocument
Html, Head, Main, NextScrip 几个组件是必要的。而且在_document.js 获取数据几个方法(getServerSideProps, getStaticProps, getStaticPaths)。
8. 全局公共组件_app.js
// 根路径 所有page 都会先进到这个页面import '../styles/globals.css'import "../styles/test.less"export default ({Component, pageProps}) => {return <Component {...pageProps}/>}
9. 自定义错误页面
10. 环境变量
.env.development (next dev) 环境执行时需要配置的;.env.production (next start) 生产环境需要配置的,这些配置的变量只能在node 环境中去执行,如果想在浏览器环境中获取到配置的变量需要 NEXT_PUBLIC_
11. 路由
nextjs 以page 页面作为路由文件,
pages/index.js
->/
pages/about/index.js
->/about
动态路由
pages/list/[id].js
->pages/list/:id
单参数pages/list/[...name].js
->pages/list/a/b/c/d
多参数- 组件跳转
获取路由参数
import { useRouter } from 'next/router'const router = useRouter()const {query} = router
12. 重定向
重定向,您可以使用以下redirects
键next.config.js
redirects
是一个异步函数,它期望返回一个数组,其中包含具有source
、destination
和permanent
属性的对象:source
是传入请求路径模式 destination
是您要路由到的路径。permanent
true
或false
- 如果true
将使用指示客户端/搜索引擎永久缓存重定向的 308 状态码,如果false
将使用临时且未缓存的 307 状态码;
source / destination 路径支持 [通配符路径]
[正则表达式路径]
[标头、Cookie 和查询]
basePath
i18n
api res.redirect()
重定向
13. 动态导入
Next.js 支持 JavaScript 的 ES2020动态import()
const Fuse = (await import('fuse.js')).defaultconst fuse = new Fuse(names)
Nextjs 提供了next/dynamic 组件,支持动态导入组件,可以自定义加载文件和开启服务端渲染,类似 React18中的 React.lazy``<Suspense>``fallback
import dynamic from 'next/dynamic'const DynamicComponent = dynamic(() => import('@/components/dynamic_com'), {loading: () => <p style={{fontSize: '20px'}}>加载中</p>, // 自定义加载文件ssr: false // 是否需要服务端渲染(false 就是在浏览器中)})export default () => {return (<><p>测试动态组件</p><DynamicComponent /><style jsx>{`p{display: block;font-size: 20px !important;}`}</style></>)}
14. MDX
MDX 是 Markdown 的超集,可以直接在 Markdown 文件中编写 JSX,可以更加直观和快速的开发你的页面,由于 Markdown 本质上是静态内容,因此您无法根据用户交互性创建动态内容
。MDX 的亮点在于它能够让您直接在标记中创建和使用 React 组件
npm install @next/mdx @mdx-js/loader
!
如果你想让页面更加美观 还可以自定义组件
!
15. 打包部署 vercel
npm run build #打包npm run start #运行打包文件
docker 部署
# Install dependencies only when neededFROM node:16-alpine AS deps# Check https://github.com/nodejs/docker-node/tree/b4117f9333da4138b03a546ec926ef50a31506c3#nodealpine to understand why libc6-compat might be needed.RUN apk add --no-cache libc6-compatWORKDIR /appCOPY package.json yarn.lock ./RUN yarn install --frozen-lockfile# If using npm with a `package-lock.json` comment out above and use below instead# COPY package.json package-lock.json ./# RUN npm ci# Rebuild the source code only when neededFROM node:16-alpine AS builderWORKDIR /appCOPY --from=deps /app/node_modules ./node_modulesCOPY . .# Next.js collects completely anonymous telemetry data about general usage.# Learn more here: https://nextjs.org/telemetry# Uncomment the following line in case you want to disable telemetry during the build.# ENV NEXT_TELEMETRY_DISABLED 1RUN yarn build# If using npm comment out above and use below instead# RUN npm run build# Production image, copy all the files and run nextFROM node:16-alpine AS runnerWORKDIR /appENV NODE_ENV production# Uncomment the following line in case you want to disable telemetry during runtime.# ENV NEXT_TELEMETRY_DISABLED 1RUN addgroup --system --gid 1001 nodejsRUN adduser --system --uid 1001 nextjs# You only need to copy next.config.js if you are NOT using the default configuration# COPY --from=builder /app/next.config.js ./COPY --from=builder /app/public ./publicCOPY --from=builder /app/package.json ./package.json# Automatically leverage output traces to reduce image size# https://nextjs.org/docs/advanced-features/output-file-tracingCOPY --from=builder --chown=nextjs:nodejs /app/.next/standalone ./COPY --from=builder --chown=nextjs:nodejs /app/.next/static ./.next/staticUSER nextjsEXPOSE 3000ENV PORT 3000CMD ["node", "server.js"]
docker build -t nextjs-docker .docker run -p 3000:3000 nextjs-docker
vercle 托管
如果不会vercel 小伙伴 请自行学习 vercel
主要的流程是vercle 会绑定github/gitlab -> 导入 next 项目,vercel 会自动识别是哪个框架,会自动执行 install -> build->start 命令 完成部署之后会提供一个可以访问的地址,并且每次代码提交之后 会自动 构建 无需手动 构建。