初面网初面网

Next.js

简介

Next.js 是一个基于 React 的开源框架,用于构建快速、现代化的 Web 应用程序。

Next.js 用于构建高性能的服务器端渲染(SSR)和静态生成(Static Generation)的现代 Web 应用。

主要特性

文件系统路由: Next.js 使用文件系统来自动化路由的创建。你只需要在 pages 目录下创建文件,它就会自动映射为相应的路由,不需要额外的路由配置。

静态生成(SSG)与服务端渲染(SSR): Next.js 支持这两种渲染方式,可以根据需要灵活选择。静态生成适用于大多数情况,尤其是内容不会频繁变化的页面,而 SSR 适用于需要动态获取数据的页面。

API 路由: Next.js 允许你在应用中直接创建 API 路由,可以在 pages/api 目录下轻松创建后端 API 端点,处理前后端逻辑。

自动代码拆分: 每个页面只会加载它所需的 JavaScript 代码,确保应用启动速度更快,减少不必要的资源消耗。

优化图片: Next.js 内置了图片优化功能,使用 next/image 组件可以自动为图像选择最佳格式、压缩、懒加载等,以提升页面加载性能。

支持 TypeScript: Next.js 默认支持 TypeScript,可以让开发者在开发过程中享受更强的类型检查。

安装

确保安装了node和npm

node -v
npm -v

手动安装

npm install next@latest react@latest react-dom@latest

然后打开package.json文件,添加下面内容

{
  "scripts": {
    "dev": "next dev",        // 以开发模式启动 Next.js
    "build": "next build",    // 构建应用以供生产使用
    "start": "next start",    // 启动 Next.js 生产环境服务器
    "lint": "next lint"       // 配置 Next.js 的内置 ESLint
  }
}

然后,创建一个 app 文件夹,然后添加一个 layout.tsx 和一个 page.tsx 文件。当用户访问应用根路径 / 时,这些文件会被渲染。

根布局文件 layout.tsx

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html lang="en">
      <body>{children}</body>
    </html>
  )
}

首页文件 page.tsx

export default function Page() {
  return <h1>Hello, Next.js!</h1>
}

注意事项

如果你忘记创建 layout.tsx,Next.js 会在运行开发服务器(next dev)时自动创建这个文件。

你可以选择性地在项目根目录中使用 src 目录,将应用代码与配置文件分开。

如果需要存储静态资源,如图片、字体等,你可以选择在项目根目录创建一个 public 文件夹。public 文件夹中的文件可以通过 / 路径来引用。

运行。默认运行在 3000 端口。访问 http://localhost:3000 查看应用。

npm run dev

npx安装(推荐)

npx create-next-app@latest my-next-app

npx 是一个 Node.js 工具,用于运行包中的二进制文件,而无需全局安装。create-next-app 是脚手架工具。my-next-app 是项目的名字,你可以根据需要修改为任何名称。执行命令后会自动安装项目所需的依赖。自动完成的操作有:

  • 创建一个名为 my-next-app 的文件夹。
  • 初始化项目,安装必要的依赖。
  • 创建基本的项目结构,包括 app 文件夹、public 文件夹、package.json 文件等。

目录结构

  • app/ 目录用于存放页面和布局文件,是 Next.js 中的核心目录。
  • public/ 用于存放公开的静态资源文件。
  • styles/ 用于存放样式文件。
  • node_modules/ 存放项目的所有依赖包。
  • package.json 是项目的配置文件,包含项目依赖和脚本命令。
  • next.config.js 用于定制 Next.js 的配置。

app 目录

app/
├── layout.tsx           # 根布局文件
├── page.tsx             # 根页面
└── [slug]/              # 动态路由目录
    ├── page.tsx         # 动态页面
    └── layout.tsx       # 特定页面的布局
  • layout.tsx:在这个文件中定义了根布局或页面布局,通常用于设置全局结构(如 <html><body> 标签)。所有子页面都会被嵌套在这个布局内。
  • page.tsx:每个页面组件都应该有一个 page.tsx 文件。这个文件代表该路由对应的页面内容。例如,app/page.tsx 对应的是根路径 (/) 的页面。
  • 动态路由:Next.js 支持动态路由,动态路由目录的名称用方括号包围,如 [slug]。这意味着你可以根据 URL 中的不同部分加载不同的页面。例如,[slug]/page.tsx 会根据 slug 参数动态渲染页面。

public 目录

