首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >文本查找和替换铬扩展-它们是如何工作这么快?

文本查找和替换铬扩展-它们是如何工作这么快?
EN

Stack Overflow用户
提问于 2018-04-02 22:08:06
回答 2查看 209关注 0票数 4

在我的chrome扩展中,我试图获取页面上的每个文本元素,检查它是什么,如果是那个东西,就替换它。这是我的第一个方法:

代码语言:javascript
运行
复制
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开发人员,所以我意识到我可能在这里找错了树,但是-大多数文本如何找到并替换样式的铬扩展工作这么快?在这里,像网络工作者一样的东西会派上用场吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2018-04-07 08:09:45

一个很大的瓶颈是,您可能正在编译该内部循环中的每一次迭代中的正则表达式。编译正则表达式不需要太长时间,但是当您正在爬行的每个节点乘以所拥有的替换对的数量时,它就会加起来。

您似乎要将替换信息存储为对象,因此需要使用RegExp构造函数将字符串转换为正则表达式:

代码语言:javascript
运行
复制
const whatToReplace = {
  ipsum: 'IPSUM',
  'Vivamus|vehicula': 'VROOM!',
  '^Donec': 'donut',
  'eros': 'lust',
  'semper': 'always'
};

相反,我会将它们存储为一个数组,其中包含一个RegExp文本及其替代文本。

代码语言:javascript
运行
复制
const whatToReplace = [
  [/ipsum/gi, 'IPSUM'],
  [/Vivamus|vehicula/, 'VROOM!'],
  [/^Donec/, 'donut'],
  [/eros/, 'lust'],
  [/semper/, 'always']
];

然后您可以使用for...of而不是for...in来迭代它:

代码语言:javascript
运行
复制
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,因为它只查找一个元素,而不是那个类的所有元素,所以它也应该更快。

如果您不能将它们存储为文本,例如,如果您从用户输入中获得它们,您仍然可以在循环之外一次编译它们:

代码语言:javascript
运行
复制
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中:

代码语言:javascript
运行
复制
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文本数组似乎更快。

再读

票数 2
EN

Stack Overflow用户

发布于 2018-04-03 08:32:20

以下是我的建议:

  1. 不要将所有文本节点收集到一个数组中。这可能会导致一个巨大的阵列。
  2. regexp对象可以被一些缓冲技巧重用。这可能有助于减少处理时间。
  3. 您可以跳过一些文本节点以节省执行时间。例如,跳过包含空/全空格文本的文本节点。
  4. 不需要节点数组。文本节点可以通过treeWalker递归地一次执行一个。
票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49619470

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档