结合NextJS, Meta 的 Stylex 快速入门

介绍

Stylex 是 Meta 最近(截至 2023 年 12 月)开发的开源 CSS-in-JS 解决方案。它允许在 React/JavaScript 组件中编写原子内联 CSS 样式,并通过全局可访问的 CSS 变量结合了静态 CSS 的强大功能。Stylex 通过为每种样式创建唯一的类标识符,并通过最少地使用冲突贡献者(例如伪类)来缓解特异性问题,从而促进无冲突的 CSS。这些使 Stylex 比其他 CSS-in-JS 解决方案(如 Emotion)更具确定性、可靠性和可扩展性。

Stylex 十分强大,因为 Stylex 样式旨在跨库重用和扩展。它还配备了用于 Flow 的编译时类型,并且对 TypeScript 有很好的支持 - 使其很容易被静态类型的代码库采用。

使用 Stylex,我们可以在 React 组件中定义共置样式,操作它们的渲染逻辑,并在标记中相应地内联使用它们。我们可以在组件外部声明全局变量,导出它们,然后在给定组件中导入和使用它们。全局声明的变量对于动态布局、网格系统、调色板、排版、间距、大小、重复设计以及主题非常有用。

在这篇介绍性文章中,我们将介绍如何在已设置的 Next.js 应用程序中使用 和 API 定义 stylex.createstylex.props 使用 Stylex 样式。我们花了精力去理解用 Stylex 编写无冲突内联 CSS 的一些怪癖。在此过程中,我们遇到了实现简单样式声明、带有导入的 Stylex 变量的样式声明、条件样式以及使用媒体查询的响应式组件的片段。我们还探讨了如何使用 stylex.defineVars API 创建变量并在组件中使用它们。

我们使用这个来自 Facebook 的 Next.js 应用程序示例作为基础,并对其进行调整以构建我们自己的页面和组件。如果需要,请随时克隆它,在本地使用它并从中采用你自己的。

使用 Stylex 和 TypeScript 的 CSS-in-JS

Stylex 有两个核心 API:和 create props 方法。 stylex.create() 允许我们使用 JavaScript 对象声明 CSS 样式。该对象应具有表示 CSS 类的属性标识符及其代表 CSS 规则的值。该 stylex.props 方法允许我们从内联标记中访问声明的样式。

stylex.defineVars API 有助于声明全局 Stylex 变量,这些变量表示实际的 CSS 变量,并且可以从应用程序范围的 React 组件访问。因此,Stylex 变量可用于动态布局、网格系统、调色板、间距、大小、主题等。

在前面的章节和小节中,我们将通过代码片段来说明 stylex.create 如何在 Next.js 页面和组件中使用 和 stylex.props 实现 Stylex 样式 stylex.defineVars 的方法。

对于涵盖的每个主题,我们将分析与 Stylex 相关的更改,然后尝试理解它们。

我们的大部分更改都在 app/page.tsx 文件和 <Card /> 组件中。我们将首先关注包含 <Home /> 组件 page.tsx 的文件,以了解如何创建和应用 Stylex 样式。

使用 Stylex 设置 Next.js 应用程序的样式

该文件 app/page.tsx 包含组件, <Home /> 在使用 Stylex 样式添加我们自己的标记后如下所示:

js 复制代码
import stylex from "@stylexjs/stylex";
import Card from "./components/Card";
import { colors } from "./stylex/cssVars.stylex";
import { globalTokens as $ } from "./stylex/globalTokens.stylex";

const MEDIA_MOBILE = "@media (max-width: 700px)" as const;

