第一阶段 · 最小可运行闭环

01 · 项目初始化与最小架构

搭出 Chat Bot 的前端骨架,理解页面布局、组件边界和后续扩展落点。

课时资源

一、学习目标

本课所在阶段:第一阶段 · 最小可运行闭环

学完这一课后,你应该能够:

  • 理解为什么课程一开始要先搭出 Chat Bot 的前端壳子
  • 看懂首页里侧边栏、主内容区、输入区和背景层分别承担什么职责
  • 明白为什么这一课不急着接 API、Agent 和数据库
  • 知道后续课程会在当前页面骨架的哪些位置继续叠加真实能力

二、问题背景

如果一开始就把聊天请求、流式输出、Agent、数据库全部塞进项目里,你通常会同时卡在这几件事上:

  • 页面结构还没稳定,功能一增加就要反复改 UI
  • 组件职责还没理顺,输入区、消息区、侧边栏的逻辑容易混在一起
  • 还没看见完整产品长什么样,就已经被接口、状态和模型调用淹没

所以这一课真正要解决的问题不是"怎么马上让它能聊",而是:怎么先把一个 Chat Bot 的前端骨架搭出来,让后面的功能有稳定落点。

你可以把这一课理解成先搭舞台:

  • 左侧侧边栏先把会话区的位置留出来
  • 中间主内容区先把欢迎态和消息展示区的位置留出来
  • 底部输入区先把交互入口留出来
  • 全局样式先把产品氛围定下来

舞台先搭好,后面课程再把演员、灯光和剧情逐步补进去,学起来会更顺。

三、核心概念

这一课虽然没有真实聊天逻辑,但已经在解决一个很重要的工程问题:先把产品外壳和组件边界立住

你可以把这几个文件理解成前端骨架的四层:

  • app/layout.tsx — 负责整个应用的最外层容器
  • app/page.tsx — 负责首页的页面编排
  • app/components/ — 负责可独立演进的 UI 组件
  • app/globals.css — 负责统一视觉语言、布局规则和氛围效果

为什么这一步要放在第一课?因为后面的每一课都会依赖这个骨架:

如果现在不先做后面会发生什么
页面区域没定下来每加一个功能都要回头改布局
组件边界不清楚UI 和业务逻辑会越写越混
视觉风格没立住你会感觉每课都像不同项目

所以这一课的定位不是"功能实现课",而是"产品骨架课"。

这一课和 template 的关系

课程提供的 template/ 目录是工程基线,它包含的是 Next.js 初始化后的默认页面。lesson-01 在 template 基础上做了这些增量:

维度templatelesson-01
page.tsxNext.js 默认欢迎页Chat Bot 产品骨架
layout.tsx含 Google Fonts 配置精简为最小 HTML 容器
globals.css几行 CSS 变量完整的深色主题 + 玻璃态 + 动画系统
components/4 个独立组件
app/api/ app/agent/占位目录 + README

四、整体流程

页面骨架是怎么组成的

这张图对应的不是"未来所有功能",而是这一课真实已经存在的页面结构。你先把这张图看懂,后面每课新增的内容就更容易定位。

这一课的前端分层

这张图的重点是让你知道:后续新增功能并不是往一个文件里不断堆代码,而是沿着现有分层继续补能力。

后续课程会在哪些位置扩展

五、运行过程

1. 先从 template/ 起步

这一课不是从空目录手写项目,而是先用 template/ 作为工程基线。template 里已经有完整的 package.jsontsconfig.jsonnext.config.ts 和 App Router 结构。lesson-01 直接沿用这套配置,所以你只需要关注真正变化的文件。

如果你想知道这个目录名是怎么来的,可以把它理解成执行下面这一步之后的产物:

pnpm create next-app
 What is your project named? template
? Would you like to use the recommended Next.js defaults? › - Use arrow-keys. Return to submit.
   Yes, use recommended defaults - TypeScript, ESLint, Tailwind CSS, App Router
    No, reuse previous settings
    No, customize options

