Next.js 快速上手指南

发布于:2025-08-31 ⋅ 阅读:(29) ⋅ 点赞:(0)

1. 概念

Next.js 是一款基于 React 的 Web 开发框架。React 是一个用于构建用户界面的 JavaScript 库,而 Next.js 则在 React 的基础上提供了更多功能,如服务器端渲染(SSR)、静态生成(SSG)等,帮助开发者更高效地构建 Web 应用。

2. 项目搭建

搭建 Next.js 项目的脚手架命令如下:

npx create-next-app@latest

运行命令后,你会看到以下配置选项:

  • 项目名称What is your project named?
  • 是否使用 TypeScriptWould you like to use TypeScript? (推荐使用,有助于类型检查和代码提示)
  • 代码规范工具Which linter would you like to use? (推荐使用 ESLint,用于检查代码规范)
  • 是否使用 Tailwind CSSWould you like to use Tailwind CSS? (推荐使用,方便快速开发响应式布局)
  • 代码是否放在 src/ 目录Would you like your code inside a src/ directory? (推荐放在 src/ 目录,有助于项目结构清晰)
  • 是否使用 App RouterWould you like to use App Router? (推荐使用,这是 Next.js 的新一代路由系统)
  • 是否使用 TurbopackWould you like to use Turbopack? (推荐使用,这是一个新的打包工具,可以提高构建速度)

3. 项目结构

Next.js 的项目结构与 Vue 类似。在第一次运行开发服务器时,你可能需要对 layout.tsx 文件进行一些调整。例如,如果你在国内,可能需要注释掉以下代码,因为这些是谷歌的字体,没有 VPN 的情况下加载会很慢:

const geistSans = Geist({
  variable: "--font-geist-sans",
  subsets: ["latin"],
});

const geistMono = Geist_Mono({
  variable: "--font-geist-mono",
  subsets: ["latin"],
});

<body
  className={`${geistSans.variable} ${geistMono.variable} antialiased`}
>
  {children}
</body>

注释掉 ${geistSans.variable} ${geistMono.variable} 部分即可。

4. 组件

组件是可复用的代码块,通常是一个函数。以 page.tsx 为例,function Home 里的 return 返回的是一个 HTML 模板。

你可以在同一个文件中创建自己的组件,例如一个按钮组件:

function Button() {
  return (
    <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
      Click Me
    </button>
  );
}

然后在页面的其他地方通过 <Button /> 来使用它。你也可以在 components 文件夹中新增 .tsx 文件来处理组件,但要注意添加 export default,并在需要使用它的页面中通过 import 导入。

你还可以将页面中的 mainfooter 内容单独提取为组件,如 MyMainMyFooter,以便复用。

父子组件之间可以传递参数。例如,MyButton 组件可以接受一个 children 参数:

export default function Button({ children }: { children: React.ReactNode }) {
  return (
    <button className="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition">
      {children}
    </button>
  );
}

使用时:

<MyButton><b>我很帅</b></MyButton>

你也可以传递一个名为 title 的参数:

function MyMain({ title }: { title: string }) {
  return <div>{title}</div>;
}

使用时:

<MyMain title="hello world" />

在组件的 return 之前,你可以定义自己的逻辑。例如:

function MyMain({ title }: { title: string }) {
  const newTitle = title + '1234';
  return <div>{newTitle}</div>;
}

若要在项目中引入远端的线上图片,可以在 next.config.js 中配置如下内容:

const nextConfig = {
  images: {
    remotePatterns: [
      {
        protocol: 'https',
        hostname: 'www.baidu.com',
      },
    ],
  },
};

5. 路由

动态路由

动态路由是 Next.js 中一个非常强大的功能,它允许你根据 URL 的参数动态地加载页面内容。这对于构建如博客、电商网站等需要根据 ID 或其他参数显示不同内容的页面非常有用。

什么是动态路由?

动态路由允许你定义一个页面模板,该模板可以根据 URL 中的参数动态加载不同的内容。例如,你有一个博客网站,每个博客文章都有一个唯一的 ID。你可以使用动态路由来创建一个页面模板,该模板可以根据文章 ID 显示不同的文章内容。

示例

假设你有一个博客文章页面,每个文章都有一个唯一的 ID。你可以这样设置动态路由:
文件目录详细配置

  1. 创建动态路由文件夹:在 app 文件夹下创建一个名为 [id] 文件夹(文件夹就叫[id]这个名字),其中 id 是动态参数的名称,如1、2、3、4等。

  2. 创建页面文件:在 [id] 文件夹下创建一个 page.tsx 文件。这个文件将作为动态页面的模板。

