今天想认真聊一聊 caveman 这个项目。它不是什么惊天动地的开源框架,也不是某个新冒出来的AI工具,更不是只有技术极客才能看懂的高深概念——恰恰相反,caveman 是我给自己的一套极简实践起的代号。穴居人,原始,简陋,返璞归真。有一次我折腾个人网站,本来计划上框架、上数据库、上打包工具,做到一半突然觉得不对劲:我不过是想发几十篇文字,为什么要养一堆依赖?最后我把整个项目砍成一个单文件 HTML,竟然比原先那个“完整方案”更顺手。之后我把这套思路搬到了笔记、文档和日常任务里,发现很多看似复杂的事情,最笨的办法反而是最快的。这篇就是那次实践的全部记录,包括操作步骤、踩过的坑,以及后来一步步总结出的避坑清单。
如果你正被各种工具、框架、平台折腾得头大,或者只是想做一个能稳定运行很多年的小网站,这篇文章应该能帮上忙。你能看到:单文件 HTML 怎么做出一个真正能用的个人网站,怎么把它发布到公网,遇到乱码、缓存、手机适配这类问题时怎么快速排查,以及这套“返璞归真”的工作方法怎么扩展到日常任务里。没有编程基础的人照着抄也能完成,有经验的人则可以当一份避坑清单来看。
1. Caveman 是什么:一个代号背后的极简主义
1.1 为什么叫“穴居人”
起这个名字不是一时兴起。穴居人给人的第一印象是原始、工具少、日子过得糙,但换个角度看,他们恰恰是“用最少资源解决生存问题”的高手。现代数字生活则相反,工具多到人成了工具的附属品:写一篇文章要先想用哪个编辑器,做个页面要先想用哪套框架,存个笔记要先想数据放在哪个平台。工具越来越多,产出却越来越难。
caveman 这个代号就是想提醒我:真正重要的从来不是工具的多少,而是你用手头那点东西能做出什么。一台电脑、一个编辑器、一个浏览器,在很多场景下已经足够。别急着往项目里加东西,先问自己一句:“不加这些,是不是也做得到?”
1.2 Caveman 要对抗的真正问题
我们要对抗的不是框架本身,而是“不必要的复杂度”。我自己就干过这种蠢事:想写个十几篇文章的博客,结果先搭了个脚手架,装了打包器,接好了数据库,还要处理编译报错。等到真正坐下来写文章时,我已经被环境配置磨掉了一半热情。
做个对比你就有感觉了:
| 传统方案 | Caveman 方案 |
|---|---|
| Node.js + 框架 + 打包工具 + 数据库 | 单文件 HTML + 静态托管 |
| 每次更新内容都要跑一遍构建流程 | 改动文字,直接覆盖文件 |
| 换台电脑先装一堆环境才能开工 | 任意浏览器打开就能看 |
| 依赖几十个第三方库,随时可能有安全公告 | 零依赖,没有攻击面 |
| 一个版本升级可能牵连整个项目 | 文件和它自己,自成一体 |
这不是说传统方案一无是处,而是说很多场景根本不值得付出这些成本。caveman 的立项逻辑就一句话:先判断这件事是不是“杀鸡用牛刀”,如果是,赶紧收手,换最笨的方式做。
1.3 Caveman 的边界在哪里
也得说清楚,caveman 不是万能的。需要用户登录、需要实时多人协作、需要处理大量结构化数据的产品,不该硬塞进一个单文件里。这是工具选择问题,不是立场问题。我的做法是每接到一件事,先分个类:它本质上是“展示内容”,还是“处理交互”?前者非常适合 caveman 思路,后者还是老老实实选成熟方案。
这种边界意识,反而让 caveman 这个极简方案变得可靠。知道自己能做什么、不能做什么,才不会翻车。
2. Caveman 核心方案:如何用单文件 HTML 做一个能用的网站
2.1 为什么单文件可行
早期我会觉得一个网站至少要有个服务器、一套后台,后来才意识到,对内容型页面来说,浏览器就是最好的运行时。一个 html 文档就能承载文字、图片、样式和交互逻辑,现代 CSS 已经把排版布局全包了,原生 JavaScript 也足够处理绝大多数轻交互。你把文件保存在本地,双击就能浏览;上传到任意静态托管,全世界就都能访问。
我常打一个比方:国宴确实需要一整套专业厨房,但平时一个人做饭,一把好刀加一口锅就够了。caveman 单页网站就是那把刀和那口锅。它丢掉的东西,是过去为了应付极端复杂场景而准备的装备;它留下的东西,恰恰是内容最需要的安全、轻量和可复制。之后的实践证明,一个展示性质的小站用这种方式维护,远比想象中省事。
2.2 从零写一个最小的 Caveman 单页
直接上代码。这就是一个可用的 caveman 单页完整代码,不需要安装任何东西,新建一个index.html,把内容粘进去,浏览器打开就能跑:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1"> <title>Caveman 单页示例</title> <style> :root { --bg-color: #f7f7f5; --text-color: #222222; } body { font-family: system-ui, -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif; max-width: 42em; margin: 4rem auto; padding: 0 1.2rem; background: var(--bg-color); color: var(--text-color); line-height: 1.8; } a { color: #b45309; } pre { background: #f0efe9; padding: 1rem; overflow-x: auto; } @media (max-width: 600px) { body { margin: 2rem auto; } } </style> </head> <body> <h1>你好,我是 Caveman</h1> <p>这是一整份网页的全部代码:样式、内容、脚本都在同一个文件里。</p> <article id="content"> <h2>写点什么</h2> <p>以后要改文字,直接改这里。</p> </article> <button id="toggle">展开/收起</button> <div id="extra" hidden>这里是原生 JavaScript 控制的交互区域。</div> <script> document.getElementById('toggle').addEventListener('click', function () { var extra = document.getElementById('extra'); extra.hidden = !extra.hidden; }); </script> </body> </html>每一行都值得解释一下。lang="zh-CN"告诉浏览器页面是简体中文,对读屏软件和搜索引擎都更友好;<meta charset="utf-8">防止中文变成乱码,必须放在 head 靠前位置;viewport是手机端适配的关键,少了它,手机上看页面字会小成一团。
样式部分我用了一个 CSS 变量--bg-color和--text-color,之后想换成暗色主题,只需要改这两个值,不用全局找颜色。字体栈从系统字体开始,往后逐个降级,这种写法能保证在不同设备上都有不错的观感。max-width: 42em限制行宽,阅读长文时眼睛不容易疲劳。
交互部分我没有引任何库,一个原生事件绑定就够用。hidden属性在 HTML 里本来就存在,配合 JavaScript 改一下它的值,收起展开就完成了。这种效果放在大多数博客上都够用。
2.3 把内容当一等公民:如何把单页扩展成博客
单文件直接当博客用,完全可以。我的做法是让一个文件对应一篇文章,文件名就当标题;再维护一个最简单的索引页,罗列文章链接,本质上是静态网站最初的样子。
如果觉得每篇文章都复制一整份公共头尾太麻烦,也别急着上框架。写一个十几行的本地脚本,做字符串替换就能完成模板拼接。比如建一个template.html,里面留好<!-- TITLE -->和<!-- CONTENT -->占位符,然后用一段脚本把文章正文填进去,输出成品页面。这种“半自动”方式既没有引入外界依赖,又剩下不少手工活。
还有一个 Caveman 思路的变体:正文用 Markdown 写,日常编辑和阅读体验都不错;发布之前用一个命令把 Markdown 渲染成一个独立 HTML 文件。这里不要求编辑器实时预览,因为绝大多数时候,你只需要做好一件事——把内容写出来。
如果只有一篇文章,连脚本都省了。复制模板,改掉标题和正文,保存,上传,完事。这套流程的最大特点,是你可以完全掌控自己的内容,没有平台锁,也没有格式绑定的风险。
2.4 加一点视觉体验,但不要过度设计
很多人一提到做页面,脑子里就冒出各种动效、渐变色、粒子背景。我劝你缓一缓。caveman 不等于丑,它只是把视觉优先级放得靠后。先把内容排清楚,确保任何人都能顺畅读下来,再考虑主题配色。
CSS 变量很适合做这件事。你可以在:root里定义一套浅色默认值,然后写一套深色变量覆盖方案:
:root { --bg-color: #f7f7f5; --text-color: #222222; } @media (prefers-color-scheme: dark) { :root { --bg-color: #1a1a1a; --text-color: #f0efe9; } }prefers-color-scheme是 CSS 里非常实用的特性,系统开了深色模式,页面就自动切换,完全不用 JavaScript 参与。字体、标题、链接、代码块这几个元素的样式定好之后,整站基本就成型了。不要为了丰富视觉而加无关元素,空白和克制本身就是设计。
3. 从单文件到上线:Caveman 的完整发布流程
3.1 上线前做一次“单手测试”
页面写完先别急着发布,我最常提醒别人的一句话是:先把自己当“完全不懂技术的人”测一遍。双击打开本地文件,如果这一步就是打不开、乱码或者布局稀烂,那后面多少优化都白搭。
我列了一个简单的发布前检查清单,每次上线前都照着过一遍:
| 检查项 | 说明 |
|---|---|
| 中文内容 | 无乱码,文件编码统一为 UTF-8 |
| 手机布局 | 缩小浏览器窗口或直接用手机浏览,没有横向滚动条 |
| 标签闭合 | 所有 div、article、section 都有结束标签 |
| 图片体积 | 图片已经压缩,不需要加载几 MB 的“原图” |
| 链接有效性 | 页面里每个链接都点一遍,确认没有 404 |
| 主题适配 | 浅色、深色模式都看一眼,文字可读 |
| 脚本报错 | 浏览器控制台没有红色报错 |
这一套检查做完不超过十分钟,却能避免大量“上线后被人指出问题”的尴尬。特别是移动端适配,很多人开发时只在电脑浏览器里看,发布后才发现手机上字号、间距全都怪怪的。其实只需要打开开发者工具,切换到手机尺寸预览几秒钟,就能解决大半问题。
3.2 发布到公网:最接地气的三种方式
文件准备好了,接下来是怎么让别人也能访问。不需要浮夸的方案,静态文件放在哪都行。
第一种,你有服务器的话,直接把index.html放到站点目录下,再配一段最简单的 Nginx 配置:
server { listen 80; server_name yourdomain.example; root /var/www/caveman; index index.html; }保存配置文件,执行nginx -t检查语法,然后nginx -s reload重载,页面就能访问了。root指向的是文件所在目录,index指明默认首页是index.html。如果服务器上有安全组或防火墙,记得放行 80 端口,不然别人访问时会有奇奇怪怪的超时。
第二种,没有服务器,用对象存储的静态网站托管。流程都差不多:建一个公开读的桶,开启“静态网站托管”功能,把index.html传上去,再绑定自己的域名。这种方式的好处是无需关心服务器运维,访问量大了也能自动扛住。
第三种,如果你日常会用 GitHub 或同类代码托管平台,那上面的 Pages 功能也能直接托管静态文件。把index.html一推,平台自动生成访问地址。这个方式适合文件已经放在代码仓库里的情况,很方便做版本管理。
无论用哪种方式,发布这件事不应该成为负担。caveman 强调的就是:从“写完”到“上线”,距离越短越好。
3.3 上线后的最小维护工作
静态单页的维护,本质上就是“更新文件”。今天想在首页加一篇文章,两个方式任选:如果文件在服务器上,直接 ssh 进去改文本;如果在对象存储或代码托管平台上,下载、改好、传回,或者直接在线编辑器里改。
这里我有一个从实践中养成的习惯:每次改版前,先把旧文件复制一份,文件名带日期,比如index-20250425.html。这样做的好处很直接——哪天改坏了,大不了把这个旧版本恢复回来。不要依赖“我我记得原来是什么样”,版本备份是性价比最高的保险。
“维护”听上去像是个苦差事,但对于 caveman 单页来说,它真的只是改文字而已。没有依赖升级的连环炸弹,没有框架停更的焦虑,页面文件自己就构成一个完整世界。哪怕三年后再打开,内容和当初一模一样。
4. Caveman 实践中常见的坑与排查方法
4.1 中文乱码问题
这是新手最容易遇到的坑,也是最容易解决的一个。页面里如果没有<meta charset="utf-8">,浏览器可能会按系统默认编码去猜,中文就变成了一堆乱码。另一个隐蔽原因是文件本身存成了 GBK,虽然页面声明了 UTF-8,两者对不上,照样乱。
解决办法很简单:编辑器保存文件时,统一选择“UTF-8 无 BOM”编码;HTML 里把 charset 声明放在 head 最前面;假如服务器返回的响应头也带 charset,尽量保持和文件一致。遇到乱码时先看这两处,九成问题能解决。
4.2 改了内容,浏览器里却不显示
这种是缓存搞的鬼,而且几乎人人都会遇到。第一次访问页面,浏览器把 CSS 和页面内容缓存了;你再更新文件,重新打开,浏览器觉得本地缓存还“新鲜”,就直接拿缓存展示,改动自然看不到。
经验做法是:本地调试时按 Ctrl+Shift+R 强制刷新;正式发布时,给资源链接加上版本号,比如style?v=20250425。不要小看这个动作,它能让你处理线上问题时省下一大笔反复确认的时间。
<link rel="stylesheet" href="style.css?v=20250425">文件每次更新,把版本号改一下,浏览器就会当成新资源去加载。文件引用得少时,这一招尤其好使。
4.3 手机端打开,布局一团糟
相当一部分原因是少了viewport这个 meta 标签。没有它,手机浏览器默认把页面按桌面宽度渲染,用户看到的是一堆挤在一起的小字。加上<meta name="viewport" content="width=device-width, initial-scale=1">,页面就会按设备宽度缩放。
布局上也有讲究。我建议所有宽度限制都用max-width而不是固定的px;文字大小用rem而不是px;图片给max-width: 100%。这样各种屏幕尺寸下都能自然缩放,不用写一堆媒体查询去过适配。
4.4 点了按钮没反应,交互失灵
原生 JavaScript 交互出问题,先打开浏览器控制台看有没有红色报错。常见原因大概三个:脚本写在元素前面,结果绑定事件时元素还不存在;某个 id 重复定义了,getElementById找到了多个或找不到;变量名拼写错误。
最简单可靠的方案,是把<script>标签放在 body 末尾,确保脚本运行时页面里的元素都已经解析完。如果用了多个函数,多加点console.log打印,一段段缩小问题范围。caveman 方案里脚本量通常很小,这个级别的排查就够用了。
4.5 页面加载缓慢,图片拖后腿
单文件页面理论上加载非常快,慢一般慢在图片和外部资源上。很多人直接把手机拍的照片传到页面里,一张原图几 MB,再快的网络也扛不住。个人站点上的图片,全部压缩处理一下是最好习惯。
还有一个常见问题:用了外部字体库或在线图标库,每次访问都要跨站请求,如果对方服务不稳定,又慢又影响可靠性。caveman 更推荐用系统自带字体,或者把字体文件下载下来随页面一起发布。少依赖几个外部域名,加载速度立竿见影。
最后放一张速查表,方便你日后对照:
| 现象 | 常见原因 | 快速解决 |
|---|---|---|
| 中文乱码 | 没有声明 charset 或文件编码不对 | 统一 UTF-8,声明放 head 最前 |
| 改动后页面不变 | 浏览器缓存 | 强制刷新,给资源加版本号 |
| 手机端错位 | 缺 viewport 标签 | 加相应 meta,用相对单位 |
| 点击没反应 | 脚本顺序、id 冲突、报错 | 把 script 放尾部,看 Console |
| 加载慢 | 大图、外部字体依赖 | 压缩图片,资源本地化 |
5. Caveman 工作法在日常生活里的扩展
5.1 写文章:先用纯文本
现在很多人写东西,第一步是打开编辑器,选模板,调字体,配主题色,折腾半小时一个字没写。我换到 caveman 思路后,所有长文章一律先用纯文本或 Markdown 写,排版、配图、格式统统留给最后处理。
这样做的好处,一是内容不会被任何平台绑架,随时能导出去换地方;二是写作时注意力集中在文字本身,而不是页面长什么样。一篇三千字的文章,如果一开始就在复杂编辑器里写,光是滚动、拖动组件就能消耗大量精力。
5.2 做个一次性小工具:别急着做成“产品”
技术领域也有 Caveman 时刻。我经常需要处理一些临时任务,比如把散落在几个文档里的记录汇总成一张表,或者批量重命名一批文件。以前我会想:要不要做个界面?要不要做成可配置工具?后来发现,用脚本直接跑一遍才是对的。
拿按周汇总记录来说,写一个几十行的小脚本,一次运行,拿到结果,用完就删。它不需要界面,不需要建配置文件,更不需要做成线上服务。这种“一次性的极简工具”反而比精心打磨的完整系统更实用,因为你不需要花时间为永远不会再用的功能付出维护成本。
5.3 计划和备忘:回归纸笔
这个建议听起来有点反科技,但我自己测试下来确实有效。头脑风暴、排版本周计划、记录临时灵感时,纸和笔比任何笔记软件都快。它没有弹窗,不会抖动,没有通知打扰,纯粹到只有“记下来”这一个动作。
等过了几天,再把其中真正有价值的条目录入到电子文档里。这时候你会发现,八成内容不值得归档,剩下两成才是真正要做的事。先粗糙地记录,再刻意地筛选,这本身就是一种 Caveman 式的工作流。
5.4 怎么判断一件事需不需要复杂方案
我后来总结出四个问题,遇到任何任务都会先问一遍:
- 这件事的核心交付物到底是什么?
- 用户或读者真正需要的功能有几项?
- 三个月后还在用吗?
- 如果只用手头最简单的工具,缺的是哪一块?
答案越清楚,方案选型就越果断。更多时候你会发现,所谓缺的那一块,根本不存在,你只是被“做得更完整”的惯性推着走。caveman 不拒绝复杂工具,但它坚持一点:复杂必须是被真实需求逼出来的,而不是主动塞进去的。
我自己在这套方法上最深的感受是:做减法比做加法更考验判断力。每次往项目里加东西之前,先想想能不能不加;每次觉得“以后可能会用上”时,先想想“现在是不是必须”。这样一个一个文件攒下来,我手里的工具越来越少,功能却越来越顺手。捎带说一句,给单页文件的 CSS 和 JS 加?v=20250425这种版本号习惯,是我建议你第一天就养成的——线上缓存的问题,几乎人人都要踩,提前防住能省一堆事。按日期备份文件名这件事也一样,看上去笨,实际救过我好几次场。如果你最近也被各种复杂方案折腾得够呛,不妨给自己也立一个 caveman 词条:试试看,哪些依赖是真正需要的,哪些只是习惯使然。