快速搭建Next.js+Antd服务端渲染项目
前言
无论在公司项目还是在个人项目中,我们都希望能够尽快开始一个前端项目,而这也是 Next.js 框架和 Ant Design 这样的 UI 库如此流行和有用的重要原因。本文中,我们将一步一步展示如何从0开始搭建一个 Next.js 服务端渲染项目。
本文中所用到的技术有:
- Next.js:React 框架,通过扩展最新的 React 功能来创建全栈web应用程序,并集成强大的基于 Rust 的 JavaScript 工具来进行最快速构建。
- TypeScript:一种基于 JavaScript 构建的强类型编程语言。
- Ant Design:一套企业级 UI 设计语言和 React 组件库。
- Axios:一个基于 promise 的网络请求库,可以用于浏览器和 node.js 。
- Sass:Sass是目前世界上最成熟、稳定、功能强大的专业级CSS扩展语言。
Next.js 的特性
- 内置优化——自动优化图像,字体和脚本,来改善用户体验和核心 Web Vitals。
- 动态 HTML 流——服务器即时流 UI,集成应用路由和React Suspense。
- React 服务器组件——无需发送额外的客户端 JavaScript 即可添加组件,基于最新的 React 特性构建。
- 数据获取——让你的React组件异步并等待你的数据。js同时支持服务器和客户端数据获取。
- CSS 支持——用你最喜欢的工具来编写你应用的样式,支持 CSS Modules, Sass, Tailwind CSS, styled-jsx…
- 客户端和服务器渲染——灵活渲染和缓存选项,包括逐页级的渐进静态再生(ISR)。
- Node.js & Edge 运行时——使用无服务器函数构建可扩展解决方案,并使用Edge交付快速动态的个性化内容。
- 路由处理器——构建API端点以安全地连接第三方服务并在前端使用。
- 高级路由和嵌套布局——使用文件系统创建路由,包括支持更高级的路由模式和UI布局。
- 中间件——控制传入的请求。使用代码定义路由和访问规则,来实现权限认证和国际化。
为什么要去集成 Next.js 和 Ant Design?
我们选择 Next.js 框架的重要原因是,它允许我们可以预先渲染web应用程序的每个页面。当我们从浏览器输入网页地址进行访问页面时,Next.js将在服务器端提前生成HTML,而不是让JavaScript在客户端完成这些工作,这样做的话有利于提高网站的性能和 SEO。
但是服务器端提前生成HTML也给我们带来了新的问题。如果我们使用了第三方的UI库,却不将样式包含在服务器响应中,而是让客户端注入CSS,那么我们的页面有可能面临样式闪烁的风险,极大影响用户体验。这个也将是本文要解决的问题。
一、创建 Next.js 项目
系统环境准备:
- Node.js 18.17 或更高级版本.
- 支持macOS, Windows (including WSL), 和 Linux.
执行以下指令来创建 Next.js 项目:
lua代码解读复制代码npx create-next-app@latest
我们会看到以下提示:
bash代码解读复制代码What is your project named? my-app
Would you like to use TypeScript? No / Yes ==>> yes
Would you like to use ESLint? No / Yes ==>> yes
Would you like to use Tailwind CSS? No / Yes ==>> yes
Would you like to use `src/` directory? No / Yes ==>> yes
Would you like to use App Router? (recommended) No / Yes ==>> yes
Would you like to customize the default import alias (@/*)? No / Yes ==>> yes
What import alias would you like configured? @/*
全部选 yes
。等待安装完成之后,我们进入到项目目录下,执行 npm run dev
启动项目。
项目结构如下:
arduino代码解读复制代码├── public // 服务器静态资源
| ├── next.svg
| └── vercel.svg
├── src // 源代码目录
| └── app // APP Router
| | ├── blog
| | ├── favicon.ico
| | ├── globals.scss
| | ├── layout.tsx
| | └── page.tsx
├── .eslintrc.json
├── .gitignore
├── next-env.d.ts // Next.js TS 定义文件
├── next.config.js // Next.js 配置文件
├── package-lock.json
├── package.json
├── postcss.config.js
├── README.md
├── tailwind.config.ts // tailwind.css 配置文件
└── tsconfig.json // TS 配置文件
注意:
- 告警提示
在没有 "node" 模块解析策略的情况下,无法指定选项 "-resolveJsonModule"
,将 tsconfig.json 中的"moduleResolution": "bundler"
修改成"moduleResolution": "node"
即可。 - 告警提示
Cannot find module 'next/babel'
,在.eslintrc
中 ,将现有代码替换为:
js代码解读复制代码{
"extends": ["next/babel","next/core-web-vitals"]
}
现在我们就快速创建了一个 Next.js 项目,我们稍微调整一下文件目录:在 src 目录下,新增 theme 目录。
注意:本文撰写时 Next.js 的版本是14.0.1,当前版本对后文内容有影响,所以建议先降级为13.5.6版本。当然,你也可以先进行本文学习,可以在遇到问题时再尝试降级 Next.js 版本。
二、安装scss
通过 npm install --save-dev sass
安装sass安装包之后,Next.js 即可支持 .sccs 和 .sass 的扩展。
现在我们将 /src/app/globals.css
改为 /src/app/globals.css
,然后将 layout.tsx 中的引用,改成 import "./globals.scss"
,我们可以看到,样式依然生效。
1. 配置 sass 选项
我们可以在 next.config.js 中的 sassOptions 来配置 sass,如下:
js代码解读复制代码module.exports = {
sassOptions: {
prependData: "@import '@/theme/variable.scss';",
},
}
这样就可以在每个scss文件头部自动引入scss变量文件。variable.scss
文件中可以存放一些scss变量、混入代码等,我们可以在任意一个scss文件中引用这些变量。
三、安装 Ant Design
我们通过 npm install antd --save
来引入 Ant Design组件库。
接着我们在页面中引入 Button 组件:
js代码解读复制代码// /src/app/page.tsx
import {Button} from "antd"
export default function Home() {
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24 text-global">
<Button type="primary">Button</Button>
</main>
)
}
我们可以看到按钮组件已经被引入。但是,这边按钮的显示会出现一点问题,预计是一个蓝色按钮,但是我们看到的是一个透明按钮。
出现这个问题的原因是,我们引入的 tailwind.css 与 Ant Design 的样式冲突了,解决办法就是调整 tailwind 的配置,在其配置文件中添加下面的配置:
js代码解读复制代码// tailwind.config.ts
{
corePlugins: {
preflight: false,
},
}
刷新页面,我们可以看到按钮颜色变成了蓝色。
如果你
1. 样式闪烁问题
虽然我们引入了 Ant Design 组件库,但是这边还有一个小问题,我们试着刷新页面的时候,我们可以看到,首屏加载时,Ant Design的样式没有立即加载出来,导致开始一段时间样式缺失,然后才恢复正常。
这样的用户体验会比较差,幸运的是,Ant Design 提供了解决方案来解决这个问题。
首先我们安装 @ant-design/cssinjs
这个库。
bash代码解读复制代码npm install @ant-design/cssinjs --save
然后创建 src/lib/AntdRegistry.tsx
文件:
js代码解读复制代码'use client'
import React from 'react'
import {createCache, extractStyle, StyleProvider} from '@ant-design/cssinjs'
import type Entity from '@ant-design/cssinjs/es/Cache'
import {useServerInsertedHTML} from 'next/navigation'
const StyledComponentsRegistry = ({children}: React.PropsWithChildren) => {
const cache = React.useMemo<Entity>(() => createCache(), [])
useServerInsertedHTML(() => <style id="antd" dangerouslySetInnerHTML={{__html: extractStyle(cache, true)}} />)
return <StyleProvider cache={cache}>{children}</StyleProvider>
}
export default StyledComponentsRegistry
改造 src/app/layout.tsx
文件:
js代码解读复制代码import type {Metadata} from 'next'
import {Inter} from 'next/font/google'
import StyledComponentsRegistry from '../lib/AntdRegistry'
import './globals.scss'
const inter = Inter({subsets: ['latin']})
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body className={inter.className}>
<StyledComponentsRegistry>{children}</StyledComponentsRegistry>
</body>
</html>
)
}
这样改造之后,我们可以就可以看到,页面刷新正常了。
2. 添加主题配置
Ant Design 5.x 版本和之前版本最大的区别在于,采用 CSS-in-JS 替代less和CSS变量来编写组件样式,因此V5版本的主题定制和之前完全不一样,它的动态主题的能力也得到了加强。
在 5.0 版本中我们把影响主题的最小元素称为 Design Token。通过修改 Design Token,我们可以呈现出各种各样的主题或者组件。通过在
ConfigProvider
中传入theme
属性,可以配置主题。在升级 v5 后,将默认使用 v5 的主题。
首先我们创建 /src/theme/themeConfig.ts
:
js代码解读复制代码import type {ThemeConfig} from "antd"
const theme: ThemeConfig = {
token: {
fontSize: 16,
colorPrimary: "#52c41a",
},
}
export default theme
在主题配置文件中,我们定义了 token
来修改主题变量,通过 theme
中的 token
属性,可以修改一些主题变量。部分主题变量会引起其他主题变量的变化,我们把这些主题变量称为 Seed Token。
然后我们改造 src/app/layout.tsx
文件:
js代码解读复制代码import type {Metadata} from "next"
import {Inter} from "next/font/google"
import StyledComponentsRegistry from "../lib/AntdRegistry"
import theme from "@/theme/themeConfig"
import {ConfigProvider} from "antd"
import "./globals.scss"
const inter = Inter({subsets: ["latin"]})
export const metadata: Metadata = {
title: "Create Next App",
description: "Generated by create next app",
}
export default function RootLayout({children}: {children: React.ReactNode}) {
return (
<html lang="en">
<body className={inter.className}>
<StyledComponentsRegistry>
<ConfigProvider theme={theme}>{children}</ConfigProvider>
</StyledComponentsRegistry>
</body>
</html>
)
}
现在,我们可以看到,按钮变成了绿色,我们初步完成了项目自定义主题的配置,接下来就是去定义更多的主题变量。在 themeConfig.ts中,我们也可以JS动态切换我们的主题颜色。当然,这些都是自定义主题的常规操作,比较简单,不再赘述。Ant Design 主题更多高级的用法,可以参考定制主题 – Ant Design (antgroup.com)。
四、API请求
虽然 Next.js 提供了强大的数据获取的功能,甚至可以直接连接mongodb进行但是实际开发中,有可能还是通过封装Axios来进行接口请求获取数据。
我们通过 npm install axios
指令进行安装 Axios。然后新建文件/src/http/index.ts
:
js代码解读复制代码import axios, {AxiosResponse, InternalAxiosRequestConfig} from "axios"
//请求枚举
export enum MethodEnum {
Get = "GET",
Post = "POST",
}
//返回结果
export interface ResponseResultInterface<Body> {
Header: {}
Body: Body
}
//请求参数
export interface RequestInterface<params> {
url: string
params?: params
method?: MethodEnum
}
// 添加请求拦截器
axios.interceptors.request.use(
(config: InternalAxiosRequestConfig) => {
return config
},
error => {
return Promise.reject(error)
}
)
// 添加响应拦截器
axios.interceptors.response.use(
(response: AxiosResponse) => {
return response
},
error => {
return Promise.reject(error)
}
)
const baseRequest = async <params, responseData>(requestPar: RequestInterface<params>): Promise<responseData> => {
const requestResult: AxiosResponse = await axios({
method: requestPar.method || MethodEnum.Post,
url: requestPar.url,
data: requestPar.params,
})
return requestResult.data as responseData
}
export default baseRequest
这边我们对Axios进行了一个封装,添加了请求拦截器和响应拦截器,在请求拦截器中,我们可以在发起请求之前,做出一些操作,比如获取用户登录信息,而在响应拦截器中,我们可以对请求响应做出统一处理,比如异常code码的处理。
当然我们对请求代码的封装还有一些局限性,如果在从浏览器端发起请求,在接口未出现跨域的请求下,Ajax请求会携带cookie,如果用这段代码从服务器端发起请求,比如请求的cookie如何获取?别担心,Next.js 提供了解决办法。
通过 next/headers
提供的方法 ‘cookies’ 来获取请求Next.js页面时携带的cookie。
我们可以尝试在页面手动添加一个 ‘test-cookie’ 试试看。
然后改造我们的 src/app/page.tsx
文件:
js代码解读复制代码import {Button} from "antd"
import {cookies} from "next/headers"
export default function Page() {
let testCookie = cookies().get("test-cookie")?.value
return (
<main className="flex min-h-screen flex-col items-center justify-between p-24 text-global">
<div>
<Button type="primary">Button</Button>
<div>cookie:{testCookie}</div>
</div>
</main>
)
}
刷新页面,我们可以看到页面返回了我们刚刚获取到的cookie值:
注意:cookies 方法在标记了”use client”的页面或组件中不能使用!!!
虽然前面内容在笔者的项目中已经满足所有场景,但是如果你遇到了其它问题,或者想了解更多关于 Next.js 数据请求的内容,可以参考官方文档——Building Your Application: Data Fetching | Next.js (nextjs.org)。
结尾
截止目前,我们已经快速完成了一个Next.js项目的搭建,并添加了SCSS
、Ant Design
、Axios
等库,解决了taliwind
和 Ant Design
样式冲突的问题,解决了 Ant Design
首次渲染样式闪烁的问题,对于接口请求用 Axios
做了一个简单的封装,基本能够满足不少的应用场景。
但是Next.js的强大不仅仅如此,本文只能算是一个小小的入门教程,如果你有更多的需求,可以参考Next.js的文档以及其它库相关文档。
本文如有错误,敬请指正!