// app/[id]/page.tsx
import { notFound } from 'next/navigation';

export default async function PostPage({ params }: { params: { id: string } }) {
  const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`);
  if (!res.ok) {
    notFound(); // 如果请求失败,返回 404 页面
  }
  const post = await res.json();

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </div>
  );
}

在这个例子中,params.id 是从 URL 中提取的动态参数。当用户访问 /1/2 等路径时,Next.js 会自动将 12 等值传递给 params.id,然后根据这个 ID 获取相应的文章内容。

每个组件的 layout

layout 是 Next.js 中用于定义页面布局的组件。它允许你为一组页面共享相同的布局结构,例如头部、底部、侧边栏等。每个文件夹可以有自己的 layout,这些 layout 会嵌套在一起,形成一个完整的页面结构。

什么是 layout

layout 是一个 React 组件,它定义了页面的共享结构。你可以在这个组件中放置导航栏、底部栏等公共部分,然后通过 children 属性嵌入具体的页面内容。

示例

假设你有一个博客应用,每个页面都有一个公共的头部和底部。你可以这样设置 layout

  1. 创建根 layout:在 app 文件夹下创建一个 layout.tsx 文件。这个文件定义了整个应用的根布局。
// app/layout.tsx
import React from 'react';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <header>
          <nav>
            <ul>
              <li><a href="/">Home</a></li>
              <li><a href="/about">About</a></li>
              <li><a href="/contact">Contact</a></li>
            </ul>
          </nav>
        </header>
        <main>{children}</main>
        <footer>
          <p>&copy; 2023 My Blog</p>
        </footer>
      </body>
    </html>
  );
}
  1. 创建子文件夹的 layout:在子文件夹下创建自己的 layout 文件。这些 layout 会嵌套在根 layout 中。
// app/blog/layout.tsx
import React from 'react';

export default function BlogLayout({ children }: { children: React.ReactNode }) {
  return (
    <section>
      <h1>Blog</h1>
      {children}
    </section>
  );
}
  1. 创建页面文件:在子文件夹下创建具体的页面文件。这些页面文件会自动嵌套在父文件夹的 layout 中。
// app/blog/[id]/page.tsx
import { notFound } from 'next/navigation';

export default async function PostPage({ params }: { params: { id: string } }) {
  const res = await fetch(`https://jsonplaceholder.typicode.com/posts/${params.id}`);
  if (!res.ok) {
    notFound();
  }
  const post = await res.json();

  return (
    <article>

      <h2>{post.title}</h2>
      <p>{post.body}</p>
    </article>
  );
}

在这个例子中,app/layout.tsx 定义了整个应用的根布局,包括头部和底部。app/blog/layout.tsx 定义了博客部分的布局,包括一个标题。app/blog/[id]/page.tsx 是具体的博客文章页面,它会自动嵌套在 app/blog/layout.tsxapp/layout.tsx 中。

嵌套 layout 的工作原理

当 Next.js 渲染页面时,它会从根目录开始,逐级查找 layout 文件,并将它们嵌套在一起。每个 layout 都会通过 children 属性接收子组件的内容,最终形成一个完整的页面结构。

例如,当你访问 /blog/1 时,Next.js 会按照以下顺序渲染组件:

  1. app/layout.tsx(根布局)
  2. app/blog/layout.tsx(博客布局)
  3. app/blog/[id]/page.tsx(具体文章页面)

最终的页面结构如下:

<html lang="en">
  <body>
    <header>
      <nav>
        <ul>
          <li><a href="/">Home</a></li>
          <li><a href="/about">About</a></li>
          <li><a href="/contact">Contact</a></li>
        </ul>
      </nav>
    </header>
    <main>
      <section>
        <h1>Blog</h1>
        <article>
          <h2>Post Title</h2>
          <p>Post Body</p>
        </article>
      </section>
    </main>
    <footer>
      <p>&copy; 2023 My Blog</p>
    </footer>
  </body>
</html>

总结

通过动态路由,你可以根据 URL 参数动态加载页面内容,这非常适合构建需要根据参数显示不同内容的页面。而 layout 组件则允许你定义页面的共享结构,通过嵌套 layout,你可以轻松地构建复杂的页面布局。

6. 客户端与服务器端组件

Next.js 默认使用服务器端组件模式(Server Components),即先在服务器端渲染 HTML,然后返回给客户端。

如果你想使用客户端模式,只需要在 page.tsx 的开头写上 'use client' 即可:

'use client'

