Next.js 13.4引入的App Router让多页面项目的目录约定发生了本质变化。很多从旧版本迁移或刚接触该版本的开发者在新增页面后会遇到访问路径直接返回404的问题,这并非框架缺陷,而是路由系统的工作方式与之前不同。理解App Router的文件约定和渲染边界,才能稳定地扩展出多个可访问的页面。

一、App Router下的路由匹配原理
在Next.js 13.4中,app目录取代了传统的pages目录成为默认的路由根。框架会递归读取app目录下的文件夹,每个文件夹代表一个路由段,但只有当中包含名为page.js(或page.tsx、page.jsx)的文件时,该段才会被注册为可访问的页面路由。如果仅仅创建了一个文件夹而没有page文件,或者文件名拼写错误,框架在构建时就不会生成对应的HTML入口,浏览器请求自然收到404响应。
这种机制与pages目录时代完全不同。过去在pages下新建about.js就能通过/about访问,而现在必须在app/about/page.js中导出组件。下面是一段最小可运行的页面示例,注意文件位置与导出的默认组件:
// 文件路径:app/about/page.js
export default function AboutPage() {
return (
<div>
<h1>关于我们</h1>
<p>这是使用App Router创建的关于页面。</p>
</div>
);
}
只要上述文件存在,访问/about就不会再出现404。如果项目同时保留了pages目录,需要注意两者路由优先级:App Router中的同名路由会覆盖pages中的路由,这也常导致开发者误以为自己写的pages页面失效而报404,实际是被app中的空目录拦截了。
二、动态路由与多页面批量生成
当我们需要根据数据创建多个内容页,例如博客文章列表,就会用到动态段。App Router使用方括号文件夹表示动态参数,如app/posts/[id]/page.js。但仅这样写,在构建时若未提供参数列表,直接访问具体id仍可能404,尤其是在启用静态导出时。此时要用generateStaticParams函数预先返回所有可能的参数。
以下代码展示了如何为文章详情页预生成路由,避免运行时因参数未注册而404:
// 文件路径:app/posts/[id]/page.js
export function generateStaticParams() {
// 假设从接口或本地数据获取文章id列表
const posts = [{ id: '1' }, { id: '2' }, { id: '3' }];
return posts.map((post) => ({ id: post.id }));
}
export default function PostPage({ params }) {
return (
<article>
<h2>文章编号:{params.id}</h2>
<p>此处渲染对应文章内容。</p>
</article>
);
}
如果省略generateStaticParams且项目设置为output: 'export',那么除已知路径外的动态页都不会被预渲染,访问即404。对于不需要静态导出的项目,动态页会在请求时按服务端渲染处理,但也要保证params读取逻辑正确,否则同样会抛错中断响应。
三、服务端组件与客户端边界错误引发的404
App Router默认所有组件都是服务端组件,不能直接使用useState、useEffect等客户端钩子。若在page.js中误写浏览器相关代码且未添加'use client'指令,构建阶段可能通过,但运行时会因环境不匹配导致组件渲染失败,进而使整个路由不可达,表现为404或500。区分边界是排查多页面异常的关键。
下面的例子演示了错误的写法与修正方式。错误写法在服务端组件中调用了点击事件:
// 错误示例:app/contact/page.js(缺少客户端声明)
export default function ContactPage() {
// 以下代码在服务端环境中无法绑定真实DOM事件
const handleClick = () => alert('提交');
return <button onClick={handleClick}>联系我们</button>;
}
修正方法是把交互部分拆为客户端组件,或在文件顶部声明。正确结构如下:
// 正确示例:app/contact/page.js
'use client';
import { useState } from 'react';
export default function ContactPage() {
const [sent, setSent] = useState(false);
return (
<div>
<button onClick={() => setSent(true)}>发送消息</button>
{sent && <p>已发送</p>}
</div>
);
}
通过明确'use client',该页面被标记为客户端组件,交互逻辑正常执行,路由稳定存在。建议将纯展示部分保留为服务端组件,仅把需要状态的子组件设为客户端,这样既利于性能也减少边界错误。
四、常见排查清单与解决步骤
遇到多页面404时,可按照以下顺序自查:确认文件是否位于app目录内且命名为page.js;确认父级文件夹路径与期望URL一致;确认没有同名的pages文件造成覆盖;确认动态路由已提供generateStaticParams或关闭了静态导出;确认page文件顶部必要时有'use client'。借助next dev的控制台输出,能直接看到哪些路由被注册。
另外,可在项目根目录创建next.config.js检查配置是否限制了trailingSlash或basePath,这些设置会改变URL匹配规则。例如basePath设为'/docs'后,所有页面需带此前缀访问,疏忽也会让人误判为404。参考以下基础配置:
// next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
// 若部署在子路径下才需要,否则留空
// basePath: '/docs',
reactStrictMode: true,
};
module.exports = nextConfig;
只要对照上述清单逐一核对,Next.js 13.4中创建多页面导致的404基本都能定位并修复。核心还是适应App Router基于文件系统的显式路由声明,以及分清服务端与客户端组件的职责。
Next.jsApp_Router404错误修改时间:2026-08-02 04:51:44