Chrome的console.log()打印DOM节点不一致

pkmbmrz7  于 12个月前  发布在  Go
关注(0)|答案(2)|浏览(148)

不是Google Chrome console.log() inconsistency with objects and arrays的重复,因为这个问题不是关于对象和数组的。
下面是一个简单的HTML页面:

<head>
  <link rel="stylesheet" href="style.css">
</head>

<body>
  <div></div>
  <div></div>
  <div></div>
  <div></div>

  <script>
    for (const div of document.querySelectorAll("div")) {
      console.log(div);
    }
  </script>
</body>

有时候,结果会显示为它们应该显示的样子:

有时候我用console.dir()代替console.log()

很少,我得到一个混合:

当我尝试创建上面所示的minimal, reproducible example时,我删除了<link>,因为它似乎与问题无关。然而,在删除它之后,不一致性消失了,所以我恢复了它,并试图最小化CSS本身,直到style.css当前 * 完全 * 为空。因此,<link>标记的存在似乎至少与问题有关。
这种行为是有原因的,还是一个bug?

ar5n3qh5

ar5n3qh51#

这不是一个错误,也不是一个功能,而是一个权衡。我给了一个answer类似的问题,关于“如何”,因此是短的。这里讲的是“为什么”,所以需要更多的“学术”解释。
它与Chromium DevTools日志记录如何与Blink引擎联合工作有关,这里是DOM节点的简化场景:

console.log(document.querySelector('div'));

Blink V8::console.log-> IPC ->Chromium Process-> IPC ->DevTools front-end fork
也就是说,V8执行由Blink实现的console.log方法,该方法触发DevTools的消息。该消息由IPC处理,在Blink中主要是异步的。一旦DevTools Console接收到一个Node对象,它必须检查DOM Renderer以与它的实际 * 渲染树 * 同步,这样当鼠标悬停在控制台中的节点上时,Renderer将在浏览器页面中突出显示该节点,所以它是:
DevTools inspect-> IPC ->Chromium Process-> IPC ->Blink DOM Render
这里有一个问题,由于DOM Renderer忙碌同步样式加载,在这种特定情况下,渲染阻塞资源是link元素,因此它无法及时响应,而不是 * 渲染树 * 表示,控制台回退到它的 * 对象 * 表示,这样做而不是只显示 * 字符串 * 表示,无论如何都可以检查该节点,这是一个明智的权衡,尽管没有看到它的镜像引用。
此外,DevTools Console的其他特性可以注意到:

  • 调用哪个console方法并不重要,因为它们主要是 log level 定义的别名,而不是对象检查的特殊处理,因此console.error的行为也是如此。
  • 仅当Console选项卡处于活动状态或变为活动状态时,才会发出DevTools inspect。因此,当使用活动的Console选项卡执行测试时,它可能会显示节点的 object 表示,当在另一个选项卡上执行测试时,假设Performance,然后切换到Console,我们可以看到这些节点一个接一个地显示为 render tree,因为到那时DOM Render将可以访问。
  • 关于日志对象的一个常见误解是,当JS运行时改变它们时,它们在控制台中也会改变。他们不是。Console只为 * 当前级别 * 树创建对象的静态“指纹”。然后,当手动展开它时,它会根据需要创建一个更深的树,因此它只是为新树使用当前值,这可能会给予人一种错误的印象,即它改变了 log

我对最初的示例做了一些修改,使其更像一个“真实的的”应用程序,同时也揭示了上面的行为。此外,对于渲染引擎的更多压力,可以在DevTools Performance选项卡中使用x4x6 CPU节流来播放。

<head>
  <script>
    document.addEventListener('DOMContentLoaded', () =>
      console.log('DOMContentLoaded'));
  </script>
  <link rel="stylesheet" href="css.css">
</head>
<body>
  <div></div>
  <div></div>
  <div></div>
  <div></div>

  <script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
  <script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
  <script>
    for (const div of document.querySelectorAll("div")) {
      console.log(div);
    }

    let i = 1000, div, divs = [], ids = [];
    while (i--) {
      div = document.createElement('div');
      div.textContent = new Date().toISOString();
      document.body.appendChild(div);
      if (!(i % 100)) {
        const id = {id: i};
        ids.push(id);
        divs.push(div);
        console.log(id, {div}, div);
      }
    }
    setTimeout(() => {
      for (const id of ids)
        id.id = Math.random();

      for (const div of divs) {
        div.style.color = 'red';
        const child = document.createElement('div');
        child.textContent = 'OK';
        div.appendChild(child);
      }
    }, 1000);
  </script>
</body>

注意:console.log的结果必须在DevTools中检查,而不是在这里的 playground 中,因为它只是用自己的解析器解析console.log的参数,与真实的console.log行为无关。

pgvzfuti

pgvzfuti2#

我读过@Corey的评论,但我想指出与Corey所说的相矛盾的docs
请注意,如果您在最新版本的Chrome和Firefox中记录对象,那么您在控制台上记录的是对象的引用,它不一定是您调用console.log()时对象的“值”,**但它是您打开控制台时对象的值。
现在根据console.log()console.dir()之间的差异。除了打印DOM元素的方式不同之外,另一个区别如下:
log对DOM元素进行了特殊处理,而console.dir则没有。当试图查看DOM JS对象的完整表示时,这通常很有用。
可能的解决方案/另一个测试用例是,再次引用文档,记录DOM元素不是console.log(obj),而是实际上是console.log(JSON.parse(JSON.stringify(obj)))(这实际上是将对象记录到控制台的推荐方法)。
这个建议背后的逻辑是,通过这种方式,您可以确保在记录obj的时候看到它的值。否则,许多浏览器提供实时视图,该视图会随着值的更改而不断更新。这可能不是你想要的。

相关问题