{
"title": "xLog 修补小记 —— 纯文本节点引发的应用崩溃",
"tags": [
"post"
],
"sources": [
"xlog"
],
"external_urls": [
"https://zsakvo.xlog.app/textNode-cause-app-crash"
],
"date_published": "2023-04-11T15:12:47.442Z",
"content": "## 背景\n\n在之前就发现过本平台一个颇为微妙的 bug:\n当使用 Chrome 内建的翻译工具进行全局翻译时,页面会出现错误,具体提示为 \n\n> 应用程序错误:发生了客户端异常(有关详细信息,请参阅浏览器控制台)。\n\n不过因为平台本身具有良好的 i18n,加上也并无过于复杂的内容,极少会在相关页面使用全局翻译,也就没有多做关注。直到近日看到其他的小伙伴提起这个问题 https://github.com/Crossbell-Box/xLog/issues/229 ,勾起了好奇心,遂简单看看具体缘故。\n\n\n\n## 探究\n\n既然说了参阅控制台,那么就看看到底说了什么:\n\n```javascript\nreact-dom.production.min.js:189 DOMException: Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.\n at e (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:89708)\n at e (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:89781)\n at e (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:89781)\n at uK (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:89833)\n at uq (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86503)\n at uQ (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86382)\n at uq (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86495)\n at uQ (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86382)\n at uq (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86692)\n at uQ (https://xlog.xlog.app/_next/static/chunks/framework-9e494f4410668bc3.js:9:86382)\n```\n粗略扫一眼,嗯,看不出来。再看看,猜测应该是 React 内部爆出的错误。那么直接操起 Google,很轻易地就搜到了原因以及解决方案:https://github.com/facebook/react/issues/11538#issuecomment-390386520\n\n> 问题在于 Google Translate 用包含翻译的 `<font>` 标签替换文本节点,而 React 保留了不在 DOM 树中的文本节点的引用。\n\n而解决方案也堪称简单粗暴:\n\n> 最简单的解决方法是使用 `<span>` 将那些文本节点包装起来,这样被 React 引用的节点即使其内容被替换为 `<font>` 标签,也会保留在 DOM 树中。\n\n\n## 解决\n\n有了原因和解决方案,那么只需要找到出错的地方修复即可。\n\nxLog 的代码也不算少,直接去找也算是个大工程,所以为了定位,干脆用了个很粗暴的方案,使用二分法逐步在控制台删除元素。\n\n运气颇好地,没用几下就找到了引起问题的地方:`src/components/site/SiteFooter.tsx`\n\n```javascript\n<div className=\"max-w-screen-md mx-auto px-5 py-10 text-xs flex justify-between\">\n <div className=\"font-medium text-base\">\n ©{\" \"}\n <UniLink href=\"/\" className=\"hover:text-accent\">\n {site?.name}\n </UniLink>{\" \"}\n ·{\" \"}\n <Trans\n i18nKey=\"powered by\"\n defaults={\"Powered by <name/>\"}\n components={{\n name: <LogoWithLink />,\n }}\n ns=\"site\"\n />\n</div>\n```\n\n大致两步搞定:\n1. 直接用 `<span>` 标签包裹住纯文本\n2. 修正 `Trans` 组件输出的结构:按照 https://react.i18next.com/latest/trans-component 所言修改对应语言文件中的字符串模板,然后在 `components` 字段中添加对应的 `span` 声明即可。\n\n粗略看了下,没什么问题了,当然不排除没改干净的情况,遇到了再说吧。\n\n## 总结\n\n没啥可说的,多用 Google 就行 :_)\n",
"attributes": [
{
"value": "textNode-cause-app-crash",
"trait_type": "xlog_slug"
}
]
}