const style = stylex.create({
  main: {
    margin: "auto",
    fontFamily: $.fontMono,
  },
  jumbotron: {
    border: "1px transparent solid",
    padding: "16px 24px",
    backgroundColor: "#e9ecef",
  },
  jtBody: {
    padding: "8px 0",
  },
  jtHeading: {
    margin: "12px 0",
    fontFamily: $.fontSans,
    fontSize: "54px",
    fontWeight: "bold",
    color: "#4d4d4d",
  },
  jtText: {
    margin: "24px 0",
    fontSize: "24px",
  },
  jtFooter: {
    margin: "24px 0",
  },
  jtButton: {
    padding: "12px 24px",
    fontFamily: $.fontMono,
    fontSize: "20px",
    fontWeight: "bold",
    color: colors.white,
    border: "1px solid transparent",
    borderRadius: "4px",
    backgroundColor: colors.primary,
    textDecoration: {
      default: "none",
      ":hover": "underline",
    },
  },
  deck: {
    display: "flex",
    flexDirection: {
      default: "row",
      [MEDIA_MOBILE]: "column",
    },
    justifyContent: {
      default: "space-betweem",
      [MEDIA_MOBILE]: "center",
    },
    alignItems: {
      default: "center",
      [MEDIA_MOBILE]: "space-between",
    },
    margin: "24px auto",
  },
  cardHeading: {
    margin: "16px 0",
    fontFamily: $.fontMono,
    fontSize: "32px",
  },
  cardText: {
    margin: "16px 0",
    fontFamily: $.fontSans,
    fontSize: "16px",
  },
  featuredBg: {
    backgroundColor: "orange",
  },
});

export default function Home() {
  return (
    <main {...stylex.props(style.main)}>
      <div {...stylex.props(style.jumbotron)}>
        <div {...stylex.props(style.jtBody)}>
          <h1 {...stylex.props(style.jtHeading)}>Hello, world!</h1>
          <p {...stylex.props(style.jtText)}>
            This is a template for a simple marketing or informational website. It includes a large callout called a
            jumbotron and three supporting pieces of content. Use it as a starting point to create something more
            unique.
          </p>
        </div>
        <div {...stylex.props(style.jtFooter)}>
          <a {...stylex.props(style.jtButton)} href="#" role="button">
            Learn more >>
          </a>
        </div>
      </div>

      <div {...stylex.props(style.deck)}>
        <Card featuredBg={{ backgroundColor: "orange" }}>
          <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
          <p {...stylex.props(style.cardText)}>
            Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
            condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
            Donec sed odio dui.{" "}
          </p>
          <p>
            <a href="#" role="button">
              View details >>
            </a>
          </p>
        </Card>
        <Card>
          <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
          <p {...stylex.props(style.cardText)}>
            Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
            condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
            Donec sed odio dui.{" "}
          </p>
          <p>
            <a href="#" role="button">
              View details >>
            </a>
          </p>
        </Card>
        <Card>
          <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
          <p {...stylex.props(style.cardText)}>
            Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
            condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
            Donec sed odio dui.{" "}
          </p>
          <p>
            <a href="#" role="button">
              View details >>
            </a>
          </p>
        </Card>
      </div>
    </main>
  );
}

正如我们所看到的,样式完全由 Stylex 处理。浏览器中的页面现在如下所示:

让我们在下面的部分中分解它。

创建样式 stylex.create

我们用以下 stylex.create 方法声明了 Stylex 样式:

js 复制代码
const style = stylex.create({
  main: {
    margin: "auto",
    fontFamily: $.fontMono,
  },
  jumbotron: {
    border: "1px transparent solid",
    padding: "16px 24px",
    backgroundColor: "#e9ecef",
  },
  jtBody: {
    padding: "8px 0",
  },
  jtHeading: {
    margin: "12px 0",
    fontFamily: $.fontSans,
    fontSize: "54px",
    fontWeight: "bold",
    color: "#4d4d4d",
  },
  jtText: {
    margin: "24px 0",
    fontSize: "24px",
  },
  jtFooter: {
    margin: "24px 0",
  },
  jtButton: {
    padding: "12px 24px",
    fontFamily: $.fontMono,
    fontSize: "20px",
    fontWeight: "bold",
    color: colors.white,
    border: "1px solid transparent",
    borderRadius: "4px",
    backgroundColor: colors.primary,
    textDecoration: {
      default: "none",
      ":hover": "underline",
    },
  },
  deck: {
    display: "flex",
    flexDirection: {
      default: "row",
      [MEDIA_MOBILE]: "column",
    },
    justifyContent: {
      default: "space-betweem",
      [MEDIA_MOBILE]: "center",
    },
    alignItems: {
      default: "center",
      [MEDIA_MOBILE]: "space-between",
    },
    margin: "24px auto",
  },
  cardHeading: {
    margin: "16px 0",
    fontFamily: $.fontMono,
    fontSize: "32px",
  },
  cardText: {
    margin: "16px 0",
    fontFamily: $.fontSans,
    fontSize: "16px",
  },
  featuredBg: {
    backgroundColor: "orange",
  },
});

