javascript

快速搭建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 的特性

  1. 内置优化——自动优化图像,字体和脚本,来改善用户体验和核心 Web Vitals。
  2. 动态 HTML 流——服务器即时流 UI,集成应用路由和React Suspense。
  3. React 服务器组件——无需发送额外的客户端 JavaScript 即可添加组件,基于最新的 React 特性构建。
  4. 数据获取——让你的React组件异步并等待你的数据。js同时支持服务器和客户端数据获取。
  5. CSS 支持——用你最喜欢的工具来编写你应用的样式,支持 CSS Modules, Sass, Tailwind CSS, styled-jsx…
  6. 客户端和服务器渲染——灵活渲染和缓存选项,包括逐页级的渐进静态再生(ISR)。
  7. Node.js & Edge 运行时——使用无服务器函数构建可扩展解决方案,并使用Edge交付快速动态的个性化内容。
  8. 路由处理器——构建API端点以安全地连接第三方服务并在前端使用。
  9. 高级路由和嵌套布局——使用文件系统创建路由,包括支持更高级的路由模式和UI布局。
  10. 中间件——控制传入的请求。使用代码定义路由和访问规则,来实现权限认证和国际化。

为什么要去集成 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 配置文件

注意:

  1. 告警提示在没有 "node" 模块解析策略的情况下,无法指定选项 "-resolveJsonModule",将 tsconfig.json 中的 "moduleResolution": "bundler" 修改成 "moduleResolution": "node" 即可。
  2. 告警提示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>
    )
}

我们可以看到按钮组件已经被引入。但是,这边按钮的显示会出现一点问题,预计是一个蓝色按钮,但是我们看到的是一个透明按钮。

1698921586798.png

出现这个问题的原因是,我们引入的 tailwind.css 与 Ant Design 的样式冲突了,解决办法就是调整 tailwind 的配置,在其配置文件中添加下面的配置:

js代码解读复制代码// tailwind.config.ts
{
    corePlugins: {
      preflight: false,
    },
}

刷新页面,我们可以看到按钮颜色变成了蓝色。

1698922495944.png

如果你

1. 样式闪烁问题

虽然我们引入了 Ant Design 组件库,但是这边还有一个小问题,我们试着刷新页面的时候,我们可以看到,首屏加载时,Ant Design的样式没有立即加载出来,导致开始一段时间样式缺失,然后才恢复正常。

chrome-capture-2023-10-2.gif

这样的用户体验会比较差,幸运的是,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’ 试试看。

1699280429783.png

然后改造我们的 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值:

1699280563485.png

注意:cookies 方法在标记了”use client”的页面或组件中不能使用!!!

虽然前面内容在笔者的项目中已经满足所有场景,但是如果你遇到了其它问题,或者想了解更多关于 Next.js 数据请求的内容,可以参考官方文档——Building Your Application: Data Fetching | Next.js (nextjs.org)

结尾

截止目前,我们已经快速完成了一个Next.js项目的搭建,并添加了SCSSAnt DesignAxios 等库,解决了taliwind 和 Ant Design 样式冲突的问题,解决了 Ant Design 首次渲染样式闪烁的问题,对于接口请求用 Axios做了一个简单的封装,基本能够满足不少的应用场景。

但是Next.js的强大不仅仅如此,本文只能算是一个小小的入门教程,如果你有更多的需求,可以参考Next.js的文档以及其它库相关文档。

本文如有错误,敬请指正!

AI相关的一切

留言

您的邮箱地址不会被公开。 必填项已用 * 标注