也就是说,在 CLI 提问 What is your project named? 时输入了 template,并选择了推荐默认配置。因此这里的 template/ 本质上就是一个用 Next.js 官方推荐选项创建出来的初始工程目录,而不是框架额外内置的一种特殊模板类型。

2. 先搭能看懂的页面骨架

app/page.tsx 把页面分成几个非常明确的区域:

  • 左侧侧边栏(SessionSidebar
  • 中间主内容区(app-main > EmptyState + StaticChatInput
  • 背景氛围层(BackgroundEffects + tech-grid-bg + ambient-glow

这样你一打开页面,就能先看到"最终产品大概长什么样"。

3. 先保留静态组件,再延后真实交互

这一步是教学上的关键取舍。SessionSidebarEmptyStateStaticChatInput 现在都还是静态版本,但它们已经把各自的职责位置占住了。这样下一课只需要往这些组件对应的位置接逻辑,而不是重新拆页面。

4. 先把组件边界讲清楚

app/page.tsx 只负责组合,不负责业务逻辑。这样页面层和组件层的边界很清楚,后面课程加功能时也更容易理解"改动到底落在哪一层"。

5. 先把视觉系统定下来

app/globals.css 不只是"让页面更好看",而是在第一课就把产品风格定下来。后面课程持续往里加能力时,你会觉得这是同一个 Chat Bot,而不是每节课一个新项目。

6. 为后续课程预留结构

app/api/README.mdapp/agent/README.mdapp/services/README.mdapp/database/README.md 这些占位文件,本质上是在告诉你:这个项目后面会逐步长出哪些层。

六、关键代码解析

app/page.tsx - 首页入口文件,负责把背景层、侧边栏、标题区域、欢迎态和输入区拼成完整页面。

app/layout.tsx - 应用根布局,负责页面最外层结构、语言环境和基础元信息。

app/components/SessionSidebar.tsx - 左侧侧边栏的静态版本,保留会话区的结构和风格。

app/components/EmptyState.tsx - 中间欢迎态区域,展示当前阶段重点是页面骨架。

app/components/StaticChatInput.tsx - 底部输入区的静态版本,保留交互外观。

app/globals.css - 全局样式文件,决定深色背景、玻璃态、网格感和整体视觉气质。

关键代码 1:首页是怎么把组件拼起来的

export default function Home() {
  return (
    <main className="app-shell">
      <BackgroundEffects />
      <div className="tech-grid-bg" />
      <div className="ambient-glow" />

      <SessionSidebar />

      <section className="app-main">
        <header className="app-header">
          ...
        </header>

        <div className="app-content">
          <EmptyState />
          <div className="app-input-wrap">
            <StaticChatInput />
          </div>
        </div>
      </section>
    </main>
  );
}

代码解析

  1. 背景层(BackgroundEffectstech-grid-bgambient-glow)先出现,用来决定整个应用的视觉氛围。这些元素用 position: absolute 铺满全屏,不参与布局流。
  2. SessionSidebar 作为左侧独立区域出现,在本课是纯静态展示,但它已经占住了侧边栏的组件位置。第六课的记忆系统会让它开始展示真实会话列表。
  3. 主内容区 app-main 内部再分成标题区(app-header)和内容区(app-content),内容区里又拆出欢迎态和输入区。
  4. 这说明"页面编排"的骨架已经成立:page.tsx 本身不包含任何业务逻辑或状态管理,它只负责声明"哪些组件放在哪个位置"。如果把业务逻辑写在这一层,后面每加一个功能都要改 page.tsx,维护成本会急剧上升。

关键代码 2:静态侧边栏为什么要用假数据

const sessions = [
  { id: 'welcome', name: '欢迎了解 LangGraph.js' },
  { id: 'agent', name: 'Agent 架构拆解' },
  { id: 'tooling', name: '工具调用设计' },
];

export function SessionSidebar() {
  return (
    <aside className="sidebar glass-panel">
      ...
      {sessions.map((session, index) => (
        <div
          key={session.id}
          className={`sidebar-session ${index === 0 ? 'is-active' : ''}`}
        >
          <div className="sidebar-session-indicator" />
          <span className="sidebar-session-text">{session.name}</span>
        </div>
      ))}
      ...
    </aside>
  );
}

代码解析

  1. sessions 写死了三条假数据,不需要任何后端调用。它的作用不是"展示功能已经完成了",而是让你在第一课就能看到侧边栏布局有了真实的内容填充。
  2. index === 0 ? 'is-active' : '' 让第一条会话显示为选中态。这是一种典型的"先让 UI 看上去是对的,再把行为接上"的教学方式。
  3. 为什么不直接留空?因为一个空空的侧边栏会让你根本看不出这个区域将来要做什么。有了假数据,你才能理解"后面的记忆系统会把这些假数据换成真实线程"。
  4. 这个组件后面会在 lesson-06 被改造为接收 sessions props、支持 onSelect 回调的受控组件。但在这一课,先保持独立和简单是更好的教学选择。

关键代码 3:静态输入区为什么要保留

<textarea
  placeholder="输入您的问题,开启 AI 之旅..."
  rows={1}
  readOnly
/>

代码解析

  1. readOnly 明确告诉你:这一课先保留交互位置,不执行真实发送。你可以看到输入框的视觉效果,但不会误以为它已经能工作。
  2. 这是一种典型的课程拆解方式——先把"位置"和"角色"讲清楚,再接"行为"和"状态"。
  3. 下方工具栏里的 chat-input-hint 也明确写着"这一课先保留输入区外观,下一课再接入真实消息发送"。这种在组件里直接放教学提示的做法,能让你即使不打开 README,也知道当前功能的边界在哪。
  4. 这个组件到 lesson-02 会整体替换为 ChatInput,后者接受 onSend 回调并真正触发发送。

七、常见问题

为什么第一课不直接接 API?

因为如果一上来就把页面、接口、Agent、状态管理都混在一起,你很容易在还没看清页面结构时就被实现细节拖走。先把骨架搭出来、把区域和组件边界讲清楚,后面加功能时每一步落在哪都更清楚。

为什么这些组件现在还是静态的?

因为这节课要先让你看清"页面结构和角色分工"。真实逻辑会在后续课程逐步接入:lesson-02 替换输入区为可发送的 ChatInput,lesson-06 替换侧边栏为真实会话列表。

为什么目录里要先放 apiagentservicesdatabase

因为这些目录本身就是课程地图的一部分。它们在告诉你:这个 Chat Bot 后面会往哪些层次继续演进。在参考项目中,这四个目录分别承载了完整的 API 路由、LangGraph 工作流、ChatService 业务逻辑以及 Supabase 数据层。

这一课和参考项目(langgraphjs-chat-app)有什么关系?

参考项目是一个完整的生产级 Chat Bot,拥有真实的 LangGraph Agent、Supabase 数据库、OAuth 认证和 Canvas 预览。这一课只取用了它的首页视觉风格和布局结构,把所有业务能力全部去掉,留下最小的前端壳子。后续课程会逐步把这些能力一层层加回来。

八、练习题

  1. 说明为什么课程第一课要先搭页面骨架,而不是先接聊天逻辑。
  2. 说出 app/page.tsxapp/components/SessionSidebar.tsxapp/globals.css 三个文件各自承担什么职责。
  3. 画出这一课的页面区域结构图,并标出后续最可能优先接入业务逻辑的区域。
  4. 打开 StaticChatInput.tsx,把 readOnly 去掉,在输入框里输入一段文字然后按回车。观察页面有什么反应,想一想为什么什么都没发生——这正是 lesson-02 要解决的问题。

九、总结

这一课真正要学会的,不是某一段具体代码,而是:先把 Chat Bot 的页面骨架和组件边界搭出来,让后续所有真实功能都有稳定落点。

只要你已经能说清楚"页面分成哪些区域、为什么输入区和侧边栏现在还是静态的、后续功能会往哪里接",这一课就学到了点子上。

和参考项目的完整首页相比,这一课刻意只保留了最薄的一层前端壳子。后续课程做的事情,就是沿着这个骨架,逐步把真实能力一层层叠加进来。

登录以继续阅读

解锁完整文档、代码示例及更多高级功能。

立即登录

On this page