它采用一个 styles 对象,该对象具有表示 CSS 类的属性标识符和组成实际 CSS 规则的值。在后台,Stylex 创建一个 CSS 类,每个 Stylex 样式对象属性的标识符都以开头 x 。当样式应用于 JSX 元素时 stylex.props ,生成的 CSS 类将添加到元素的 className 属性中。

Stylex 样式声明 - 必须是静态可分析的

声明 Stylex 样式存在一些限制。例如,样式对象属性

不应超过一个级别的深度,因为进一步的嵌套属于元素的 CSS 属性。

无法调用非 Stylex 函数。

无法从非 Stylex 模块导入值。

作为一般规则:Stylex 样式声明必须是静态可分析的。在此处查看更全面的列表。

具有导入的 Stylex 变量的 Stylex 样式

导入和使用 Stylex 变量是典型的:

js 复制代码
{
    fontFamily: $.fontMono,
}

使用全局 Stylex 变量对于动态布局、响应式设计和生成间距、排版、颜色和主题的变体非常重要。

Stylex 条件样式定义

我们可以应用条件样式定义来分配 CSS 伪类:

css 复制代码
textDecoration: {
  default: "none",
  ':hover': "underline",
},

Stylex 媒体查询

我们还能够通过有条件定义的媒体查询来维护响应式设计:

csharp 复制代码
flexDirection: {
  default: "row",
  [MEDIA_MOBILE]: "column",
},
justifyContent: {
  default: "space-betweem",
  [MEDIA_MOBILE]: "center",
},
alignItems: {
  default: "center",
  [MEDIA_MOBILE]: "space-between",
},

通过上面的样式声明,我们定义了用于 JSX 元素的局部原子样式。

stylex.props- 在 Stylex 中应用样式

然后,我们将上述声明的样式应用于我们的 JSX 标记中,内联和原子化:

javascript 复制代码
return (
  <main {...stylex.props(style.main)}>
    <div {...stylex.props(style.jumbotron)}>
      <div {...stylex.props(style.jtBody)}>
        <h1 {...stylex.props(style.jtHeading)}>Hello, world!</h1>
        <p {...stylex.props(style.jtText)}>
          This is a template for a simple marketing or informational website. It includes a large callout called a
          jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.
        </p>
      </div>
      <div {...stylex.props(style.jtFooter)}>
        <a {...stylex.props(style.jtButton)} href="#" role="button">
          Learn more >>
        </a>
      </div>
    </div>

    <div {...stylex.props(style.deck)}>
      <Card featuredBg={{ backgroundColor: "orange" }}>
        <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
        <p {...stylex.props(style.cardText)}>
          Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
          condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
          Donec sed odio dui.{" "}
        </p>
        <p>
          <a href="#" role="button">
            View details >>
          </a>
        </p>
      </Card>
      <Card>
        <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
        <p {...stylex.props(style.cardText)}>
          Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
          condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
          Donec sed odio dui.{" "}
        </p>
        <p>
          <a href="#" role="button">
            View details >>
          </a>
        </p>
      </Card>
      <Card>
        <h2 {...stylex.props(style.cardHeading)}>Heading</h2>
        <p {...stylex.props(style.cardText)}>
          Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris
          condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod.
          Donec sed odio dui.{" "}
        </p>
        <p>
          <a href="#" role="button">
            View details >>
          </a>
        </p>
      </Card>
    </div>
  </main>
);

请注意,对于应用的每种样式,我们调用该方法并将样式对象属性作为其参数传递。在后台,Stylex 获取它生成的带有前缀的 CSS 类标识符,并将其添加到 JSX 元素的属性中。stylex.props``x``className

我们能够将多种样式传递给所有样式,并将其合并到一个类中。当特殊性成为合并中的一个问题时,最后一个样式的排名最高。请随时在文档的这一部分中了解更多信息。stylex.props()

在 Next.js 应用程序中使用 stylex 变量

如上所示,我们已经在组件中使用了全局 Stylex 变量 ,:$``<Home />

