共计 5783 个字符,预计需要花费 15 分钟才能阅读完成。
你真的认为 Google 翻译不影响 ” 前端 ” 页面性能吗?
背景
又是一个时光飞逝的工作日, QA 同学忽然提出 bug, 文本输入框的 ” 计数 ” 性能生效, 通过大家多方查找最初发现居然是因为测试同学开启了 ” 谷歌翻译 ” 造成的无奈 ” 计数 ”, 这就引起了我的浓厚兴趣, 到底这个看起来 ” 人畜有害 ” 的翻译性能是如何影响了我的输入框? 魏蜀吴争霸从此揭开序幕, 啊~ 额(中箭)。
这篇文章是按我的探索过程编写的, 所以并不是一下就找到正确的方向, 跟着我过后的思路一起摸索吧。
一、dom 构造的扭转 (以 react 代码为例)
想要晓得为什么 ” 翻译性能 ” 会影响页面, 要从察看每次 ” 翻译 ” 性能失效时页面构造的变动开始, 最为简略的一段代码:
return (<div> 你好 </div>)
翻译前:
翻译后:
查问一下 font 标签的个性, 居然曾经不倡议应用了:
尝试一下是否能够获取到这个 font 标签元素, 将代码改装一下
function handleShowDom() {console.log(document.getElementById("box").children);
}
return(<div id="box" onClick={handleShowDom}>
你好
</div>
)
能够获取到这个元素就有意思了, 如果某些状况下代码外面存在通过获取子元素进行操作的逻辑, 那么实践上就会出问题啊。
难道是因为 dom 构造的扭转, 导致的 文本输入框
的计数性能会隐没吗?
二、比照 vue 与 react 各类 ui 库
通过查看 element ui
的源码没有发现非凡的代码逻辑, 那到底是怎么回事? 满心的疑难让我萌发一种想法, 难道跟 vue 与 react 框架自身的原理无关? 那么接下来我会别离试验 4 种 ui 框架的成果:
1: react -> antd (无 bug 👍🏻)
antd 官网
2: react -> arco (有 bug)
arco 官网
3: vue -> element(有 bug)
这个不必演示了, 开局就是靠它。
4: vue -> iview(有 bug)
iview 官网
上述只有 antd
是完满解决了这个 bug (必须: respect), 那必须要钻研一下它是如何 ” 力压群芳 ” 的呢?
三、深究 textarea
既然是这个输入框元素产生的问题, 那么咱们就从它开始吧, 一起看一下 element-ui
与antd
别离产生的 textarea
元素长什么样子:
区别还是比拟显著的, element-ui
的内容信息不是放在标签外部的, 因为咱们在 dom 构造的视角无奈查看到内容, 然而 antd
恰恰相反, 这兴许能够成为一个突破口。
那咱们把 ” 火力 ” 集中在 <textarea>
身上, 看看它到底有多少种赋值写法:
return (
<div className="App">
<textarea rows="5" cols="40">
内容 1
</textarea>
<br />
<textarea rows="5" cols="40" value="内容 2"></textarea>
<br />
<textarea rows="5" cols="40" defaultValue="内容 3"></textarea>
</div>
很微妙, 上述三种写法都会产生同样的 dom 构造, 那么就比拟好奇是什么样的写法能够暗藏标签内的文字? 我就尝试了一下 js 指定 value 的形式:
useEffect(() => {document.getElementById("wrap").value = "你好世界";
}, []);
return (
<div className="App">
<textarea id="wrap" rows="5" cols="40">
内容 4
</textarea>
</div>
)
果然问题出在这里, dom 构造内保留的竟然是初始值, 外界显示的是正确的值, 除了这些还有什么区别那? 咱们把相干的值都打印进去吧:
vs🌩
真是不钻研不晓得, 原来同样的组件成果不同的库实现进去差这么多, 感觉属性值差的还挺多的。
然而通过试验发现, 这些属性并不是这个 bug 的间接起因, 咱们还须要深刻源码进行探索, 此时我意识到这个问题可能不是 elemnet 的责任。
四、谷歌翻译是否影响 vue
通过屡次尝试连本人都不敢相信, 居然 vue 的计算属性等双向绑定的数据会在翻译后生效:
<template>
<div id="app">
<button @click="handleClick"> 你好: {{n}}</button>
</div>
</template>
<script>
export default {
name: 'App',
methods:{handleClick(){
this.n += 1;
console.log(this.n)
}
},
data(){
return {n:1}
},
}
</script>
下面能够看出 vue 的变量曾经变动, 只是因为 dom 构造的扭转导致了页面无奈被正确更新。
这是一个很危险的隐患, 因为有的用户可能会抉择 ” 总是翻译 ”, 这就导致页面性能齐全乱掉了啊!
五、谷歌翻译是否影响 react
既然 react 的 arco 框架会出问题, 那么 react 框架也肯定会被影响, 让咱们尝试一下什么状况下会出 bug, 翻译之后点击:
import {useState} from "react";
import "./App.css";
function App() {const [n, setN] = useState(0);
function handleClick() {setN(n + 1);
console.log(n + 1);
}
return <div onClick={handleClick}> 你好:{n}</div>;
}
export default App;
如果改成则不会受影响:
<div onClick={handleClick}>{n}</div>;
六、受影响范畴 (以 react 的写法为例)
通过观察 antd 与 element-ui 实现的不同我发现, antd 是应用 after
伪类类做的, 难道此事与伪类无关? 这也证实了某些状况是不受影响的, 让咱们试验一下谷歌翻译都影响哪些写法:
1: 拼接文本
不受影响的写法, 甚至这种写法每次 n 发生变化, 会移除 font 标签的 dom 构造。
<div>{n}</div>;
受影响的写法次要是拼接的文本。
<div> 你好 {n}</div>;
<div>{n} {n}</div>;
2: 伪元素(不会被翻译)
空空的 dom
<div className={"box"}>.</div>
定义伪元素
.box {
height: 10px;
width: 10px;
border: 1px solid red;
}
.box::after {
content: 'hello';
display: inline-block;
}
所以 antd 很可能是因为伪元素才没有出 bug, 如果 antd 不是无心之举那就太细节了。
3: 属性(十分非凡)
之所以说它十分非凡, 是因为属性是否被翻译须要分两种状况探讨, 是否有文本元素, 并且这个文本不能是 input 外部的文本。
第一种状况: 页面只有一个输入框
<div>
<span>
<input
type="Please enter content"
value={value}
onChange={change}
placeholder={"Please enter content"}
/>
</span>
</div>
不言而喻, 这个输入框没有被翻译:
第二种状况: 随便增加一个字符串
<div>
<span> 你好 </span>
<span>
<input
type="Please enter content"
value={value}
onChange={change}
placeholder={"Please enter content"}
/>
</span>
</div>
胜利进行了翻译并且像 placeholder
这种可能会展现给用户的属性也被翻译了, 其余功能性的属性并不会被翻译。
<span xxx={"xxxxxxxxx hello"}> 你好 </span>
七、如何屏蔽翻译
为避免因谷歌翻译引起不良体验, 只须要在 html
标签上增加 translate="no"
属性即可屏蔽掉翻译性能:
八、检测被翻译成了什么
其实存在一部分用户默认就开启谷歌翻译性能的, 更有甚者你做的是国际化我的项目, 不同语言的人应用你的网站更有可能应用默认的谷歌翻译, 那么不编间接屏蔽的状况下, 咱们要至多要监听一下用户都将咱们的网站翻译成了什么语言? 这样也不便咱们日后对网站的 i18n 进行优化。
比方咱们网站本人没有 韩语
的翻译, 然而常常被用过翻译成 韩语
, 那么咱们能够思考减少 韩语
呢?
再比方咱们尽管提供了 韩语
的翻译, 然而用户依然被动将网站翻译成 韩语
, 那就要思考是不是咱们能够将已有的翻译性能, 更明确的提醒给用户应用? 毕竟咱们自带的翻译更精确体验也更好。
1: MutationObserver 简介
能够监控 dom 元素自身的所有扭转, MutationObserver
实例化后要传入一个配置项, 这个配置项能够自定义须要监听 dom 的哪些变动, 上面列出次要的几个属性:
attributes
布尔值, 监测 dom 的属性变动attributeFilter
数组, 监测 dom 的具体某个属性的变动, 填写这个参数就不必每次判断哪个属性变动了childList
布尔值, 子元素的变动characterData
布尔值, 监督指定指标节点或子节点树中节点所蕴含的字符数据的变动
须要留神的是: childList,attributes 或者 characterData 三个属性之中,至多有一个必须为 true,否则会抛出 TypeError 异样。
2: 监控翻译的思路
因为每次谷歌翻译都会批改咱们的 <html lang="en">
标签的 lang
属性, 所以咱们就监控这个属性的变动, 并且因为哪怕以后是 lang="en"
谷歌翻译成英语还是lang="en"
, 也是会被检测到的 , 所以这个技术计划也是可行的。
新建一个 listenerI18n.js
文件, 已插件的模式引入即可:
(function () {const oHtml = document.getElementsByTagName("html")[0];
const observer = new window.MutationObserver((mutations) => {console.log(` 上报: 翻译为 ${oHtml.getAttribute("lang")}`);
});
observer.observe(oHtml, { attributes: true, attributeFilter: ["lang"] });
})();
比拟侥幸的是就算你设置了 <html lang="en" translate="no">
禁止应用翻译性能, 上述办法仍能检测到用户想要用谷歌翻译翻译成什么语言。
我整顿了一份谷歌翻译的对照表, 有须要的同学请自取, 外面并不全面但列举出比拟罕用的国家:
[
{
"name": "简体中文",
"lang": "zh-CN"
},
{
"name": "英语",
"lang": "en"
},
{
"name": "阿拉伯语",
"lang": "ar"
},
{
"name": "爱尔兰语",
"lang": "ga"
},
{
"name": "白俄罗斯语",
"lang": "be"
},
{
"name": "保加利亚语",
"lang": "bg"
},
{
"name": "繁体中文",
"lang": "zh-TW"
},
{
"name": "波兰语",
"lang": "pl"
},
{
"name": "波斯语",
"lang": "fa"
},
{
"name": "丹麦语",
"lang": "da"
},
{
"name": "德语",
"lang": "de"
},
{
"name": "俄语",
"lang": "ru"
},
{
"name": "法语",
"lang": "fr"
},
{
"name": "菲律宾语",
"lang": "tl"
},
{
"name": "芬兰语",
"lang": "fi"
},
{
"name": "高棉语",
"lang": "km"
},
{
"name": "格鲁吉亚语",
"lang": "ka"
},
{
"name": "哈萨克语",
"lang": "kk"
},
{
"name": "韩语",
"lang": "ko"
},
{
"name": "荷兰语",
"lang": "nl"
},
{
"name": "老挝语",
"lang": "lo"
},
{
"name": "罗马尼亚语",
"lang": "ro"
},
{
"name": "马来语",
"lang": "ms"
},
{
"name": "蒙古语",
"lang": "mn"
},
{
"name": "孟加拉语",
"lang": "bn"
},
{
"name": "缅甸语",
"lang": "my"
},
{
"name": "尼泊尔语",
"lang": "ne"
},
{
"name": "挪威语",
"lang": "no"
},
{
"name": "葡头牙语",
"lang": "pt"
},
{
"name": "日语",
"lang": "ja"
},
{
"name": "瑞典语",
"lang": "sv"
},
{
"name": "世界语",
"lang": "eo"
},
{
"name": "泰语",
"lang": "th"
},
{
"name": "土耳其语",
"lang": "tr"
},
{
"name": "乌克兰语",
"lang": "uk"
},
{
"name": "西班牙语",
"lang": "es"
},
{
"name": "希腊语",
"lang": "el"
},
{
"name": "匈牙利语",
"lang": "hu"
},
{
"name": "意大利语",
"lang": "it"
},
{
"name": "印度尼西亚语",
"lang": "id"
},
{
"name": "越南语",
"lang": "vi"
},
{
"name": "爪哇语",
"lang": "jw"
}
]
九、总结
看似 ” 人畜有害 ” 的翻译性能, 背地居然暗藏着各种深浅不一的 ” 坑井 ”, 假如在某些 ” 金额结算页 ” 用户应用了谷歌翻译, 那么是否会造成不小的问题, 所以最好的方法还是提供欠缺的 ” 语言切换 ” 性能才是王道啊。
end
这次就是这样, 心愿与你一起提高。