首页  >  文章  >  web前端  >  解码 StyleX:Meta 的尖端造型系统

解码 StyleX:Meta 的尖端造型系统

Susan Sarandon
Susan Sarandon原创
2024-11-10 21:32:02705浏览

每年十月,最大的国际 React 会议都会在印度果阿举行。是的,我说的是 React India。今年(2024 年)对我来说更加特别,因为我有机会在这场盛大的会议上发言。如果您错过了现场观看的话,这是我的演讲录音。如果您更喜欢阅读而不是观看视频,那么这个博客非常适合您!让我们深入探讨一下。

什么是 StyleX?

StyleX 是 Meta 的新的、可扩展的样式库,现在被用作 Facebook、Instagram 和 WhatsApp 等平台背后的主要系统。它解决了 CSS-in-JS 方法所遇到的痛点,特别是在大规模 React 应用程序中。通过提供融合原子 CSS 和静态 CSS 最佳功能的混合解决方案,StyleX 提供了高效、模块化和可扩展的替代方案。

Meta 如何以及为何创建 StyleX?

  • Meta 构建 StyleX 来解决传统 CSS-in-JS 库在大型项目中遇到的特定挑战:
  1. 未使用的样式:随着项目的增长,CSS 经常会积累未使用的规则,使样式表变得臃肿。
  2. 性能问题: CSS-in-JS 解决方案可能会导致 CSS 文件过大或出现性能瓶颈,尤其是与应用程序捆绑在一起时。
  3. CSS-in-JS 库大小: 许多用于 JavaScript 样式的流行库给包添加了不必要的重量,影响了加载时间。
  • StyleX 简介:它于 2019 年创建,作为 Facebook UI 改造的一部分,并于 2023 年 12 月将其开源。
  • CSS 优化:在使用 StyleX 之前,Facebook 上的单个页面将加载大约 15-45MB 的 CSS 样式。通过利用单个 CSS 捆绑包,StyleX 的大小大大减少到 200-300KB 左右。
  • StyleX 的目的:开发它是为了有效地大规模管理样式的复杂性。它解决了众多开发人员创建数千个组件时出现的挑战,这通常会导致 CSS 内的特殊性冲突。通过提供结构化的样式框架,StyleX 有助于保持样式过程的一致性和清晰度。
  • 原子类生成:从一开始,StyleX 就一致地生成原子类,接受每个组件具有多个类名的权衡,以提高可维护性并减少样式冲突。

StyleX 的主要特点:

  1. 原子 CSS 生成:StyleX 采用原子 CSS 生成,这意味着它为每个样式规则创建小型、可重用的类。这种方法不仅可以最大限度地减少最终 CSS 包中的冗余,还可以通过减小样式表的整体大小来提高性能。

  2. CSS 重复数据删除:通过为每种样式生成唯一的类标识符,StyleX 有效地消除了重复的样式。此重复数据删除过程可确保每个属性值对仅渲染一次,进一步有助于实现更精简的 CSS 输出。

  3. “最后应用的样式总是获胜!”:StyleX 遵循可预测的样式规则,其中最后应用的样式优先。此功能简化了调试并增强了开发人员的信心,因为它减轻了对样式规则冲突的担忧。

  4. 针对 React 进行了优化:StyleX 专为 React 应用程序设计,无缝集成到 React 生态系统中。它允许开发人员直接在其组件中定义样式,从而形成更具凝聚力的开发工​​作流程。

  5. Flow 和 TypeScript 支持:StyleX 是用“Flow”(由 Meta 创建)编写的,它还为 TypeScript 提供强大的支持,为样式和主题启用类型安全的 API。这种类型安全性增强了代码的可靠性和可维护性,使管理复杂的样式场景变得更加容易。

  6. 灵活的条件样式:使用 StyleX,开发人员可以根据组件状态或 props 有条件地应用样式。这种灵活性允许动态样式适应用户交互或应用程序状态的变化。

  7. 范围样式:StyleX 的范围样式功能可确保样式仅应用于其预期的组件。这可以防止大型代码库中经常出现的意外副作用和特异性问题。

  8. 更少的运行时计算:StyleX 通过在编译时将所有样式捆绑到静态 CSS 文件中来最大限度地减少运行时计算。这种优化可以缩短渲染时间并提高性能,尤其是在大型应用程序中。

  9. 更好的代码可维护性:通过将样式与其各自的组件共置并利用原子类,StyleX 提高了代码的可维护性。开发人员可以轻松理解和修改样式,而无需筛选大量样式表。

  10. 最小 CSS 输出:使用原子 CSS 会导致最小 CSS 输出,这对性能特别有利。随着项目规模和复杂性的增加,StyleX 可确保 CSS 捆绑包在不牺牲功能的情况下保持可管理性。

  11. 适用于各种规模的项目:虽然 StyleX 适用于各种规模的项目,但它在大型应用程序中确实表现出色。其架构旨在处理广泛的样式需求的复杂性,而不影响性能或可维护性。

