用了這個瀏覽器功能,前端頁面直接崩潰了,咋搞啊?
你是否遇到過這樣的場景:在瀏覽器中使用谷歌翻譯功能后,網(wǎng)頁突然崩了,控制臺錯誤:
這一切的罪魁禍首,其實是谷歌翻譯悄悄修改了你的頁面結(jié)構(gòu)!下面就來簡單解釋原因和解決方案。
谷歌翻譯如何“搞破壞”?
我們先看看谷歌翻譯是如何工作的。
下面是未翻譯的文字的 HTML 結(jié)構(gòu):
翻譯之后文字的 HTML 結(jié)構(gòu)是這樣的:
翻譯引擎將純文本節(jié)點包裹在<font>標簽中,這種看似無害的操作,卻為現(xiàn)代前端框架埋下了定時炸彈。
兩大致命場景
場景1:狀態(tài)更新失效
對于像React這樣的現(xiàn)代前端框架,它們依賴虛擬DOM來提高性能。當組件的狀態(tài)或?qū)傩园l(fā)生變化時,React會基于虛擬DOM的狀態(tài)來操作真實DOM。然而,當真實DOM被谷歌翻譯修改后,原節(jié)點的位置和狀態(tài)與React的預期不一致,導致更新操作無法執(zhí)行,頁面因此無法正確更新。
例如,在以下計數(shù)器組件中:
import React, { useState } from"react";
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return (
<div>
<h1>example</h1>
<p>count:{count}</p>
<p>{count}</p>
<button onClick={increment}>+1</button>
<button onClick={decrement}>-1</button>
</div>
);
};
exportdefault Counter;
在這段代碼中,兩種顯示方式在操作時的結(jié)果完全不同:
可以看到,使用<p>count:{count}</p>顯示時,當頁面使用翻譯功能后,點擊按鈕將不會再更新,而<p>{count}</p>在使用谷歌翻譯前后都是正常更新。
- 對于第一種情況:谷歌翻譯將原始文本拆分為多個嵌套的 <font> 節(jié)點,破壞了 React 原本控制的文本結(jié)構(gòu)。當 count 更新時,React 嘗試直接修改原始文本節(jié)點的內(nèi)容,但此時該節(jié)點已被替換為 <font> 節(jié)點,導致更新失效。
- 對于第二種情況:即使谷歌翻譯包裹了 <font> 標簽,但 整個 <p> 的唯一子節(jié)點仍然是動態(tài)值 {count} 對應的 <font>。React 在更新時可以通過對比虛擬 DOM 發(fā)現(xiàn) <font> 內(nèi)部的文本變化,從而正確更新內(nèi)容。
場景2:應用崩潰
谷歌翻譯的干擾還可能導致應用崩潰。當應用試圖從DOM中移除一個已被谷歌翻譯修改的文本節(jié)點時,會拋出一個NotFoundError錯誤。這是因為當應用試圖從 DOM 中移除一個條件渲染的文本節(jié)點時,這個節(jié)點已被谷歌翻譯卸載。
下面來看一個例子:
import React, { useState } from "react";
const ToggleText = () => {
const [showText, setShowText] = useState(true);
return (
<div>
<button onClick={() => setShowText(!showText)}>切換文本</button>
{showText && "Hello, World!"}
</div>
);
};
export default ToggleText;
翻譯后的文本被<font>標簽包裹后,不再是父元素的直接子節(jié)點。當狀態(tài)切換觸發(fā)DOM卸載時,React會因找不到原始節(jié)點而拋出NotFoundError,最終導致組件樹崩潰。
如何解決?
盡管這個問題在 React 社區(qū)中早已被提出,但官方至今都沒有提供完美的解決方案。Dan 給出了一個方法來修復,但是會犧牲一定的性能,需要開發(fā)者根據(jù)實際情況來權(quán)衡是否有必要添加這段代碼,并且這段代碼也并沒有解決本質(zhì)問題。
圖片
其實,對于上面提到的例子,只需要添加標簽來分離文本和動態(tài)內(nèi)容,這樣 React 就不會直接刪除和插入文本節(jié)點,就可以避免這個問題。但這只是其中一種情況,我們很難避開所有導致應用崩潰的情況。
有以下解決方案可供參考:
- 阻止部分翻譯 :可以通過給需要保持原樣的部分添加 notranslate 屬性來防止谷歌翻譯對其進行操作。
<p translate="no"></p>
- 完全禁用翻譯 :如果希望整個頁面都不被谷歌翻譯,可以在 HTML 頭部加入以下meta標簽:
<meta name="google" cnotallow="notranslate">
- 使用 Error Boundary 隔離錯誤: 對每個組件使用 Error Boundary,防止錯誤擴散到整個應用,導致應用崩潰。
<ErrorBoundary>
<ToggleText />
</ErrorBoundary>
- 必要時進行國際化:如果應用需要支持多種語言,可以考慮實現(xiàn)應用的多語言支持,而不是依賴谷歌翻譯。這樣可以更好地控制文本的顯示和更新,告別第三方翻譯插件的 DOM 污染。