Next.js 是怎么把一次点击变成一页 HTML 的:从 next dev 启动到缓存命中的完整指南 Next.js 是怎么把一次点击变成一页 HTML 的从 next dev 启动到缓存命中的完整指南【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.jsNext.js 是 React 生态里的全栈框架它把构建、路由、服务端渲染打包成一个命令。但你敲下next dev之后请求到底经过谁的手我们跟着一路看下去从next dev启动那一刻到浏览器发出一个/blog/1的请求再到改一行代码页面秒变中间每一站源码都在仓库里路径我会指给你。 启动时 Next 到底扫描了你的项目next dev跑起来之后你怎么知道/about这个路径该找哪个文件答案在 packages/next/src/build/构建入口在这里其中route-discovery.ts负责把app/和pages/目录递归读一遍。它干的事很像酒店开业前盘点房号每发现一个about.js或about/page.tsx就在内部路由表里登记一条房号到房间的映射连_app、layout这些约定文件也一并归类。路由表不是运行时现算的而是启动时一次性盘点出来的。你可以试试在app/下新建notes/page.tsx什么都不配置刷新next dev就能访问/notes——目录即路由这就是登记制的全部意义。 请求进来后第一张转接单开给谁浏览器发来/blog/1服务器凭什么知道该执行哪个组件服务端的总入口是 packages/next/src/server/next-server.ts它先做解析和中间件拦截真正分派请求的是packages/next/src/server/lib/router-server.ts。可以把这里想象成医院分诊台挂号护士next-server先量体温、问过敏史再按症状把病人送到对应科室router-server 手里攥着启动时那张房号台账把 URL 和构建产物对上号然后才把渲染任务派给 App Router 或 Pages Router 各自的渲染器。App Router 和 Pages Router 两套路由系统就靠这张表共存互不干扰。避坑提醒middleware.ts在分诊台之前执行能改写路径和响应所以它一旦return了响应后面的路由匹配根本不会发生——排查路由没命中时先查中间件。 动态路由 /blog/[id] 是怎么匹配上的[id]这种方括号目录凭什么能接住/blog/1、/blog/42一整类请求核心工具在 packages/next/src/shared/lib/router/utils/route-regex.ts里面的getRouteRegex()把路由模板编译成正则。// 模板翻译成可匹配的函数 const route /blog/[id] // 生成的路由键形如 // 参数: id // 正则: ^/blog/([^/])$ // 精确段: [blog]这个过程像把一句模糊的招工启事翻译成打卡机规则/blog/[id]不再是一个具体路径而是一条第一段是 blog、第二段什么都行、第二段落进 id 这个袋子的规则。...通配符和[[...slug]]可选段也走同一套编译器所以嵌套目录写得再深匹配逻辑始终是同一条流水线。你可以试试同时建blog/[id]/page.tsx和blog/new/page.tsx访问/blog/new时会命中精确的那个——精确段排在动态段前面优先级写在匹配顺序里不是猜的。⚙️ 同一份代码为什么有的页面预渲染、有的现场渲染明明都是页面为什么有的打开快得像本地文件有的每次都要等服务器判定逻辑在 packages/next/src/build/rendering-mode.ts。它像后厨给每道菜贴标签只读静态数据的贴提前做好静态生成构建时渲染成 HTML 存盘调了cookies()、读了请求头的贴来单再做动态渲染每次现做App Router 里还能标半份提前做PPR外壳预渲染、数据区流式补全。标签贴在构建阶段所以行为在next build那一刻就定了运行时不会偷偷换策略。避坑提醒页面里随手调一个headers()整页就从静态变动态缓存直接失效。上线前跑一次next build看输出里哪些路由被标成ƒ动态、哪些是○静态心里就有底了。 缓存到底缓在哪、失效时谁负责同样的请求第二次来为什么有时毫秒返回数据缓存由服务端渲染管线统一管相关实现在 packages/next/src/server/ 下的render.tsx与app-render/目录。机制像酒店钥匙牌构建时预渲染完Next 把这个路由的渲染结果和一把带有效期的钥匙牌放在一起请求进来牌在且没过期直接递给你存好的 HTML过期了按你声明的revalidate周期决定是后台悄悄重做stale-while-revalidate还是立刻重新渲染。所以缓存不是一个文件而是一组结果 时效规则的配对。你可以试试给一个数据页加export const revalidate 6060 秒内连点刷新响应头里的年龄会涨但内容不变——这就是钥匙牌生效的直接证据。 改一行代码浏览器为什么不用刷新整个应用你只改了一个按钮的文案为什么整页没闪就变了开发端的客户端入口在 packages/next/src/client/app-next-dev.ts它通过 WebSocket 和构建器保持长连接构建器dev 模式下是 Turbopack一发现文件变化就把新的模块版本推过来。这像一条传送带上的热交换工位只有你动过的那块电路板被抽出来替换旁边的 React 状态、路由上下文原封不动留在原插座上换下来的是旧板子换上去的是新板子——所以毫秒级生效且不用重载页面。生产构建里这套机制整个不打包你上线的代码里没有这层传送带。你可以试试next dev状态下改app/page.tsx的一行文案再看终端输出HMR 事件里会精确列出被替换的模块路径——对照一下你会发现它和你改的文件一一对应。回头看这条路构建时先盘点房号、启动时备好转接单、请求进来按策略渲染、结果按钥匙牌缓存、开发期靠传送带换件——Next.js 的复杂都被压在约定里你写的是目录结构它替你做完剩下四件事。动手验证跑npx create-next-applatest或git clone https://gitcode.com/GitHub_Trending/next/next.js后看 examples/reproduction-template/ 这种最小示例建一个静态页和一个调headers()的页各跑一次next build对比输出里○和ƒ的分布你会对渲染模式在构建期就定死这句话有切身体感。延伸阅读官方文档 docs/01-app/App Router 从路由约定到缓存语义的完整说明都在里面。【免费下载链接】next.jsThe React Framework项目地址: https://gitcode.com/GitHub_Trending/next/next.js创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考