让我们看看它是如何工作的?

本文中的代码示例是用 React 编写的,我们将主要使用两个组件:App.jsx 和 Button.jsx。在添加样式之前,我们先看一下这些组件的基本结构。

import Button from "./components/Button";

const App = () => {
  return (
    <div>
      <h1>StyleX by Meta</h1>
      <Button text="Get Started" />
    </div>
  );
};

export default App;
// Button.jsx
import PropTypes from "prop-types";

const Button = ({ text }) => {
  return <button>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

Decoding StyleX: Meta

使用 StyleX 添加样式

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: "black",
    color: "white",
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

要使用这些样式,我们需要从 styleX 包中导入它们,然后使用以对象作为参数的 stylex.create 方法定义样式。然后我们可以使用 stylex.props 方法将样式应用到组件。

在此示例中,base 是我们要应用的样式的名称。我们在 StyleX 中将它们称为 命名空间。这就是我们的按钮组件现在的样子。

Decoding StyleX: Meta

向伪类添加样式

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: {
      default: "black",
      ":hover": "blue",
    },
    color: "white",
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

使用 StyleX,向伪类添加样式非常简单。在前面的示例中,backgroundColor 是一个字符串。在这里,我们将其转换为具有默认值和伪类的对象。

Decoding StyleX: Meta

使用媒体查询

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: {
      default: "black",
      ":hover": "blue",
    },
    color: "white",
    width: {
      default: "100px",
      "@media (max-width: 476px)": "100%",
    },
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

与其他样式库相比,我们在 StyleX 中做的不同的一件事是媒体查询。在这里,我们根据需求将媒体查询应用于每个命名空间。在此示例中,我们将较大屏幕的按钮宽度定义为 100px,较小屏幕或移动设备的按钮宽度定义为 100%。

Decoding StyleX: Meta

让我们看看如何“最后应用的风格总是获胜”

让我们扩展前面的示例,看看如何创建此按钮的不同变体。

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: {
      default: "teal",
      ":hover": "blue",
    },
    color: "white",
    width: {
      default: "100px",
      "@media (max-width: 476px)": "100%",
    },
  },
  highlighted: {
    backgroundColor: "orange",
  },
  danger: {
    backgroundColor: "red",
  },
  primary: {
    backgroundColor: "green",
  },
});

const Button = ({ text, isHighlighted, variant }) => {
  return (
    <button
      {...stylex.props(
        styles.base,
        isHighlighted && styles.highlighted, // conditional styling
        styles[variant]
      )}
    >
      {text}
    </button>
  );
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
  isHighlighted: PropTypes.bool,
  variant: PropTypes.oneOf(["danger", "primary"]),
};

让我们向 stylex.create 方法添加更多命名空间,并为它们提供不同的背景颜色。此外,我们在 Button 组件中接受了 2 个新的 props。 isHighlighted 是一个布尔属性,我们用它来应用突出显示的命名空间。变量是我们用来应用主要、危险或突出显示的命名空间的道具。

// App.jsx
import Button from "./components/Button";

const App = () => {
  return (
    <div>
      <h1>StyleX by Meta</h1>
      <div {...stylex.props(styles.main)}>
        <Button text="Base Button" />
        <Button text="Highlighted Button" isHighlighted />
        <Button text="Danger Button" isHighlighted variant="danger" />
        <Button text="Primary Button" variant="primary" />
      </div>
    </div>
  );
};