服务器端组件性能更好,因为它们返回的是 HTML 而不是 JavaScript。但是,一些复杂的动画和交互需要客户端模式来实现。

7. Hooks

Hooks 只能用于客户端模式。常用的有 useStateuseEffect

'use client'
import { useState, useEffect } from "react"

export default function NewBlog2() {
  const [count, setCount] = useState(0);
  const [input, setInput] = useState('');

  useEffect(() => {
    setCount(100);
  }, []);

  return (
    <div>
      <h1>New Blog 2 Page</h1>
      <b>这里是 useState 的相关演示</b>
      <p>Count: {count}</p>
      <button onClick={() => setCount(count + 1)}>add</button>
      <button onClick={() => setCount(count - 1)}>sub</button>
      <br />
      <input type="text" value={input} onChange={(e) => setInput(e.target.value)} />
      <p>Input: {input}</p>
      <br />
      <br />
      <b>这里是 useEffect 的相关演示</b>
    </div>
  );
}

这里,count 是一个变量,setCount 是一个函数,用于操作 count。逻辑是 onClick={() => setCount(操作)}

useEffect 用于异步操作或者获取第三方 API 数据。最后的 [] 里面可以放变量,后续只要这个变量修改了就会触发一次 useEffect

8. 数据获取 (Data Fetching)

你可以使用 useEffect 来获取数据:

useEffect(() => {
  const fetchPost = async () => {
    const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
    const data = await res.json();
    setPost(data);
  };
  fetchPost();
}, []);

或者,你可以直接将整个组件做成异步组件,这样就不需要在开头声明模式了,更加方便快捷:

export default async function BlogPost() {
  const res = await fetch('https://jsonplaceholder.typicode.com/posts/1');
  const post = await res.json();

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </div>
  );
}

9. 后端 API

Next.js 是一个强大的全栈框架,支持后端 API 的开发。你可以参考 Next.js 官方文档中的 Route Handlers

后端路由与前端路由类似,文件路径决定了 API 的地址。后端文件通常是 .ts 文件,而不是 .tsx 文件。

使用 Axios 实例处理前后端交互

Axios 是一个基于 Promise 的 HTTP 客户端,适用于浏览器和 Node.js,非常适合用于处理前后端交互。你可以在 Next.js 的后端 API 路由中使用 Axios 来发送 HTTP 请求。

首先,确保你已经安装了 Axios:

pnpm install axios

然后,配置一个 Axios 实例,如下所示:

// utils/axios.ts
import axios from 'axios';

const request = axios.create({
  baseURL: 'https://jsonplaceholder.typicode.com', // 设置请求的基础路径
  timeout: 3000000, // 设置请求超时时间(这个时间因个人而定,up本人喜欢时间长一点呢)
});

// 添加请求拦截器
request.interceptors.request.use(
  (config) => {
    // 在发送请求之前做些什么
    return config;
  },
  (error) => {
    // 对请求错误做些什么
    return Promise.reject(error);
  }
);

// 添加响应拦截器
request.interceptors.response.use(
  (response) => {
    // 2xx 范围内的状态码都会触发该函数。
    // 对响应数据做点什么
    return response.data; // 直接返回响应数据
  },
  (error) => {
    // 超出 2xx 范围的状态码都会触发该函数。
    // 对响应错误做点什么
    console.error('Request failed:', error);
    return Promise.reject(error);
  }
);

export default request;

接下来,你可以使用这个配置好的 Axios 实例来处理 API 请求。

创建一个简单的 API
// app/api/blog/route.ts
import request from '@/utils/axios';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  try {
    const data = await request.get('/posts/1'); // 使用配置好的 Axios 实例
    return NextResponse.json(data);
  } catch (error) {
    console.error('Error fetching data:', error);
    return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 });
  }
}

这个 API 的路径将是 http://localhost:3000/api/blog

其中,return NextResponse.json(data) 用于将数据以 JSON 格式返回给客户端。它会自动设置响应的 Content-Typeapplication/json,并默认将状态码设置为 200(请求成功)。如果需要,你还可以通过传递一个选项对象来设置其他状态码,例如 return NextResponse.json({ error: 'Not found' }, { status: 404 }) 用于表示资源未找到。

添加更多的 HTTP 方法

你可以根据需要添加更多的 HTTP 方法,如 POSTPUTDELETE 等:

// app/api/blog/route.ts
import request from '@/utils/axios';
import { NextResponse } from 'next/server';

