五个没人提、但人人都会撞上的需求
一个站做完了,自己看哪儿都顺。
问题出在「自己看」——我知道每个按钮在哪、每篇文章多长、走错地址会怎样。访客不知道。
于是换个身份走一遍:用手机,从搜索引擎点进来,点错一个链接,想订阅但找不到入口,网慢的时候刷新一次。
走完发现五处「不会有人专门抱怨、但每次都在吃亏」的地方。
一、手机上根本没有目录
文章页右边有目录,只在宽屏出现。这个决定当时觉得合理:窄屏放不下。
但长文章在手机上恰恰最需要目录——屏幕只有一掌宽,从头划到尾要划十几下,想回到「第三版」那一节只能靠记位置。
改法不是把侧栏硬塞进窄屏,那会更糟。改成底部抽屉:右下角一个悬浮圆钮,点开从底部滑上来,遮罩压暗背景,点条目跳转后自动收起。
技术上没什么新东西,麻烦在清理:目录和抽屉都挂在正文容器外面,页面切换时不会随容器一起被覆盖。销毁函数里漏删按钮和遮罩,进第二篇带目录的文章就会叠两份。这个漏了两次才记住。
二、不知道一篇要读多久
站上二十多篇文章,从三百字到三千字都有。点进去之前完全不知道会花多久。
加了「约 X 分钟」。算法很土:字符数除以 400。
为什么是 400?中文阅读速度普遍在每分钟 300~500 字,技术文章带代码会更慢,取中间值再偏保守一点。
为什么用字符数不用词数?中文没有空格分词,split(' ') 对中文毫无意义。字符数是唯一对所有语言都成立的粗略尺度。
表格和代码块的字数也算进去了,会让估计偏高。没做精细剔除——偏保守比偏乐观好,读者不会被「还剩 2 分钟」骗到。
三、走错地址只有三个字
这是最难看的一处。
随便敲一个不存在的路径,页面上只有一个 404。纯文本,三个字节。没有导航、没有返回、没有搜索框,连样式都没有。
原因在兜底逻辑:找不到文件就 res.end('404')。功能上没错,状态码是对的,但对人是死路。
改成完整页面:标题、说明、搜索框、分类入口、最近六篇。
状态码仍然是 404。
这一点要强调。把 404 改成 200 会变成「软 404」,搜索引擎会把这些页面当正常内容收录,比三个字更糟。可读性和状态码是两件事,不要为了好看牺牲后者。
前端也得有同样的页面。单页应用里点错站内链接不会真的发请求,是 JS 在客户端判断路由找不到——如果服务端有富 404、前端没有,用户从站内点错看到的是另一个样子。
富 404 不等于「什么都不返回 404」
改完之后我一度想更省事:任何路径都回那个漂亮页面,反正上面写着「没有这个页面」。
不能这么干。之前踩过这个坑——兜底逻辑是「找不到就回首页」,结果 /.env、/.git/config、/wp-json/wp/v2/users/me 全部返回 200。扫描器看到 200 就认为这些文件存在,会继续深挖同类路径。
所以现在是两层:
- 白名单内的路由:回 200,渲染页面
- 白名单外的路径:回真 404,但同样渲染那个带搜索框的页面
状态码归状态码,页面归页面。两者可以同时正确。
两端结构写成一样,才不会在一个页面里「闪」一下。
四、想订阅但找不到入口
站有 RSS,/rss.xml 一直能访问。
但首页没有任何地方提到它。
等于:功能存在,只有知道地址的人能用。
页脚加了一个链接。一行 HTML。
五、首屏白一下
这个最隐蔽,因为它「看起来正常」——网速快的时候感觉不到。
实测首页返回的 HTML 只有 3.5 KB,里面只有一个标题。文章列表全靠 JavaScript 拉回来再渲染。也就是说:
- 慢网下,用户先看到标题,然后空白,几百毫秒后才出列表
- 搜索引擎抓到的也是这个空壳
改成服务端先渲染前 12 篇进 HTML,客户端接管后再补齐(变成 20 篇、加上交互)。HTML 从 3.5 KB 涨到 10.7 KB,gzip 后 2.9 KB。
代价是要写第二套渲染逻辑——服务端一份、客户端一份。这就是不一致的风险:两边结构不一样,接管时页面会「跳」一下。
所以规则是:服务端只渲染静态结构,不渲染任何依赖交互的状态;客户端接管时结构一致就不重绘,只补内容和事件。
不是所有页面都值得预渲染
预渲染的代价是两套渲染逻辑要一起维护,所以只对「爬虫会抓、人也会直接打开」的少数入口做:首页、文章页。
搜索页、后台、登录页预渲染没有意义——它们本来就需要交互,而且不应该被索引。
判断标准很朴素:这个地址会出现在搜索结果里、或者出现在别人发的链接里吗。会,就为它花这份成本;不会,就让它由前端渲染。
共同点
这五件事没有一件是「功能缺失」——目录、阅读时间、404、RSS、列表,本来都有。
缺的是从别人的位置看一遍。
自己用的时候会自动绕过所有摩擦:知道哪儿能点、记得文章多长、不会走错地址。这些「自动绕过」,就是访客的摩擦点。
修它们花的时间,比做任何一个新功能都少。