从零开始学前端 | 第四十五章:第五阶段综合实战:个人博客项目初始化、路由设计与公共布局搭建 本章定位上一章我们已经正式开启了第五阶段综合实战。你已经先把这次个人博客项目最关键的前置问题想清楚了这个项目为什么值得做。这个项目主要做给谁看。第一版必须完成哪些核心页面。第一版先不做什么。为什么框架继续选 Next.js语言继续选 TypeScript。为什么第一版先用本地 TypeScript 数据文件和 CSS Modules。也就是说到现在为止你已经不是“模模糊糊想做个博客”而是已经知道这个博客项目第一版的大致目标、边界、数据方向和技术路线是什么。接下来就到了非常关键的一步把这些想清楚的内容真正落到项目骨架里。这一章非常重要。因为一个综合项目后面能不能写得稳往往先取决于三件事项目是不是初始化得清楚。路由目录是不是搭得清楚。公共布局是不是一开始就组织得清楚。如果这三件事一开始没理顺后面页面越写越多就很容易出现路由目录越来越乱公共结构到处重复页面和组件职责混在一起样式和布局反复返工所以这一篇我们不会急着把文章列表和详情逻辑全部写满。这一篇的重点是先把项目创建出来。先把app/路由骨架搭出来。先把公共导航、页脚和layout.tsx组织起来。你可以把这一章理解成第五阶段综合实战真正开始落地的第一篇先把整个博客网站的地基搭稳。本章学习目标学完这一章后你应该能做到理解为什么综合项目一开始先搭骨架比先写很多细节页更稳。学会用pnpm create next-app创建一个适合当前阶段的 Next.js 项目。知道初始化项目时哪些选项更适合当前这次博客实战。理解src/app、public、components、data、types等目录的基础职责。学会按第一版页面清单搭出博客项目的路由结构。理解page.tsx、layout.tsx和动态路由目录的基础作用。知道为什么博客项目需要先抽出公共导航和公共页脚。学会用layout.tsx把站点公共外壳连接起来。理解children在布局里的意义。知道第一版项目初始化时哪些内容先做占位哪些细节后面再补。掌握一个适合初学者继续扩展的博客项目目录骨架。为下一篇继续进入首页、文章列表与本地数据接入做好准备。一、这一篇到底要完成什么这一篇我们要做的不是马上把整个博客做完。这一篇要完成的是先让博客网站“站起来”。所谓“站起来”主要是指下面这些事情项目已经创建完成可以正常运行。站点最基础的页面路由已经搭出来。公共导航和页脚已经有了统一入口。layout.tsx已经把整站公共骨架接起来。首页、文章列表页、文章详情页、关于页、联系页都已经有基础位置。这一步非常关键。因为从综合项目的角度看它解决的是先让“网站结构”成立而不是先让某个页面局部非常丰富。二、为什么先搭项目骨架比先写内容细节更重要很多人在综合实战刚开始时最容易做的事是先去写一个看起来最有意思的页面。比如先写文章卡片先写详情页排版先写首页横幅区这些当然都重要。但如果路由和布局还没搭好后面往往会遇到这些问题页面文件放哪儿开始犹豫。导航要不要重写一遍。页脚怎么每页都出现。页面结构和样式会不会后面推翻重来。所以更稳的节奏通常是先把站点骨架、公共外壳和页面入口搭起来再往里填具体内容。你可以把这一步理解成先把房子的承重墙和楼梯搭好再去布置每个房间。三、第一步先创建项目这一版博客项目我们继续使用Next.jsTypeScriptsrc/目录App RouterCSS Modules 全局样式如果你使用pnpm一个很适合当前阶段的初始化命令可以这样写pnpmcreate next-app personal-blog\--ts\--eslint\--app\--src-dir\--use-pnpm\--import-alias/*1. 如果你是交互式创建关键选项怎么选如果创建过程中出现交互式问题当前阶段建议这样选TypeScriptYesESLintYesTailwind CSSNosrc/directoryYesApp RouterYesImport alias保持默认/*2. 为什么这里先不选 Tailwind不是因为 Tailwind 不好而是因为这一套学习路线前面已经明确了第一版更适合继续用你已经掌握的 CSS 基础和 CSS Modules。这样更有助于你真正建立布局组织感样式分层感组件局部样式边界感3. 为什么src/目录值得打开因为当前阶段项目文件会越来越多。把源码统一放进src/会更容易区分源码目录配置文件静态资源四、项目创建完成后先认识这些文件项目创建出来后不要急着立刻删改很多文件。先认识当前最重要的几个入口。如果我们使用了src/目录第一版你通常会先看到这些东西personal-blog/ ├── public/ ├── src/ │ └── app/ │ ├── favicon.ico │ ├── globals.css │ ├── layout.tsx │ └── page.tsx ├── package.json ├── tsconfig.json └── next.config.ts1.src/app/layout.tsx是什么它是整站最基础的公共布局入口。后面我们的页头页脚全局结构都会慢慢接到这里。2.src/app/page.tsx是什么它对应网站首页也就是/3.src/app/globals.css是什么它负责全局基础样式例如字体背景链接默认样式通用容器规则4.public/是什么它适合放站点图片图标文章封面图也就是那些会被页面直接引用的静态资源。五、第二步先把第一版路由目录搭出来上一章我们已经明确了第一版最重要的五个页面首页文章列表页文章详情页关于页联系页所以这一章最关键的动作之一就是把这些页面先变成真实的目录结构。一个适合第一版的src/app路由骨架可以先这样搭src/app/ ├── layout.tsx ├── page.tsx ├── posts/ │ ├── page.tsx │ └── [slug]/ │ └── page.tsx ├── about/ │ └── page.tsx └── contact/ └── page.tsx1. 这几个目录分别对应什么地址你可以先建立下面这组直觉src/app/page.tsx对应/src/app/posts/page.tsx对应/postssrc/app/posts/[slug]/page.tsx对应/posts/某篇文章标识src/app/about/page.tsx对应/aboutsrc/app/contact/page.tsx对应/contact2. 为什么先把目录搭出来很重要因为它会帮你立刻把这些问题变清楚网站到底有哪些页面页面之间的跳转路径是什么哪些页面是静态入口哪些页面是详情页入口六、为什么文章详情页要用[slug]博客项目里的文章详情页不会只有一篇文章。它通常是多篇文章共用同一个详情页结构只是内容不同。这时就很适合使用动态路由目录[slug]1.slug可以先怎么理解你可以先把它理解成一篇文章在 URL 里的唯一标识。例如react-basicnextjs-routingtypescript-generics它们分别可以对应这样的网址/posts/react-basic /posts/nextjs-routing /posts/typescript-generics2. 为什么博客项目特别适合用这种方式因为它天然符合同一类内容很多条每条都有自己的详情页。所以这一章虽然还不会把详情页数据全部写满但我们要先把这条路由骨架搭出来。七、先让每个页面“占住位置”综合项目刚开始时一个非常稳的做法是先让每个页面都有一个基础占位版本。也就是说先让这些页面都能打开再逐步往里面填内容。例如首页可以先写成export default function HomePage() { return ( section h1你好这里是我的个人博客/h1 p后面我们会在这里接入个人介绍、推荐文章和内容入口。/p /section ); }文章列表页也可以先写成export default function PostsPage() { return ( section h1文章列表页/h1 p后面我们会在这里渲染文章卡片列表。/p /section ); }1. 为什么占位页很有价值因为它可以帮助你先确认路由是否正确页面是否能访问公共布局是否已经套上2. 当前阶段不需要一开始就让每页都很丰富现在更重要的是每个页面的位置和职责先清楚地站住。八、第三步先准备导航配置博客项目只要有多个页面就会很快遇到一个问题导航到底怎么组织当前阶段更稳的做法通常是先把导航做成一份单独配置。例如可以在src/data/site-nav.ts里先放这样一份数据exportinterfaceNavItem{label:string;href:string;}exportconstsiteNavList:NavItem[][{label:首页,href:/},{label:文章,href:/posts},{label:关于,href:/about},{label:联系,href:/contact}];1. 为什么导航值得先单独抽出来因为它会被页头导航可能的移动端菜单后续的页脚快捷链接重复使用。2. 当前阶段最值得先建立什么意识你可以先记住站点级配置尽量不要直接写死在页面 JSX 里。九、第四步抽出SiteHeader博客项目里最值得先抽出来的公共组件之一就是页头导航。例如我们可以先在src/components/site-header.tsx创建一个基础组件。它可以先写成这样import Link from next/link; import { siteNavList } from /data/site-nav; export function SiteHeader() { return ( header classNamesite-header div classNamesite-shell Link href/ classNamesite-logo 从零开始学前端 /Link nav classNamesite-nav {siteNavList.map(function (navItem) { return ( Link key{navItem.href} href{navItem.href} {navItem.label} /Link ); })} /nav /div /header ); }1. 这段代码最关键的点是什么不是样式而是结构Logo 区域有了导航区域有了导航来自统一配置2. 为什么Link很重要因为在 Next.js 项目里页面之间跳转通常要优先使用Link它比直接写普通超链接更符合框架主线。十、第五步抽出SiteFooter页脚和页头一样也是很典型的公共结构。第一版页脚不需要很复杂。只要先把这些信息放上去就已经非常有价值网站名称一句简短说明版权或年份信息例如export function SiteFooter() { return ( footer classNamesite-footer div classNamesite-shell p从零开始学前端 | 个人博客项目/p p这里会持续记录前端学习、项目实践与思考。/p /div /footer ); }1. 为什么第一版页脚不需要做得太满因为它当前最主要的价值是先让站点“有一个稳定结尾”。2. 后面它还能继续扩展什么后面完全可以再加社交链接邮箱快捷导航更新时间说明但现在先让骨架成立更重要。十一、第六步用layout.tsx把站点外壳接起来这一步是本章最关键的核心动作之一。因为layout.tsx会真正把页头页面主体页脚连接成一个完整站点外壳。一个适合当前阶段的src/app/layout.tsx可以先写成这样import type { Metadata } from next; import { SiteFooter } from /components/site-footer; import { SiteHeader } from /components/site-header; import ./globals.css; export const metadata: Metadata { title: 从零开始学前端 | 个人博客, description: 一个使用 Next.js 构建的个人博客项目 }; export default function RootLayout({ children }: Readonly{ children: React.ReactNode }) { return ( html langzh-CN body SiteHeader / main classNamesite-main {/* children 会被当前路由页面的内容替换 */} {children} /main SiteFooter / /body /html ); }1. 这段代码里最值得先看懂什么先看懂这四个点就够了全局样式从这里引入页头和页脚从这里统一挂上main区域是页面主体children是每个页面真正会变化的部分2. 为什么这一步这么关键因为从这一刻开始你的博客项目不再是一堆分散页面而是已经有了统一站点外壳。十二、为什么children是布局的主线这一节很重要。很多初学者第一次看layout.tsx时最容易模糊的地方就是children到底是什么你现在可以先用最朴素的方式理解children就是当前路由页面会填进来的那块主要内容。例如当你访问/时children会被首页内容替换。当你访问/posts时children会被文章列表页内容替换。当你访问/about时children会被关于页内容替换。1. 为什么这能减少重复代码因为页头和页脚不需要每个页面都重新写。只要布局统一挂一次页面就只关心自己的主体内容。2. 这和前面学过的什么思想是一致的它和我们前面反复强调的公共结构放公共层变化内容放变化层其实是同一条主线。十三、第七步先给页面一个统一容器层当站点布局开始成形后下一步很值得先做的就是给页面准备一个统一容器。因为博客项目里的很多页面都会需要一定的最大宽度左右留白统一的内容区域节奏当前阶段最简单的做法是先在全局样式里约定一个通用类名例如.site-shell{width:min(1100px,calc(100% - 32px));margin:0 auto;}1. 为什么这个容器层很重要因为它可以帮助你让页头页脚页面内容在视觉上保持统一宽度感。2. 当前阶段最重要的不是样式多复杂而是站点整体节奏开始统一。十四、全局样式第一版先管什么这一章我们不追求把所有视觉细节都定死。但全局样式里第一版至少可以先管下面几件事body的默认背景、文字颜色和字体链接默认样式站点最大宽度容器页头、页脚基础留白main主区域的上下间距例如:root{color-scheme:light;}*{box-sizing:border-box;}body{margin:0;color:#1f2937;background:#f8fafc;font-family:Arial,sans-serif;}a{color:inherit;text-decoration:none;}.site-main{padding:32px 0 56px;}1. 为什么这些基础样式就够重要因为它们会直接影响站点是不是一打开就比较整齐各页面是不是共享同一种基础节奏2. 当前阶段不要先陷入什么不要先陷入太细的动画效果很复杂的主题系统一次性铺满所有设计规范现在更重要的是让站点骨架先稳定。十五、第一版页面占位内容应该先写什么有了路由和布局以后下一步就可以让每个页面先写上最基础的占位内容。但这个“占位”不是随便打一行字。更稳的做法是让占位内容就开始体现页面职责。例如1. 首页先写什么可以先写网站主标题一句站点说明后面会接最近文章入口的提示2. 文章列表页先写什么可以先写列表页标题一句说明后面会接文章卡片列表的提示3. 文章详情页先写什么可以先写详情页标题占位当前路由会展示具体文章内容的说明4. 关于页先写什么可以先写关于我标题一段基础介绍占位5. 联系页先写什么可以先写联系我标题后面会接联系表单的说明6. 为什么这种占位方式更好因为它已经开始让每个页面的职责清晰起来了。后面你再往里填真实内容时会自然很多。十六、这一阶段先不急着做哪些细节虽然这一章已经开始搭站点骨架但有些细节现在还不需要急着做。例如导航高亮文章卡片完整样式详情页正文排版细节联系表单提交逻辑图片优化细节1. 为什么现在先不急因为这一章的目标是先让路由、布局和公共结构跑起来。如果这些地基还没稳就急着做太多页面细节后面很容易反复改动。2. 当前阶段最稳的节奏是什么先搭站点骨架再逐步把首页内容文章列表文章详情联系交互一段一段接上去。十七、第一版目录结构现在可以长什么样结合这一章的初始化和公共布局内容一个比较适合当前阶段的目录结构现在可以先长成这样src/ ├── app/ │ ├── layout.tsx │ ├── page.tsx │ ├── posts/ │ │ ├── page.tsx │ │ └── [slug]/ │ │ └── page.tsx │ ├── about/ │ │ └── page.tsx │ ├── contact/ │ │ └── page.tsx │ └── globals.css ├── components/ │ ├── site-header.tsx │ └── site-footer.tsx ├── data/ │ └── site-nav.ts ├── lib/ ├── types/ └── styles/1. 为什么这个结构已经很有价值因为它已经开始让几类职责分开了页面入口在app/公共结构在components/站点配置在data/工具函数和类型目录已经预留出来2. 当前阶段不需要把所有目录一次填满目录先留出来不代表现在每个目录都必须有很多文件。更重要的是你已经先把项目往清楚的方向搭起来了。十八、开发过程中怎么验证这一步有没有搭好这一章的成果其实很适合通过最基础的方式快速验证。你可以在项目启动后逐个检查这些地址//posts/about/contact如果这些页面都能打开并且都共用了同一个页头和页脚那就说明路由骨架和公共布局已经搭成功了。1. 当前阶段验证的重点是什么不是页面多漂亮而是地址能否对应正确页面布局是否统一生效页面主体内容是否能被children正常替换2. 为什么这种“小验证”很重要因为综合项目最怕的是一路猛写到后面才发现骨架一开始就有问题。现在这样小步确认会稳很多。十九、这一章最容易踩的几个坑这一节建议你认真看。因为博客项目刚起步时很多问题看起来不大但后面会反复拖慢你。1. 坑一项目初始化选项和技术路线不一致例如上一章明明决定先用 CSS Modules却创建时又把样式方案弄成完全另一套。这会让后面学习主线变乱。2. 坑二路由目录一开始就随手乱放这样后面一多页面就很容易不知道文件放哪儿静态页和详情页混在一起3. 坑三页头和页脚直接复制到每个页面这样短期看方便长期会非常难改。4. 坑四layout.tsx里塞太多页面专属逻辑布局层更适合放公共结构不适合一开始就混入大量页面独有内容。5. 坑五路由骨架没搭稳就急着做复杂样式和交互这样后面返工的概率会很高。二十、本章实践练习这一章的练习重点是把“博客项目第一版骨架”真正搭起来。1. 练习 1创建项目并跑起来请你用pnpm create next-app创建一个新的博客项目并确认TypeScript 已开启App Router 已开启使用了src/目录项目可以通过pnpm dev正常运行这个练习的重点是把技术选型真正落到一个可运行项目里。2. 练习 2搭出第一版路由目录请你根据这一章内容创建首页文章列表页文章详情页动态路由关于页联系页这个练习会帮助你真正建立页面清单要变成真实目录结构项目主线才算开始成立。3. 练习 3抽出SiteHeader和SiteFooter请你把公共导航和页脚分别抽成组件并通过layout.tsx统一挂载。这个练习会帮助你真正理解公共结构应该放公共层而不是写在每个页面里。4. 练习 4给所有页面先写职责占位请你让首页、列表页、详情页、关于页、联系页都先有一份最小占位内容。这个练习的重点是先让每个页面的职责站住再逐步填充真实内容。二十一、学习重点提示这一章请你重点记住下面这些话综合项目开始落地时最重要的不是马上写很多内容而是先把项目骨架搭稳。项目初始化、路由目录和公共布局是博客项目后面能不能写得顺的基础。第一版先把页面入口搭出来比一开始就死磕页面细节更稳。layout.tsx的核心价值是把公共结构统一挂起来让页面只关心自己的主体内容。children表示当前路由页面会被填进布局里的那块变化区域。导航配置、页头组件、页脚组件尽早抽出来后面会省很多重复劳动。骨架阶段先做占位页是一种非常有效的小步推进方式。先主结构再主内容再补交互和样式细节。如果你只记一句话请记住第五阶段综合实战真正开始落地时最关键的不是谁先写出最花的页面而是谁先把项目初始化、路由骨架和公共布局搭清楚。二十二、本章小结这一章我们正式把第五阶段综合实战从“需求分析与技术选型”推进到了“项目骨架真正落地”的阶段。你已经理解了为什么博客项目一开始要先搭骨架项目初始化时哪些选项更适合当前这次实战第一版页面清单应该怎样落成真实路由目录为什么文章详情页适合使用[slug]动态路由为什么页头和页脚应该尽早抽成公共组件layout.tsx和children在站点骨架里分别扮演什么角色全局样式第一版最值得先管哪些基础内容为什么这一阶段先做占位页是一个更稳的推进方式更重要的是你开始真正建立一种非常关键的项目意识综合项目不是从某个漂亮模块开始而是从项目地基开始。这一步非常关键。因为从这里开始你已经不只是在学 Next.js 的概念而是在开始真正进入个人博客项目第一版骨架的搭建阶段。二十三、课后思考题请你认真思考下面这些问题为什么综合项目一开始先搭路由和布局会比先写某个局部页面更稳为什么第一版博客项目适合先让每个页面“占住位置”为什么页头和页脚尽早抽成公共组件会更省事layout.tsx和children的关系最朴素的理解是什么为什么动态路由[slug]特别适合文章详情页为什么项目初始化阶段不适合一开始就做太多页面样式和交互细节你觉得当前这套博客项目骨架里哪一部分最容易在刚开始时被忽略为什么建议你把这些问题用自己的话写下来。只要你能把这些问题讲清楚说明你已经真正开始进入第五阶段综合实战的落地主线了。二十四、下一篇预告下一篇我们会继续推进第五阶段综合实战进入第五阶段综合实战博客首页、文章列表与本地文章数据接入到那时你会开始真正把这一篇搭好的路由和布局骨架填起来本地文章数据怎么组织首页怎么展示推荐内容文章列表页怎么渲染文章卡片内容页主线怎么真正开始跑起来也就是说下一篇开始我们会从“项目初始化、路由设计与公共布局搭建”继续走到个人博客项目第一版内容展示主线的真正实现。