初识 Next.js

Finder / 2025-12-12


Next.js 是一个运行在 Node.js 服务器上的全栈框架,它用服务端能力来增强前端应用的性能和开发体验。它不是纯粹的前端或后端,而是以前端开发体验为中心,整合了后端能力的完整解决方案。

本文为Next.js学习指引,并非Next.js的教程本身。系统学习Next.js,请参看:

  • Next.js 官方教程:https://nextjs.org/learn
  • Next.js 视频教程: https://www.youtube.com/watch?v=b4ba60j_4o8&list=PLC3y8-rFHvwhIEc4I4YsRz5C7GOBnxSJY

判断自己学会的标准:本人能够看懂每一行由AI工具生成的代码。

序言:NodeJS、npm、Next.js 三者的关系 #

技术说明
NodeJS房屋地基和主体框架,提供坚实基础,保证项目可以运行起来
NPM建筑的工具箱和建材商店,需要自己没有的建筑材料时,通过它来完成购入(安装)。避免所有的材料自己建造。
Next.js房屋设计方案,帮我们规划了房间布置,方便我们清晰的知道什么设施(代码)放在什么位置,方便管理

首先安装NodeJS,其自带NPM。然后用NPM安装Next.js。这样就可以开始建造房屋(代码项目工程)了。

入门Next.js #

环境准备 #

  1. 打开终端,输入 npx create-next-app@latest ,点击回车
  2. 一直点击回车,选择默认选项即可
  3. 等待项目创建完成,Next.js初始项目文件就创建好了。要记住项目路径
  4. 打开Cursor,打开上面的项目路径即可

项目结构 #

Next.js 项目结构如下:

├── app/                  # ➜*App Router 核心*
│   ├── layout.tsx        # 所有子路由的根布局
│   ├── page.tsx          # 访问 / 时渲染
│   └── globals.css
├── public/               # 静态资源
├── tsconfig.json
└── tailwind.config.ts

app文件夹 : Next.js路由核心。在这个文件夹下,每个文件或者文件夹都对应一个页面路由。例如,创建一个 home.js(或者 home.tsx)文件在app文件夹下,就可以通过浏览器访问对应的页面;如果创建子文件夹,如 app/blog/[slug].js , 可以实现动态路由,用于展示博客详情页。举个例子:

  • 创建app/home.js 访问 http://localhost:3000 就可以看到这个页面
  • 创建 app/blog/[id].js 访问 http://localhost:3000/blog/[id] 就可以根据id显示对应的文章

public文件夹: 用于存储静态资源,如图像、字体、图标等。在页面中使用相对路径应用这些资源,例如在组件中使用< img src="/logo.png" alt=“公司logo”,这里的logo就可以放在public文件夹下。

运行项目 #

启动开发环境 : 终端运行 npm run dev ,会在终端出现一个访问地址,在浏览器中打开地址,就可以看到相应页面了。举个例子:

  1. 在app文件夹下建一个 about文件夹,在about文件夹内创建一个 page.tsx
  2. 在 http://localhost:3000/about ,查看页面

Next.js 会自动将app文件夹下的文件和文件夹结构映射为对应的URL路径

  • 文件夹对应的URL路径中的路径段。创建about 文件夹,就对应了URL中的/about 路径部分
  • page 文件是可公开访问的页面组件载体。当在 about文件夹中创建page文件时,它所导出的React组件内容,会被渲染到该文件夹对应的URL路径页面上。比如 about/page.tsx 中导出的组件,会在访问/about 时展示给用户。page文件就像一个页面内容提供者,告诉浏览器在对应的URL下显示什么。

注意:一个文件夹about下,仅page.tsx会被渲染加载,可以通过 http://localhost:3000/about 访问。 其他tsx文件并不能加载显示。

组件 #

Next.js 里有两种组件(Components),分别是服务端组件和客户端组件。Next.js默认 Server Components,仅在需要浏览器交互时才标注 “use client”。

服务端组件(Server Components)

运行在 Node 边缘 / Serverless。可直接调用后端 SDK、数据库、文件系统。生成纯 HTML,体积小,SEO 友好。

