NextJs的目录结构说明

Table of Contents

以下是一个典型的 Next.js 项目(基于最新版本,使用 App Router)的目录结构及其核心文件的含义和使用说明:

项目根目录

my-next-app/
├── app/                    # 核心路由和页面(App Router)
│   ├── layout.tsx         # 全局布局组件
│   ├── page.tsx           # 首页(对应路由 `/`)
│   ├── (auth)/            # 路由组(分组路由,不反映在 URL 中)
│   ├── blog/              # 嵌套路由 `/blog`
│   │   ├── page.tsx       # `/blog` 页面
│   │   └── [slug]/       # 动态路由(如 `/blog/hello-world`)
│   │       └── page.tsx
│   └── api/               # API 路由(可选,或使用单独的 `pages/api`)
│       └── route.ts
├── public/                # 静态资源(图片、字体等,直接通过 `/` 访问)
├── components/            # 可复用的 React 组件
├── lib/                   # 工具函数、第三方库封装
├── styles/                # 全局 CSS 或 SCSS 文件
├── .env.local             # 环境变量(仅在本地生效)
├── next.config.js         # Next.js 自定义配置
├── package.json           # 项目依赖和脚本
└── middleware.ts          # 中间件(处理请求前执行)

核心文件/目录详解

1. app/ 目录(App Router)

  • 核心功能:存放页面、布局和路由逻辑。

  • 关键文件

    • layout.tsx

      • 作用:定义全局布局(如导航栏、页脚),包裹所有子页面。
      • 示例
        export default function RootLayout({ children }: { children: React.ReactNode }) {
          return (
            <html lang="en">
              <body>{children}</body>
            </html>
          );
        }
        
    • page.tsx

      • 作用:每个路由的页面组件,文件名必须为 page.tsx
      • 示例(首页 /):
        export default function Home() {
          return <h1>Welcome to Next.js!</h1>;
        }
        
    • route.ts

      • 作用:定义 API 路由(替代旧版 pages/api)。
      • 示例GET /api/users):
        export async function GET() {
          return Response.json({ users: [] });
        }
        
  • 动态路由
    使用 [param] 文件夹名(如 app/blog/[slug]/page.tsx)捕获动态参数,通过 params 获取:

    export default function BlogPage({ params }: { params: { slug: string } }) {
      return <div>Post: {params.slug}</div>;
    }
    

2. public/ 目录

  • 作用:存放静态资源,可通过 URL 直接访问(如 /logo.png 对应 public/logo.png)。
  • 典型文件
    • favicon.ico:网站图标。
    • robots.txt:搜索引擎爬虫规则。

3. components/ 目录

  • 作用:存放可复用的 React 组件(如 Button.tsxHeader.tsx)。
  • 最佳实践:按功能或模块划分子目录(如 components/ui/ 存放通用 UI 组件)。

4. next.config.js

  • 作用:自定义 Next.js 配置(如重定向、图像优化、环境变量等)。
  • 示例
    module.exports = {
      images: {
        domains: ['example.com'], // 允许加载的图片域名
      },
      redirects: async () => [
        { source: '/old', destination: '/new', permanent: true },
      ],
    };
    

5. middleware.ts

  • 作用:在请求处理前执行代码(如身份验证、重定向)。
  • 示例(重定向未登录用户):
    import { NextResponse } from 'next/server';
    export function middleware(request) {
      if (!request.cookies.get('token')) {
        return NextResponse.redirect(new URL('/login', request.url));
      }
      return NextResponse.next();
    }
    

6. 环境变量文件

  • .env.local:本地环境变量(通过 process.env.VAR_NAME 访问)。
  • 命名规则
    • NEXT_PUBLIC_ 前缀的变量会在客户端暴露(如 NEXT_PUBLIC_API_URL)。
    • 其他变量仅限服务端使用。

其他重要文件

  • styles/globals.css:全局 CSS 样式(在 layout.tsx 中导入)。
  • tailwind.config.js:Tailwind CSS 配置(如果使用)。
  • tsconfig.json:TypeScript 配置(自动生成,可自定义路径别名)。

版本差异说明

  • Pages Router(旧版):使用 pages/ 目录,动态路由通过文件名(如 pages/blog/[slug].js)实现。
  • App Router(推荐):更灵活的路由结构,支持嵌套布局和 React Server Components。

通过这种结构化的设计,Next.js 项目能够高效管理路由、组件和静态资源,同时保持代码的可维护性。


补充多语言项目目录

在 Next.js 项目中实现多语言(国际化,i18n)时,通常会结合 路由前缀(如 /en/.../zh-CN/...)和 翻译文件 来实现。以下是基于 App Router 的典型多语言项目目录结构及关键文件说明:

目录结构示例

