首页 / 资讯中心 / 文章详情

Caveman风格:零依赖手写HTML,回归原始的极简网页搭建

Caveman风格:零依赖手写HTML,回归原始的极简网页搭建 ★ FEATURED ARTICLE
Caveman这个词第一次被程序员圈子当成褒义词大概是从Caveman Debugging这个梗开始的——不用断点、不用调试器、不用任何花哨工具就在代码里塞几个print()一遍遍把变量打出来看。听起来很原始对吧但我在实际项目里待得越久越发现这种穴居人式的思维方式恰恰是现在最稀缺的东西。今天想聊的caveman不只是那个调试梗。它代表的是一整套退回最原始工具链、却依然把事办成的思路。这篇文章里我会先从穴居人调试法说起然后带你把一个零依赖、零框架、零构建步骤的Caveman风格网页从头到尾手搓出来再聊聊部署和性能收益最后把同一种哲学延伸到日常协作里。适合被复杂工具链折磨得头疼的前端新手也适合想给自己日常开发做减法、但一直没找到落点的人。1. Caveman不只是一个梗我为什么决定退回最原始的工具链先说结论我手头一个长期维护的个人项目从一堆框架、依赖、构建脚本最后被我精简成了一个纯静态目录整个站点加起来不到 30KB。速度、稳定性、可维护性全部变好了唯一变差的是说出去不够炫。这个项目一开始不是这样的。它用过主流的单页应用框架配了路由、组件、状态管理接了 CI 流水线光node_modules就有三四百兆。结果呢每次改个文案都要走一遍构建、走一遍缓存刷新时不时还要处理依赖升级带来的破坏性变更。最离谱的一次因为某个传递依赖的小版本更新整个构建流程挂了半天。后来我做了个很caveman的决定把这个项目重写成一堆纯 HTML 文件每个页面公共部分用脚本生成CSS 全部手写JavaScript 降到最低限度。做完之后整个维护体验完全变了。为什么原始反而更好这里有个特别容易被忽视的道理工具的复杂度是会复利的。每加一层依赖你就要为这一层持续支付认知成本——它怎么升级、它跟别的库怎么兼容、它有没有安全漏洞、它在新版 Node 上还能不能跑。这些成本平时不显眼但一出问题就是连锁反应。Caveman 风格不是不用工具而是只保留能自己完全理解的工具。如果哪天它出了问题我打开源码就能看懂不用去翻三层抽象之外的文档。这个项目让我重新审视了一个词原始。原始不是简陋是零件足够少坏了你自己修得了。就像一台老式机械手表和一个智能手表比功能完全比不上但它的每一个齿轮你都看得懂拆得开。说回主题。这篇文章后面要动手做的这个Caveman风格主页就是这种哲学的直接体现。它不用任何框架、不经过任何构建工具、不用图片字体图标库一个小编辑器和一台能跑静态服务器的电脑就能开始。2. Caveman DebuggingPrintf 大法为什么到今天依然能打穴居人调试法听起来像在骂人但它其实是我见过的最稳定的调试策略。所谓 Caveman Debugging就是在代码关键路径上插入输出语句比如 Python 的print、JavaScript 的console.log、C 的printf把中间变量值、函数调用顺序、分支走向全部打到终端里靠人眼看逻辑在哪一步断了。我第一次深刻理解这个方法是几年前排查一个诡异的内存相关问题。那个平台上的调试器支持断点、支持表达式求值理论上一应俱全但我加断点之后发现只要暂停在某个线程上程序的时序就会被改变问题就不复现了。一去掉断点问题又出现。折腾了一整天最后是同事默默在旁边加了几行日志把缓存命中、状态切换顺序打出来跑了三分钟就定位到了问题。从那以后我形成了一个习惯遇到诡异问题先往关键路径上写输出语句而不是急着开调试器。这个方法论有个很实在的优势它在任何环境里都能用。嵌入式设备上远程调试器可能连不上生产环境你不能随便挂断点断了进程就死了微服务之间的问题你总不能跑到对方节点上去断点。但日志不一样日志是零侵入的——它只在当前进程打几行字不断线程、不改时序大多数情况下问题该复现还是复现只是你多了几双看流程的眼睛。当然Caveman Debugging 不是万能药。如果业务逻辑极其复杂、状态非常多纯靠打印变量会非常低效这时候断点调试、录制重放这些高级手段还是更合适。我的实用标准是这样的场景推荐方式理由确定在哪段代码、但想知道它经过了哪些分支输出语句直接看流程成本最低问题只在特定数据、特定时序下触发输出语句不改变时序容易复现生产环境、不可随意重启服务结构化日志只能在现有日志框架下加信息无从下手、不知道问题跟哪段代码有关断点调试可以逐步探路需要查看调用栈和局部变量断点调试输出语句做不到有一点需要提醒你用输出语句调试千万别随手把日志留在生产代码里。我见过有人排查完问题明明删了临时日志但没重启服务结果生产环境在关键路径上打了一整天文本日志把磁盘塞满了。所以我的做法是临时调试日志里必须带上明显前缀比如DEBUG_TMP 排查结束全局搜这个前缀确认全部清干净再提交。3. 手搓一个纯 HTML 的Caveman风格主页零框架、零构建、零依赖下面进入正题。我们要做的这个穴居人风格主页不是说故意把页面做得像 1991 年那样只有黑底白字虽然不是不行而是说开发方式回到最原始的状态一个文件就是一个页面没有打包、没有编译、没有路由浏览器打开什么你就看到什么。3.1 先定规矩这种开发风格的约束条件既然要走 Caveman 路线那就给自己立几条军规后面所有代码都在这几条规矩下写不引入任何前端框架。你要会原生 HTML、CSS、JavaScript 的基础知识就够。不引入任何第三方 UI 库、图标库、CSS 框架。字体只用系统字体栈图标能找到 Unicode 字符就用字符找不到就不放。不配置构建工具。本地不需要 Node 环境跑开发服务器不需要 webpack/vite 做热更新文件改完刷新浏览器就能看到效果。网络请求越少越好理想情况是一个页面只发一次 HTML 请求CSS 可以内嵌也可以外链但不外链任何公共 CDN 资源。因为一旦某个 CDN 挂了你的页面也残了。这个约束的核心思想是每个字节你都认识每个文件你都知道它是干嘛的。3.2 页面骨架只用一个文件就能跑起来我先把完整的 HTML 文件贴出来这个文件没有任何外部依赖!DOCTYPE html html langzh-CN head meta charsetUTF-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title刀耕火种小站/title style /* 这里写 CSS后面会展开 */ /style /head body header nav a href#about关于/a a href#projects项目/a a href#reading阅读/a a href#contact联系/a /nav /header main section idabout h1Caveman Style Personal Site/h1 p这是一个刻意做旧的个人主页。没有框架打包没有 CDN 依赖没有花哨动画。/p /section section idprojects h2做过的项目/h2 ul li极简书签管理脚本/li li纯 CSS 图标集/li li离线版 SQLite 速查手册/li /ul /section section idreading h2最近在读/h2 p这类区块完全靠手写 HTML 维护增加的条目也只是复制一行 codelt;ligt;/code 而已。/p /section section idcontact h2怎么联系我/h2 p邮件是传统但可靠的方式。在社交平台上找人也行但回得慢别怪。/p /section /main footer p手工构建更新于 2026 年。/p /footer script // 这里写极少量 JavaScript后面解释 /script /body /html注意这个文件没有引入任何外部资源CSS 直接写在style标签里JavaScript 直接写在script标签里。你可能觉得这我也会写。对这就是重点。等页面多了我并不需要每次都手写全部骨架而是做一个小的生成脚本。但生成脚本是我的脚手架不是线上依赖——最终部署给用户的就是这一堆平铺直叙的 HTML 文件。这样即使生成脚本某天坏了我用手工也能修页面。3.3 样式部分没有 CSS 框架的排版怎么做接下来写 CSS。没有框架的情况下我推荐你先把排版调好再谈美化。所谓排版就是留白、字体、行高、间距这些主心骨。我的做法是先定义一个最小的设计变量但这次写在这个单文件里所以要限定在 CSS 自定义属性里:root { --bg: #f4f0e8; --fg: #2b2b2b; --accent: #8c4a2f; --line: #cfc8bc; --font: Helvetica Neue, Arial, PingFang SC, Microsoft YaHei, sans-serif; --mono: SFMono-Regular, Consolas, Liberation Mono, monospace; --max-width: 680px; } * { margin: 0; padding: 0; box-sizing: border-box; } body { font-family: var(--font); background: var(--bg); color: var(--fg); line-height: 1.75; padding: 2rem 1rem; } main { max-width: var(--max-width); margin: 0 auto; } header { max-width: var(--max-width); margin: 0 auto 3rem auto; } nav { display: flex; gap: 1.5rem; flex-wrap: wrap; border-bottom: 1px solid var(--line); padding-bottom: 0.8rem; } nav a { color: var(--fg); text-decoration: none; font-size: 0.95rem; } nav a:hover { color: var(--accent); text-decoration: underline; } section { margin-bottom: 3.5rem; } h1 { font-size: 2rem; margin-bottom: 0.8rem; letter-spacing: -0.01em; } h2 { font-size: 1.3rem; margin-bottom: 1rem; padding-left: 0.6rem; border-left: 3px solid var(--accent); } p { margin-bottom: 1rem; } ul { margin: 1.2rem 0; padding-left: 1.5rem; } li { margin-bottom: 0.4rem; } code { font-family: var(--mono); font-size: 0.9em; background: #e8e2d8; padding: 0.15em 0.4em; border-radius: 3px; } footer { max-width: var(--max-width); margin: 4rem auto 0; padding-top: 1.5rem; border-top: 1px solid var(--line); font-size: 0.85rem; color: #6a655e; }有几个地方值得展开说说为什么这么写第一max-width: 680px。这是从纯文本阅读体验里学来的。一行文字太长眼睛会累太短视线来回跳也很累。680 到 720px 是我试下来最舒服的区间。没有用栅格系统、没有用分栏整个页面就是单列上下滚动——这种方式在移动端和桌面端的适配都特别省心。第二系统字体栈。PingFang SC, Microsoft YaHei是给中文用户准备的优先用苹果和微软系统中文字体sans-serif兜底。这套字体栈不用从服务器下载任何字体文件每个用户看到的都是自己设备上渲染最好、最熟悉的字体加载速度天然快。第三用边框代替装饰图。h2左侧那条 3 像素的装饰线是自己给自己划分视觉层次的笨办法。我特意把它做成血橙色因为 Caveman 风格虽然原始但不是无聊。你完全可以根据自己的喜好改这个强调色。如果你想在这个阶段做点更有个性的效果我建议玩一个复古浏览器技巧给 HTML 加一个 CRT 屏幕的纹理遮罩用 CSS 背景渐变模拟扫描线。但我要泼一盆冷水这种效果在移动端和高刷新率屏幕上容易让人觉得头晕第一次做玩玩可以长期用真不建议。3.4 一点克制的 JavaScript只在真需要交互的时候动刚才的页面里我留了一个空的script标签现在来聊 JavaScript 该不该加。很多做过项目的人有个习惯先引入一堆工具函数或交互库再想这些交互是不是真的需要。Caveman 风格的思路正好反过来先想想哪个页面没有 JavaScript 还能不能用再用最原始的 DOM 操作补上它必需的部分。如果这个页面几乎不需要交互我的选择是不要 JavaScript。静态 HTML 文件又不是不能活。但如果页面有页头导航在移动端太长、要一个展开收起按钮这种需求那就用原生 JS 写个十几行的折叠效果nav idsite-nav button idnav-toggle aria-expandedfalse aria-controlsnav-menu菜单/button ul idnav-menu hidden lia href#about关于/a/li lia href#projects项目/a/li lia href#reading阅读/a/li lia href#contact联系/a/li /ul /nav script const toggle document.getElementById(nav-toggle); const menu document.getElementById(nav-menu); toggle.addEventListener(click, () { const expanded toggle.getAttribute(aria-expanded) true; toggle.setAttribute(aria-expanded, String(!expanded)); menu.hidden expanded; // expanded 为 true 时隐藏 }); /script这段代码没有任何依赖运行效率也够并且保持了无障碍的基本规范——按钮上的aria-expanded属性可以让屏幕阅读器正确读出菜单状态。这么多就够用了不要再加滑动动画、折叠动画、页面切换动画。如果你实在想要平滑过渡加一行 CSS 的transition: opacity 0.15s ease意思到位就行。我把 JavaScript 的使用原则总结成三条能不用就不用。这个页面是内容展示型用户来看的是文字和链接不是交互。用也只用到getElementById/addEventListener这种原生方法不引第三方封装库。所有 JavaScript 必须在页面拿到内容后渐进增强如果脚本加载失败或者被浏览器禁用页面本身依然是完整可读的。这其实用的是渐进增强思路我在折腾好几个项目后才真正体会到它的价值。3.5 在不同设备和浏览器上的表现写完这个页面我通常会在本地起一个静态服务器快速看一眼效果。不需要构建服务器只需要把工作目录作为静态资源根目录一条命令就行python3 -m http.server 8080然后浏览器打开http://localhost:8080。在 Python 没有装或者用的是 Windows 环境的情况下可以用npx serve不过那个会拉一点依赖——Caveman 风格要求比较高的话建议直接装一个 Python。我实测下来这个页面在以下环境的表现基本一致新版 Chrome、Edge、Firefox、Safari 桌面版都不需要加前缀就能正常渲染。手机上的 Chrome 和 Safari 也能自适应因为设置了viewport单列布局天然适配小屏幕。我甚至把它丢到一个老掉牙的 Android WebView 里测试过依然能正常打开渲染因为用到的都是十几年甚至二十年前就存在的 HTML/CSS 特性谈不上兼容性适配。这种兼容性不是巧合而是我选择的特性都足够老flex布局、::before伪元素、CSS 变量这个稍微新一点但它已经普及近十年了、hidden属性、原生 DOM 事件。这些技术经受住了时间的检验也扛得住尽量少的 bug这个朴素要求。4. 部署、缓存与性能极简页面能带来多实在的收益很多人小看了纯静态站点的收益觉得不就几个 HTML 嘛。等你真的布上去做对比测试才知道省了多少事。4.1 部署到哪静态托管是最省心的路纯静态页面的部署选择非常多这里我只说两种我实际用过的**方式一对象存储托管。**把整个目录拖进云端对象存储开启静态网站托管功能一个桶就是一个站点。这种方式的优点是自带 CDN 和 HTTPS缺点是控制台操作有点繁琐不过设置一次之后以后每次更新就是上传覆盖几十秒搞定。**方式二一台小内存服务器跑 Nginx。**如果你手头已经有一台虚拟机那就更简单了把 HTML 目录放到指定 web 根目录在 Nginx 里加一个 server 块server { listen 80; server_name caveman.example; root /var/www/caveman; index index.html; location / { try_files $uri $uri/ 404; } }这个配置不到十行就完成了站点服务。反观我以前用单页应用框架时的 Nginx 配置又要处理历史路由回退、又要配缓存策略、还得注意静态资源 hash 文件的位置复杂度根本不在一个量级。4.2 缓存策略没有构建也能精准过期静态站点最让我满意的是缓存控制非常简单。HTML 文件不希望被缓存太久因为内容更新靠它CSS 和图片如果以后有额外资源就放在另一个路径下用长缓存。在没有构建工具的前提下我通常用 Nginx 这么配location ~* \.(css|js|png|jpg|woff2)$ { expires 30d; add_header Cache-Control public, max-age2592000; } location / { expires 1h; add_header Cache-Control public, max-age3600; }没有文件名 hash 也没关系——因为 CSS 文件里内容更新时我只需手动改下文件名从style.css改成style-v2.css然后更新 HTML 里的引用。虽然这个操作听起来原始但它绝对有效而且完全可预测这就是控制感的来源。4.3 实际性能数据小占有、秒开不是玄学我拿一个真实站点做了一次对比测试改造之前那个站点的首页要加载五十几个请求总共 2.3MB 大小首屏完全渲染需要约 2.8 秒改造为 Caveman 风格之后首页只有一个 HTML 文件带内嵌样式和脚本请求数为 1大小约 28KB性能测试工具的加载时间稳定在 0.2 秒以内。每次看到这个对比我都觉得大部分页面压根不需要那些资源。它们只是被业界标配卷进来的——图标库、UI 库、动画库、前端框架、状态管理层层叠加最后真正为用户带来的价值可能和三十年前用表格布局做出来的页面没有质变。我不是说你所有项目都得退回原始时代。需要复杂交互的产品框架是合理的。但作为一个内容型、文档型、个人工具型的站点Caveman 风格的收益非常具体请求少、体积小、加载快、部署简单、没有框架升级的连锁爆炸不用担心某个依赖库被黑客投毒导致上游供应链问题因为你根本不从上游拉任何东西。5. 从网页到工作方式把Caveman哲学用到日常项目里这种退回原始工具链的思路一旦尝到甜头你会忍不住把它用到别的地方。我个人实践下来最有价值的是把 Caveman 哲学移植到了项目管理、文档维护和团队协作三个场景。5.1 项目管理用纯文本清单代替复杂看板有一次我需要管理一个只有三个人参与的小项目。按理说上个在线协作工具很正常但我发现那段时间维护工具比维护项目本身还累——按钮不知道被谁移动了自动化规则要写各种公式。后来我一怒之下建了一个纯文本文件叫todo.md# 项目清单2026-03 更新 ## 待办 - [ ] 首页文案第二版 - [ ] 接口超时重试机制 ## 进行中 - [ ] 用户反馈收集页 ## 已完成 - [x] 域名迁移 - [x] 基础缓存配置就靠这个文件加一个 Git 仓库三个人都能维护改起来还特别顺手。Markdown 本身就是一种原始格式没有服务端、没有权限控制、没有通知风暴但它的透明度和可控性对一个微型团队来说已经完全够用。5.2 文档维护手动更新比自动生成更可靠很多项目文档都上了一些自动化文档生成框架输入代码注释自动输出在线文档。听起来很先进但实际用下来常常是注释的格式改一次文档的目录结构就得跟着变某条接口改了参数生成脚本没跑文档和代码就悄悄不一致了。我后来在一部分项目里回到手写文档的路子每个模块一个 Markdown 文件版本是 Git 历史格式是最简单的标题加表格。不扯自动化反而减少了生成流程出错这个新的错误源也更符合 Caveman 哲学里只有完全理解每层才会用的原则。5.3 团队协作先开小会再写文档有句老话叫能用一句话说清的事不需要一段文档能用一段文档说清的事不需要一场会。在一个追求极简的团队里沟通方式也应该这样排序当面或者语音说一句解决百分之六十的误会。在聊天工具里发一段文字留个可搜索的记录。写进共享文档作为正式记录和长期参考。最后才开会而且开会时必须有结论、有下一步动作。这个顺序看起来简单但执行起来非常难因为人天然倾向于把事情复杂化。Caveman 哲学在这里的作用是逼你问一句这个动作真的必须这么重吗5.4 这种哲学的代价我主动放弃的那些东西讲完甜头也必须讲代价。Caveman 风格不是免费的它要求你自己搞定那些框架替你搞定的事。没有脚手架新建一个页面的重复劳动变多你需要靠自己的脚本或者模板来减轻。没有状态管理页面一多跨页面的状态同步全都得自己设计一般就是 URL 参数或者本地存储灵活度有限。没有组件库一个日期选择器都要从零写写错交互的例子比比皆是。所以我现在有一个判断标准**复杂度该放在哪里不取决于技术潮流而取决于项目的本质。**如果你的核心复杂度来自业务逻辑那使用框架帮你管理 UI 状态是合理的如果你的核心复杂度只是展示一些内容那么框架带来的额外复杂度就是你纯纯的负担你只是为了用而用在付账。我还发现在Caveman 风格里待久了的人会慢慢获得一种选型直觉遇到一个新的效率神器时先不去想它能帮我做多少而是去想它坏了谁来修、我能不能读懂它、它引入了多少看不见的依赖。这个直觉帮我躲开了很多不必要的坑也让我写代码时更有底气。最后再分享一个小技巧不管你是不是真的打算长期使用Caveman风格我都建议你偶尔用一个纯文本编辑器、不借助任何 AI 自动补全地去完成一个小页面或者抛开脚手架去写一个原型。这个练习的目的不是回到过去而是帮你重新校准那些被你当成理所当然的便利到底是真正需要还是仅仅因为大家都在用。等做完这件事你回过头再看自己手头那些非用不可的工具会发现至少有一半其实可以扔进岩洞里。
阅读完成 · 觉得有帮助?
咨询建站