css 复制代码
{
    fontFamily: $.fontSans,
}

我们正在使用 Stylex 文档中的这些示例令牌,并在我们的应用程序中使用它们的字体。这个特定示例提供了对 Stylex 变量在设计复杂的动态响应式布局方面的功能的见解,这些布局具有易于自定义的颜色、间距、排版、主题等变体。

为了获得一个想法,在这篇文章中,我们将探讨如何定义最简单的调色板集。

Stylex 变量 - 定义和使用变量stylex.defineVars

除了修改我们的页面和组件之外,我们还使用以下命令声明了一组颜色:app/stylex/cssVars.ts``stylex.defineVars

app/stylex/cssVars.ts

php 复制代码
import stylex from "@stylexjs/stylex";

export const colors = stylex.defineVars({
  primary: "#007bff",
  secondary: "#f8f9fa",
  white: "#f8f9fa",
});

stylex.defineVars的工作是使导出的变量全局可用于发出的静态 CSS 样式和 TS 应用程序,以便从其任何 React 组件导入。在我们的应用程序中,我们能够使用必要的颜色 Stylex ,在内部和内部:colors``colors``app/page.tsx``<Card />

yaml 复制代码
  backgroundColor: colors.secondary,

Stylex 中的静态类型样式

Stylex 将编译时类型检查应用于传递给组件的样式属性。它是 Flow 的典型代表,并且还具有 TypeScript 所需的所有支持。

例如,该组件接受一个类型化为 type 的 prop:<Card />``featuredBg``StyleXArray<any>

typescript 复制代码
import stylex from "@stylexjs/stylex";
import { ReactNode } from "react";
import { StyleXArray } from "@stylexjs/stylex/lib/StyleXTypes";
import { colors } from "../stylex/cssVars.stylex";

const styles = stylex.create({
  card: {
    margin: "32px",
    padding: "16px",
    border: "1px spolid transparent",
    borderRadius: "8px",
    backgroundColor: colors.secondary,
  },
});

type Props = Readonly<{
  featuredBg?: StyleXArray<any>;
  children: ReactNode;
}>;

export default function Card({ featuredBg, children }: Props) {
  return <div {...stylex.props(styles.card, featuredBg)}>{children}</div>;
}

Stylex 样式的静态类型允许它们被准确地类型化,并为我们的代码库提供类型安全性和稳定性。

总结

在这篇文章中,我们通过在 Next.js 应用程序中应用样式来快速介绍 Stylex 的基础知识。我们特别关注核心和 API,用于为我们的页面和组件定义 CSS-in-JS 样式。我们还介绍了如何定义 Stylex 变量并将其与该方法一起使用。最后,我们看到了一个如何在组件中使用 Stylex 静态类型化样式属性的示例。stylex.create``stylex.props``stylex.defineVars

这些 API 使 Stylex 成为内联和静态 CSS 样式的强大工具箱,有助于生成可重用、可扩展和高性能的样式,以满足可扩展的大型应用程序的需求。Stylex 是一个处于起步阶段的新生库,由于其全局可用的变量、干净和精简的 API 表面的能力,我们应该很快就会看到在其之上构建的更强大、更小的 UI 框架。

相关推荐
旧林84318 分钟前
第八章 利用CSS制作导航菜单
前端·css
yngsqq29 分钟前
c#使用高版本8.0步骤
java·前端·c#
Myli_ing1 小时前
考研倒计时-配色+1
前端·javascript·考研
余道各努力,千里自同风1 小时前
前端 vue 如何区分开发环境
前端·javascript·vue.js
PandaCave1 小时前
vue工程运行、构建、引用环境参数学习记录
javascript·vue.js·学习
软件小伟1 小时前
Vue3+element-plus 实现中英文切换(Vue-i18n组件的使用)
前端·javascript·vue.js
醉の虾2 小时前
Vue3 使用v-for 渲染列表数据后更新
前端·javascript·vue.js
张小小大智慧2 小时前
TypeScript 的发展与基本语法
前端·javascript·typescript
hummhumm2 小时前
第 22 章 - Go语言 测试与基准测试
java·大数据·开发语言·前端·python·golang·log4j
asleep7012 小时前
第8章利用CSS制作导航菜单
前端·css