public/
├── images/              # 存放图片文件
├── fonts/               # 存放字体文件
└── favicon.ico          # 网站图标

文件可以通过 /images/ 等路径直接访问。例如,public/images/logo.png 可以通过 http://localhost:3000/images/logo.png 访问。

style 目录

styles/
├── Home.module.css      # CSS 模块化样式
└── globals.css          # 全局样式
  • Home.module.css:这是一个 CSS 模块文件,默认情况下,Next.js 会自动为每个组件使用 CSS 模块,确保样式作用域仅限于该组件。
  • globals.css:存放全局样式,通常用于设置全局字体、颜色等。

项目结构目录

my-next-app/
├── node_modules/       # 项目依赖的第三方库
├── public/             # 静态资源文件夹
│   ├── favicon.ico     # 网站图标
│   └── ...             # 其他静态资源(如图片、字体等)
├── app/                # 应用路由目录(核心)
│   ├── layout.js       # 根布局组件
│   ├── page.js         # 首页组件
│   ├── loading.js      # 页面加载时显示加载状态
│   ├── error.js        # 捕获并显示错误
│   ├── not-found.js    # 404页面
│   ├── about/          # 关于页面
│   │   └── page.js     # 关于页面组件
│   ├── blog/           # 博客页面
│   │   ├── page.js     # 博客列表页
│   │   └── [slug]/     # 动态路由
│   │       └── page.js # 博客详情页
│   └── ...             # 其他页面和路由
├── components/         # 可复用的 React 组件
├── styles/             # 样式文件
├── utils/              # 工具函数
├── package.json        # 项目配置和依赖管理
├── package-lock.json   # 依赖的精确版本锁定文件
├── next.config.js      # Next.js 配置文件
└── README.md           # 项目说明文档

app 目录与传统 pages 目录的对比

Next.js 13 引入了新的 app 目录,它与传统的 pages 目录共存,并提供了更灵活的路由和布局组织方式。

特性pages 目录app 目录
路由定义文件系统路由文件夹路由
布局管理手动实现嵌套布局
数据获取getStaticProps 等Server Components + async/await
文件命名自由命名必须命名为 page.js
特殊文件_app.js, _document.js 等layout.js, loading.js 等
性能优化手动优化默认优化
兼容性完全兼容Beta 阶段,需手动启用

新特性

  1. 服务器组件

Next.js 13 默认使用服务器组件,减少了客户端 JavaScript 的加载量

该种组件可以直接访问后端数据

async function getData() {
  const res = await fetch('https://api.example.com/data');
  return res.json();
}

export default async function Page() {
  const data = await getData();
  return <div>{data.message}</div>;
}
  1. 客户端组件

需要使用 React 状态 或浏览器API,可将组件标记为客户端组件

'use client';

import { useState } from 'react';

export default function Counter() {
  const [count, setCount] = useState(0);
  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </div>
  );
}
  1. 流式渲染

页面加载时逐步渲染内容

import { Suspense } from 'react';

export default function Page() {
  return (
    <div>
      <Suspense fallback={<p>Loading...</p>}>
        <SlowComponent />
      </Suspense>
    </div>
  );
}

数据获取

允许在不同渲染阶段获取和展示数据。

  1. 从客户端获取

用 React 的 useEffect 钩子

// app/posts/page.tsx
'use client';
import { useEffect, useState } from 'react';

export default function PostsPage() {
  const [posts, setPosts] = useState([]);

  useEffect(() => {
    const fetchPosts = async () => {
      const res = await fetch('https://jsonplaceholder.typicode.com/posts');
      const data = await res.json();
      setPosts(data);
    };

    fetchPosts();
  }, []);

  return (
    <div>
      <h1>RUNOOB 测试</h1>
      <ul>
        {posts.map(post => (
          <li key={post.id}>{post.title}</li>
        ))}
      </ul>
    </div>
  );
}

useEffect 会在客户端渲染后执行,适用于客户端渲染的数据。

在客户端获取数据,适合不需要在服务端渲染的内容。

可以根据用户交互或时间等因素动态获取数据。

使用场景:

动态加载的数据,比如无限滚动或分页。

用户操作后才获取的数据,比如点击按钮后获取数据。

  1. 服务端数据获取

两种方式,fetch API和 ORM 或数据库

  • 用 fetch API
