【问题标题】:Searching element faster using document.querySelector in a large DOM在大型 DOM 中使用 document.querySelector 更快地搜索元素
【发布时间】:2014-11-13 04:34:47
【问题描述】:

在包含数百个元素的巨大 DOM 中,使用 document.querySelector("input[name='foo'][value='bar']") 查找每个元素大约需要 3-5 秒。有什么办法可以减少这个时间吗?可能是通过给出元素的完整路径,比如document.querySelector("parent child grandchild and so on 然后input[name='foo'][value='Modem']") 或任何其他方式?

我正在使用 CasperJS 测试一个大型网页,获取每个元素需要很长时间,这使我的测试运行了一个小时。我也尝试过 __utils__.findOne(),但结果相同 3-4每个元素的秒数。由于我的测试集中在整个页面的一小部分,我希望有什么方法可以告诉document.querySelector 将元素搜索集中在页面的特定部分。

那么谁能告诉我从大型 DOM 中获取元素的最快方法是什么?

更新:这就是我测量时间的方式

var init = (new Date()).getTime();
  var element=this.evaluate(function() {
        return document.querySelector('input[value='somethin'][name='somethin']');
    });
  this.echo('Time Taken :'+((new Date()).getTime() - init));

不知何故,当我从表单中获取单选按钮、选择元素和文本框但在几毫秒内返回时,时间非常长(我今天才注意到这一点)。

当我在 chrome 等现代浏览器控制台中运行 document.querySelector('input[value='somethin'][name='somethin']') 时,时间不到一秒。

我不知道这是否与 phantomjs 的无头浏览器有关。仅对于该网站中的特定页面,获取元素的速度会变慢..

是的,页面非常大,包含数十万个元素。这是一个已有十年历史的传统 web 应用程序。在使用 IE 8 的那个页面上,按 F12 查看源代码会使 IE 挂起 5 分钟,但不是 chrome 或 firefox ..也许是 phantomjs 的内存过载或其他原因,当我在该特定页面上运行测试时,很少 phantomjs 崩溃。我不知道这个信息是否有帮助,但我不确定什么是相关的。

【问题讨论】:

  • 如果您可以使用 ID 或类可能会更快。浏览器通常对此进行了优化,因为它们在 CSS 中被频繁使用。
  • 重要的是选择器应该以不需要搜索整个DOM就可以找到的东西开始。
  • 这些选择器是从右到左解析的,因此在选择器中添加parent child grandchild and so on 可能会减慢它的速度,因为它必须确保它的祖先是正确的,而不仅仅是标记名和名称和价值
  • 难以置信的是,拨打querySelector 需要3-5 秒。你是怎么测量的?如果即使在大型文档和复杂查询中也需要超过几十毫秒,我会感到惊讶。在任何情况下,您都可以调用element.querySelector,它会将搜索限制在该元素内。
  • 你能确认一下 DOM 有“数百个元素”吗,看起来并不大...

标签: javascript dom css-selectors casperjs selectors-api


【解决方案1】:

一般注意事项

最快的选择器将是 id 选择器,但即使您的 id 位于树的更高位置,它们也不会为您提供太多。正如 Ian 在 comments 中指出的那样,选择器是从右到左解析/评估的。这意味着引擎会查找所有具有匹配属性的输入,即使它只有一个,然后才向上搜索树以查看前面的元素是否匹配。

我发现如果您知道输入在哪个封闭元素中,您可以使用 JavaScript DOM 属性遍历 DOM 并在树的较小部分运行 querySelector。至少在我的测试中,这将时间减少了一半以上。

内存问题

从您更新的问题来看,似乎确实是内存问题。当您拥有数十万个元素时,相对较旧的 PhantomJS WebKit 引擎会尝试分配足够的内存。当它占用的内存超过可用内存甚至超过您的机器拥有的内存时,操作系统会通过使用硬盘上的交换内存进行补偿。

当您的脚本尝试查询当前仅处于交换状态的元素时,此查询需要很长时间,因为它必须从与内存相比非常慢的高延迟硬盘中获取数据。

