- 人工智能
- 大模型
- AI 应用
- 前端
- 后端
【免费下载链接】BibiGPT-v1
BibiGPT v1 · one-Click AI Summary for Audio/Video & Chat with Learning Content: Bilibili | YouTube | Tweet丨TikTok丨Dropbox丨Google Drive丨Local files | Websites丨Podcasts | Meetings | Lectures, etc. 音视频内容 AI 一键总结 & 对话:哔哩哔哩丨YouTube丨推特丨小红书丨抖音丨快手丨百度网盘丨阿里云盘丨网页丨播客丨会议丨本地文件等 (原 BiliGPT 省流神器 & AI课代表)
导读
本文以 BibiGPT-v1(音视频内容 AI 一键总结与对话应用)为依托,系统讲解 Next.js 文件约定(File Conventions)的核心机制:page.tsx/layout.tsx/route.ts等特殊文件、动态路由与路由组、并行路由与拦截路由、私有文件夹,以及 Next.js 16 中middleware.ts更名为proxy.ts的迁移要点。读完你将掌握如何用文件系统布局搭建多页面应用、如何组织 API 端点、如何做路由级错误处理,并能对照 BibiGPT-v1 的真实代码理解这些约定在生产项目中的落地方式。
一、文件约定是什么:从目录结构推导路由
Next.js 的 App Router 采用"文件即路由"(file-based routing)的约定式路由体系——路由不是通过配置文件声明的,而是由app/(或本项目使用的pages/)目录下的文件系统结构自动推导而来。这是 Next.js 区别于其他框架(如需要显式定义路由表的框架)的核心设计:你创建的每一个.tsx/.ts文件,都对应一个可访问的 URL 路径。
在 file-conventions.md 中给出的标准 App Router 项目结构如下:
app/ ├── layout.tsx # 根布局(必填) ├── page.tsx # 首页(/) ├── loading.tsx # 加载态 UI(Suspense 边界) ├── error.tsx # 错误态 UI(Error boundary) ├── not-found.tsx # 404 UI ├── global-error.tsx # 全局错误 UI ├── route.ts # API 端点 ├── template.tsx # 每次导航都会重新渲染的布局 ├── default.tsx # 并行路由的兜底渲染 ├── blog/ │ ├── page.tsx # /blog │ └── [slug]/ │ └── page.tsx # /blog/:slug └── (group)/ # 路由组(不影响 URL) └── page.tsx在本仓库中的实际映射:BibiGPT-v1 使用的是pages/目录(Pages Router 风格),而不是app/目录,但从 pages/[...slug].tsx 和 pages/index.tsx 可以清晰看到文件约定机制的实际效果:
pages/index.tsx→ 首页/pages/[...slug].tsx→ 捕获任意深度路径/a/b/c,BibiGPT 用它承载/video/xxx、/BV1AL4y1j7RY等由 URL 直接携带视频 ID 的分享链接场景(页面内部通过getVideoIdFromUrl解析路径并自动触发总结);pages/shop.tsx→/shop,次数耗尽后的购买页;pages/404.tsx→ 自定义 404 页面。
可见,"文件名决定路由"这一约定并非只存在于 App Router,Pages Router 同样遵循,理解文件约定就能快速读懂一个 Next.js 项目的全部路由面。
二、特殊文件(Special Files)一览与功能定位
文档以表格形式总结了 App Router 的核心特殊文件,各文件职责如下:
| 文件 | 用途 |
|---|---|
page.tsx | 路由段的 UI 页面 |
layout.tsx | 路由段及其子路由共享的 UI(布局) |
loading.tsx | 加载中 UI(基于 Suspense) |
error.tsx | 错误 UI(基于 Error boundary,必须为 Client Component) |
not-found.tsx | 404 UI |
route.ts | API 端点(对应page.tsx的"后端版") |
template.tsx | 类似 layout,但每次导航都会重新渲染(适合需要重置状态的场景) |
default.tsx | 并行路由(Parallel Routes)的兜底渲染 |
本仓库对应的实战案例
根布局与全局 Provider(layout.tsx/_app.tsx的等价物):pages/_app.tsx 扮演了全局布局的角色,它统一注入ThemeProvider(next-themes 主题切换)、SessionContextProvider(Supabase 登录会话)、TooltipProvider(Radix Tooltip)以及Header/Footer/CommandMenu等全局组件——这正是 App Router 中根layout.tsx的典型职责:只渲染一次,跨路由共享。
404 页面(not-found.tsx的等价物):pages/404.tsx 提供了完整可用的自定义 404 界面,包含品牌色大号 "404" 标题、"Something's missing." 文案与"Back to Homepage"按钮,说明 Next.js 允许开发者以文件形式覆盖默认错误页。
根文档(_document.tsx):pages/_document.tsx 对应 App Router 中由根layout.tsx+ Metadata API 完成的职责——自定义<Html>、注入 favicon、og:image、twitter:card等 SEO meta 标签。值得注意的是,Pages Router 中这部分只能在_document.tsx里以 HTML 标签形式手写;App Router 则建议用export const metadata或generateMetadata()声明式处理(详见同目录下的 metadata.md)。
三、路由段(Route Segments):静态、动态、捕获全部
App Router 将 URL 的每一级目录称为一个"路由段(segment)",通过文件夹的命名模式决定匹配行为:
app/ ├── blog/ # 静态段:/blog ├── [slug]/ # 动态段:/:slug(单个参数) ├── [...slug]/ # 捕获全部:/a/b/c(任意层级) ├── [[...slug]]/ # 可选捕获全部:/ 或 /a/b/c(可缺省) └── (marketing)/ # 路由组(组名不进入 URL)各模式的行为差异:
- 静态段:
blog/固定匹配/blog; - 动态段
[slug]:匹配单层路径,参数通过params传入组件; - 捕获全部
[...slug]:匹配任意层级的路径,常用于 CMS 内容、多级分类等不确定深度的路由; - 可选捕获全部
[[...slug]]:在捕获全部的基础上允许参数为空(/也可命中); - 路由组
(name):仅用于组织文件、不影响 URL,适合把同一布局下的营销页、文档页分组管理。
BibiGPT-v1 的真实用法:pages/[...slug].tsx 正是"捕获全部"模式在 Pages Router 中的对应实现——页面通过router.query.slug读取路径数组,再用 getVideoIdFromUrl.ts 将其解析为视频 ID 与分页参数。这意味着 BibiGPT 可以把任意形如/BV1xx...、/video/xxx的 URL 直接当作可分享的总结链接,无需为每个视频单独建页面。
注意区分:App Router 的动态段参数是
params(page.tsx的 props);Pages Router 中则是router.query。迁移时这是最容易踩的差异点之一。
四、并行路由(Parallel Routes):同一布局下的多视图渲染
并行路由允许在同一个布局中同时渲染多个互不依赖的页面,通过文件夹名的@前缀声明"插槽(slot)",父布局以 props 形式接收这些插槽:
app/ ├── @analytics/ │ └── page.tsx # analytics 插槽 ├── @sidebar/ │ └── page.tsx # sidebar 插槽 └── layout.tsx # 接收 { analytics, sidebar } 两个 props对应的布局代码模式:
// app/layout.tsx export default function RootLayout({ children, analytics, sidebar, }: { children: React.ReactNode analytics: React.ReactNode sidebar: React.ReactNode }) { return ( <html> <body> {children} {analytics} {sidebar} </body> </html> ) }并行路由的典型收益:侧边栏、分析面板等各自独立导航、互不阻塞——某个插槽里的异步数据加载失败或较慢,不会拖垮其他插槽的渲染(每个插槽自带独立 Suspense/Error 边界)。
关键约束(文档明确强调):每个@slot目录必须提供default.tsx作为兜底渲染(通常返回null),否则用户在硬刷新(刷新页面、书签直达)时 Next.js 无法确定插槽渲染内容,会直接 404。嵌套在路由组内的并行路由,每一层都要有自己的default.tsx。并行路由的完整模式(含关闭模态的正确方式router.back())可参考仓库内的 parallel-routes.md。
五、拦截路由(Intercepting Routes):实现"应用内导航显示模态、直达 URL 显示整页"
拦截路由解决的是这样一个产品需求:在应用内部点击某条内容时,期望以模态框形式轻量预览;而用户通过书签、分享链接直接访问同一 URL 时,期望看到完整页面。实现方式是配合并行路由的@modal插槽,用(.)等前缀声明"拦截目标":
app/ ├── feed/ │ └── page.tsx # 列表页 ├── @modal/ │ └── (.)photo/[id]/ # 拦截 /photo/[id](从 /feed 内导航时生效) │ └── page.tsx # 模态内容 └── photo/[id]/ └── page.tsx # 完整页面(直接访问时渲染)拦截匹配符的含义(匹配的是路由段,不是文件系统路径):
(.)— 同级拦截:@modal/(.)photos拦截/photos(..)— 上一级:@modal/(..)settings从/dashboard/@modal拦截/settings(..)(..)— 上两级(较少用)(...)— 从根目录:@modal/(...)photos可从任意位置拦截/photos
最常见的两个坑:
- 误把
(..)理解为"父文件夹"——它实际表示"父路由段",二者在路由组存在时往往不一致; - 关闭模态必须用
router.back(),而不是router.push('/')或<Link>——push会新增一条历史记录(返回键会让模态再次出现)、不会正确清理被拦截的路由,而router.back()会移除拦截路由、回到上一页并正确卸载模态。
Next.js 15+ 注意:动态路由的params已变为 Promise,须await params再解构(见 parallel-routes.md 的 TypeScript 示例)。
六、私有文件夹(Private Folders):用_前缀排除路由
以_开头的文件夹不会被路由系统纳入 URL,只作为组织代码的容器:
app/ ├── _components/ # 私有文件夹(不产生路由) │ └── Button.tsx └── page.tsx用途:把仅被组件内部引用的辅助组件、数据获取逻辑、样式等放进_components/、_lib/等私有目录,既能保持路由目录整洁,又不会意外暴露为可访问 URL。
本仓库的对照实践:虽然 BibiGPT-v1 的页面位于pages/,但它同样大量使用"非路由文件 + 约定目录"的分离方式——components/ 存放全部 UI 组件、hooks/ 存放useSummarize等自定义 Hooks、lib/ 存放字幕抓取与 OpenAI 调用逻辑、utils/ 存放工具函数。对于转向 App Router 的迁移场景,只需将这些目录下的文件原样放入app/内的_components/、_lib/等私有文件夹即可获得同样的"组织但不路由"效果。
七、Middleware / Proxy:Next.js 14-15 与 16+ 的命名迁移
中间件用于在请求到达页面/API 之前执行鉴权、重定向、改写(rewrite)、限流等逻辑,是"请求入口的第一道关卡"。
Next.js 14-15:middleware.ts
// middleware.ts(项目根目录) import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function middleware(request: NextRequest) { // 鉴权、重定向、rewrite 等 return NextResponse.next() } export const config = { matcher: ['/dashboard/:path*', '/api/:path*'], }Next.js 16+:proxy.ts
仅更改文件名与导出名,能力完全一致:
// proxy.ts(项目根目录) import { NextResponse } from 'next/server' import type { NextRequest } from 'next/server' export function proxy(request: NextRequest) { // 与 middleware 完全相同的逻辑 return NextResponse.next() } export const proxyConfig = { matcher: ['/dashboard/:path*', '/api/:path*'], }| 版本 | 文件 | 导出函数 | 配置对象 |
|---|---|---|---|
| v14-15 | middleware.ts | middleware() | config |
| v16+ | proxy.ts | proxy() | proxyConfig |
迁移方式:运行npx @next/codemod@latest upgrade可自动完成重命名。
BibiGPT-v1 中的真实 Proxy 实现(源码级解析)
BibiGPT-v1 依赖next@^16.1.6(见 package.json),其 proxy.ts 正是 Next.js 16 新约定的完整生产级范例,值得逐行对照:
- 导出签名:
export async function proxy(req: NextRequest, context: NextFetchEvent),导出proxyConfig = { matcher: ['/api/sumup', '/api/chat'] }(见 proxy.ts),只对两个总结类 API 生效; - 请求过滤:非
POST请求、非application/json请求直接放行(proxy.ts); - 限流与鉴权:优先识别用户自带的 API Key(
checkOpenaiApiKeys),合法则走ratelimitForApiKeyIps按 IP 限流;否则用validateLicenseKey校验激活码;无 Key 的免费用户走ratelimitForIps,超限后再尝试用 Supabase 登录态换取ratelimitForFreeAccounts的额度(proxy.ts); - 响应处理:命中限流/校验失败时,区分
/api/chat(返回401::/504::文本错误,前端useSummarize解析后弹出对应提示)与其他 API(返回 JSON 或重定向到/shop购买页)(proxy.ts); - Redis 缓存:通过
getCacheId(videoConfig)生成缓存键,命中缓存则直接返回,避免重复调用 LLM 产生费用(proxy.ts,缓存与限流基础设施见 lib/upstash.ts)。
这一实现覆盖了文档示例中的全部要点,并展示了生产级增强:在中间件里完成鉴权、限流、缓存三层逻辑,让后端的 API Handler 只关心业务。
八、文件约定的迁移对照:从 Pages Router 到 App Router
BibiGPT-v1 当前使用pages/目录(Pages Router),下表将本文档约定的 App Router 文件与项目现状一一对应,便于迁移时快速决策:
| App Router 文件约定 | BibiGPT-v1 现有实现 | 迁移建议 |
|---|---|---|
app/layout.tsx(根布局) | pages/_app.tsx(全局 Provider + Header/Footer) | 把_app.tsx的 Provider 逻辑迁入根layout.tsx |
app/page.tsx(首页) | pages/index.tsx + pages/[...slug].tsx | 首页迁app/page.tsx;[...slug]迁app/[...slug]/page.tsx,params改为 Promise |
app/route.ts(API) | pages/api/sumup.ts、pages/api/chat.ts、pages/api/openrouter/models.ts | 分别迁为app/api/sumup/route.ts等;注意route.ts与page.tsx不能同目录共存 |
app/not-found.tsx(404) | pages/404.tsx | 直接迁移,App Router 全局 404 文件位于app/根 |
app/_document.tsx职责 | pages/_document.tsx(SEO meta) | App Router 用根 layout + Metadata API 替代 |
app/error.tsx | 暂无独立错误页,_error.tsx由 Sentry 接管 | 按 error-handling.md 在app/根增加error.tsx |
middleware.ts/proxy.ts | proxy.ts(已是 v16 约定,无需迁移) | 保持现状 |
迁移时的三个高频坑(详见 error-handling.md 与 route-handlers.md):
error.tsx必须是 Client Component(顶部'use client'),且global-error.tsx必须自含<html>/<body>;route.ts与page.tsx不能在同一文件夹共存——若同一路径既需要页面又需要 API,应拆到/users(page)与/api/users(route)两个路径;- 不要在
try-catch里包裹redirect()/notFound()等导航 API——它们抛出的是 Next.js 内部管理的特殊错误,被 catch 后导航会失效;若必须包裹,用unstable_rethrow()重新抛出。
九、写在最后
Next.js 文件约定是"零配置、高可预测"的路线图:先靠文件名与目录结构把路由画出来,再靠layout/loading/error/not-found等特殊文件把边界补全,最后用route.ts与proxy.ts/middleware.ts把 API 与请求生命周期收口。BibiGPT-v1 的 proxy.ts 与 pages/ 目录已经演示了这套约定在生产规模下的完整形态——从[...slug]捕获分享链接、多 API 路由处理总结与对话,到中间件里完成限流、鉴权与缓存。把这套约定吃透,无论是改造现有 Pages Router 项目还是从零搭建 App Router 应用,都能一步到位地设计出清晰、健壮的路由体系。
参考与延伸阅读(仓库内)
- 本文档主来源:file-conventions.md
- 中间件迁移与代理实践:proxy.ts
- API 端点实现:pages/api/sumup.ts、pages/api/chat.ts、pages/api/openrouter/models.ts
- 动态/捕获路由实战:pages/[...slug].tsx、utils/getVideoIdFromUrl.ts
- 并行路由与拦截路由详解:parallel-routes.md
- 错误处理边界详解:error-handling.md
- Route Handlers 详解:route-handlers.md
- 人工智能
- 大模型
- AI 应用
- 前端
- 后端
【免费下载链接】BibiGPT-v1
BibiGPT v1 · one-Click AI Summary for Audio/Video & Chat with Learning Content: Bilibili | YouTube | Tweet丨TikTok丨Dropbox丨Google Drive丨Local files | Websites丨Podcasts | Meetings | Lectures, etc. 音视频内容 AI 一键总结 & 对话:哔哩哔哩丨YouTube丨推特丨小红书丨抖音丨快手丨百度网盘丨阿里云盘丨网页丨播客丨会议丨本地文件等 (原 BiliGPT 省流神器 & AI课代表)
相关推荐
Next.js App Router 文件约定(File Conventions)实战指南:以 next-shadcn-dashboard-starter 为例
Next.js App Router 文件约定(File Conventions)实战指南:以 next shadcn dashboard starter 为例
前端UI组件Next.js App Router 文件约定(File Conventions)实战指南:以 next-shadcn-dashboard-starter 为对照的完整路由文件体系解析
Next.js App Router 文件约定(File Conventions)实战指南:以 next shadcn dashboard starter 为对
前端UI组件BibiGPT-v1 中的 React/Next.js 指令实战指南:'use client'、'use server' 与 'use cache' 的边界与应用
BibiGPT v1 中的 React/Next.js 指令实战指南:'use client'、'use server' 与 'use cache' 的边界与应
人工智能大模型AI 应用前端后端
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考