
这次来学 Next.js,使用的是站内这套视频Next.js 和 React 完整指南(上)Next.js 和 React 完整指南(下)


教程老师还是熟悉的 Maximilian Schwarzmüller,之前学习 TypeScript 与 React Native 就是他,课程模块安排很清晰,学习起来很轻松。
之前的两篇文章


课程一共15个章节,视频总时长25小时左右,大纲如下:
第一章——序言(对 Next.js 速览)
第二章——复习React
第三章——介绍 Next.js 基于文件路由机制
第四章——项目练习1(复习第三章)
第五章——页面预渲染(重要‼️)
第六章——项目练习2(复习第五章)
第七章——优化(HTML meta信息与图片)
第八章——API路由(服务端)
第九章——项目练习3(复习第八章)
第十章——复习 React Context
第十一章——搭建博客(大项目,复习前面所有内容)
第十二章——部署 Next.js 应用
第十三章——鉴权(NextAuth.js库的使用)
第十四章——总复习(可选)
第十五章——结语
蓝色标出的四个章节比较特殊,第二和第十章属于复习章节,如果对 React 比较熟悉的可以跳过。第十四章对整个 Next.js 的概念进行总复习,最后的章节结语,时间不充裕的童鞋可以选择性观看。
Next.js 是在 React 上构建的一个用于生产环境框架,包含了很多功能,比如静态及服务器端融合渲染、智能化打包、 路由预取。
Next.js 的优势有以下三点
内置服务端渲染
基于文件的路由
拥有全栈的能力
可以通过以下命令安装 Next.js
npx create-next-app 视频中老师使用的 Next.js 版本是 10.0.7,咪咪全程学习使用的版本是 13.1.1,全程没有冲突,各位同学可看情况安装不同版本。
第一章对 Next.js 进行了一个速览,其中最重要的两个特性就是页面预渲染与基于文件路由机制。
复习 React,略。
第三章详细介绍了 Next.js 基于文件路由机制。
静态路由
Next.js 在构建时会自动查找根目录的 pages 文件夹,pages 文件夹中 js 文件对应渲染的 React 组件,就是 js 文件内 export default 导出的组件。
动态路由
动态路由使用中括号 [variable].js 作为文件名,其中 variable 会映射成 URL query 的变量名称,在 React 中可以通过 useRouter 获取路由信息:
import {useRouter} from 'next/router'
export default function PortfolioDemo() {
const router = useRouter();
console.log(router.pathname);
console.log(router.query);
return <>PortfolioDemo</>
} 启动项目后,访问 http://localhost:3000/portfolio/mimi,控制台输出如下信息:

动态路由 query
在匹配 URL 时,静态路由优先权大于动态路由。
⚠️注意:文件夹也可以是动态的
Link 组件
可以使用 Next.js 中的 Link 组件来导航至各个页面:
import Link from 'next/link';
<Link href="/">
首页
</Link> Link 组件的 href 不但支持字符串,还可以传入 UrlObject,接口如下:
interface UrlObject {
auth?: string | null | undefined;
hash?: string | null | undefined;
host?: string | null | undefined;
hostname?: string | null | undefined;
href?: string | null | undefined;
pathname?: string | null | undefined;
protocol?: string | null | undefined;
search?: string | null | undefined;
slashes?: boolean | null | undefined;
port?: string | number | null | undefined;
query?: string | null | ParsedUrlQueryInput | undefined;
} 如果 url 很长,参数很多的情况下可以使用这种方法传参。
router.push
在代码中,如果需要触发导航逻辑,可以使用 router.push 方法:
import {useRouter} from 'next/router'
export default function PortfolioDemo() {
const router = useRouter();
const myFunc = () => {
router.push("[url]");
}
return <>PortfolioDemo</>
} 404
pages 文件夹下创建特殊文件 404.js,Next.js 将会在返回 404 错误时,自动加载组件。
相当于用户可以自定义 404 页面。
项目练习1,对第三章的复习。
静态文件
根目录 public 目录下放静态文件,Next.js 会自动处理,放在这个文件夹外的静态文件是无法获取到的。
CSS 模块
Next.js 通过 [name].module.css 文件命名约定来支持 CSS 模块。
pages/about.module.css
.title {
font-weight: bold;
color: indigo;
} pages/about.js
import classes from './about.module.css';
const About = () => (
<div>
<p className={classes.title}>无限咪咪</p>
<p>
xxx
</p>
</div>
); icons
介绍了一个获取 icons 网站:https://heroicons.com/
第五章详细介绍了 Next.js 中页面预渲染与获取数据的方法,其中页面预渲染可能是 Next.js 中最最重要的特性。
为什么要页面预渲染?
传统 React 应用返回的 HTML 文件中,不包含应用的信息,因为页面是在客户端进行渲染的,所以服务端返回的源码通常只有一个 id 为 root 的 div 标签,不利于做 SEO(搜索引擎优化)。
而页面预渲染就可以解决这个问题,浏览器收到的 HTML 文件源码是包含了页面信息的代码。
Next.js 提供了两种页面预渲染方案,SSG 与 SSR。
SSG
SSG 是静态站点生成,就是在文件打包阶段,预先生成页面。
Next.js 默认会预渲染所有没有动态数据的页面,而动态的数据还是像 React 一样在客户端渲染的。
如果要在 HTML 源码中展现动态数据,可以使用 page 下 getStaticProps 方法。这个方法是跑在服务端环境下的,可以在服务端获取数据并渲染,并且客户端不会收到方法内任何的代码。
此外,Next.js 拓展了一些功能,比如 fetch 是浏览器的接口,在服务端是不能用的,而在getStaticProps 方法中是可以使用 fetch API 的,咪咪看了下背后使用的是 node-fetch 这个库实现的。(Node.js18.0.0 版本开始原生支持了 fetch 方法)
getStaticProps 方法返回值类型如下,一共有三种情况:
export type GetStaticPropsResult<P> =
| { props: P; revalidate?: number | boolean }
| { redirect: Redirect; revalidate?: number | boolean }
| { notFound: true; revalidate?: number | boolean } 一般正确获得数据后,返回值是第一种情况。
props 是服务端获取的需要传给组件的数据,revalidate 可以定义生产环境下 getStaticProps 调用的间隔秒数,600就是600秒,10分钟。测试环境下这个配置项无效,每次访问页面都会触发此方法。
而后两种情况适用于获取数据失败时,引导用户进行下一步操作,重定向或直接返回404错误。
此外如果需要在 getStaticProps 中访问路径参数,可以在方法的 context 参数的 params 属性获取。
getStaticPaths
上面指的没有动态数据的页面,也不能是动态路由(文件名带[]的js),否则也不会自动生成静态页面。如果需要生成静态页面,需要使用 getStaticPaths 方法。
getStaticPaths 方法定义了一组需要生成静态页面的列表,每项数据都会调用 getStaticProps 来获取数据,所以要使用 getStaticPaths 一定先要有定义 getStaticProps 。
export async function getStaticPaths() {
return {
paths: [{ params: { id: '1' } }, { params: { id: '2' } }],
fallback: false,
}
} getStaticPaths 方法返回值类型如下:
export type GetStaticPathsResult<P extends ParsedUrlQuery = ParsedUrlQuery> = {
paths: Array<string | { params: P; locale?: string }>
fallback: boolean | 'blocking'
} paths 定义了需要预先生成静态页面的 URL 请求参数,fallback 定义了在访问 paths 中未列出的 URL 请求参数时的表现,适用于如果有很多页面需要预生成的情况。
从类型定义上来看,fallback 一共有三种取值
fallback 值为 false 时,如果 URL 请求参数不在 paths 属性中定义了,那么会直接返回404页面。
fallback 值为 true 时,如果通过 Link 组件在页面中导航,那不会有问题。但是如果是直接在 URL 中访问未在 paths 中列出的路径,会直接报错,需要在 React 组件中判断对应 props 的参数,在服务器还未准备好时,先返回一个加载中的提示。
除了布尔值,fallback 还可以赋值为 'blocking',请求页面时如果数据未准备好就阻塞请求,等待页面渲染完毕后再返回页面。相比第二种情况,相当于免除了组件中判断这一环节。
fallback 值为 true 时,如果访问不存在的请求路径,就可以在 getStaticProps 中直接返回 { notFound: true } 来返回 404 页面。
SSR
SSR 是服务端渲染,getServerSideProps 方法可以针对每次请求作出处理,适用于数据变化比较频繁的页面。
getStaticProps 与 getServerSideProps 只能二选一。
getServerSideProps 也是运行在服务器上的方法,这个方法的参数 context 可以完整获取请求的所有数据,context 的类型如下:
export type GetServerSidePropsContext<
Q extends ParsedUrlQuery = ParsedUrlQuery,
D extends PreviewData = PreviewData
> = {
req: IncomingMessage & {
cookies: NextApiRequestCookies
}
res: ServerResponse
params?: Q
query: ParsedUrlQuery
preview?: boolean
previewData?: D
resolvedUrl: string
locale?: string
locales?: string[]
defaultLocale?: string
} getServerSideProps 返回值类型:
export type GetServerSidePropsResult<P> =
| { props: P | Promise<P> }
| { redirect: Redirect }
| { notFound: true } getServerSideProps 的返回值类型基本同 getStaticProps,只是少了 revalidate 属性,因为getServerSideProps 会对每次请求进行重新渲染。
实际使用时,可以将服务端预渲染的数据作为对应数据 useState 的默认值。
不适合使用页面预渲染的情况
以下三种情况不适合使用服务端预渲染:
数据变化非常频繁的页面(比如股票数据)
与用户身份高度耦合的页面(比如用户最近xxx的xxx)
页面中只有某一小部分数据不同的情况
碰到这些情况,还是在客户端使用 useEffect 中 fetch 来获取数据,Next.js 团队也编写了一个React 钩子库 SWR(https://swr.vercel.app/zh-CN) 来简化客户端请求,示例如下:
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 <div>Failed to load</div>
if (!data) return <div>Loading...</div>
return (
<div>
<h1>{data.name}</h1>
<p>{data.bio}</p>
</div>
)
}
第五章内容 SSG 与 SSR 的复习,略。
第七章讲解了 HTML meta 信息与图片优化。
增加 Meta 信息
可以使用 Next.js 提供的 Head 组件,来给每个页面增加 header 信息:
import Head from 'next/head'
export default function About(props) {
return <>
<Head>
<title>关于我</title>
</Head>
<p>{props.info.name}</p>
</>
} 复用组件
全局通用的 Head 组件可以添加在 _app.js 这个文件中。此外,相同的头标签会合并,合并的规则就是最后渲染的 Head 覆盖之前渲染的 Head。(页面组件内的优先权比全局的高)
另一个全局特殊 JS 文件是 /pages/_document.js,这个文件的默认值如下:
import { Html, Head, Main, NextScript } from 'next/document'
export default function Document() {
return (
<Html>
<Head />
<body>
<Main />
<NextScript />
</body>
</Html>
)
} _app.js 这个文件相当于 body 中的内容,_document.js 相当于整个 HTML 文档,比前者更“外面”。注意这里的 Head 组件的引入包与普通页面引入的包不同,不要搞错。
优化图片
Next.js 提供了优化图片的方案——组件。
使用 Image 组件有四点好处
对各个设备使用合适的尺寸与格式(使用Chrome访问页面时,图片会转换成webp格式)
防止Cumulative Layout Shift(累计布局偏移)
图片在视图中才会被加载
可以自定义图片尺寸
// ...
import Image from 'next/image'
export default function About(props) {
return <>
{/* ... */}
<Image
src={'/img.jpeg'}
alt="图片"
width={100}
height={100}
/>
<img
src={'/img.jpeg'}
alt="图片"
/>
</>
} Next.js 会根据 Image 的 width 与 height 值,在页面请求服务端时,转换并缓存相应大小的图片。
第八章讲解了 API 路由,Next.js 中可以编写服务端的接口,处理请求。
/pages/api 文件下的 JS 文件不会导出页面组件,Next.js 会将这些文件映射成 /api/* 的 API 端点。Next.js 团队在 NodeJS 的 http 模块之上封装,提供了类似 express 的 web 服务器开发功能。
我们可以在这这些文件里写服务端的逻辑,同 getStaticProps 方法一样,这些逻辑客户端是看不到的。
这些API路由的基本格式如下:
export default function handler(req, res) {
if (req.method === 'POST') {
// 处理POST请求
} else {
// 处理其他HTTP方法请求
}
} API路由类型:
export declare type NextApiHandler<T = any> = (req: NextApiRequest, res: NextApiResponse<T>) => unknown | Promise<unknown>; 请求参数 req 继承 IncomingMessage 类:
export interface NextApiRequest extends IncomingMessage {
/**
* Object of `query` values from url
*/
query: Partial<{
[key: string]: string | string[];
}>;
/**
* Object of `cookies` from header
*/
cookies: Partial<{
[key: string]: string;
}>;
body: any;
env: Env;
preview?: boolean;
/**
* Preview data set on the request, if any
* */
previewData?: PreviewData;
} 响应参数 res 拓展自 ServerResponse 类:
export declare type NextApiResponse<T = any> = ServerResponse & {
/**
* Send data `any` data in response
*/
send: Send<T>;
/**
* Send data `json` data in response
*/
json: Send<T>;
status: (statusCode: number) => NextApiResponse<T>;
redirect(url: string): NextApiResponse<T>;
redirect(status: number, url: string): NextApiResponse<T>;
/**
* Set preview data for Next.js' prerender mode
*/
setPreviewData: (data: object | string, options?: {
/**
* Specifies the number (in seconds) for the preview session to last for.
* The given number will be converted to an integer by rounding down.
* By default, no maximum age is set and the preview session finishes
* when the client shuts down (browser is closed).
*/
maxAge?: number;
/**
* Specifies the path for the preview session to work under. By default,
* the path is considered the "default path", i.e., any pages under "/".
*/
path?: string;
}) => NextApiResponse<T>;
/**
* Clear preview data for Next.js' prerender mode
*/
clearPreviewData: (options?: {
path?: string;
}) => NextApiResponse<T>;
revalidate: (urlPath: string, opts?: {
unstable_onlyGenerated?: boolean;
}) => Promise<void>;
}; IncomingMessage 与 ServerResponse 都来自 NodeJS 原生的 http 模块。
API路由的映射规则同页面组件一样,静态与动态映射下,文件目录与请求的对应关系:
<===>
<===>
第九章是 API 路由的复习,略。
此外这一章还讲解了 MongoDB 的使用方法,有兴趣的同学可以看一看。
复习 React Context,略。
第十一章实现一个博客,是对前面所有概念做一个复习,巩固知识点。
实现过程中安装了两个第三方包:
在页面渲染 markdown 方法,使用 包。
高亮代码语法,使用 包。
第十二章讲解了部署 Next.js 应用的方法。
构建
构建 Next.js 应用有两种方式:
第一种是“标准构建”,使用命令 next build 构建,课程之前构建都是使用这种方式。
使用这种方式构建,我们会得到优化后的前端项目 + 一个 NodeJS 服务端程序。这个服务端程序提供了 API 路由、SSR 与页面重验证等功能。所以如果要部署这个应用,需要服务器有NodeJS 环境。
第二种构建方式是静态打包,使用命令 next export 构建。
使用这种方式生成的代码,只会包含纯前端的内容,HTML、CSS、JS 以及静态资源。没有 NodeJS 服务端程序,所以部署可以不需要 NodeJS 环境。当然这样的话,API路由、SSR 等 Next.js 提供的特性就不能使用了。
配置
项目根目录 next.config.js 文件,可以对 Next.js 进行配置。
/** @type {import(&#39;next&#39;).NextConfig} */
const nextConfig = {
reactStrictMode: true,
}
module.exports = nextConfig 这个文件中的代码也是服务端代码,在构建过程中以及构建生成的 NodeJS 服务端程序中会使用到。此外这个文件不会被 Webpack, Babel 或 TypeScript 处理,所以确保使用与机器NodeJS 版本相匹配的语法。
完整的配置项接口如下,不过一般还是看官方文档(https://nextjs.org/docs/api-reference/next.config.js/introduction)根据具体需求来配置:
export interface NextConfig extends Record&lt;string, any&gt; {
exportPathMap?: (defaultMap: ExportPathMap, ctx: {
dev: boolean;
dir: string;
outDir: string | null;
distDir: string;
buildId: string;
}) =&gt; Promise&lt;ExportPathMap&gt; | ExportPathMap;
/**
* Internationalization configuration
*
* @see [Internationalization docs](https://nextjs.org/docs/advanced-features/i18n-routing)
*/
i18n?: I18NConfig | null;
/**
* @since version 11
* @see [ESLint configuration](https://nextjs.org/docs/basic-features/eslint)
*/
eslint?: ESLintConfig;
/**
* @see [Next.js TypeScript documentation](https://nextjs.org/docs/basic-features/typescript)
*/
typescript?: TypeScriptConfig;
/**
* Headers allow you to set custom HTTP headers for an incoming request path.
*
* @see [Headers configuration documentation](https://nextjs.org/docs/api-reference/next.config.js/headers)
*/
headers?: () =&gt; Promise&lt;Header[]&gt;;
/**
* Rewrites allow you to map an incoming request path to a different destination path.
*
* @see [Rewrites configuration documentation](https://nextjs.org/docs/api-reference/next.config.js/rewrites)
*/
rewrites?: () =&gt; Promise&lt;Rewrite[] | {
beforeFiles: Rewrite[];
afterFiles: Rewrite[];
fallback: Rewrite[];
}&gt;;
/**
* Redirects allow you to redirect an incoming request path to a different destination path.
*
* @see [Redirects configuration documentation](https://nextjs.org/docs/api-reference/next.config.js/redirects)
*/
redirects?: () =&gt; Promise&lt;Redirect[]&gt;;
/**
* @see [Moment.js locales excluded by default](https://nextjs.org/docs/upgrading#momentjs-locales-excluded-by-default)
*/
excludeDefaultMomentLocales?: boolean;
/**
* Before continuing to add custom webpack configuration to your application make sure Next.js doesn&#39;t already support your use-case
*
* @see [Custom Webpack Config documentation](https://nextjs.org/docs/api-reference/next.config.js/custom-webpack-config)
*/
webpack?: NextJsWebpackConfig | null;
/**
* By default Next.js will redirect urls with trailing slashes to their counterpart without a trailing slash.
*
* @default false
* @see [Trailing Slash Configuration](https://nextjs.org/docs/api-reference/next.config.js/trailing-slash)
*/
trailingSlash?: boolean;
/**
* Next.js comes with built-in support for environment variables
*
* @see [Environment Variables documentation](https://nextjs.org/docs/api-reference/next.config.js/environment-variables)
*/
env?: Record&lt;string, string&gt;;
/**
* Destination directory (defaults to `.next`)
*/
distDir?: string;
/**
* The build output directory (defaults to `.next`) is now cleared by default except for the Next.js caches.
*/
cleanDistDir?: boolean;
/**
* To set up a CDN, you can set up an asset prefix and configure your CDN&#39;s origin to resolve to the domain that Next.js is hosted on.
*
* @see [CDN Support with Asset Prefix](https://nextjs.org/docs/api-reference/next.config.js/cdn-support-with-asset-prefix)
*/
assetPrefix?: string;
/**
* By default, `Next` will serve each file in the `pages` folder under a pathname matching the filename.
* To disable this behavior and prevent routing based set this to `true`.
*
* @default true
* @see [Disabling file-system routing](https://nextjs.org/docs/advanced-features/custom-server#disabling-file-system-routing)
*/
useFileSystemPublicRoutes?: boolean;
/**
* @see [Configuring the build ID](https://nextjs.org/docs/api-reference/next.config.js/configuring-the-build-id)
*/
generateBuildId?: () =&gt; string | null | Promise&lt;string | null&gt;;
/** @see [Disabling ETag Configuration](https://nextjs.org/docs/api-reference/next.config.js/disabling-etag-generation) */
generateEtags?: boolean;
/** @see [Including non-page files in the pages directory](https://nextjs.org/docs/api-reference/next.config.js/custom-page-extensions) */
pageExtensions?: string[];
/** @see [Compression documentation](https://nextjs.org/docs/api-reference/next.config.js/compression) */
compress?: boolean;
/**
* The field should only be used when a Next.js project is not hosted on Vercel while using Vercel Analytics.
* Vercel provides zero-configuration analytics for Next.js projects hosted on Vercel.
*
* @default &#39;&#39;
* @see [Next.js Analytics](https://nextjs.org/analytics)
*/
analyticsId?: string;
/** @see [Disabling x-powered-by](https://nextjs.org/docs/api-reference/next.config.js/disabling-x-powered-by) */
poweredByHeader?: boolean;
/** @see [Using the Image Component](https://nextjs.org/docs/basic-features/image-optimization#using-the-image-component) */
images?: ImageConfig;
/** Configure indicators in development environment */
devIndicators?: {
/** Show &quot;building...&quot;&quot; indicator in development */
buildActivity?: boolean;
/** Position of &quot;building...&quot; indicator in browser */
buildActivityPosition?: &#39;bottom-right&#39; | &#39;bottom-left&#39; | &#39;top-right&#39; | &#39;top-left&#39;;
};
/**
* Next.js exposes some options that give you some control over how the server will dispose or keep in memory built pages in development.
*
* @see [Configuring `onDemandEntries`](https://nextjs.org/docs/api-reference/next.config.js/configuring-onDemandEntries)
*/
onDemandEntries?: {
/** period (in ms) where the server will keep pages in the buffer */
maxInactiveAge?: number;
/** number of pages that should be kept simultaneously without being disposed */
pagesBufferLength?: number;
};
/** @see [`next/amp`](https://nextjs.org/docs/api-reference/next/amp) */
amp?: {
canonicalBase?: string;
};
/**
* Deploy a Next.js application under a sub-path of a domain
*
* @see [Base path configuration](https://nextjs.org/docs/api-reference/next.config.js/basepath)
*/
basePath?: string;
/** @see [Customizing sass options](https://nextjs.org/docs/basic-features/built-in-css-support#customizing-sass-options) */
sassOptions?: {
[key: string]: any;
};
/**
* Enable browser source map generation during the production build
*
* @see [Source Maps](https://nextjs.org/docs/advanced-features/source-maps)
*/
productionBrowserSourceMaps?: boolean;
/**
* By default, Next.js will automatically inline font CSS at build time
*
* @default true
* @since version 10.2
* @see [Font Optimization](https://nextjs.org/docs/basic-features/font-optimization)
*/
optimizeFonts?: boolean;
/**
* The Next.js runtime is Strict Mode-compliant.
*
* @see [React Strict Mode](https://nextjs.org/docs/api-reference/next.config.js/react-strict-mode)
*/
reactStrictMode?: boolean | null;
/**
* Add public (in browser) runtime configuration to your app
*
* @see [Runtime configuration](https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration)
*/
publicRuntimeConfig?: {
[key: string]: any;
};
/**
* Add server runtime configuration to your app
*
* @see [Runtime configuration](https://nextjs.org/docs/api-reference/next.config.js/runtime-configuration)
*/
serverRuntimeConfig?: {
[key: string]: any;
};
/**
* Next.js automatically polyfills node-fetch and enables HTTP Keep-Alive by default.
* You may want to disable HTTP Keep-Alive for certain `fetch()` calls or globally.
*
* @see [Disabling HTTP Keep-Alive](https://nextjs.org/docs/api-reference/next.config.js/disabling-http-keep-alive)
*/
httpAgentOptions?: {
keepAlive?: boolean;
};
/**
* During a build, Next.js will automatically trace each page and its dependencies to determine all of the files
* that are needed for deploying a production version of your application.
*
* @see [Output File Tracing](https://nextjs.org/docs/advanced-features/output-file-tracing)
*/
outputFileTracing?: boolean;
/**
* Timeout after waiting to generate static pages in seconds
*
* @default 60
*/
staticPageGenerationTimeout?: number;
/**
* Add `&quot;crossorigin&quot;` attribute to generated `&lt;script&gt;` elements generated by `&lt;Head /&gt;` or `&lt;NextScript /&gt;` components
*
*
* @see [`crossorigin` attribute documentation](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)
*/
crossOrigin?: false | &#39;anonymous&#39; | &#39;use-credentials&#39;;
/**
* Use [SWC compiler](https://swc.rs) to minify the generated JavaScript
*
* @see [SWC Minification](https://nextjs.org/docs/advanced-features/compiler#minification)
*/
swcMinify?: boolean;
/**
* Optionally enable compiler transforms
*
* @see [Supported Compiler Options](https://nextjs.org/docs/advanced-features/compiler#supported-features)
*/
compiler?: {
reactRemoveProperties?: boolean | {
properties?: string[];
};
relay?: {
src: string;
artifactDirectory?: string;
language?: &#39;typescript&#39; | &#39;javascript&#39; | &#39;flow&#39;;
};
removeConsole?: boolean | {
exclude?: string[];
};
styledComponents?: boolean | {
/**
* Enabled by default in development, disabled in production to reduce file size,
* setting this will override the default for all environments.
*/
displayName?: boolean;
topLevelImportPaths?: string[];
ssr?: boolean;
fileName?: boolean;
meaninglessFileNames?: string[];
minify?: boolean;
transpileTemplateLiterals?: boolean;
namespace?: string;
pure?: boolean;
cssProp?: boolean;
};
emotion?: boolean | {
sourceMap?: boolean;
autoLabel?: &#39;dev-only&#39; | &#39;always&#39; | &#39;never&#39;;
labelFormat?: string;
importMap?: {
[importName: string]: {
[exportName: string]: {
canonicalImport?: [string, string];
styledBaseImport?: [string, string];
};
};
};
};
};
output?: &#39;standalone&#39;;
transpilePackages?: string[];
skipMiddlewareUrlNormalize?: boolean;
skipTrailingSlashRedirect?: boolean;
modularizeImports?: Record&lt;string, {
transform: string;
preventFullImport?: boolean;
skipDefaultConversion?: boolean;
}&gt;;
/**
* Enable experimental features. Note that all experimental features are subject to breaking changes in the future.
*/
experimental?: ExperimentalConfig;
} 这个文件还可以通过函数来根据不同环境返回不同的配置参数:
module.exports = async (phase, { defaultConfig }) =&gt; {
/**
* @type {import(&#39;next&#39;).NextConfig}
*/
const nextConfig = {
/* 配置 */
}
return nextConfig
} phase 参数会根据不同的 next 命令来传入不同的值:
export declare const PHASE_EXPORT = &quot;phase-export&quot;;
export declare const PHASE_PRODUCTION_BUILD = &quot;phase-production-build&quot;;
export declare const PHASE_PRODUCTION_SERVER = &quot;phase-production-server&quot;;
export declare const PHASE_DEVELOPMENT_SERVER = &quot;phase-development-server&quot;;
export declare const PHASE_TEST = &quot;phase-test&quot;; 通过 next/constants 模块引入常量:
const { PHASE_PRODUCTION_BUILD } = require(&#39;next/constants&#39;)
module.exports = (phase, { defaultConfig }) =&gt; {
if (phase === PHASE_PRODUCTION_BUILD) {
return {
/* 生产构建配置 */
}
}
return {
/* 其他情况配置 */
}
}
第十三章讲解了鉴权,咪咪认为这算是 web 服务器编程的补充。
加密
课程使用 bcryptjs 包来实现加密逻辑,通过下列命令安装:
npm i bcryptjs bcryptjs 包我们只要关注两个函数,分别是加密 hash 和比较 compare 。注意两个方法都是异步的。
import { hash, compare } from &#39;bcryptjs&#39;
// ...
// 通过 hash 函数加密明文密码
const hashedPwd = await hash(pwd, 12)
// ...
// 通过 compare 函数比较两个密码是否相同,返回布尔值
const isValid = await compare(newPwd, hashedPwd)
鉴权
课程中使用 next-auth 包来实现相关鉴权逻辑的,使用下列命令安装:
npm install next-auth ⚠️视频中 Maximilian 使用的 next-auth 包是 v3 版本的,而咪咪写这篇文章时使用的是最新的是 v4 版本,有些语法与配置方法有区别。如果想和视频中一致,可以指定 v3 版本安装。
next-auth 包提供了前后端鉴权需要的逻辑,在 API 路由中,创建特殊文件 /api/auth/[...nextauth].js,在文件内引入 next-auth 包并实现相关逻辑:
import NextAuth from &quot;next-auth&quot;
import CredentialsProvider from &quot;next-auth/providers/credentials&quot;
export const authOptions = {
providers: [
CredentialsProvider({
name: &#39;Credentials&#39;,
session: {
strategy: &quot;jwt&quot;,
},
async authorize(credentials, req) {
// 自己实现验证逻辑
const res = await fetch(&quot;/your/auth&quot;, {
method: &#39;POST&#39;,
body: JSON.stringify(credentials),
headers: { &quot;Content-Type&quot;: &quot;application/json&quot; }
})
const user = await res.json()
// 一切正常返回用户信息
if (res.ok &amp;&amp; user) {
return user
}
// 用户信息没获取到就返回null
return null
}
})
// ...其他 providers
],
}
export default NextAuth(authOptions) 在前端组件中,使用 next-auth/react 模块的 signIn 函数登录:
import { signIn } from &quot;next-auth/react&quot;
export default () =&gt; &lt;button onClick={() =&gt; signIn(&#39;credentials&#39;, { redirect: false, username: &#39;username&#39;, password: &#39;password&#39; })}&gt;登录&lt;/button&gt; 在客户端可以通过 useSession 钩子获取鉴权信息:
// ...
import { useSession } from &quot;next-auth/react&quot;
export default function About(props) {
const { data: session, status } = useSession()
console.log(&#39;session &#39;, session)
console.log(&#39;status &#39;, status)
return &lt;&gt;
{/* ... */}
&lt;/&gt;
}
// ... 根据视频中一样直接使用会报错,因为v4版本 next-auth 需要 SessionProvider 才能使用上面这个钩子,在 _app.js 中增加相关代码:
import {SessionProvider} from &quot;next-auth/react&quot;
import &#39;../styles/globals.css&#39;
export default function App({Component, pageProps: {session, ...pageProps},}) {
return &lt;SessionProvider session={session}&gt;
&lt;Component {...pageProps} /&gt;
&lt;/SessionProvider&gt;
} 使用 next-auth/react 模块的 signOut 函数登出用户:
import { signOut } from &quot;next-auth/react&quot;
export default () =&gt; &lt;button onClick={() =&gt; signOut()}&gt;登出&lt;/button&gt; 路由守卫
在客户端侧,可以使用 getSession 获取当前的鉴权信息,在需要权限的页面,可以判断 session 的值进行进一步操作。
在服务端侧,可以在 getStaticProps 方法中使用 unstable_getServerSession 函数来获取 session,注意这里和视频中不一样,视频中 v3 版本使用的是 getSession,在v4版本中,getSession 不可以在服务端使用。
import { unstable_getServerSession } from &quot;next-auth/next&quot;
import { authOptions } from &quot;./api/auth/[...nextauth]&quot;
export async function getServerSideProps(context) {
return {
props: {
session: await unstable_getServerSession(
context.req,
context.res,
authOptions
),
},
}
}
对 Next.js 的总复习,可选,略。
结语,略。
课程内容差不多就是这些了,对于有 React 和 NodeJS 开发经验的同学来说,上手还是比较快的。Next.js 发展了那么久,已经非常完善了,生态也十分好,大部分原本需要用户来编写的代码,他们都已经做完了。用户只需要了解一下 Next.js 的几个重要函数与约定,就可以快速写出带有预渲染功能的生产环境 React 应用。希望咪咪的这篇文章可以给你带来帮助。
最后,依然感谢你看到这里~与君共勉~~