
1. 项目到底是什么给网站做一次“全身减法”这两年我折腾了不少前端项目家里电脑也算不上新网络环境更是时好时坏经常打开一个资讯站都要等白屏好几秒。后来受“把复杂留给自己把简单留给用户”这个思路影响我着手做了个小网站项目起代号叫 Caveman——意思很直接像原始人一样做网页不追框架、不堆依赖、不算力过剩只追求“内容出来得快、页面老设备也能开、弱网下面子挂得住”。Caveman 不是什么需要安装的大型系统也不是什么黑科技框架。它是一个极端克制的纯静态站点方案HTML CSS 为主JavaScript 默认禁用、按需增强优先保证文本内容在任何环境下都能被完整读取。用它解决的是“网页越来越重、打开越来越慢、普通用户越来越没耐心”这个现实问题适合个人博客、文档站、产品落地页这类内容型项目也适合所有想把性能做到极致的前端开发者参考。做这个项目之前我先给自己定下几条规矩页面首屏尽可能在弱网下也能秒开全部资源总量控制在几百 KB 以内所有核心内容不依赖脚本渲染。为什么这么严因为你真去翻一遍访问日志会发现大量用户的设备并不差差的是信号的波动、缓存失效、CDN 节点绕路这些不可控因素而还有一小部分用户用的就是好几年前的千元机你引了 300 KB 的 JS 框架他那边光是解析脚本就要白屏一两秒。与其赌用户的设备和网络不如先把自己这边做到最轻。这篇文章我把自己从定方案、搭结构、写样式、做弱网优化到上线的全过程完整复盘一遍包括每个决定背后的理由、每次实测的数据、踩过的坑和排查思路。如果你是刚接触性能优化的新手可以当成一份“如何做极简站点”的入门教程如果你已经在做前端也可以把它当成一个另类思路的对照实验。2. 整体设计与技术选型怎么做到又旧又新2.1 不做复杂框架不是倒退是重新定价很多人一听“不用框架”第一反应是“这都什么年代了还在手写 HTML”。其实得把两件事分开看开发体验和用户体验。Caveman 的目标是用户体验最大化所以页面产物必须轻、必须快至于开发阶段用什么工具那是另一层问题Caveman 不排斥工具只是不让工具成为用户端必须承担的负担。我做这个项目时用了一点点现代化的构建手段用脚本做文件打包、压缩、清理缓存指纹但最终输出的仍然是静态 HTML 文件加少量 CSS、二三十行增强脚本。这样开发者写起来不累产物又干净。上线之后整个站点加起来不超过 300 KB这还是在包含了封面图和字体文件的情况下。核心原则是“按浏览器原生能力重新分配责任”能由 CSS 做的布局效果不用 JS能由 HTML 属性完成的交互不用自定义组件能被系统字体满足的排版不用 Web Font。不要小看这些选择它们省掉的不是几 KB而是网络往返次数和客户端的解析开销。2.2 页面结构的分层信息优先、装饰靠后传统页面往往是导航、弹窗、轮播、推荐位层层嵌套Caveman 的做法正好反过来没有一个组件是必须存在的。页面分成了三层内容层包含标题、正文、图片、链接辅助层包含目录、上一篇下一篇、标签页装饰层包含主题切换、动效、背景纹理。这三层的优先级递减一旦资源紧张装饰层先被砍掉辅助层可以折叠隐藏内容层无论如何都必须完整呈现。这种分层策略实操时很有用。我先把页面里的所有模块列出来然后问自己一个问题如果用户只能看到三个信息我希望他看到哪三块答案通常是标题、正文主体、文章导航。其他东西都能后置。基于这个答案我决定整个页面首屏只有文章内容和极其简单的头部评论区、侧边栏、相关推荐这些一律放到文章底部或者干脆不做。2.3 JavaScript 策略默认禁用按需增强Caveman 对 JavaScript 的态度可以用一句话概括不是不用而是默认不用。基础页面必须做到“关闭 JS 完全可读可点”在这个前提下脚本只负责做渐进增强。我实现了一个非常小的增强层用来判断浏览器是否支持某些特性再决定要不要给某个区域挂事件、加样式类。这样的策略有几个明显的好处。第一搜索引擎抓取时不用等脚本执行抓取到的就是实际内容第二用户在弱网下打开的是最核心的信息不会被加载器挡住第三一旦某个脚本出错影响范围只是某个小功能而不是整页白屏。代价是我写交互时不能像以前那样随意先想清楚这个交互的必要性再动手写代码。2.4 CSS 方案的克制系统字体、单层布局、有限动效字体这块我纠结过一段时间。此前做项目习惯了引入字体库和可变字体文件但后来我测了一次一个完整的中文字体子集动辄几百 KB不管怎么压缩都是一笔不小的开销。最终我选择用系统字体栈通过 font-family 设置界面字体用户端完全不用下载字体文件界面看起来不仅清爽而且在中低端设备上渲染速度明显更快。布局方案我也做了限制以单列阅读流为主只在屏幕足够宽时提供一个简单侧栏。原因是页面渲染时单列布局只产生一次回流也不需要复杂的网格计算对浏览器的排版压力非常小。动效就更克制了我只保留两个过渡效果链接 hover 时的颜色变化和代码块的轻轻高亮都是纯 CSS 实现不触发重绘链。别小看动效的消耗老设备上大量的 box-shadow 和 transform 过渡会让页面滚动变得很卡。3. 核心细节拆解每一 KB 都算账3.1 图片与字体最大头的两个优化目标任何一个页面体积大头基本逃不开图片和字体。Caveman 的做法是图片一律使用现代格式优先降级策略能转 WebP 的都转 WebP不支持 WebP 的老浏览器回退到 JPEG同时控制一张正文配图的宽度上限超过上限就只显示裁剪版本点击才能看原图。图片加载采用懒加载属性但首屏第一张图必须直接加载避免页面全空。字体方面因为用了系统字体栈字体文件直接归零。如果你的项目确实需要特殊字体我的建议是使用字体子集工具只保留中英文常用的 2000 到 3000 个字符再配上 font-display: swap让文字先显示出来、字体文件后台补上。这两条经验在正式环境中改善效果极显著纯文字页从 600 KB 降到 40 KB带一张压缩图的页面也不到 200 KB。3.2 资源优先级与资源提示浏览器替你干活静态页面的一大优势是结构确定浏览器完全可以在 HTML 解析阶段提前知道有哪些资源要加载。我做了三件事给首屏 CSS 加上 preload让它第一时间加载给首屏首图加上 fetchpriorityhigh其他图片一律不加这个属性让浏览器自己判断最关键的一张正文图会使用内联尺寸设置预留占位空间避免加载完成后页面跳动。资源提示Resource Hints我也用了但没有滥用。只有用到站外字体或第三方脚本时我才会加 preconnect平时一个都不加。早前我习惯把预加载写得很激进结果各种优先级抢占首屏反而变慢后来才意识到资源提示的目的是辅助优化不是越多越好。3.3 构建流程与 CDN 配合让文件小且逼近用户Caveman 的构建脚本是用 Node 写的一个短脚本做的事情就三件把 HTML 里的 CSS 内联合并、压缩 HTML 标签之间的空白、给静态资源文件名加上内容哈希。这样上线后只要文件内容变了文件名就变浏览器不会错误地用到旧缓存。CDN 选了按区域和按运营商都能自选节点的服务配上强制缓存策略JSON 和图片这类不常变的资源缓存 30 天HTML 文件不设长缓存但设 etag每次请求校验是否更新。这一套下来第二次访问的响应时间基本在 100 毫秒内弱网环境下的重复访问也稳定很多因为缓存命中后根本不走真正的网络传输。3.4 可访问性兜底极简不代表忽视所有人有句话我一直很认同性能优化做到最后是在做可访问性。Caveman 虽然简陋但还是把无障碍基础打牢了。所有图片都有描述性 alt 文本所有链接都有清晰的文案而不是“点击这里”站点的 HTML 结构使用 h1、article、nav 等语义化标签屏幕阅读器可以直接按标题跳转。键盘操作方面焦点样式没有被去掉我用 :focus-visible 做了更明显的指示线。由于大部分交互本来就不依赖脚本这些无障碍检查几乎不用额外工作量但能让一批特殊用户顺畅地使用站点这比任何花哨功能都值。4. 实操过程复盘从写 HTML 到最终上线4.1 第一版先让一个页面在本地 10 秒内打开我第一步没有急着写全套页面而是先搭一个单页的骨架一个标题、一块正文、一张图、两个关联链接。这个页面的源 HTML 手工写大概 5 KB 左右加上一点点基础 CSS总共不超过 12 KB。目标是在本地开发服务器上禁用缓存后用浏览器的弱网模拟慢速 3G 环境打开它必须做到 10 秒内完成加载。有人可能会说12 KB 页面在慢速 3G 下当然快这算什么挑战。但关键点在于这个目标定义的是所有后续页面的底线一旦后面加了更大的模块优先保住这个底线。我后面加搜索功能、加评论占位、加文章归档时一直在反复问自己新增的这部分是否破坏了首屏加载的最核心目标。4.2 写样式与弱网实测模拟比想象中更重要接下来我花了整整一个晚上完善 CSS但整个样式表最终只有不到 4 KB。写样式时我坚持了两个细节第一所有颜色只用十六进制或 CSS 变量统一管理不给每个规则重复写颜色值第二所有选择器尽量控制在两层以内不写极端嵌套。这样虽然看起来不够“格调”但在老设备的排版速度和后续维护上都省力。完成后我做了弱网实测用 Chrome 开发者工具模拟 Slow 4G并把 CPU 降速设为 4 倍。结果是首次加载 HTML 和 CSS 总共花了 1.2 秒页面完全可读首屏图片在 3 秒时出现关闭 JS 以后页面内容和样式完全一致只有一个小交互失效。这个结果让我非常安心因为即使 CDN 节点临时抽风、用户信号差最基本的阅读是能满足的。4.3 性能预算与自检清单让优化可量化没有预算的优化是没有终点的我给 Caveman 设置了一条硬预算线全站任意页面总资源不超过 350 KB页面中不允许出现超过 100 KB 的单个 JS 文件或样式文件DOM 节点总数控制在 600 以下。每完成一个页面我都会用一个脚本自动抓取页面并统计资源体积超过预算就在构建时报错强制我回头砍东西。这招很有效。有一次我随手加了一个外链字体构建脚本马上报了预算超标我才想起之前用系统字体时的承诺。如果靠人肉去检查大概率就偷偷放过去了。这种自动化的性能门禁才是项目能长期保持简洁不受诱惑的根本保障。4.4 实际测试数据记录从数据里读懂问题上线后我用工具做了三轮测试记录这些数据不是为了炫耀而是让你知道可预期的效果。首屏加载时间4G 网络稳定状态下约 0.6 秒3G 弱网约 1.5 秒全站体积首页 210 KB文章页平均 150 KBLighthouse 性能得分移动端是 99 分无障碍 100 分。对比改造前的同类项目动辄 2 MB 的体积、5 秒以上的白屏差距非常明显。令我意外的是访问行为数据也发生了变化。页面的平均停留时长从之前同类页面的 30 多秒提升到了 50 秒左右跳出率从 55% 降到 40% 上下。我推测原因是页面加载变快后用户还没到“不耐烦离开”的那个点就已经进入阅读状态了这比任何转化率插件都更有说服力。5. 常见问题与排查技巧实录5.1 常见坑位速查极简项目也会踩的雷别看页面简单坑一点不少。我整理了一份速查表都是实际踩出来的经验拿走基本能直接套用问题现象根因解决方案首屏图片总是加载很慢没有限制图片宽高浏览器不知道预留多大空间给 img 加 width、height 属性或 CSS 设置宽高比例页面文字加载时闪烁跳变引用了网页字体且未设置回退使用系统字体栈或给 font-face 加 font-display: swap弱网下页面停在空白很久有脚本阻塞了解析或存在同步加载的第三方资源脚本加 defer第三方资源改成按需异步加载点击链接时卡顿明显页面渲染了过多 DOM或存在大量复杂阴影/滤镜减少冗余结构用普通矩形阴影替代特效静态文件更新后用户还在看旧版文件名没有内容哈希缓存无法感知更新构建时给文件加哈希同时配合 CDN 强制缓存5.2 旧浏览器的兼容教训不是所有设备都懂得新属性我在测试时专门开了几款老浏览器的模拟环境发现在极简页面上照样会出问题。最大的坑是 CSS 的 gap 属性在老浏览器里不生效我的导航和列表布局一下子全挤在一起。解决方式是写一个回退方案先用 margin 来模拟间距再写 gap 覆盖新浏览器的样式。这种做法叫“渐进增强式的回退”不复杂但得养成习惯。另一个老浏览器问题是懒加载属性 loadinglazy 在部分老内核里不支持虽然大多数现代浏览器没问题但为了稳妥我给图片加载逻辑加了一个基于 JS 的小判断如果浏览器不支持原生懒加载就切换成一个非常轻量的滚动监听脚本。问题不大但说明一个道理极简不等于不做兼容而是把兼容成本压低到可控范围。5.3 JS 增强开启时要盯着的雷区别把增强做成负担我所说的“默认禁用、按需增强”在实际实现时有几个必须要盯住的点。首先是增强脚本必须放在文件末尾或者用 defer 加载不能影响页面主要内容解析。其次是增强逻辑必须围绕已有 HTML 结构做监听而不是动态生成整个界面否则一旦脚本加载失败页面就完全不可用。最后是要防止脚本运行时对布局造成剧烈变化比如动态往正文里插入大块内容会导致页面跳动。我在接一个“文章目录自动高亮”的小功能时差点犯了这个错。目录高亮本身很轻但如果脚本在页面加载完后再去扫描所有标题、生成目录、插入到侧栏就会造成明显的布局位移。最后我把目录预生成在 HTML 里脚本只负责监听滚动位置、切换高亮类名这样既保留了功能又没破坏加载性能。5.4 后续扩展路径Build 更多东西时如何不亏本Caveman 做完后我一直在思考后续扩展的方向总结下来几条都是在不打破性能预算前提下的可行路径。第一是评论系统可以接入外部服务但评论内容用 iframe 嵌到文章底部不参与首屏加载第二是站内搜索我用一个极小的 JSON 索引文件配合关键字匹配搜索页单独生成不污染文章页第三是 RSS 订阅这个不用任何脚本天然适合静态站点。我个人最想做的其实是增加“阅读进度条”和“深色模式”两个小功能。阅读进度条只需要监听滚动事件在当前文档上画一条细线即可深色模式则用 CSS 变量切换脚本只有几行。虽然它们不改变极简原则但都是典型的“按需增强”是在保证核心性能前提下提升体验的优质扩展。我也打算把构建脚本再完善一下比如加入 HTML 压缩、图片自动压缩的更多参数选项继续压榨不必要的开销。这个项目让我最大的收获是性能优化不是靠某一个神操作一步登天而是把“少即是多”的理念贯彻到每一个选择里。每次决定要不要加一个功能、引一个库、放一张图我都会先想想它值不值得用户多等那几百毫秒。正是这种“先否定、再确认”的习惯让 Caveman 在资源和体验之间找到了一个非常舒服的平衡点。