在我的chrome扩展中,我试图获取页面上的每个文本元素,检查它是什么,如果是那个东西,就替换它。这是我的第一个方法:
function textNodesUnder(el){
var n, a=[], walk=document.createTreeWalker(el,NodeFilter.SHOW_TEXT,null,false);
while(n=walk.nextNode()) a.push(n);
return a;
}
const nodes = textNodesUnder(document.getElementsByClassName("content")[0]);
for (let i = 0; i < nodes.length; i++) {
// replace text
for (let k in whatToReplace) {
nodes[i].nodeValue = nodes[i].nodeValue.replace(new RegExp(k, "gi"), whatToReplace[k])
}
工作,但非常,非常慢- 5-10秒来处理一个页面。我更像一个服务器端/Golang开发人员,所以我意识到我可能在这里找错了树,但是-大多数文本如何找到并替换样式的铬扩展工作这么快?在这里,像网络工作者一样的东西会派上用场吗?
发布于 2018-04-07 08:09:45
一个很大的瓶颈是,您可能正在编译该内部循环中的每一次迭代中的正则表达式。编译正则表达式不需要太长时间,但是当您正在爬行的每个节点乘以所拥有的替换对的数量时,它就会加起来。
您似乎要将替换信息存储为对象,因此需要使用RegExp
构造函数将字符串转换为正则表达式:
const whatToReplace = {
ipsum: 'IPSUM',
'Vivamus|vehicula': 'VROOM!',
'^Donec': 'donut',
'eros': 'lust',
'semper': 'always'
};
相反,我会将它们存储为一个数组,其中包含一个RegExp
文本及其替代文本。
const whatToReplace = [
[/ipsum/gi, 'IPSUM'],
[/Vivamus|vehicula/, 'VROOM!'],
[/^Donec/, 'donut'],
[/eros/, 'lust'],
[/semper/, 'always']
];
然后您可以使用for...of
而不是for...in
来迭代它:
const whatToReplace = [
[/ipsum/gi, 'IPSUM'],
[/Vivamus|vehicula/gi, 'VROOM!'],
[/^Donec/gi, 'donut'],
[/eros/gi, 'lust'],
[/semper/gi, 'always']
];
const contentNode = document.querySelector(".content");
let walk = document.createTreeWalker(contentNode,NodeFilter.SHOW_TEXT,null,false);
let node;
while((node = walk.nextNode())) {
// replace text
for (let [rx, replacement] of whatToReplace) {
node.nodeValue = node.nodeValue.replace(rx, replacement);
}
}
这段代码还节省了一些时间和内存,只需在每个节点遍历树时更改它,而不是将其存储在一个数组中,然后遍历它。因为我们只是在寻找.content
类的第一个元素,所以我使用了querySelector
而不是getElementsByClassName
,因为它只查找一个元素,而不是那个类的所有元素,所以它也应该更快。
如果您不能将它们存储为文本,例如,如果您从用户输入中获得它们,您仍然可以在循环之外一次编译它们:
let whatToReplace = {
ipsum: 'IPSUM',
'Vivamus|vehicula': 'VROOM!',
'^Donec': 'donut',
'eros': 'lust',
'semper': 'always'
};
// convert whatToReplace into an array like the one in the previous example
whatToReplace = Object.entries(whatToReplace).reduce(function (acc, [key, value]) {
acc.push([new RegExp(key, 'gi'), value])
return acc;
}, []);
const contentNode = document.querySelector(".content");
let walk = document.createTreeWalker(contentNode,NodeFilter.SHOW_TEXT,null,false);
let node;
while((node = walk.nextNode())) {
// replace text
for (let [rx, replacement] of whatToReplace) {
node.nodeValue = node.nodeValue.replace(rx, replacement);
}
}
另一个瓶颈可能是你改变了DOM很多。每次更改节点时,都可能导致重新绘制和/或重新流。这会降低浏览器中的性能。您可以首先删除要更改的DOM树的部分,在它不在DOM中时对其进行修改,然后将其重新插入DOM中:
const whatToReplace = [
[/ipsum/gi, 'IPSUM'],
[/Vivamus|vehicula/gi, 'VROOM!'],
[/^Donec/gi, 'donut'],
[/eros/gi, 'lust'],
[/semper/gi, 'always']
];
const contentNode = document.querySelector(".content");
const parent = contentNode.parentNode;
const placeholder = document.createElement('div');
// remove it from the DOM and replace it with a placeholder
parent.replaceChild(placeholder, contentNode);
let walk = document.createTreeWalker(contentNode,NodeFilter.SHOW_TEXT,null,false);
let node;
while((node = walk.nextNode())) {
// replace text
for (let [rx, replacement] of whatToReplace) {
node.nodeValue = node.nodeValue.replace(rx, replacement);
}
}
// swap our altered element back into the DOM
parent.replaceChild(contentNode, placeholder);
根据我创建的性能试验,在Chrome中,首先将它从DOM中删除似乎并没有太大的区别,但是它确实使它变得更快了。如果你不得不面对火狐,那确实会有很大的不同。有趣的是,从DOM中删除它在Edge中似乎产生了5%的差异,但由于某种原因,在对象上使用字符串而不是使用RegExp文本数组似乎更快。
再读
发布于 2018-04-03 08:32:20
以下是我的建议:
https://stackoverflow.com/questions/49619470
复制相似问题