我的测试运行 100k 个表单,每个表单有一个元素,每次查询不到 30 毫秒。当我增加元素数量时,执行时间线性增长,直到某个时候我得到(registering to onError

运行时错误 R6016 - 没有足够的空间用于线程数据

所以我无法重现您在 Windows 上每次查询需要 3-5 秒的问题。

可能的解决方案

1。更好的硬件:

尝试在内存更大的机器上运行它,看看它是否运行得更好。

2。通过关闭不必要的应用程序来减少使用的内存

3。操作页面以减少内存占用:

  1. 如果您不需要测试页面的某些部分,您可以在运行测试之前将它们从 DOM 中删除。如果您需要全部测试,您可以在同一页面上运行多个测试,但每次都删除当前未测试的所有内容。

  2. Don't load images 如果这是一个图像重的网站,请设置casper.options.pageSettings.loadImages = false;

测试脚本

var page = require('webpage').create();
var content = "",
    max = 100000,
    i;

for(i = 0; i < max; i++) {
    content += '<form id="f' + i + '"><input type="hidden" name="in' + i + '" valuate"iv' + i + '"></form>';
}

page.evaluate(function(content){
    document.body.innerHTML = content;
}, content);

console.log("FORMS ADDED");

setTimeout(function(){
    var times = page.evaluate(function(max){
        var obj = {
            cssplain: 0,
            cssbyForm: 0,
            cssbyFormChild: 0,
            cssbyFormJsDomChild: 0,
            cssbyFormChildHybridChild: 0,
            cssbyFormHybridChild: 0,
            xpathplain: 0,
            xpathbyForm: 0
        },
            idx, start, el, i,
            repeat = 100;

        function runTest(name, obj, test) {
            var idx = Math.floor(Math.random()*max);
            var start = (new Date()).getTime();
            var el = test(idx);
            obj[name] += (new Date()).getTime() - start;
            return el;
        }

        for(i = 0; i < repeat; i++){
            runTest('cssplain', obj, function(idx){
                return document.querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyForm', obj, function(idx){
                return document.querySelector('#f'+idx+' input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormChild', obj, function(idx){
                return document.querySelector('form:nth-child('+(idx+1)+') input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormJsDomChild', obj, function(idx){
                return document.body.children[max-1].querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormChildHybridChild', obj, function(idx){
                return document.querySelector('form:nth-child('+(idx+1)+')').querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('cssbyFormHybridChild', obj, function(idx){
                return document.querySelector('#f'+idx).querySelector('input[name="in'+idx+'"][value="iv'+idx+'"]');
            });

            runTest('xpathplain', obj, function(idx){
                return document.evaluate('//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            });

            runTest('xpathbyForm', obj, function(idx){
                return document.evaluate('//form[@id="f'+idx+'"]//input[@name="in'+idx+'" and @value="iv'+idx+'"]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null);
            });
        }
        for(var type in obj) {
            obj[type] /= repeat;
        }
        return obj;
    }, max);
    console.log("TIMES");
    for(var type in times) {
        console.log(type+":\t"+times[type]);
    }
    phantom.exit();
}, 0); // just in case the content is not yet evaluated

在我的机器上输出(更好):

cssbyForm:29.55 cssbyFormChild:29.97 cssbyFormChildHybridChild:11.51 cssbyFormHybridChild:10.17 cssbyFormJsDomChild:11.73 cssplain:29.39 xpathbyForm:206.66 xpathplain:207.05

注意:我直接使用了 PhantomJS。在 CasperJS 中使用相同的技术时,它不应该有不同的结果。

【讨论】:

  • 这个答案无法解释 OP 报告的荒谬性能(每个查询 3-5 秒)可能是什么原因。你的实验很有趣,但我们说的是毫秒。
  • 你说得对,我应该问一下,OP 是如何测量 3-5 秒的。最初我虽然可能会发生这种情况,但是在我自己的测试和 100k 个元素并且每个查询仍然低于 30 毫秒之后,我怀疑这是一个查询问题,而是一些不必要的wait 或类似的东西。
  • 另一种可能是 DOM 太大了,它不适合内存并且操作系统正忙于交换。我在尝试 200k 表单时遇到了这个问题。
  • 他说“百种元素”;他的意思可能是“数十万”吗?当然,在那种情况下,我可以想象事情会急剧放缓。是的,您的wait 假设听起来很合理。还是他可能会在每次测试时重新加载页面,而 3-5 秒是页面加载时间?
猜你喜欢
  • 2017-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-03-15
  • 1970-01-01
  • 1970-01-01
  • 2019-05-13
  • 1970-01-01
相关资源
最近更新 更多