ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Folo 移动端 v0.5.5 发布解读:修复嵌套 div 代码块重复行与时间线滚动复位竞态

2026/9/10 4:12:57 拓冰建站 浏览量
Folo 移动端 v0.5.5 发布解读:修复嵌套 div 代码块重复行与时间线滚动复位竞态 Folo 移动端 v0.5.5 发布解读修复嵌套 div 代码块重复行与时间线滚动复位竞态【免费下载链接】follow Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow本文面向在移动端阅读 RSS 时遭遇代码块行重复下拉刷新后列表跳回顶部已读状态被误改等体验问题的开发者结合 FoloAI RSS Reader移动端 v0.5.5 的发布说明深入剖析这四个修复背后的实现机制。读完你不仅能复现与验证这些修复还能理解内容渲染器HTML Renderer如何兜底脏的订阅源 HTML以及滚动复位信号如何与已读标记、骨架屏协作避免竞态。v0.5.5 是一个不再损坏No longer broken主题的修复版本四条修复全部围绕阅读内容解析与时间线交互状态展开其中嵌套代码块的修复由社区贡献者 TonyRL 提交。以下按修复项逐一展开并给出仓库内对应源码与测试证据。修复一嵌套div包裹的代码块出现重复行问题现象部分源站如 Cloudflare 官方 changelog生成的 HTML 会把代码块的每一行都包进一层或多层嵌套的div中。Folo 移动端通过 html-renderer 把条目内容渲染到 WebView若渲染器按 DOM 树简单遍历文本同一个代码行会因为被多层节点包含而被收集多次最终出现每行重复一遍的乱象。源码层面的修复策略核心逻辑位于 parser.tsx渲染器拦截pre节点提取纯文本代码后交给ShikiHighLighter进行高亮。提取纯文本的过程发生在extractCodeFromHtml函数见 parser.tsx它通过一套逐级兜底策略从任意 HTML 中还原代码文本判定pre是否存在先把 HTML 挂到临时div若内部没有pre标签则自动包裹为precode.../code/pre保证后续查找基于统一结构。方案一针对div /换行结构查找所有div元素只有当某个div不再嵌套其他div时才取它的textContent并追加一个换行。这正是修复嵌套div的关键——多行 div 包裹只取最内层文本从根源上消除重复收集。方案二针对span.../span行包裹结构若不存在可用的div换行结构则回退到span span的查找逻辑识别span 作为行分隔符的模式见 parser.tsx 附近的分支。之所以要区分多层结构逐一命中是因为不同博客平台导出的代码块 DOM 形态差异极大Markdown 渲染多为precodeGist 风格为 span 行包裹而 Cloudflare changelog 一类站点则是 div 行包裹。extractCodeFromHtml的div 优先、span 兜底策略保证了这些形态都能被规约为干净的单行文本序列再交由 Shiki 高亮渲染。从调用链看pre渲染器会先尝试把props.children规约成 code 字符串处理children为对象或数组的不同情况只有确认是真正的代码块才进入extractCodeFromHtml任何解析异常都会捕获并回退为原始pre渲染避免整篇内容白屏。对这段渲染管线感兴趣的读者可继续阅读 html-renderer/src 目录下的parser.tsx、HTML.tsx与 shiki 子目录。特别致谢该修复由社区贡献者 TonyRL 提供见 v0.5.5 发布说明的 Thanks 部分。修复二时间线刷新不再先回顶再渲染新内容问题现象用户触发时间线刷新/重置时若列表立即把滚动位置归零而新内容尚未渲染完成屏幕会先闪过顶部空白或骨架屏造成内容闪烁 丢失阅读位置的糟糕体验。源码层面的复位信号机制Folo 移动端把是否复位滚动抽象为一套显式信号机制核心实现在 scroll-reset.tsshouldApplyScrollResetSignal({ resetSignal, appliedResetSignal })判断当前是否存在一个尚未被消费appliedResetSignal 未跟上的新复位信号避免重复复位与信号丢失。getResetScrollSignalForContent({ entryCount, hasScrollableSkeleton, isReady, resetScrollSignal })决定该信号是否真正下发到内容列表——当列表处于未就绪 条目数为 0 存在可滚动骨架屏的状态时先不转发复位信号直到真实内容渲染完成isReady为真且已有条目才执行复位。这条规则正是对刷新回顶过早的根治复位动作被延迟到新内容渲染完成后才发生用户在等待期间看到的是连续的可滚动骨架屏而不是瞬间跳回顶部的空列表。对应测试见 scroll-reset.test.ts其中明确断言对可滚动加载骨架屏entryCount: 0, hasScrollableSkeleton: true, isReady: false不会转发复位信号而对已就绪内容isReady: true会正常返回信号。修复三时间线重置期间不再误触发标记已读问题现象Folo 支持滚动阅读时自动把条目标记为已读mark-as-read on scroll。但当时间线正被重置如上一条所述进行复位时滚动位置的变化并非用户真实的阅读行为若此时仍执行滚过即已读的逻辑会把用户尚未真正看过的条目批量误标为已读。源码层面的修复策略v0.5.5 让已读标记逻辑与复位信号建立显式关联。在 scroll-reset.ts 中定义了export const shouldSuspendMarkReadForScrollReset shouldApplyScrollResetSignal也就是说只要存在一个尚未被消费的滚动复位信号就应挂起suspend滚过即已读的判定待复位信号被应用appliedResetSignal追上resetSignal后已读标记逻辑才恢复正常。测试 scroll-reset.test.ts 对这一点做了精确覆盖当resetSignal: 1, appliedResetSignal: undefined复位待执行时shouldSuspendMarkReadForScrollReset返回true——禁止已读标记当信号已被应用两者相等时返回false——恢复已读标记。从源码结构看移动端四种条目列表布局Article/Picture/Social/Video都实现了滚过即已读逻辑见 modules/entry-list 下的EntryListContentArticle.tsx、EntryListContentPicture.tsx、EntryListContentSocial.tsx、EntryListContentVideo.tsx。因此该挂起机制需要在所有布局类型中统一生效这也是本版本把复位期间不误标已读作为独立修复项的原因。修复四社交时间线在不同列表布局间切换时保持滚动位置问题现象移动端的时间线可在文章流、图文流、社交流、视频流等多种列表布局间切换。此前在布局切换或时间线视图重挂载过程中社交流的滚动位置容易被意外清零导致用户读着读着就回到开头。修复要点本条与修复二、三同属滚动复位工程化的一部分通过统一的resetSignal/appliedResetSignal信号对见 scroll-reset.ts把何时允许复位从组件挂载即复位的默认行为中剥离出来。只有信号被显式产生且未被消费时才复位布局重挂载本身不再无条件把滚动位置归零。社交流相关列表组件位于 EntryListContentSocial.tsx时间线视图选择器见 TimelineViewSelector.tsx。滚动复位逻辑与四个布局实现解耦后任意一种布局在切换时都能复用同一套信号守卫从而保证社交时间线的阅读位置得以保留。验证与回归本轮修复的滚动相关改动具有完整的单元测试背书集中在 scroll-reset.test.ts共覆盖四个断言组测试分组验证点对应发布项shouldApplyScrollResetSignal新信号被应用、重复/过期信号被忽略修复二shouldSuspendMarkReadForScrollReset复位待执行期间挂起已读标记修复三getResetScrollSignalForContent加载中不向骨架屏转发复位信号修复二骨架屏/就绪态差异就绪后正常返回复位信号修复二代码块去重则属于渲染侧纯函数逻辑parser.tsx可通过构造每行嵌套多个div的 HTML输入验证输出是否逐行还原且无重复涉及语言识别与 Shiki 高亮的完整路径可参见 html-renderer 中pre渲染器及 shiki 组件实现。小结v0.5.5 的四个修复本质上指向移动端阅读体验的两个核心矛盾订阅源 HTML 有多脏与列表状态何时可变。前者用extractCodeFromHtml的多级 DOM 规约兜底解决附 TonyRL 的社区贡献后者用一套显式的滚动复位信号把复位滚动已读标记骨架屏就绪三者串成有依赖顺序的状态机并用单元测试锁住边界行为。对于想在自己应用中复现此类竞态的读者modules/screen/scroll-reset.ts 与 scroll-reset.test.ts 是一份足够精简的参考实现。【免费下载链接】follow Folo is the AI RSS Reader项目地址: https://gitcode.com/GitHub_Trending/fol/follow创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考