问题描述
聊天消息中包含 emoji 时,页面进入无限闪动循环:emoji 短暂出现约 1 秒后消失,随后图片占位符与空白持续交替闪烁,整个页面无法正常使用。
复现步骤
发送或接收任意包含 emoji 的消息
emoji 首次正常渲染约 1 秒(React useEffect 之前,浏览器原生渲染)
useTwemojiRenderer 的 useEffect 触发,调用 twemoji.parse()
页面开始无限闪动
根因
useTwemojiRenderer hook 将 Unicode emoji 替换为 <img> 标签,图片源指向 https://twemoji.maxcdn.com/v/14.0.2/。MaxCDN 域名有问题(连接级超时,非 404)。
这触发了一个死循环:
① Unicode emoji → twemoji.parse() → <img src="...maxcdn...">② maxcdn 不可达 → 图片加载失败 → onerror 触发
③ onerror: replaceChild 恢复 emoji 文字 → DOM 变化
④ MutationObserver 检测到 DOM 变化 → 再次 twemoji.parse()
⑤ emoji 文字又变成 <img> → 回到 ② → 无限循环
涉及文件
en-US-Br1mQzod.js:useTwemojiRenderer hook,含 MutationObserver 监听 DOM 变化并反复调用 twemoji.parse()
label-COK6pl_R.js 第 553 行:twemoji 默认 base URL 指向已失效的 MaxCDN
建议修复
更换 CDN(主修复):将 twemoji 默认 base URL 从 maxcdn 换为 jsDelivr
// 改前
base: "https://twemoji.maxcdn.com/v/14.0.2/"
// 改后
base: "https://cdn.jsdelivr.net/gh/jdecked/twemoji@14.0.2/assets/"onerror 加防重入(防御性修复):防止任何 CDN 故障时再次陷入循环
onerror: function onerror() {
if (this.dataset.twemojiFailed) return;
this.dataset.twemojiFailed = "1";
if (this.parentNode) {
this.parentNode.replaceChild(createText(this.alt, false), this);
}
}MutationObserver 防抖(可选优化):当前 observer 在流式渲染时每次 DOM 变化都重新 parse 全量 emoji,建议加 debounce 或已解析标记
用win的兄弟如果有遇到同样问题也可以临时修一下(ps:可以直接丢给alma,让它自己修复):
在 Alma 的 app.asar 中,Twemoji 的 onerror 处理函数位于一个已知的固定字符串:
this.parentNode.replaceChild(createText(this.alt, false), this);用任意十六进制编辑器搜索这段字符串(64 字节),替换为:
this.style.display='none'; ← 后面用空格补齐到 64 字节效果:broken image 被 display:none 隐藏,不再恢复 emoji 文字,MutationObserver 不会再次触发 twemoji.parse(),死循环被打断。emoji 位置显示为空白,但页面不再闪动。
回滚:替换前备份 app.asar,原样覆盖回来即可。
前提:修改前需关闭 Alma,修改后重新启动。
Please authenticate to join the conversation.
In Review
Bug Reports
About 1 hour ago

name my
Get notified by email when there are changes.
In Review
Bug Reports
About 1 hour ago

name my
Get notified by email when there are changes.