工程实践
个人网站工程架构与长期可维护设计
这个站从零搭起来,分享我用 Nuxt 加 Spring Boot 把个人项目做成敢一直改下去的经过。
这个网站我从零开始搭,前前后后改了好几版。现在回头看,最想分享的其实不是"用了什么框架",而是怎么让一个个人项目别在三个月后就变成一堆没法碰的烂代码。
我见过太多个人站点:上线的时候很风光,过半年想加个功能,发现没人敢动了——到处是散落的逻辑、混乱的路由、全局样式互相打架。所以我从一开始就定了几个原则。
前端为什么选 Nuxt
技术选型上,我选了前后端分离:前端 Nuxt 做服务端渲染,后端 Spring Boot 分层,数据库用 PostgreSQL,中间 Nginx 反代。为什么选 Nuxt 而不是纯前端 SPA?因为内容站点最怕两件事:首屏白屏和搜索引擎不收录。
SPA 的痛点是内容要靠 JavaScript 在浏览器里现渲染,首屏慢不说,爬虫还不一定能执行到那一步。Nuxt 的 SSR 能把页面在服务端渲染成完整的 HTML 再返回,首屏快、爬虫能抓到、SEO 友好。它还支持 SSG(静态生成),对不常变的页面可以提前生成静态文件,配合 CDN 就是又快又省。框架自带的约定式路由、布局插槽,帮我省掉了大量重复的页面结构代码。
这里多说一句 SSR 的原理:页面在服务端先跑一遍组件,渲染出完整的 HTML 字符串返回浏览器,浏览器拿到手就是"所见即所得"的内容,然后再通过"水合(hydration)"把 JavaScript 事件绑定上去。这样既拿到了"首屏即内容"的速度,又保留了单页应用的交互能力。代价是会多一点服务端渲染的耗时——但对个人站来说,这个耗时完全可以接受。如果哪天流量大了,我还能把热门页面切成 SSG,进一步压缩渲染成本。
后端分层的意义
后端我坚持用经典的四层:Controller 只接 HTTP 请求和返回,Service 放业务规则,DAO 只写 SQL,Entity 描述数据库结构。谁越界谁负责,规则写清楚,加功能的时候就不会乱。接口统一返回 {code, message, data} 的结构,异常全部交给全局处理器,绝不让 Java 的堆栈直接抛给前端——不然前端写起来全是噩梦。
这套分层看着像老生常谈,但它是让项目"敢一直改下去"的关键。因为每一层只干自己的事,改一个接口的返回格式,不用动业务逻辑;换一个数据库方言,不用动 Service 层。耦合度降下来,迭代速度才能上去。连接池用的是 HikariCP,性能好、配置简单,默认就能扛住个人站的并发。
接口设计上我也定了几条约定:所有写操作都要校验参数(Bean Validation),所有查询都要做分页上限控制,所有返回都走统一的响应结构。这些约定看着琐碎,但它们是"接口一致性"的保证——前端不用为每个接口单独写适配,一次封装全局复用。
数据库层的小约定
数据库层我做了几件小事,但都很管用:所有内容表都有 id、slug、created_at、updated_at 和逻辑删除标记。逻辑删除不是偷懒,是防误删——哪天误操作了还能恢复。文章表再加上 status、published_at、reading_minutes,草稿和正式发布就能分清楚。
slug 字段也值得单独说:它是文章的 URL 标识,用可读的英文短横线,而不是数字 ID。这样链接更友好、更好记,迁移域名时也不用担心 ID 变动导致链接失效。列表页和详情页的查询,都靠 status + published_at 的联合索引,性能在个人站这个量级绰绰有余。
还有几个容易被忽略的细节:所有时间字段统一存 UTC,展示时再转本地时区,避免时区混乱;全文搜索我用的是 PostgreSQL 自带的 tsvector 全文检索,配合中文分词,个人站的内容量完全够用,不用额外引入 Elasticsearch 这种重型组件——这也符合"匹配业务体量"的选型原则。
发布流程和回滚
发布流程我也固定成了一条链:构建后端 → 备份旧版 → 上传新包 → 查健康接口 → 切前端 → 验证页面。发布失败必须能一键回滚到上一版,不能只记录一句"上线成功"就完事。部署用的 systemd 托管服务,Nginx 反代 + HTTPS,再配合定时备份,这套东西虽然不花哨,但很稳。
回头总结,个人项目的核心竞争力从来不是界面多好看,而是它能不能一直演进下去。架构规范、分层约束、代码范式统一,这些前期花的时间,会在后面每一次迭代里加倍还给你。对一个要长期维护的项目来说,架构的"可演进能力",比一时的功能多少重要得多。
如果你在这里有了 10 分钟的顿悟,那它折叠得刚刚好。
