技术博客周更十周复盘:静态站点、自动化部署与性能优化
第十周,技术博客写到第十周,算是一个不大不小的里程碑。很多人写技术博客都是三分钟热度,能连续更新十周,已经超过大多数人了。这篇第十期,我打算换个写法,不聊某个具体框架的API怎么用,而是把前九周从零搭建个人技术博客、持续写作和维护的完整过程做个阶段性复盘:内容怎么规划、技术栈怎么选、自动化构建怎么搭、线上遇到哪些坑。如果你也在做周更技术博客,或者准备自己搭一个用于写作记录的静态站点,这篇应该能帮你少走不少弯路。
先交代一下背景:我的博客是基于Node.js生态的静态站点方案,用Markdown写文章,通过本地脚本构建成纯静态HTML,再推送到托管平台。整套流程跑熟之后,每周的发布成本基本控制在三十分钟以内,其中大头是写正文,而不是折腾工具。第十周这篇正好可以当一份“技术博客基建实录”,从内容策略到技术细节,按我的实际踩坑顺序讲一遍。
1. 周更十周的定位与内容规划
1.1 为什么第十周值得单独写一篇
周更技术博客有一个很明显的特点:前四周靠新鲜感撑着,中间几周最难受,因为想写的题目都写得差不多了,后面就会开始怀疑“还有啥可写的”。到了第十周,其实是一个很好的时间节点,此时已经积累了足够多的文章,但还没到内容枯竭的绝望期,数据也能看出哪些主题更受关注,这时候做阶段复盘,性价比是最高的。
我在前九周里做过的事情大概可以分成这几类:环境搭建、性能调优、自动化部署、写作流程优化。这也符合个人技术博客的自然演进路径——先解决“能不能顺利写出来”,再解决“写出来好不好看”,最后解决“怎么高效地持续写”。第十周这篇,相当于把这几个阶段串起来做一次复盘,把零散的经验整理成一套可以复用的流程。
给同样在更新技术博客的朋友一个建议:不要盲目追求每天更新。日更对质量的损耗是肉眼可见的,而对个人技术博客来说,稳定一周一篇,每篇能解决一个实际问题,比日更水文章要有价值得多。宁缺毋滥,这话放在技术写作里太真实了。
1.2 前十周的内容结构与主题策略
技术博客最容易犯的毛病是主题太散,今天写Java,明天写Docker,后天写前端,每篇都像独立的小短文,串不成体系。我前几周也踩过这个坑,写出来的内容自己回头看都觉得零散。后来调整了策略,把内容按“一条主线串行推进”的逻辑来排布。
前九周我大致是这样规划的:
| 阶段 | 周次 | 主题方向 | 主要目标 |
|---|---|---|---|
| 起步期 | 第1-2周 | 开发环境与基础搭建 | 跑通基础流程,解决编辑器、命令行、版本管理 |
| 工具期 | 第3-5周 | 构建工具与模板系统 | 优化博客生成链路,统一页面结构 |
| 优化期 | 第6-8周 | 性能、SEO、访问速度 | 提升阅读体验和搜索引擎收录 |
| 实战期 | 第9周 | 完整项目复盘 | 把前面内容串成一个真实场景落地 |
| 复盘期 | 第10周 | 本篇 | 阶段总结、流程固化、问题排查经验沉淀 |
这样安排有一个明显的好处:每周的新文章都能基于上一周的内容往下推进,读者如果跟着一路看下来,会形成一个完整的技术脉络,而不是零散的知识点。对于写作的人来说,也不用每周挖空心思找选题,只要顺着主线往前推就行,压力小很多。
从后台评论和留言的反馈来看,大家最感兴趣的是自动化部署和构建脚本这两个方向,其次是问题排查实录。这说明读者看技术博客,要的是“可复现的解决方案”和“别人踩坑的教训”,而不是干巴巴的文档翻译。所以从第五周开始,我刻意加大了“实操步骤”和“踩坑记录”在文章里的比重,效果比纯讲原理好很多。
2. 博客技术栈的选型与调整
2.1 为什么从动态博客迁移到静态博客
最早我是在云服务器上搭动态博客程序来用的,数据库、后台管理、评论系统都有,确实功能全面。但用了不到两个月问题就暴露出来了:服务器要定期打安全补丁,数据库要备份,程序版本更新之后插件不兼容,还时不时被扫描工具盯上。对只想安心写文章的人来说,这些维护成本真的有点撑不住。
后来我算了一笔账:对一个以文字内容为主、日访问量也不会特别夸张的个人博客来说,动态博客绝大多数的功能都属于过剩。我们真正需要的只是“把Markdown写成HTML,然后让别人能访问”,这个需求用静态站点生成器就能完美覆盖。
动静态方案对比下来很直观:
| 对比项 | 动态博客 | 静态博客 |
|---|---|---|
| 部署复杂度 | 需要服务器、数据库、运行时 | 构建后只有纯HTML文件,随便扔到静态托管即可 |
| 安全性 | 暴露面大,需要持续维护 | 没有服务端执行逻辑,攻击面小很多 |
| 访问性能 | 每次请求动态渲染 | 直接返回静态文件,天然快 |
| 备份迁移 | 数据库结构复杂 | 就是一个文件夹,随时打包带走 |
| 写作体验 | 网页编辑器 | 本地Markdown,配合版本管理记录历史 |
所以我最终把博客切换成了基于Node.js的静态站点方案。选择Node.js而不是其他语言,主要是因为我对JavaScript生态更熟,而且npm里关于Markdown解析、模板渲染的轮子非常丰富,不用自己从零造。你如果更熟悉Python或Go,也可以选对应的方案,核心思路是一样的——重要的是理解“静态生成”这套逻辑,具体工具反而是次要的。
2.2 构建脚本的核心设计思路
静态博客的核心就是一个构建过程:把Markdown文件跑一遍,生成最终的HTML页面。这个过程中有几个关键节点需要处理:解析Markdown内容、读取每篇文章的元信息、把这些元信息注入到页面模板里、生成首页列表、生成标签归档页、生成RSS订阅文件。
我在调研了一圈之后,没有选用特别重的框架,而是选择了Eleventy这个工具来构建。它的设计很合我意,不强制绑定前端框架,输入输出就是一个Node脚本,模板语法也够自由。再加上Nunjucks做页面模板,两者配合起来就可以把整个博客的骨架搭得清清楚楚。
下面这段是我构建脚本里最核心的部分,虽然压缩过,但逻辑结构很清晰:
这段代码做的事情很直白:把源文件夹 src 里的Markdown内容交给Eleventy处理,经过模板渲染之后输出到 dist 目录。构建失败时立刻用非零退出码终止进程,这样在后续接入自动化流水线的时候,脚本一旦出错就能被及时发现。
构建脚本里还有一个容易被忽视的点:slug生成。每篇文章的URL是从Markdown文件名或者front matter里的slug字段转换来的,比如一篇名为 2025-01-12-build-static-blog.md 的文件,最终URL路径是 /2025/01/12/build-static-blog/。这种带日期的路径结构对技术博客非常友好,既能保证URL永久不变,也能让读者一眼看出文章的发布时间线。如果文件名改来改去,URL就乱了,搜索引擎也会很难受。
3. 本周核心实践:Markdown写作与自动化发布全流程
3.1 Markdown写作规范与模板设计
写技术博客最怕的就是每篇文章格式都不一样,改样式的时候改到你怀疑人生。我的做法是在博客项目里放一个文章模板文件,每次新建内容就直接从这个模板复制出来填。模板的front matter字段是固定的,构建和页面展示都依赖这些字段,所以格式一定要统一。
文章模板的元信息大致长这样:
这些字段缺一不可。tags 用来生成标签聚合页,summary 控制着首页列表和搜索引擎的描述信息,date 决定文章在时间轴上的位置,cover 控制分享时的缩略图。你可能会问,title 里已经有了标题,为什么summary还要单独写一遍?因为首页列表如果直接截取正文作为摘要,很容易截到半截代码或者不完整的句子,观感很差。手写summary虽然多花一分钟,但效果完全不一样。
具体的写作流程我现在已经形成肌肉记忆了:先在编辑器里建好带front matter的空文章,然后顺着“遇到的问题—排查过程—最终方案—效果对比”这个结构往下写。这样写出来天然就是一篇结构完整的博客,不需要写完再大规模调整。
3.2 图片资源与本地存储管理
技术博客离不开截图,但图片管理如果做得不好,后期维护就是灾难。前几周我的图片都是随手往 assets 文件夹里一丢,文件名是 截图12345.png 这种,后来找一张图要找半天。而且截图默认是PNG格式,动不动就几MB一张,传上去之后页面加载被拖得很慢。
现在的做法是这样:每篇文章在 assets 下建一个以文章ID命名的子目录,图片全部放里面。比如这篇文章的图片就放在 assets/post-010/ 目录下。这个目录结构在Markdown里用相对路径引用,比如 ,整个项目打包迁移的时候,图片跟文章一起走,不会出现图片丢在某个图床上下不来的尴尬。
图片压缩这块我用的是批量扫描加WebP转换的方案。构建的时候会对新增图片跑一遍压缩脚本,PNG/JPG统一转换成WebP格式,单张超过100KB的再额外压一档。这样处理之后,普通截图基本都能压到几十KB,页面加载速度会有非常明显的改善。具体操作上其实不用每张图都调参数,用默认的80%质量压缩,肉眼几乎看不出区别,体积能缩小一半以上。
3.3 从提交到发布:一条自动化流水线
以前发布一篇博客要走好几道手工步骤:本地构建、把dist目录传到服务器、还要手动清理缓存。这中间只要哪一步忘了,页面就会出现各种奇奇怪怪的问题。后来自从接入了自动化部署流水线,我把整个发布动作收敛成一个操作:本地 git push。
用到的自动化配置文件也很简单,核心逻辑就是在远程仓库触发构建任务,然后把产物推送到托管平台的对应分支。下面是我实际在用的部署配置,精简掉了项目专属的细节:
这个流水线解决的最大问题不是“省时间”,而是“标准化”。以前手动发布的时候,每次操作结果都可能不一样:有时候忘了压缩图片,有时候模板改到一半就传上去了。现在只要push代码,流水线就会自动执行一遍同样的流程,构建环境也是干净的全新环境,很少出现“在我电脑上是好的,部署之后就坏了”这种经典问题。第9周写复盘文章的时候,我还特意在流水线里加了一个手动触发按钮,这样有些不想公开的半成品文章,我也可以在预览分支上先看效果。
4. 性能优化与阅读体验调优
4.1 首屏体积控制与图片懒加载
静态博客虽然没有服务端渲染的压力,但不代表不需要做性能优化。如果首页一次性加载十几张未压缩的图片和几百KB的JavaScript文件,体验依然会很差。我给自己定的目标很简单:首屏完整显示控制在2秒以内,所有静态资源总体积控制在300KB以内。
具体做了几件事。首先是图片统一加上了 loading="lazy" 属性,让视口外的图片不急着加载。其次是全站JavaScript使用 defer 加载,不阻塞HTML解析。然后是CSS,把首屏用到的关键样式直接内联到HTML里,其余部分放到外部样式表。这三个操作都是“低成本高收益”的优化,不用改架构,就是配置层面的调整。
这里放一段首页图片的写法示例,注意 loading、width 和 height 这三个属性的配合:
width 和 height 一定要写上,这能避免页面滚动过程中图片加载完成后突然把内容往下挤的问题,也就是常说的“布局偏移”。很多新手会忽略这个细节,只加了懒加载,结果页面滚动时内容一直在跳,体验很差。现在我每次写完新文章,都会顺手在浏览器开发者工具里跑一下性能面板,看看有没有布局偏移警告,有就直接改掉。
4.2 全文检索与标签系统
博客文章写到几十篇之后,站内搜索就是一个绕不开的功能。没有搜索的时候,读者只能靠首页翻页或者搜索引擎站内搜索来找内容,效率太低。我调研了几个方案,包括接入第三方搜索服务,但考虑到技术博客的访问量没到那么大,而且第三方服务往往要引入外部脚本,反而拖慢加载速度,最终还是决定自建一套轻量级的本地索引。
做法其实很简单:在构建时生成一个 search.json 文件,里面汇总所有文章的标题、URL、摘要和标签,前端只负责在这个JSON里做关键词匹配。整个索引文件也就几十KB,对于个人博客这个体量完全够用。生成索引的脚本思路大致是遍历所有文章,把需要的字段提取出来塞进数组,然后写入到构建产物里。
标签系统的逻辑也类似。每篇文章的front matter里声明了tags数组,构建时自动扫描所有文章,按标签归类,生成独立的标签页。这一步如果用现成插件可以做,但自己写一遍之后,你对整个数据流会理解得更透彻:文章只是数据源,页面只是数据的表现形式,两者解耦之后,想加什么页面都很灵活。
4.3 数据统计与访问日志
做技术博客一定想知道自己的内容到底有没有人看,所以访问统计是刚需。但这个需求量级不需要特别精细的产品分析能力,我只需要知道三件事:每天大概多少访问量、哪些文章最受欢迎、读者主要从哪个渠道来的。
我没有接入复杂的数据分析平台,而是在站点里放了一个极简的计数脚本:页面加载时向后端接口发送一次访问记录,记录文章路径和来源页面类型。不会跟踪具体用户,不写任何形式的标识符Cookie,也不需要用户授权。这个方案虽然没有商业统计工具那么精细,但能覆盖日常观察需求,而且完全不拖慢页面加载,对读者隐私也友好得多。
隐私这件事多说一句,技术博客的读者大多是对技术敏感的人群,站点里堆满了各种第三方追踪脚本的话,观感会非常差。能用轻量方案解决的事情,没必要引入一大串几十KB的外部脚本。做技术博客,保持简洁本身就是一种专业态度的体现。
5. 常见问题与排查技巧实录
5.1 构建成功但线上页面空白
这个问题我前几周真实遇到过,而且是那种很诡异的“本地没事,一部署就白屏”。后来排查发现,问题出在模板语法上:Nunjucks模板里用了一个过滤器的参数写法,本地因为环境变量不同没有触发错误分支,而自动化构建是完全干净的环境,渲染时报错被静默吞掉,页面就空白了。
排查的思路给大家一个参考顺序:先在本地跑一次干净的构建命令,加上debug日志;再看构建产物的HTML是不是完整;最后用编辑器打开生成的HTML,直接搜关键正文段落,确认内容有没有被渲染进去。这套顺序能覆盖绝大多数“构建成功但结果不对”的问题。
从那之后我就给自己定了一个规矩:每次改模板或者构建脚本,都要顺手看一遍构建日志的warning信息,不要因为“反正构建成功了”就忽略警告。很多表面正常的问题,根源就是你忽略了那些非致命错误。
5.2 文章更新后线上一直没变化
这个问题的经典场景是:本地浏览器已经能看到新文章了,但读者那边访问还是旧页面。一开始我还以为是部署流水线的问题,反复排查之后发现是缓存策略导致的。托管平台的默认缓存策略会对一些静态资源做一段时间的长缓存,更新频率不高的博客页面很可能被缓存覆盖。
解决方法是调整缓存头和资源文件名策略。我的做法是:文章HTML页面设置很短的缓存时间,按需回源;CSS和JavaScript资源文件名加上内容哈希,内容变了文件名就变,这样既能享受缓存加速,又不会在更新后加载到旧版本。这里要说明的是,不同托管平台的缓存配置方式有差异,但核心逻辑都是一样的——区分“不会变的资源”和“经常变的资源”,给它们设置不同的缓存时间。
如果你也遇到了线上怎么刷都不更新的情况,先别怀疑部署脚本,用无痕窗口访问一次,或者用一个没打开过这个站点的设备测试,大概率就是缓存的问题。
5.3 文章摘要截断出现HTML标签
这是写博客过程中比较隐蔽的坑。之前我用的是从正文中自动截断前面多少字符来生成摘要的方式,结果有一篇文章因为第一段里包含行内代码,摘要截断的位置正好落在代码标记中间,导致首页上直接显示了一段残缺的标签代码,看着非常尴尬。
后来我把摘要策略改成了“手写为主,自动截断为辅”。每篇文章都在front matter里手写summary字段,模板渲染时统一做HTML标签转义处理。如果某篇文章确实忘记写summary,再用正文前多少个字符作为兜底,但必须经过转义过滤。
这里也提醒一下:如果你在首页模板里直接输出文章正文的第一段作为摘要,而正文又包含Markdown语法,那一定要确认你的模板引擎默认是否做了转义。没转义的话,用户看到的不只是难看,特殊字符还可能引发样式错乱。
5.4 第十周之后如何保持写作节奏
写了十周之后,我最大的体会是:技术的深度不是靠文章数量堆出来的,而是靠持续稳定的输出来积累的。很多人问我怎么做到每周都能有内容可写,我的回答是两条:一是准备一个灵感箱,平时看到有意思的问题、解决的一个Bug、同事问过的一个技术点,只要感觉值得写,随时扔进灵感箱;二是固定写作时段,到点就坐在电脑前,不追求一次写完,但保证每周至少动笔两次。
文章写到第10周的周期,我其实已经很少为“这周写什么”发愁了。灵感箱里还躺着十几个备选题目,排期已经排到了后面好几周。今天的复盘差不多就到这里,这个经验流程已经在实际跑题中验证过很多次,希望对同样在坚持写技术博客的人有一点参考价值。下周的选题,我已经想好了。