my-next-app/
├── app/
│   ├── [lang]/            # 动态语言路由(核心变化!)
│   │   ├── layout.tsx     # 语言层级的布局(可在此加载翻译)
│   │   ├── page.tsx       # 首页(如 `/en` 或 `/zh-CN`)
│   │   ├── blog/          # 嵌套路由 `/en/blog` 或 `/zh-CN/blog`
│   │   │   └── page.tsx
│   │   └── (common)/      # 无需语言前缀的路由组(如登录页)
│   ├── (root)/            # 无语言前缀的路由组(可选,如重定向逻辑)
│   │   └── page.tsx       # 自动重定向到默认语言
├── public/
├── locales/               # 存放多语言翻译文件(核心新增!)
│   ├── en/
│   │   ├── common.json    # 通用翻译
│   │   └── blog.json
│   └── zh-CN/
│       ├── common.json
│       └── blog.json
├── middleware.ts          # 处理语言检测和重定向
└── ...

关键变化说明

1. 动态语言路由 (app/[lang]/)

  • 作用:通过 URL 路径前缀区分语言(如 /en/blog/zh-CN/blog)。
  • 实现方式
    • 将整个应用包裹在 app/[lang]/ 目录下。
    • 通过 params.lang 获取当前语言:
      // app/[lang]/layout.tsx
      export default function Layout({ params, children }) {
        const lang = params.lang; // 'en' 或 'zh-CN'
        return <>{children}</>;
      }
      

2. 语言翻译文件 (locales/)

  • 作用:按语言和模块组织翻译内容。
  • 典型结构
    locales/
    ├── en/
    │   ├── common.json      // 通用文本
    │   └── blog.json        // 博客相关文本
    └── zh-CN/
        ├── common.json
        └── blog.json
    
  • 示例内容 (locales/en/common.json):
    {
      "header": {
        "home": "Home",
        "blog": "Blog"
      }
    }
    

3. 中间件 (middleware.ts)

  • 作用:自动检测用户语言并重定向到对应路由(基于浏览器设置或 Cookie)。

  • 示例代码

    import { NextRequest, NextResponse } from 'next/server';
    
    const locales = ['en', 'zh-CN'];
    const defaultLocale = 'en';
    
    export function middleware(request: NextRequest) {
      // 1. 检查 URL 中是否已包含语言
      const pathname = request.nextUrl.pathname;
      if (pathname.startsWith(`/${defaultLocale}/`)) {
        return NextResponse.redirect(new URL(pathname.replace(/^\/en/, ''), request.url));
      }
    
      // 2. 从 Cookie 或浏览器语言获取偏好
      const langCookie = request.cookies.get('lang');
      const acceptLanguage = request.headers.get('accept-language')?.split(',')[0];
      const preferredLang = langCookie || acceptLanguage || defaultLocale;
    
      // 3. 重定向到匹配的语言路由
      if (!locales.some(locale => pathname.startsWith(`/${locale}`))) {
        return NextResponse.redirect(new URL(`/${preferredLang}${pathname}`, request.url));
      }
    
      return NextResponse.next();
    }
    

4. 语言切换组件

  • 作用:允许用户手动切换语言(需更新 URL 和 Cookie)。

  • 示例实现

    // components/LanguageSwitcher.tsx
    import Link from 'next/link';
    import { usePathname } from 'next/navigation';
    
    export default function LanguageSwitcher() {
      const pathname = usePathname();
      const currentLang = pathname.split('/')[1];
    
      const switchLanguage = (lang: string) => {
        // 替换当前路径中的语言部分
        const newPath = pathname.replace(/^\/[a-z-]+/, `/${lang}`);
        return newPath;
      };
    
      return (
        <div>
          <Link href={switchLanguage('en')} locale={false}>English</Link>
          <Link href={switchLanguage('zh-CN')} locale={false}>中文</Link>
        </div>
      );
    }
    

使用 i18n 库(推荐)

虽然可以手动实现,但使用专门的库(如 next-intlnext-i18next)可简化流程:

使用 next-intl 的调整:

  1. 配置 (next.config.js):

    const withNextIntl = require('next-intl/plugin')();
    module.exports = withNextIntl();
    
  2. 翻译加载 (app/[locale]/layout.tsx):

    import { useTranslations } from 'next-intl';
    
    export default function RootLayout({ children, params: { locale } }) {
      const t = useTranslations('common');
      return (
        <html lang={locale}>
          <body>
            <nav>{t('header.home')}</nav>
            {children}
          </body>
        </html>
      );
    }
    

注意事项

  1. SEO 优化

    • 使用 <link rel="alternate" hreflang="x" href="..."> 标签声明多语言页面。
    • generateMetadata 中动态生成语言相关的元数据。
  2. 静态生成

    • 使用 generateStaticParams 预生成所有语言版本:
      // app/[lang]/page.tsx
      export async function generateStaticParams() {
        return [{ lang: 'en' }, { lang: 'zh-CN' }];
      }
      
  3. 默认语言处理

    • 通过中间件将根路径(/)重定向到默认语言(如 /en)。

通过这种结构,你可以实现:

  • 清晰的 URL 语言标识(如 /en/blog
  • 按模块组织的翻译文件
  • 自动语言检测与切换
  • 与 Next.js 特性(SSG、ISR)无缝集成

明天了解一下 NextJs 的后端目录结构