Next.js实战:从零搭建企业级项目,避开这5个大坑

刚开始我也以为Next.js项目搭建就是npx create-next-app一把梭,结果真干起来才发现,从配置到部署每个环节都能踩出坑来。今天就把我最近从零搭建一个中型电商项目的过程掰开了说,包括那些让我半夜骂娘的bug和最终怎么解决的。

先说说环境:Node 18+,包管理器我用pnpm(npm慢到怀疑人生)。你要用yarn也行,但别用npm,真的。

开篇图:一张Next.js项目目录结构截图,标注了关键文件

第一步:别用默认模板,自己配

默认的create-next-app生成的代码太啰嗦了,一堆用不到的demo代码。我建议这样初始化:

bash
pnpm create next-app@latest my-app --typescript --tailwind --eslint --app --src-dir --import-alias "@/*"
`

为什么要这么写?--src-dir把代码放在src目录下,结构更清晰;--app用App Router(Pages Router已经过时了);--import-alias设置@别名,避免../../这种恶心路径。

装完依赖后第一件事:删掉app/page.tsx里默认的demo代码,只保留:

`tsx
export default function Home() {
return (

Hello World


)
}
/>code>`

另一个坑:eslint配置。默认的eslint规则太松了,等于没配。我直接在.eslintrc.json加了这些:

`json
{
"extends": ["next/core-web-vitals", "eslint:recommended"],
"rules": {
"no-unused-vars": "error",
"prefer-const": "error",
"no-console": "warn"
}
}
`

别问,问就是被未使用变量

坑过三次。

>第二步:环境变量配置,这个设计真的反人类

Next.js的环境变量机制是我见过最奇葩的设计之一。官方文档文档不够清晰,我踩了整整两天才搞明白。

关键规则就三条:

  • NEXT_PUBLIC_ 开头的变量会暴露给前端,其他的只在服务端可用
  • .env.local 优先级最高,千万别提交到git
  • 运行时环境变量只能在服务端组件里用
  • 这是我的配置模板:

    `

    .env.local(本地开发用,不提交)

    DATABASE_URL="postgresql://localhost:5432/mydb"
    NEXT_PUBLIC_API_URL="http://localhost:3000/api"

    .env.production(生产环境)

    NEXT_PUBLIC_API_URL="https://api.example.com"
    `

    还有个技巧:如果你想在build时读取环境变量,需要在next.config.js里用publicRuntimeConfig。但这玩意儿坑太多,我建议直接用process.env.NEXT_PUBLIC_*解决所有前端需求。

    核心图:一张环境变量加载优先级流程图,展示了.env.local > .env.production > .env的覆盖关系

    第三步:路由设计,App Router的坑比想象的多

    App Router听起来很美,但实际用起来有不少反直觉的地方。先说layout的嵌套问题:

    `tsx
    // app/layout.tsx - 根布局
    export default function RootLayout({ children }: { children: React.ReactNode }) {
    return (


    {/* 所有页面都显示 */}
    {children}



    )
    }

    // app/dashboard/layout.tsx - 仪表盘布局
    export default function DashboardLayout({ children }: { children: React.ReactNode }) {
    return (

    {/* 只在仪表盘页面显示 */}
    {children}

    )
    }
    `

    刚开始我以为layout会自动合并,结果发现子layout会完全覆盖父layout的部分内容。正确的理解是:layout是嵌套的,子layout只控制自己的children区域。

    另一个让我崩溃的点:动态路由的params获取。在Pages Router里可以直接从props拿,App Router里必须这样:

    `tsx
    // app/posts/[id]/page.tsx
    export default async function PostPage({ params }: { params: { id: string } }) {
    // params是Promise,需要await
    const { id } = await params

    const post = await getPost(id)

    return

    {post.title}

    }
    `

    等等,params是Promise?是的,Next.js 15开始,params改成了异步的。这设计真让人无语,但你必须适应。

    第四步:数据获取,从3.2秒到0.8秒的优化

    数据获取是Next.js的亮点,但用不好就是灾难。我一开始用fetch直接请求,结果每个页面都慢得像蜗牛。

    先看错误做法:

    `tsx
    // ❌ 这样写会导致每个请求都重新执行
    export default async function ProductsPage() {
    const res = await fetch('https://api.example.com/products', {
    cache: 'no-store' // 每次都重新请求
    })
    const products = await res.json()

    return
    }
    `

    正确做法是使用缓存策略:

    `tsx
    // ✅ 生产级写法
    export default async function ProductsPage() {
    const products = await fetch('https://api.example.com/products', {
    next: { revalidate: 3600 }, // 每小时重新验证一次
    }).then(res => {
    if (!res.ok) throw new Error('获取商品列表失败')
    return res.json()
    })

    return
    }

    // 或者用React Cache(需要自己实现)
    import { cache } from 'react'

    export const getProducts = cache(async () => {
    const res = await fetch('https://api.example.com/products')
    return res.json()
    })
    `

    用了缓存后,页面加载时间从3.2秒降到了0.8秒。这提升太明显了,所以记住:能缓存就缓存,别让用户等。

    还有个技巧:静态生成(SSG)和服务器端渲染(SSR)的选择。我的原则是:

    • 内容很少变动的页面(比如关于我们、帮助中心)→ 用SSG
    • 需要实时数据的页面(比如订单状态、用户信息)→ 用SSR或ISR
    • 完全动态的内容(比如搜索功能)→ 用客户端组件 + API

    第五步:部署,从Vercel到自托管

    部署这事儿,Vercel确实方便,但如果你要省钱或者有合规要求,还是得自托管。我踩的坑主要是静态资源问题。

    先说Vercel部署(最简单):

    `bash

    在项目根目录创建 vercel.json

    {
    "framework": "nextjs",
    "buildCommand": "next build",
    "outputDirectory": ".next"
    }
    `

    然后vercel –prod就完事了。但注意:免费版的冷启动时间能到10秒以上,生产环境还是建议付费。

    自托管的话,我的方案是Docker + Nginx:

    `dockerfile

    Dockerfile

    FROM node:18-alpine AS builder
    WORKDIR /app
    COPY package*.json ./
    RUN pnpm install
    COPY . .
    RUN pnpm build

    FROM node:18-alpine AS runner
    WORKDIR /app
    COPY --from=builder /app/.next ./.next
    COPY --from=builder /app/public ./public
    COPY --from=builder /app/package.json ./package.json
    EXPOSE 3000
    CMD ["pnpm", "start"]
    `

    这里有个坑:.next目录不能直接复制,必须用COPY –from=builder`完整路径。我之前踩过这个雷,结果build出来的东西跑不起来。

    总结图:一张Next.js项目从开发到部署的完整流程图,标注了每个环节的关键点

    总结一下,你可以立刻用的三个点

  • 环境变量记住NEXT_PUBLIC规则:前端能读到的一定要加这个前缀,其他敏感信息放服务端组件里
  • 缓存是你的好朋友:能静态生成的就别动态渲染,ISR的revalidate时间根据业务需求设置,别偷懒全用no-store
  • 部署前检查三个文件:next.config.js(确认没有敏感信息泄露)、.env.production(确认环境变量正确)、Dockerfile(确认构建缓存设置正确)
  • 最后说一句:Next.js虽然坑多,但熟练掌握后绝对是生产级项目的利器。有问题直接看官方文档的示例代码,别看社区博客(包括我这篇,哈哈)。


    滚动至顶部