export default App;

我们创建了几个 Button 组件的副本,并传递不同的 props。这就是我们的应用程序现在的样子。

Decoding StyleX: Meta

现在,仔细看看“危险按钮”。即使我们将 isHighlighted 传入 true,突出显示的命名空间也不会应用。最后提到了危险变体,因此将应用它。因此,该按钮将具有红色背景颜色。

覆盖父级的样式

我们可以直接从 App.jsx 覆盖此 Button 组件的样式属性。

import Button from "./components/Button";

const App = () => {
  return (
    <div>
      <h1>StyleX by Meta</h1>
      <Button text="Get Started" />
    </div>
  );
};

export default App;

Decoding StyleX: Meta

在此示例中,覆盖命名空间当前允许任何属性。然而,StyleX 使我们能够限制哪些属性可以被覆盖。使用 TypeScript 时,此功能变得特别有用。

// Button.jsx
import PropTypes from "prop-types";

const Button = ({ text }) => {
  return <button>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

此限制确保只有 backgroundColor 和 color 属性可以被覆盖。

原子类如何工作(内部)

Decoding StyleX: Meta

如果您向上滚动到前面的示例代码,您将看到我们已将 margin: "1rem" 样式添加到 3 个不同的命名空间 - App.jsx 中的 main、Button.jsx 中的突出显示和主要。当我们使用 Devtools 检查元素时,我们可以看到不同的组件(主容器、突出显示的按钮和主按钮)附加有相同的类名,并且只有 1 个类 x42y017 保留 margin: "1rem" 样式。

这就是 StyleX 通过使用原子类显着减小其包大小的方式。达到一定阈值后,不再生成新的类;相反,他们只是重用现有的类。

全局变量和主题

能够在粒度级别覆盖样式真是太棒了!然而,任何给定的设计系统都需要支持设计标记和主题。这就是 StyleX 的用武之地。StyleX 中主题 API 的设计直接受到 React Context API 的启发。变量的定义默认值与 React Context 的创建方式类似,并且可以创建主题来为 UI 子树的这些变量“提供”不同的值。

我们可以通过创建 x.stylex.js 文件来创建全局样式。确保遵循此命名约定。在此文件中,我们使用 stylex.defineVars,如下所示。

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: "black",
    color: "white",
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

我们指的是用户的首选主题并将其设置为常量值 - DARK。此外,让我们使用此颜色变量创建一个新主题。

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: {
      default: "black",
      ":hover": "blue",
    },
    color: "white",
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

主题创建后,就可以像 StyleX 中的任何其他样式一样使用。

import PropTypes from "prop-types";
import * as stylex from "@stylexjs/stylex";

const styles = stylex.create({
  base: {
    fontSize: 18,
    backgroundColor: {
      default: "black",
      ":hover": "blue",
    },
    color: "white",
    width: {
      default: "100px",
      "@media (max-width: 476px)": "100%",
    },
  },
});

const Button = ({ text }) => {
  return <button {...stylex.props(styles.base)}>{text}</button>;
};

Button.propTypes = {
  text: PropTypes.string.isRequired,
};

export default Button;

这就是我们如何分别在浅色和深色模式下看到与 myCustomTheme 相同的页面。

Decoding StyleX: Meta

这就是裹?

万岁!我们已经成功掌握了与 StyleX 合作的要点。感谢您阅读本文。我希望它能让您更好地理解什么是 StyleX、Meta 是如何创建它以及如何使用它。请在评论部分或 Twitter 上分享您的想法/疑问。如果您对这个博客感兴趣,如果您能给这篇文章点个赞(用您最喜欢的表情符号?),我将不胜感激。

平安✌

参考

  • 关于我的课程
  • 在这里观看我的演讲!
  • React India 对我来说怎么样?

在 Topmate 上与我联系以进行面试准备
Decoding StyleX: Meta

以上是解码 StyleX:Meta 的尖端造型系统的详细内容。更多信息请关注PHP中文网其他相关文章!

声明:
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn