🙃 我让爬虫终于看到了我的网站,后端同事说"这也行?"(下):App Router 全栈实战

写在前面:上篇我们把"为什么"讲透了------SPA 体验虽好但 SEO 是短板,SSR 把 React 组件搬到服务器渲染,爬虫就能读到完整内容。这篇进入"怎么做":一条命令创建全栈项目、App Router 约定大于一切、layout.tsx 布局 + metadata 做 SEO、'use client' 声明强交互页面、水合(hydration)激活交互,最后用一个 Todos 全栈 demo 把前后端串起来。上篇讲思想,这篇全是干货,开干!


五、create-next-app:开箱即用的全栈框架

5.1 一条命令,全家桶配齐

bash 复制代码
npx create-next-app@latest

选择默认配置,一路回车。它已经把全家桶给你配好了:

组件 作用
react / react-dom React 界面
typescript 类型安全
tailwindcss 样式
eslint 代码风格规范

老师说:

"next.js 是给react 开发者的开箱即用的利器。"

Next.js 是 React 全栈框架(Nuxt 是 Vue 全栈框架),能写页面(前端),也能写 API(后端)。背靠 Vercel,SEO 做得非常棒,很多 AI 产品用它做官网。

5.2 约定大于一切:App Router

最颠覆认知的一点:

"约定大于一切" "不需要建,文件就是路由,嵌套路由 建立文件夹" "page.tsx 就是页面" "nav 共用的,layout.js 布局文件"

不需要建路由配置文件,文件本身就是路由! 这就是 App Router:

bash 复制代码
app/
├── layout.tsx          # 根布局:全站共用的导航
├── page.tsx            # 首页:/  → Hello World
├── about/
│   └── page.tsx        # /about → About Us
├── dashboard/
│   ├── layout.tsx      # 嵌套布局:后台自己的导航
│   ├── page.tsx        # /dashboard → Hello Dashboard
│   └── settings/
│       └── page.tsx    # /dashboard/settings → Hello Setting
├── todos/
│   ├── page.tsx        # /todos → 待办事项
│   └── types.ts        # Todo 类型定义
└── api/
    └── todos/
        └── route.ts    # /api/todos → JSON 接口

六、布局与路由:layout.tsx + page.tsx

6.1 根布局与 metadata

渲染规则是这样的:

"先到layout.tsx 布局 - page.tsx"

先渲染布局,再渲染页面。根布局里挂全站导航,用 next/linkLink 做前端跳转:

tsx 复制代码
import type { Metadata } from "next";
import Link from "next/link";

export const metadata: Metadata = {
  title: "Create Next App",
  description: "Generated by create next app",
};

export default function RootLayout({ children }: LayoutProps<"/">) {
  return (
    <html lang="en" className="h-full antialiased">
      <body className="min-h-full flex flex-col">
        <nav>
          <ul>
            <li><Link href="/">首页</Link></li>
            <li><Link href="/about">关于</Link></li>
            <li><Link href="/dashboard">后台</Link></li>
            <li><Link href="/todos">Todos</Link></li>
          </ul>
        </nav>
        {children}
      </body>
    </html>
  );
}

注意 metadata------上篇讲的 SEO 三件套里的 <title><description>,Next.js 直接让你用 TypeScript 写在组件里。这就是"为了 SEO 而生的框架"。

6.2 嵌套路由与嵌套布局

后台管理系统有自己独立的导航,用嵌套布局:

tsx 复制代码
import Link from 'next/link'

export default function DashboardLayout({children}) {
    return (
        <div>
            <nav>Nav
                <Link href="/dashboard/settings">settings</Link>
            </nav>
            {children}
        </div>
    )
}

/dashboard 的页面先套「后台专用」布局,再套根布局,层层嵌套,像俄罗斯套娃。页面本身朴素得像个招牌:

tsx 复制代码
// 服务器端组件
// 在服务器端 react node 的运行方式
// jsx -> html
export default function Home() {
  return (
    <h1>Hello World</h1>
  );
}

七、服务端组件与客户端组件

7.1 默认都是服务端组件

老师说:

"next.js 将react server component 带到服务器端渲染,ssr开发模式" "jsx -> html seo"

Next.js 把 React Server Component 带到了服务端渲染里:默认情况下组件都是服务端组件,在服务器上编译成 HTML,为了 SEO。

7.2 'use client':强交互页面

那交互怎么办?总不能让用户点按钮就刷新页面吧?

老师说:

"有些页面 强交互" "'use client' 声明" "不是只在浏览器渲染, 先在服务器端把能渲染的渲染完,再去客户端渲染。"

需要强交互的页面,在文件顶部写一行 'use client' 声明即可:

tsx 复制代码
// react 可以在后端运行
'use client';
// 组件在前端渲染的标记
// 添加事件,useEffect,调用后端接口 csr next.js 也支持
// React Server Component

7.3 水合(hydration):水饺理论

老师讲 hydration 的时候打了个极其生动的比方,我直接引用原话:

"包好了水饺,冻上,给你送过来" "水煮 水和" "水合(hydration):浏览器拿到静态 HTML 之后,挂载客户端 js、绑定点击事激活交互;"

服务器先"包好水饺、冻上"(渲染成静态 HTML 发过来),浏览器拿到之后"下水煮"------挂载客户端 JS、绑定点击事件激活交互,这就是水合(hydration)

所以:

"csr 组件 会执行两次,1次载服务器,第二次是在客户端,打补丁,"

客户端组件其实执行两次:第一次在服务器(速冻发货),第二次在客户端(下水煮开、打补丁)。你拿到的不是生面团,是熟的、能直接下锅的饺子。


八、全栈 Demo:Todos

理论讲完,直接上老师的实战 demo:一个全栈待办事项(Todos),类型、API、页面一条龙。

8.1 类型定义:types.ts

ts 复制代码
export type Todo = {
    id: number;
    content: string;
    completed: boolean;
}

8.2 API 接口:route.ts

Next.js 里除了带 'use client' 的文件都是后端,API 接口也遵守 App Router 约定:

ts 复制代码
// next.js 除了 use client 都是后端
// api 数据接口 仍然满足 app router 约定
// route.ts 返回json 数据接口的
import { type Todo } from '../../todos/types';

let todos: Todo[] = [
    { id: 1, content: "学习AppRouter", completed: false },
    { id: 2, content: "Next.js 个人官网开发", completed: false },
]
// /api/todos get 请求 restful
export async function GET() {
    // 返回json 数据接口 next.js 封装好了Response
    return Response.json(todos);
}

export async function POST(req:Request) {
    const body = await req.json();
    const newTodo: Todo = {
        id: +Date.now(),
        content: body.content,
        completed: false,
    }
    todos.push(newTodo);
    return Response.json(newTodo);
}

GETPOST 就是两个导出的异步函数,Response.json() 帮你把 JSON 封装好了------RESTful 接口就是这么简单。id: +Date.now() 那个 + 号是把时间戳字符串转成数字,老师的偷懒神器。

8.3 前端页面:page.tsx

'use client' 的交互组件,useState 管状态、useEffect 拉数据、fetch 调接口:

tsx 复制代码
export default function TodosPage() {
    const [todos, setTodos] = useState<Todo[]>([]);
    const [ text, setText ] = useState<string>('');
    const fetchTodos = async() => {
        const res = await fetch("/api/todos");
        const data: Todo[] = await res.json();
        setTodos(data);
    }
    const handleAdd = async() => {
        if (!text.trim()) return
        await fetch("/api/todos", {
            method: "POST",
            headers: {"Content-Type":"application/json"},
            body:JSON.stringify({content:text})
        })
    }
    useEffect(() => {
        fetchTodos();
    }, [])
    return (
        <div style={{marginTop: '12px'}}>
            <h1>待办事项</h1>
            <input value={text} onChange={
                (e) => setText(e.target.value)}
                placeholder="请输入新的待办任务"
            />
            <button onClick={handleAdd} style={{marginLeft:"8px"}}>添加</button>
            <ul style={{paddingLeft: "0", listStyle: "none"}}>
                {
                    todos.map((item) => (
                        <li key={item.id} style={{
                            margin:"8px 0", display:"flex",
                            gap:"10px"
                            }}>
                            <span style={{
                                textDecoration: item.completed ? "line-through" : "none",
                                cursor:"pointer"
                                }}>
                                {item.content}
                            </span>
                            <button>删除</button>
                        </li>
                    ))
                }
            </ul>
        </div>
    )
}

这就是一整套全栈闭环:前端页面 fetch("/api/todos") → Next.js 后端 route.ts 处理 → 返回 JSON → 前端渲染。之前学 React 前后端分离,要开两个项目;现在 Next.js 一个项目全搞定。


九、总结:大前端手里的 Next.js

概念 说明
Next.js React 全栈框架,能写页面也能写 API,背靠 Vercel,SEO 极佳
App Router 约定大于一切,文件就是路由
layout.tsx 布局文件,先渲染布局再渲染页面
page.tsx 页面组件,编译成 HTML
route.ts API 接口,返回 JSON
'use client' 强交互页面声明,先在服务器渲染再去客户端水合
水合(hydration) 服务器包好速冻水饺,浏览器下水煮激活交互

Next.js 是给 React 开发者的开箱即用利器:App Router 约定式路由、layout 布局 + metadata SEO、服务端组件默认 SSR、route.ts 直接写 API,一套代码打通全栈。


写在最后(下篇)

今天最大的收获,是亲手搭出了第一个全栈项目。以前觉得"SEO 优化"是运营的事,现在发现前端就能解决------用 Next.js 把 React 组件搬到服务器渲染,爬虫看到的就不再是空 #root,而是完整的内容。很多 AI 产品官网都用 Next.js,不是没有道理的:SEO 好、能全栈、背靠 Vercel 部署方便。老师说后面还有一章 Next.js 的内容,估计是数据获取、动态路由这些更进阶的玩法,学完继续更!

下次面试官问你:"Next.js 的 App Router 是什么?'use client' 是干嘛的?怎么用 Next.js 写 API?"

你可以淡定地说:

"Next.js 的 App Router 约定大于一切:不需要建路由配置文件,文件本身就是路由,page.tsx 就是页面、layout.tsx 是布局、route.ts 是 API 接口,嵌套路由直接建文件夹。默认情况下组件都是服务端组件,在服务器上编译成 HTML,为了 SEO;强交互页面在文件顶部写 'use client' 声明,组件会先在服务器渲染一遍、再到客户端水合(hydration)激活交互------就像包好的速冻水饺,服务器发货,浏览器拿回家下水煮。写 API 也很简单,route.ts 里导出 GET、POST 异步函数,Response.json() 直接返回 JSON,前端页面 fetch 自己的接口就能形成全栈闭环。"

然后看着面试官满意的表情,心里默念:这波,又稳了。


本文所有代码示例均来自课堂学习资料,真实可运行。

相关推荐
九九落1 小时前
JavaScript 实现北京时间精确到毫秒显示:UTC+8、Asia/Shanghai 与在线时间校准详解
开发语言·javascript·ecmascript
葡萄城技术团队1 小时前
一个单元格放置两个日期选择器:用 SpreadJS CellButtons 录入日期范围
前端
FEF前端团队2 小时前
小程序微信支付 V3 接入实战手册:从商户配置到前后端落地
javascript·后端·node.js
马可家的菠萝2 小时前
自动保存已经有了,为什么笔记软件还需要“历史版本”?
前端·后端·架构
半个落月2 小时前
从 "use client" 到 Route Handler:用 Todos 理解 Next.js 水合与全栈请求
前端·react.js·next.js
qq_452396233 小时前
第七篇:《大型前端项目的模块化与目录结构设计》
前端
你脑门上的脚印3 小时前
Vue 项目从零实现语音转文字、文字转语音功能(完整可用 + 踩坑指南)
前端·vue.js
paopaokaka_luck3 小时前
基于springboot3+vue3的车间生产管理系统(Echarts图形化分析、BI报表)
java·前端·spring boot·学习·echarts
程序员鱼皮3 小时前
3 大 DeepSeek Harness 进阶玩法,招多个大肥鱼帮我干活!
前端·后端·ai编程