export async function GET(request: Request) {
  try {
    const data = await request.get('/posts/1'); // 使用配置好的 Axios 实例
    return NextResponse.json(data);
  } catch (error) {
    console.error('Error fetching data:', error);
    return NextResponse.json({ error: 'Failed to fetch data' }, { status: 500 });
  }
}

export async function POST(request: Request) {
  try {
    const data = await request.json();
    const response = await request.post('/posts', data); // 使用配置好的 Axios 实例
    return NextResponse.json({ message: 'Data received', data: response });
  } catch (error) {
    console.error('Error posting data:', error);
    return NextResponse.json({ error: 'Failed to post data' }, { status: 500 });
  }
}

使用 Axios 实例的优势

  1. 简洁易用:Axios 提供了简洁的 API,易于上手和使用。
  2. 功能强大:支持请求和响应拦截器,可以方便地处理请求和响应数据。
  3. 跨浏览器兼容:在各种浏览器环境中都能稳定运行。
  4. 支持 Promise:基于 Promise 的设计,使得异步请求更加方便。
  5. 统一配置:通过配置 Axios 实例,可以统一管理请求和响应的处理逻辑,减少重复代码。

示例:使用 Axios 实例创建一个动态路由 API

假设你有一个博客应用,每个博客文章都有一个唯一的 ID。你可以使用 Axios 实例来创建一个动态路由 API,根据文章 ID 获取文章内容:

// app/api/blog/[id]/route.ts
import request from '@/utils/axios';
import { NextResponse } from 'next/server';

export async function GET(request: Request, { params }: { params: { id: string } }) {
  try {
    const { data } = await request.get(`/posts/${params.id}`); // 从外部 API 解构获取数据
    return NextResponse.json(data); // 将数据以 JSON 格式返回给前端
  } catch (error) {
    console.error('Error fetching post:', error);
    return NextResponse.json({ error: 'Post not found' }, { status: 404 });
  }
}

这个 API 的路径将是 http://localhost:3000/api/blog/1,其中 1 是动态参数 id 的值。

前端调用 API 并渲染数据

接下来,我们将在 Next.js 的前端页面中调用这个 API 并渲染数据。假设你有一个页面组件 BlogPostPage,它根据文章 ID 获取文章内容并渲染。

创建前端页面组件
// app/blog/[id]/page.tsx
import { useParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import request from '@/utils/axios';

export default function BlogPostPage() {
  const { id } = useParams(); // 获取动态参数 id
  const [post, setPost] = useState(null);

  useEffect(() => {
    async function fetchPost() {
      try {
        const response = await request.get(`http://localhost:3000/api/blog/${id}`); // 调用后端 API
        setPost(response);
      } catch (error) {
        console.error('Error fetching post:', error);
      }
    }
    if (id) {
      fetchPost();
    }
  }, [id]);
  
  if (!post) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </div>
  );
}

说明

  1. 获取动态参数

    • 使用 useParams 钩子从 Next.js 的路由中获取动态参数 id
  2. 调用后端 API

    • 使用之前配置好的 Axios 实例 request 来调用后端 API。
    • useEffect 中调用 API,并将返回的数据存储在状态 post 中。
  3. 渲染数据

    • 如果 post 数据尚未加载完成,显示一个加载中的提示。
    • 一旦数据加载完成,渲染文章的标题和内容。

完整的前端页面代码

// app/blog/[id]/page.tsx
import { useParams } from 'next/navigation';
import { useEffect, useState } from 'react';
import request from '@/utils/axios';

export default function BlogPostPage() {
  const { id } = useParams(); // 获取动态参数 id
  const [post, setPost] = useState(null);

  useEffect(() => {
    async function fetchPost() {
      try {
        const response = await request.get(`http://localhost:3000/api/blog/${id}`); // 调用后端 API
        setPost(response);
      } catch (error) {
        console.error('Error fetching post:', error);
      }
    }

    if (id) {
      fetchPost();
    }
  }, [id]);

  if (!post) {
    return <div>Loading...</div>;
  }

  return (
    <div>
      <h1>{post.title}</h1>
      <p>{post.body}</p>
    </div>
  );
}

10. 总结

通过以上内容,你应该能够快速上手 Next.js,并开始构建自己的 Web 应用。Next.js 提供了许多强大的功能,如服务器端渲染、静态生成和后端 API 开发,能够帮助你构建高性能、可扩展的 Web 应用。

希望这篇指南对你有所帮助!如果你有任何问题或建议,欢迎在评论区留言。


以上是完整的 Next.js 快速上手指南,涵盖了从项目搭建到后端 API 开发的各个方面。如果你还有其他需要补充或修改的地方,请随时告诉我。