1. 首页
  2. 前端基础

CSS-in-JS 性能成本缓解策略

作为一种将组件逻辑链接到其样式的方式,CSS-in-JS 在某些场景中变得流行起来。 Aggelos Arvanitakis 提醒开发人员,在某些情况下,不再能忽略 CSS-in-JS 的成本,并且提供了缓解策略。

Arvanitakis 在一篇文章中指出,尽管 CSS-in-JS 带来了好处,但是,它仍可能在某些应用程序中造成性能问题。Arvanitakis 把重点放在React 和两个流行的CSS-in-JS 库( styled-componentsemotion )上,他比较了相同代码的两个版本,其中只有一个版本使用了 CSS-in-JS 样式。无样式版本如下所示:

import React from 'react';
const NormalDiv = props => <div {...props} />
const App = () => {
  const [randomValue, setRandomValue] = React.useState(0);
  return (
    <React.Fragment>
      {new Array(50).fill(null).map((__, i) => (
        <NormalDiv key={i}>Hello World</NormalDiv>
      ))}
      <button onClick={() => setRandomValue(Math.random())}>Force Rerender</button>
    </React.Fragment>
  );
};

样式版本如下所示:

import styled from '@emotion/styled';
const StyledDiv = styled.div``;
const App = () => {
  const [randomValue, setRandomValue] = React.useState(0);
  return (
    <React.Fragment>
      {new Array(50).fill(null).map((__, i) => (
        <StyledDiv key={i}>Hello World</StyledDiv>
      ))}
      <button onClick={() => setRandomValue(Math.random())}>Force Rerender</button>
    </React.Fragment>
  );
};


样式化的 CSS-in-JS 的实现比无样式版本要多花 50% 的时间在渲染上。尽管在很多情况下,很难察觉与 CSS-in-JS 相关的性能成本,但在其他情况下(如具有大型组件树),它的成本是很难忽略的。Arvantitakis 猜测使用某些库观察到的性能成本可能要归因于它们修改组件树(使用 Context 并添加 Context.Consumer 以读取样式值)以及动态地应用样式( style 标签包含动态注入的 CSS )。Arvanitakis 解释道:

一切都非常正常,直到我实现了一个 Table。我开始注意到渲染的速度很慢,尤其是行数超过 50 行之后。因此,我打开了 devtools 尝试研究它。

(……)
因此,总结来说,多个 Context 的使用者(这意味着 React 必须协调其他元素)以及动态样式附带的固有清理工作组合起来可能让应用程序的运行速度变慢了。

因此,Arvanitakis 得出了如下建议:

1. 不要过度组合样式化的组件
2. 首选“静态”组件
3. 避免不必要的 React 重渲染
4. 研究零运行时 CSS-in-JS 库是否适合我们的项目
(……)如果我们的应用程序不需要支持主题化,并且也没有使用大量复杂的 csspro,那么,零运行时 CSS-in-JS 库可能是个不错的选择。我们可以得到的好处是,整个包的大小将缩减大约 12KB,这是因为大多数 CSS-in-JS 库的大小在 10KB 到 15KB 之间,而零运行时库(如 linaria)的大小不到 1KB。

但是,Arvanitakis 警告大家,性能重构只应该发生在遇到或测量到性能问题之后。 JSS CSS-in-JS 库的作者 Oleg Isonen 解释了 4 种常用的 CSS-in-JS 策略的权衡,并对比了 CSS-in-JS 库性能基准(截至 2019 年 3 月)。用选定的库进行基准测试得到的结果如下:

CSS-in-JS性能成本缓解策略

CSS-in-JS 可能仅限于类似 React 这样的基于组件的框架。其他流行的框架,如 Vue、Svelte 或 Angular 使用其他的托管策略,为开发人员提供类似的好处(像作用域 CSS 和摇树优化 CSS)。例如,Angular 的开发人员可以用类似 html 的模板文件、CSS 文件和 JavaScript 文件来定义其组件。然后,.js 的文件将引用其他两个文件:

// ./app.component.js
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export  class AppComponent {
  title = 'angular programming.';
}

后缀名为.js、.css 和.html 的文件应位于同一个目录中。另外,在模板和样式足够简短的情况下,Angular 开发人员可能更喜欢直接在 @Component 定义中以字符串的形式将模板和样式放到一起。这两种方法在语法上是不同的,但都提供了相同的作用域优势。

Vue 鼓励采用单个.vue 文件,文件中包含 style 标记,其中含有 CSS 样式信息、类似 html 的模板和处理组件行为的 JavaScript 方法。类似的, Svelte 读取.svelte 文件中的组件定义,.svelte 文件也包含同一文件中的样式、模板和逻辑信息。CSS-in-JS 还是 React 等框架使用托管(colocation)的另一种形式,这些框架使用 JavaScript 渲染函数而不是模板。

正如 Elm 的创建者 Evan Czaplicki 在推文中这样写道,组件就是对象。托管和封装样式以及模板属性,并将它们与处理组件逻辑的方法处理放到一起是对单一职责原则(Single Responsibility Principle)的一种反应,就像 Robert C. Martin 所解释的:

换句话说,单一职责原则就是:
把基于相同原因而改变的事物放在一起。把出于不同原因而改变的事物分开来。

如果仔细想想,那么我们就会明白,这只是定义内聚和耦合的另一种方式。我们希望提高因相同原因而改变的事物之间的内聚性,并且,我们希望降低因不同原因而改变的事物之间的耦合性。

Arvanitakis 的全文包括其他个人资料,读者可以上网查看。

原文链接:

CSS-in-JS Performance Cost – Mitigating Strategies

书籍推荐

作者:Bruno Couriol
链接:https://www.infoq.cn/article/Ie1CU2rJdXh7HjlexeeJ

看完两件小事

如果你觉得这篇文章对你挺有启发,我想请你帮我两个小忙:

  1. 关注我们的 GitHub 博客,让我们成为长期关系
  2. 把这篇文章分享给你的朋友 / 交流群,让更多的人看到,一起进步,一起成长!
  3. 关注公众号 「IT平头哥联盟」,公众号后台回复「资源」 免费领取我精心整理的前端进阶资源教程

JS中文网是中国领先的新一代开发者社区和专业的技术媒体,一个帮助开发者成长的社区,目前已经覆盖和服务了超过 300 万开发者,你每天都可以在这里找到技术世界的头条内容。欢迎热爱技术的你一起加入交流与学习,JS中文网的使命是帮助开发者用代码改变世界

本文著作权归作者所有,如若转载,请注明出处

转载请注明:文章转载自「 Js中文网 · 前端进阶资源教程 」https://www.javascriptc.com

标题:CSS-in-JS 性能成本缓解策略

链接:https://www.javascriptc.com/3749.html

« Electron 加入 OpenJS 基金会:JavaScript 框架找到了“新家”
10 万人的大场馆如何“画座位”?»
Flutter 中文教程资源

相关推荐

QR code