【问题标题】:Performance difference between JavaScript created inline styles and JavaScript created stylesheetsJavaScript 创建的内联样式和 JavaScript 创建的样式表之间的性能差异
【发布时间】:2018-02-03 06:50:41
【问题描述】:

我想在我的 DOM 中动态设置给定选择器的所有元素的样式。我或多或少地看到了两种方法。对于下面的示例,我将使用 p 元素,它是 text-align 属性,但我对这两种可能的方法的优缺点更感兴趣,而不是在专门的文本对齐段落中。

1。内联(每个元素)样式

var nodes = document.getElementsByTagName('p');
Array.prototype.forEach.call (nodes, function (node) {
     node.style.textAlign = "center";
});

2。样式表

var sheet = (function() {
  // Create the <style> tag
  var style = document.createElement("style");

  // WebKit hack :(
  style.appendChild(document.createTextNode(""));

  // Add the <style> element to the page
  document.head.appendChild(style);

  return style.sheet;
})();

sheet.insertRule("p { text-align: center; }");

通常我会走内联样式的路线,因为它看起来更简单,并且可以确保样式更改会覆盖现有样式表。但我突然想到,一方面:有时不覆盖样式表可能更可取,另一方面:修改一个style 元素可能比修改一个未知数量的p 元素更高效。但这只是我的假设。

在性能方面,是否存在将内联样式应用于每个单独的元素会比创建样式表更好的情况?假设答案可能取决于我设计了多少元素,那么创建样式表是否会变得更有效率?

编辑:为了澄清我问这个问题的原因,我将解释一下我问这个问题的原因:我最近将一些我经常在项目之间复制粘贴和改编的 JS hack 变成一组可重用的 CommonJS 模块。在最高或最宽的测量值可能会因窗口调整大小或其他触发器而发生变化的情况下,它们会执行以下操作,例如将给定选择器的所有元素设置为与集合中最高或最宽的元素相同的高度或宽度。

这是一篇关于它的博文:http://davejtoews.com/blog/post/javascript-layout-hacks

这里是模块的 GitHub 存储库:

此时,所有这些模块都使用内联样式,但我正在考虑将它们转换为样式表。对于这两种方法的优缺点,我找不到一个好的答案,所以我在这里发布了这个问题。

【问题讨论】:

  • 您考虑过响应性吗?我相信这就是级联样式表成为必需品的地方。您将如何内联添加@media 查询?或者你打算通过javaScriptCSS 正在做的事情?你真的认为它会更快吗?
  • 媒体查询是个好点。对于促使我提出这个问题的用例,不会涉及媒体查询,但对于内联与样式表的一般问题,这绝对是样式表的重点。
  • 我个人更喜欢第三种方式:CSS body.p_are_centered p { text-align: center } js : document.body.classList.add('p_are_centered')。不需要时不要乱用 DOM。 (当然你可以用任何父节点替换body
  • 为什么是动态的?我们感兴趣,我想这有助于理解用例,从而了解性能如何重要或受到影响。
  • @Kaiido:对于我心目中的用例,css 不是一个选项,这就是为什么我专门询问两种纯 JavaScript 解决方案之间的区别。我将在下面发表另一条评论,并编辑该帖子以详细说明我在做什么。

标签: javascript css performance dom


【解决方案1】:

对于您的问题,我没有一个好的通用答案(我不确定是否有),但我使用开发工具对您在 Chrome 57.0.2987.98 beta 上发布的示例进行了一些实验时间线。


以下是在单个框架中完成的工作的细分,该框架使用内联样式(左)和动态样式表(右)更新了 10,000 个&lt;p&gt; 元素


为了比较,以下是使用 100 &lt;p&gt; 元素进行相同测试的结果:


总而言之,对于少量元素,差异可以忽略不计。对于大量元素,当使用内联样式时,浏览器会花费更多时间编写脚本(这是意料之中的,因为存在严重的循环)重新计算样式(我认为这可以通过浏览器必须为每个元素解析一个样式规则而不是单个常见样式规则来解释)。

所有其他步骤所花费的时间大致相同。尤其是使用内联样式时,绘制时间不会增加。


作为参考,我使用了以下测试页面。

<!DOCTYPE html>
<html>
<head>
    <title></title>
</head>
<body>
    <button id="add-stylesheet">Stylesheet</button>
    <button id="add-inline-styles">Inline</button>
    <script type="text/javascript">
        let stylesheet = document.getElementById('add-stylesheet');
        let inline = document.getElementById('add-inline-styles');

        stylesheet.addEventListener('click', addStylesheet);
        inline.addEventListener('click', addInlineStyles);

        function addStylesheet() {
            let style = document.createElement("style");
            style.appendChild(document.createTextNode(""));
            document.head.appendChild(style);
            style.sheet.insertRule('p { text-align: center }', 0);
        }

        function addInlineStyles() {
            let nodes = document.getElementsByTagName('p');
            for (let node of nodes) {
                node.style.textAlign = 'center';
            }
        }

        // initialize <p> elements
        init(10000);
        function init(numElements) {
            for (let i = 0; i < numElements; ++i) {
                let p = document.createElement('p');
                p.innerText = 'testing'
                document.body.appendChild(p);
            }
        }
    </script>
</html>

【讨论】:

  • 谢谢!在重现您的测试时,我学到了一些关于 Chrome 开发工具的知识。我注意到,如果我使用 30 个元素而不是 10,000 个元素,那么使用内联样式似乎一切都更快。正如我所怀疑的那样,样式表赶上内联样式似乎有一个门槛。您能否确认这一点并在您的答案中添加有关它的注释?我想将您的答案标记为已接受,因为它使我达到了所需的 99%,但我觉得有关元素数量的信息很重要。
  • @dave 感谢您指出这一点,我曾想指出,对于少数元素,差异变得微不足道,但在我的初稿中忘记了。请查看编辑。
  • 谢谢。你有没有以更低的数字进行任何测试?正如我上面所说,在我的机器上,当元素数量为 30 时,我实际上使用内联样式显示出更好的性能。差异很小,正如你所说,它在桌面上可能微不足道。但如果我使用开发工具的节流功能,它会更加突出,这告诉我它可以在移动设备上产生影响。
  • @dave 我认为此时有必要设计一个测试,在循环中运行每个更新,确保每次迭代都重新绘制,然后平均时间。即使从 100 个元素中获得好的结果也可能有太多的噪音。例如,命中测试减速对我来说真的没有意义。当我有空闲时间回复你时,我会尝试做一些更强大的事情。
【解决方案2】:

内联样式的优缺点

自从 React 和 JSX 大受欢迎以来,在过去的几年里,关于这个主题的争论很多。

我尝试了一些解决方案,因此我将在此处列出它们。首先是一般性讨论..

CSS 基本上是唯一提倡使用全局命名空间的语言,这也是人们远离纯 CSS 和繁重的总体框架的第一大原因。使用 flexbox,响应式布局可以在几行代码中完成,而不是像 bootstrap 这样的整个网格系统。

CSS 以可重用的方式解决了为文档提供样式的问题,但随着应用程序变得越来越庞大和越来越复杂,并且包含了更多具有自己 CSS 的第三方库,全局命名空间冲突的机会变得几乎不可避免。以至于创作和提倡了一些不同的模式,例如 BEM 和 SMACSS。

随之而来的反应使管理和创建可重用的内联样式变得相对简单。您可以使用所有的 javascript,这意味着您可以使用 _.extendObject.assign 之类的东西覆盖样式。这使得共享包含组件及其样式的模块和包变得容易,并且不需要任何类型的样式加载器,例如使用 webpack 时所需的。不过,也不全是玫瑰。普通内联样式不支持:hover、其他伪选择器和媒体查询等内容。

为了解决这些限制,开发人员实现了触发样式更改的事件,例如用于悬停的onmouseover,以及用于媒体查询的窗口调整大小事件。此后不久,创建了一个标准化这些 js 事件并定义类似 CSS 的 API 的库,并获得了普及,称为 Radium。在野外,镭也不公平(相信我我试过)。在大型应用程序中,在下载所有 JS 之前,无法执行任何媒体查询,我不建议这样做!

这导致创建了一些采用不同方法的新工具。这些新一代工具使用 JS 中定义的样式,但生成 CSS。这为您提供了内联样式的全部功能和 CSS 的全部功能。两全其美。哪个库最好可能取决于您的用例,但这些库由 Fela.js、Aphrodite 和 JSS 组成。

我最喜欢的解决方案是 Fela.js。在 fela.js.org 上查看。 Fela 可能是您将获得的最佳性能,它并不特定于任何特定框架。话虽如此,它适用于 React 和 React Native。它有几个简洁的功能,例如允许您从您的样式中访问道具。您在页面顶部设置了renderer。 Fela 最适合使用 SSR,但也可以根据您的需要在纯粹的客户端工作。与 SSR 一起使用时,您可以获得极快的页面加载速度,因为 Fela 优化了您写入原子 css 类的样式,并在初始请求时将它们发送回客户端。

如果您担心获得最快的页面,这些工具非常棒。您可以轻松完成困难的模式,例如关键路径 css 优化,其中必要的样式作为初始 HTTP 请求的一部分返回,而不是指向也必须下载的外部工作表的链接。

最后,我不得不提到 CSS 模块。这些确实创建了一个外部样式表,但允许您仍然拥有每个模块的命名空间 CSS。这允许您编写真正的 CSS 或 SASS 等,但会带来额外的设置开销。例如,在 webpack 中,您需要结合使用假样式加载器和文本提取工具来创建 .css 文件。

【讨论】:

  • 如果您有更多问题或此答案遗漏了什么,请随时提问!
  • 谢谢。我不认为我想为我正在构建的工具包含一个像 Fela 这样的库。我正在寻找严格的客户端样式更改,以及相当简单的更改。然而,知道这样的工具选择了样式表而不是内联样式,这可能表明我也应该这样做。
  • 好吧,它们呈现 CSS,而不是样式表本身。我假设的样式表将是一个外部文档。这些只是将 CSS 渲染到通常位于文档头部的renderer。 (除了 css 模块,它会渲染一个外部样式表)
  • 一种“虚拟样式表”,如果你愿意的话。 stylo 似乎与 fela 相似,正如@Hayko 所提到的,但不会遇到必须重新渲染页面以更新样式的警告,似乎样式是如何转换为原子 css 类的。
  • 我明白了。我使用术语“样式表”来指代 JS 创建的附加到文档头部的 CSS,而不是内联样式。我不考虑使用外部样式表,只是权衡我对 javascript 创建样式的选择。
【解决方案3】:

这完全取决于您要完成的任务的上下文。在我目前的雇主,我最终创建了一个名为 stylo 的小型库,负责更新动态样式表。

当您尝试更新的项目数量达到数千个时,通过内联样式进行更新将非常昂贵。上面链接中的演示在几分之一秒内更新了 6,400 个项目。

但是,如果您只更新少数项目,则更新样式表可能会触发整个页面重新呈现,如果更新内联样式则不会出现这种情况。

【讨论】:

  • 您能否详细说明为什么以及何时更新样式表可能会导致整个页面重新呈现?对于我想到的用例,我可能会更新
  • 据我所知,动态添加/删除/更新样式表会导致整个页面重排,这意味着必须重新计算页面的布局,但是更改内联样式只会影响元素(尽管取决于更改的属性,它可能会导致页面重排,例如更改页面根容器的高度)。
  • @HaykoKoryun 添加/删除规则和更新现有规则的内容有区别吗?
  • @Bergi 一般或当涉及到我的库时(有native 更新样式表中的规则的方法,但我发现它更冗长且更难更新)?跨度>
  • 一般来说,用native方式。我猜想当规则集发生变化时,它可能会重新呈现整个事物,但是当特定规则中的属性发生变化时(你当然需要参考,因此我假设原生CSSOM)它可能只适用于那些。你是如何收集你的发现的,你是否对你的图书馆进行了基准测试?
猜你喜欢
  • 2012-01-07
  • 2015-02-02
  • 1970-01-01
  • 2016-11-18
  • 2013-07-02
  • 2014-09-27
  • 2013-05-17
  • 1970-01-01
  • 2015-11-11
相关资源
最近更新 更多