import { fetchUsers } from "@/lib/api";

export default async function Users() {
  const users = await fetchUsers();        // 直接 await
  return (
    <ul>
      {users.map(u => <li key={u.id}>{u.name}</li>)}
    </ul>
  );
}

客户端组件 (Client Components)

"use client";

import { useState } from "react";

export default function Counter() {
  const [n, setN] = useState(0);
  return (
    <button
      className="rounded bg-blue-600 px-3 py-1 text-white"
      onClick={() => setN(n + 1)}
    >
      点击 {n}
    </button>
  );
}

共享布局 layout.tsx

export const metadata = { title: "一小时 Next.js" };

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="zh">
      <body className="prose mx-auto p-4">{children}</body>
    </html>
  );
}

数据获取 #

如果在Server Component里,可以直接使用fetch()函数获取数据、以及调用API。

export default async function ApiExample() {
  const res = await fetch("https://jsonplaceholder.typicode.com/users", {
    // 缓存 60 秒,等同 ISR
    next: { revalidate: 60 }
  });
  const users: { id: number; name: string }[] = await res.json();

  return (
    <ul className="list-disc pl-5">
      {users.map(u => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

如果时在 client Component 里,可以使用Ajax的方式获取数据。

"use client";

import { useEffect, useState } from "react";

type User = { id: number; name: string };

export default function UsersClient() {
  const [users, setUsers]   = useState<User[]>([]);
  const [loading, setLoading] = useState(true);
  const [error, setError]     = useState<Error | null>(null);

  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/users")
      .then(r => {
        if (!r.ok) throw new Error("请求失败");
        return r.json();
      })
      .then((data: User[]) => setUsers(data))
      .catch(err => setError(err))
      .finally(() => setLoading(false));
  }, []);           // 只在首屏执行一次

  if (loading) return <p>加载中…</p>;
  if (error)   return <p className="text-red-600">出错:{error.message}</p>;

  return (
    <ul className="list-disc pl-5">
      {users.map(u => (
        <li key={u.id}>{u.name}</li>
      ))}
    </ul>
  );
}

相关说明:

  • “use client” : 声明这是 Client Component
  • 用 useEffect 把网络请求放到浏览器执行,避免 SSR 时跑两遍。
  • 本地维护 loading / error / data 三态即可;初学者先别上管理库。
  • 与 Server 组件不同,这里无法使用 revalidate 等 Next.js 缓存指令,完全由浏览器缓存策略决定。

元数据 & SEO #

元数据就是"关于网页的介绍信息"。当你把网页链接分享到微信、Twitter 时,会显示一个卡片,上面有标题、描述、配图——这些就是元数据在起作用。 同时,搜索引擎(百度、Google)也靠读取这些信息来理解你的网页内容,从而决定在搜索结果中如何展示你的页面。

export const revalidate = 60;  // ISR - 每60秒重新生成一次

export async function generateMetadata({ params }) {
  const post = await fetchPost(params.slug);  // 获取文章数据
  
  return {
    title: post.title,              // 网页标题(浏览器标签页显示)
    description: post.excerpt,      // 网页描述(搜索结果下方的简介)
    openGraph: {                    // 社交媒体分享卡片
      images: post.cover            // 分享时显示的配图
    }
  };
}

title - 页面标题

title: post.title  // 比如 "10分钟学会 React Hooks"

显示在浏览器标题页,显示在搜索结果的额蓝色标题位置,SEO最重要的一环,需要包含关键词。

description - 页面描述 :

description: post.excerpt  // 比如 "本文用最简单的例子教你..."

显示在搜索结果标题下方的灰色文字,让用户快速了解页面内容,提高点击率。

opengraph - 社交分享卡片

openGraph: {
  images: post.cover  // 分享链接时显示的大图
}

在微信/X/Facebook分享时,会显示漂亮的卡片。有图有真相,分享效率翻倍。

如果没有设置数据,搜索结果就会显示一串网址,很丑。

#Next.js #AI出海指南

最后一次修改于 2025-12-12