// app/posts/page.tsx
export default async function Page() {
  const data = await fetch('https://jsonplaceholder.typicode.com/posts')
  const posts = await data.json()
  return (
    <ul>
      {posts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}
  • 用 ORM 或 数据库
// app/posts/page.tsx 
import { db, posts } from '@/lib/db'

export default async function Page() {
  const allPosts = await db.select().from(posts)
  return (
    <ul>
      {allPosts.map((post) => (
        <li key={post.id}>{post.title}</li>
      ))}
    </ul>
  )
}

路由

路由

app/
├── page.tsx             # 对应根路径 /
├── about/
│   └── page.tsx         # 对应 /about
├── [slug]/
│   └── page.tsx         # 对应动态路径,如 /[slug]

获取动态路由参数

'use client';

import { useParams } from 'next/navigation';

export default function Post() {
  const params = useParams();
  const { id } = params;  // 获取动态路由参数 `id`

  return <div>Post ID: {id}</div>;
}

路由导航

import Link from 'next/link';

export default function HomePage() {
  return (
    <div>
      <h1>Welcome to Next.js!</h1>
      <Link href="/about">About Us</Link>
    </div>
  );
}

CSS 样式

全局样式影响整个应用的 CSS 样式,它通常用于设置页面的基础样式,如字体、颜色、布局等。

Next.js 通过 app/layout.tsx 中引入全局 CSS 文件。

默认情况下 app 目录已经有了全局样式 globals.css 文件,如果不存在,可以在 styles 目录下创建globals.css 文件,然后在 app/layout.tsx 中引入。

// app/layout.tsx
// 这些样式会应用到应用中的每个路由
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}

支持 Sass

配置 Sass 的选项,可以在 next.config.js 中使用 sassOptions 选项

// next.config.ts
import type { NextConfig } from 'next'

const nextConfig: NextConfig = {
  sassOptions: {
    additionalData: `$var: red;`, // 在每个 Sass 文件中自动注入这些样式
  },
}

export default nextConfig

通过这种方式,你可以配置一些默认的 Sass 数据,或者进行其他自定义设置。

创建一个 SCSS 文件,如 styles/Button.module.scss:

/* styles/Button.module.scss */
.button {
  padding: 10px 20px;
  background-color: #0070f3;
  color: white;
  border: none;
  border-radius: 5px;
  cursor: pointer;
}

组件中使用

// app/components/Button.tsx
import styles from '../styles/Button.module.scss';

export default function Button() {
  return <button className={styles.button}>Click Me</button>;
}

安装支持 Tailwind CSS

  1. 安装
npm install -D tailwindcss postcss autoprefixer
npx tailwindcss init -p
  1. 配置

一般有已配置好的 tailwind.config.ts 文件

import type { Config } from "tailwindcss";

export default {
  content: [
    "./pages/**/*.{js,ts,jsx,tsx,mdx}",
    "./components/**/*.{js,ts,jsx,tsx,mdx}",
    "./app/**/*.{js,ts,jsx,tsx,mdx}",  // 注意添加了  app  目录。
  ],
  theme: {
    extend: {
      colors: {
        background: "var(--background)",
        foreground: "var(--foreground)",
      },
    },
  },
  plugins: [],
} satisfies Config;

postcss.config.mjs 文件也无需修改。

  1. 添加基础样式
/* app/globals.css */
@tailwind base;
@tailwind components;
@tailwind utilities;

根布局 app/layout.tsx 中导入 globals.css 样式表

// app/layout.tsx
// 这些样式会应用到应用中的每个路由
import './globals.css'

export default function RootLayout({
  children,
}: {
  children: React.ReactNode
}) {
  return (
    <html>
      <body>{children}</body>
    </html>
  )
}

图片与字体

nextjs提供了 Image 组件,优化图片加载,扩展了HTML的img元素,提供以下功能:

  • 尺寸优化,自动为不同设备提供正确尺寸的图片,支持现代图片格式(如 WebP)。

  • 视觉稳定性:防止图片加载时的布局偏移(Layout Shift)。

  • 更快加载:使用原生浏览器懒加载,仅在图片进入视口时加载。

  • 资源灵活性:支持按需调整图片尺寸,即使是远程图片。

使用 Image 组件

import Image from 'next/image';

export default function Page() {
  return <Image src="/images/logo.png" alt="Logo" width={500} height={300} />;
}

本地图片与远程图片

import Image from 'next/image';
import profilePic from './me.png';

export default function Page() {
  return (
    <Image
      src={profilePic}
      alt="Picture of the author"
      // width 和 height 会自动从图片文件中获取
      // blurDataURL 和 placeholder 可选
    />
  );
}

// 远程图片要指定 width 和 height,避免布局偏移
export default function Page2() {
  return (
    <Image
      src="https://s3.amazonaws.com/my-bucket/profile.png"
      alt="Picture of the author"
      width={500}
      height={500}
    />
  );
}

配置远程图片

const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 's3.amazonaws.com',
        port: '',
        pathname: '/my-bucket/**',
      },
    ],
  },
};

export default nextConfig;

字体优化

Next.js 提供了 next/font 模块,用于优化字体加载。它支持自动托管字体文件,提升隐私和性能。

import { Geist } from 'next/font/google';

const geist = Geist({
  subsets: ['latin'],
});

export default function Layout({ children }) {
  return (
    <html lang="en" className={geist.className}>
      <body>{children}</body>
    </html>
  );
}

如果使用非可变字体,需要指定 weight

import { Roboto } from 'next/font/google';

const roboto = Roboto({
  weight: '400',
  subsets: ['latin'],
});

export default function Layout({ children }) {
  return (
    <html lang="en" className={roboto.className}>
      <body>{children}</body>
    </html>
  );
}

使用本地字体

import localFont from 'next/font/local';

const myFont = localFont({
  src: './my-font.woff2',
});

export default function Layout({ children }) {
  return (
    <html lang="en" className={myFont.className}>
      <body>{children}</body>
    </html>
  );
}

使用多个字体文件

const roboto = localFont({
  src: [
    {
      path: './Roboto-Regular.woff2',
      weight: '400',
      style: 'normal',
    },
    {
      path: './Roboto-Italic.woff2',
      weight: '400',
      style: 'italic',
    },
    {
      path: './Roboto-Bold.woff2',
      weight: '700',
      style: 'normal',
    },
    {
      path: './Roboto-BoldItalic.woff2',
      weight: '700',
      style: 'italic',
    },
  ],
});

组件与布局

组件,可以是页面组件、UI组件或功能组件。

// 页面组件 pages/about.js 或 app/about/page.js
export default function About() {
  return <div>About Page</div>;
}

// UI组件
// components/Button.js
export default function Button({ children }) {
  return <button>{children}</button>;
}

// 功能组件实现特定功能的组件,例如数据获取、状态管理等。通常存放在 hooks 或 utils 目录下。
// hooks/useData.js
import { useEffect, useState } from 'react';

export default function useData(url) {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((data) => setData(data));
  }, [url]);

  return data;
}

布局,用于定义页面结构的组件,通常包含页眉、页脚、导航栏等公共元素。

全局布局

// pages/_app.js 或 app/layout.js
import Header from '../components/Header';
import Footer from '../components/Footer';

export default function Layout({ children }) {
  return (
    <div>
      <Header />
      <main>{children}</main>
      <Footer />
    </div>
  );
}

嵌套布局

app/
├── layout.js        // 根布局
├── about/
│   ├── layout.js    // /about 的布局
│   └── page.js      // /about 页面
└── dashboard/
    ├── layout.js    // /dashboard 的布局
    └── page.js      // /dashboard 页面
// app/dashboard/layout.js
export default function DashboardLayout({ children }) {
  return (
    <div>
      <h1>Dashboard Layout</h1>
      {children}
    </div>
  );
}

动态布局允许根据条件动态选择布局。

// app/layout.js
export default function Layout({ children, isDashboard }) {
  return (
    <div>
      {isDashboard ? <DashboardLayout>{children}</DashboardLayout> : <MainLayout>{children}</MainLayout>}
    </div>
  );
}

旧版本中的特殊文件

  • _app.js, 全局组件,包装所有页面组件
  • _document.js,自定义HTML文档结构
  • _error.js,自定义错误页面
// pages/_app.js
import Layout from '../components/Layout';

export default function MyApp({ Component, pageProps }) {
  return (
    <Layout>
      <Component {...pageProps} />
    </Layout>
  );
}
// pages/_document.js
import { Html, Head, Main, NextScript } from 'next/document';

export default function Document() {
  return (
    <Html lang="en">
      <Head />
      <body>
        <Main />
        <NextScript />
      </body>
    </Html>
  );
}
// pages/_error.js
export default function Error({ statusCode }) {
  return (
    <div>
      <h1>{statusCode} Error</h1>
      <p>Sorry, something went wrong.</p>
    </div>
  );
}

更新于 2026/8/12