文章目录滑不动?锅在 flex,账在我头上

1392 字
7 分钟
文章目录滑不动?锅在 flex,账在我头上
AI 智能总结内容由 AI 生成,仅供参考

PC 端文章页的目录因侧栏使用 flex 布局而被压缩导致滚动失效,修复只需调整 flex 子元素的收缩属性;随后发现手动滚动被“自动跟随”功能抢占,改为监听滚动事件并设置短暂静默期解决;后台内容列表慢主要因为每次请求前同步 git 仓库,改为后台线程定期同步(45 秒一次)并加硬超时,将接口响应时间从秒级降至毫秒级。两件事共通的经验是:症状不一定是根本问题,需排查隐藏的布局或前置操作。

这周又攒了两个问题一起修:一个是 PC 端文章页的目录滑不动,一个是后台的内容列表慢半拍。两件事八竿子打不着,但排查完发现它们的剧本出奇地像——表面症状和真正的病灶,隔了好几层。老规矩,把过程记下来,给下次的自己抄作业。

一、目录滑不动:先给滚动条洗清冤屈#

现象很朴素:文章长了以后,PC 端右侧的目录显示不全,底部直接被裁掉,滚轮放上去毫无反应。移动端没事,就 PC 有。

我的第一反应是滚动条样式的问题——毕竟这个站的滚动条定制过,透明度调得很低,第一眼确实看不见。改了半天透明度,滚动条是看得见了,但目录还是滚不动。看得见摸不着,那就不是样式的问题。

回头把侧栏的布局翻出来看,才找到真凶。侧栏是个纵向 flex 容器,外面套着 max-height 和 overflow-y: auto,意图很直白:最多这么高,装不下就内部滚动。但 flex 有个不太直觉的默认行为——子元素默认 flex-shrink: 1,容器装不下的时候,所有子元素会按比例被压扁。

也就是说:目录卡片、作者卡片这些加起来比视口高的时候,flex 先做的不是触发外层滚动,而是把每个卡片均匀压扁。目录内容被裁掉一截之后,卡片内部的高度刚好没超出——没超出,自然就没得滚,滚动条委屈地认为「我没有活干」。

修复反而简单得可笑:

/* 子卡片禁止收缩,装不下就让侧栏整体滚 */
#left-sidebar-sticky > *,
#right-sidebar-sticky > * {
flex-shrink: 0;
}

一行 CSS,目录恢复滑动。写这段布局的时候,我大概默认了「装不下就会滚动」,但 flex 的收缩优先级排在溢出前面。这个默认值,坑过的大概不止我一个。

二、修完压缩,又撞上「弹回去」#

以为完事了,实测时冒出了更迷惑的现象:我把目录往下滚到想看的小节,松手不到一秒,它自己弹回去了。

这个就不是 CSS 了,是交互逻辑打架。目录有个「自动跟随」功能——页面滚到哪个小节,目录就把对应条目滚到可视区中间,之前写的,一直工作得挺好。问题在于它太尽职:我手动滚动目录这个动作,在它眼里和「用户翻了页面」没区别,下一帧就坚持把活动项拉回中间。我的手和它的逻辑,只能活一个。

解法是给「用户的手」加发言权:在目录上监听 wheel 和 touchmove,一旦检测到手动滚动就打个时间戳,四秒之内自动跟随闭嘴:

private userScrollingUntil = 0;
private readonly markUserScrolling = () => {
this.userScrollingUntil = Date.now() + 4000;
};
// scrollToActiveItem 开头:
if (Date.now() < this.userScrollingUntil) return;

四秒是试出来的。太短的话页面稍微一滚它又开始抢方向盘,太长的话点到新小节时目录跟不上趟。

顺手还对了另外两笔账:侧栏的 top 和 max-height 之前是各写各的,底部会超出视口几十像素,这次对齐了;滚动条透明度也从「隐身模式」调回到肉眼可见——一个用户看不见的滚动条等于没有滚动条,这是我第一节白忙活半小时换来的觉悟。

三、后台慢半拍:接口在替 git 排队#

第二个问题是后台的内容列表越用越慢,刷新一次要等好几秒,网络不好的时候直接超时。

排查时先看接口本身——查询逻辑没什么毛病,本地数据读取快得很。但每个读接口在干活之前都先干了一件事:同步一次内容仓库。当初的思路是「读之前先拉最新,保证内容新鲜」,听起来很负责,代价是每次请求都背一次网络往返。列表页一打开,好几个接口排着队跑网络。同步快的时候几百毫秒,慢的时候——就是你看到的转圈圈。

修复思路一句话:把慢的东西从关键路径上挪走。仓库同步不再跟着请求走,改成后台线程自己定期做,同步加硬超时,防止哪次网络抽风把线程挂死在一个请求上:

def _repo_sync_loop(interval=45):
while True:
try:
gitops.pull()
except Exception:
pass # 这轮失败了下轮再来,别影响主流程
time.sleep(interval)

同步周期 45 秒。对个人后台来说,「内容最多延迟一分钟」和「每次打开都等几秒」之间,根本不用犹豫。改完之后列表页的响应从秒级等待回到毫秒级,终于像个本地工具该有的样子。

写在最后#

两件事修完,发现教训居然是同一句话:

症状出现在哪里,病灶不一定在哪里。看得见的问题(滚不动)可能藏在看不见的布局默认值里;等得起的耗时(内容同步)不该出现在等不起的地方(接口路径上)。

再补一条给写 CSS 的自己:flex 的默认值不会「大概按我想的来」。flex-shrink 这个属性我看了很多年,第一次真正理解它,是在自己博客的目录滚不动的那天。

文章分享

如果这篇文章对你有帮助,欢迎分享给更多人!

文章目录滑不动?锅在 flex,账在我头上
https://li8.me/posts/9/
作者
八奈奈
发布于
2026-10-11
许可协议
CC BY-NC-SA 4.0
Profile Image of the Author
八奈奈
记录技术与生活
公告
欢迎来到「二次元宅男录」!这里记录技术与生活,欢迎常来逛逛。
分类
标签
站点统计
文章
10
分类
4
标签
18
总字数
17,017
运行时长
0 天
最后活动
0 天前
文章目录