您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

你了解 JSX,那你了解 StyleX 么?

时间:2023-12-08 12:13:46  来源:微信公众号  作者:魔术师卡颂

大家好,我卡颂。

近日,Meta开源了一款「css-in-JS库」 —— StyleX。看命名方式,Style - X是不是有点像JS - X,他们有关系么?当然有。

JSX是一种「用JS描述html」的语法规范,广泛应用于前端框架中(比如React、SolidJS...),由Meta公司提出。

同样的,按照Meta的设想,StyleX是一种「用JS描述CSS」的语法规范。

早在React Conf 2019[1],Meta工程师「Frank」就介绍了这种Meta内部使用的「CSS-in-JS库」。

你了解 JSX,那你了解 StyleX 么?

从Meta内部使用,到大会对外宣传,这期间肯定已经经历大量内部项目的洗礼。而从做完宣传到最终开源,又经历了快5年时间。

那么,这款Meta出品、打磨这么长时间的「CSS-in-JS库」,到底有什么特点呢?

本文让我们来聊聊。

为什么需要CSS解决方案

市面上有非常多「CSS解决方案」,比如:

  • BEM命名规范
  • CSS Module规范
  • 原子CSS(比如TAIlwindCSS)
  • CSS-in-JS(比如emotion)

为什么需要这些方案?原生CSS哪里不好?在这里,我们举个小例子(例子来源于「React Conf 2019」)。考虑如下代码:

CSS文件如下:

.blue {color: blue;}
.red {color: red;}

HTML文件如下:

<p class="red blue">我是什么颜色?</p>

请问p标签是什么颜色的?

从class来看,blue在red后面,p应该是蓝色的么?

实际上,样式取决于他们在样式表中定义的顺序,.red的定义在.blue后面,所以p应该是红色的。

是不是已经有点晕了?再增加点难度。如果.red和.blue分别在两个文件中定义呢?

# css文件1
.blue {color: blue;}
# css文件2
.red {color: red;}

那p的样式就取决于最终打包代码中样式文件的加载顺序。

上面只是原生CSS中「选择器优先级相关的一个缺陷」(除此外还有其他缺陷,比如「作用域缺失」...)。随着项目体积增大、项目维护时间变长、项目维护人员更迭,这些缺陷会被逐渐放大。

正是由于这些原因,才出现了各种「CSS解决方案」。

StyleX的基本使用

StyleX的API很少,掌握下面两个就能上手使用:

  • stylex.create,创建样式。
  • stylex.props,定义props。

比如:

import * as stylex from 'stylex';

// 创建样式
const styles = stylex.create({
  red: {color: 'red'},
});

// 定义props
const redStyleProps = stylex.props(styles.red);

使用时:

<div {...redStyleProps}>文字颜色是红色</div>

stylex是如何解决上面提到的red blue优先级问题呢?其实很简单,考虑如下代码:

import * as stylex from 'stylex';

// 创建样式
const styles = stylex.create({
  red: {color: 'red'},
  blue: {color: 'blue'}
});

// 使用
<p {...styles.props(styles.red, styles.blue)}></p>

样式的优先级只需要考虑styles.props中的定义顺序(blue在red后面,所以颜色为blue),不需要考虑样式表的存在。

有些同学会说,看起来和常见的CSS-in-JS没啥区别啊。那stylex相比于他们的优势是啥呢?

相比其他CSS-in-JS的优势

首先要明确,stylex虽然以CSS-in-JS的形式存在,但本质上他是一种「用JS描述CSS的规范」。文章开头也提到,他的定位类似JSX。

既然是规范,那他就不是对CSS的简单封装、增强,而是一套「自定义的样式编写规范」,只不过这套规范最终会被编译为CSS。

作为对比,Less、Sass这样的「CSS预处理器」就是对CSS语法的封装、增强

那么,stylex都有哪些规范呢?

比如,stylex鼓励将样式与组件写在同一个文件,类似Vue的SFC(单文件组件)。这么做除了让组件的样式与逻辑更方便维护,也减少了stylex编译的实现难度。

再比如,CSS中各种选择器的复杂组合增强了选择器的灵活性。但同时也增强了不确定性。举个例子,考虑如下三个选择器:

  • .className > *
  • .className ~ *
  • .className:hover > div:first-child

这些对.className应用的选择器将影响.className的某些后代。当这样的选择器多了后,很可能会在开发者不知道的情况下改变某些后代元素的样式。

遇到这种情况我们一般会怎么处理呢?正确的选择当然是找到上述影响后代的选择器,再修改他。

但大家工作都这么忙,遇到这种问题,多半就是用新的选择器覆写样式,必要的时候还会加!important后缀。久而久之,这代码就没法维护了。

为了规避这种情况,在stylex中,除了「可继承样式」(指「当父元素应用后,子孙元素默认会继承的样式」,比如color)外,不支持这些「可以改变子孙后代样式的选择器」。

那我该如何让子孙组件获得父组件同样的样式呢?通过props透传啊~

也就是说,stylex禁用了CSS中可能造成混淆的选择器,用JS的灵活性弥补这部分功能的缺失。

有些同学可能会说:这些功能,其他「CSS-in-JS库」也能做啊。

这就要谈到「CSS-in-JS库」最大的劣势 —— 为了计算出最终样式,在运行时会造成额外的样式计算开销。

stylex通过编译来减少运行时的开销。比如对于上面提到过的stylex的代码:

import * as stylex from 'stylex';

// 创建样式
const styles = stylex.create({
  red: {color: 'red'},
});

// 定义props
const redStyleProps = stylex.props(styles.red);

编译后的产物包括如下两部分:

JS的编译产物:

import * as stylex from 'stylex';
const redStyleProps = {className: 'x1e2nbdu'};

CSS的编译产物:

.x1e2nbdu {
  color: red;
}

所以,运行时实际运行的代码始终为:

<div {...{className: 'x1e2nbdu'}}>...</div>

对于再复杂的样式,stylex都会通过编译生成「可复用的原子类名」。

即使是跨文件使用样式,比如我们在另一个文件也定义个使用color: 'red'样式的stylex属性foo:

import * as stylex from '@stylexjs/stylex';

const styles = stylex.create({
  foo: {
    color: 'red',
  },
  bar: {
    backgroundColor: 'blue',
  },
});

会得到如下编译结果,其中x1e2nbdu是一个原子类名,他是上一个文件中styles.red的编译产物:

import * as stylex from '@stylexjs/stylex';

const styles = {
  foo: {
    color: 'x1e2nbdu',
    $$css: true,
  },
  bar: {
    backgroundColor: 'x1t391ir',
    $$css: true,
  },
};

随着项目体积增大,样式表的体积也能控制在合理的范围内。这种对原子类名的控制粒度是其他「CSS-in-JS库」办不到的。

相比于原子CSS的优势

stylex相比TailwindCSS这样的原子CSS有什么优势呢?

这就要谈到原子CSS的一个特点 —— 使用约定好的字符串实现样式。比如,使用TailwindCSS定义图片的样式:

<img class="w-24 h-24 rounded-full mx-auto" src="/sarah-dayan.jpg" alt="" width="384" height="512">

效果如下:

你了解 JSX,那你了解 StyleX 么?

由于样式都是由不同的「原子类名字符串」组合而成,TS没法分析,这就没法实现「样式的类型安全」。

什么叫「样式的类型安全」?通俗的讲,如果我实现一个组件,组件通过style props定义样式,我只希望使用者能够改变color与fontSize两个样式属性,不能修改其他属性。如果能实现这一点,就是「样式的类型安全」。

「样式的类型安全」有什么意义呢?举个例子:设想开发基础组件库的团队使用stylex。那么当业务团队使用该组件库时,就只能自定义组件的一些样式(由组件库团队约束)。

当基础组件库升级时,组件库团队能很好对组件样式向下兼容(因为知道只有哪些样式允许被修改)。

在stylex中,由于stylex.create的产物本质是对象,所以我们可以为每个产物定义类型声明。比如在如下代码中,我们限制了组件style props只能接受如下stylex样式:

import type {StyleXStyles} from '@stylexjs/stylex';

type Props = {
  // ...
  style?: StyleXStyles<{
    color?: string;
    backgroundColor?: string;
    borderColor?: string;
    borderTopColor?: string;
    borderEndColor?: string;
    borderBottomColor?: string;
    borderStartColor?: string;
  }>;
};

总结

我猜想,当更多人知道stylex后,他会收到比当初TailwindCSS火时更多的两级分化的评价。

毕竟,stylex的设计初衷是为了解决Meta内部复杂应用的样式管理。如果:

  • 你项目没有达到Meta这样的体量
  • 你项目没有多年的迭代周期
  • 你项目前前后后没有多个工程师经手

那大概率是不能接受stylex设计理念中的这些约束。

对于stylex,你怎么看?

参考资料

[1]React Conf 2019:https://www.YouTube.com/watch?v=9JZHodNR184&t=270s。



Tags:JSX   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,不构成投资建议。投资者据此操作,风险自担。如有任何标注错误或版权侵犯请与我们联系,我们将及时更正、删除。
▌相关推荐
你了解 JSX,那你了解 StyleX 么?
大家好,我卡颂。近日,Meta开源了一款「CSS-in-JS库」 &mdash;&mdash; StyleX。看命名方式,Style - X是不是有点像JS - X,他们有关系么?当然有。JSX是一种「用JS描述HTML」的语法...【详细内容】
2023-12-08  Search: JSX  点击:(157)  评论:(0)  加入收藏
JSX是Vue前端开发的未来吗?
在前端开发中,Vue 一直以其简单、高效的框架而备受开发者青睐。然而,随着 React 在市场上的流行,许多开发者开始对 JSX(JavaScript XML)这种声明式编程风格产生兴趣。本文将探讨...【详细内容】
2023-08-16  Search: JSX  点击:(256)  评论:(0)  加入收藏
实现JSX的转换
当我们在项目中使用 React 构建界面时,主要使用的就是 React 包。它提供了开发者需要的所有API。如React.Component、React.createElement、React.useState等等,所以它也是大...【详细内容】
2023-03-20  Search: JSX  点击:(127)  评论:(0)  加入收藏
简单理解JavaScript,TypeScript和JSX
JavaScript:基本概念:JavaScript一种直译式脚本语言,是一种动态类型、弱类型、基于原型的语言,内置支持类型。它的解释器被称为JavaScript引擎,为浏览器的一部分,广泛用于浏览...【详细内容】
2019-06-18  Search: JSX  点击:(1194)  评论:(0)  加入收藏
▌简易百科推荐
即将过时的 5 种软件开发技能!
作者 | Eran Yahav编译 | 言征出品 | 51CTO技术栈(微信号:blog51cto) 时至今日,AI编码工具已经进化到足够强大了吗?这未必好回答,但从2023 年 Stack Overflow 上的调查数据来看,44%...【详细内容】
2024-04-03    51CTO  Tags:软件开发   点击:(5)  评论:(0)  加入收藏
跳转链接代码怎么写?
在网页开发中,跳转链接是一项常见的功能。然而,对于非技术人员来说,编写跳转链接代码可能会显得有些困难。不用担心!我们可以借助外链平台来简化操作,即使没有编程经验,也能轻松实...【详细内容】
2024-03-27  蓝色天纪    Tags:跳转链接   点击:(12)  评论:(0)  加入收藏
中台亡了,问题到底出在哪里?
曾几何时,中台一度被当做“变革灵药”,嫁接在“前台作战单元”和“后台资源部门”之间,实现企业各业务线的“打通”和全域业务能力集成,提高开发和服务效率。但在中台如火如荼之...【详细内容】
2024-03-27  dbaplus社群    Tags:中台   点击:(8)  评论:(0)  加入收藏
员工写了个比删库更可怕的Bug!
想必大家都听说过删库跑路吧,我之前一直把它当一个段子来看。可万万没想到,就在昨天,我们公司的某位员工,竟然写了一个比删库更可怕的 Bug!给大家分享一下(不是公开处刑),希望朋友们...【详细内容】
2024-03-26  dbaplus社群    Tags:Bug   点击:(5)  评论:(0)  加入收藏
我们一起聊聊什么是正向代理和反向代理
从字面意思上看,代理就是代替处理的意思,一个对象有能力代替另一个对象处理某一件事。代理,这个词在我们的日常生活中也不陌生,比如在购物、旅游等场景中,我们经常会委托别人代替...【详细内容】
2024-03-26  萤火架构  微信公众号  Tags:正向代理   点击:(10)  评论:(0)  加入收藏
看一遍就理解:IO模型详解
前言大家好,我是程序员田螺。今天我们一起来学习IO模型。在本文开始前呢,先问问大家几个问题哈~什么是IO呢?什么是阻塞非阻塞IO?什么是同步异步IO?什么是IO多路复用?select/epoll...【详细内容】
2024-03-26  捡田螺的小男孩  微信公众号  Tags:IO模型   点击:(8)  评论:(0)  加入收藏
为什么都说 HashMap 是线程不安全的?
做Java开发的人,应该都用过 HashMap 这种集合。今天就和大家来聊聊,为什么 HashMap 是线程不安全的。1.HashMap 数据结构简单来说,HashMap 基于哈希表实现。它使用键的哈希码来...【详细内容】
2024-03-22  Java技术指北  微信公众号  Tags:HashMap   点击:(11)  评论:(0)  加入收藏
如何从头开始编写LoRA代码,这有一份教程
选自 lightning.ai作者:Sebastian Raschka机器之心编译编辑:陈萍作者表示:在各种有效的 LLM 微调方法中,LoRA 仍然是他的首选。LoRA(Low-Rank Adaptation)作为一种用于微调 LLM(大...【详细内容】
2024-03-21  机器之心Pro    Tags:LoRA   点击:(12)  评论:(0)  加入收藏
这样搭建日志中心,传统的ELK就扔了吧!
最近客户有个新需求,就是想查看网站的访问情况。由于网站没有做google的统计和百度的统计,所以访问情况,只能通过日志查看,通过脚本的形式给客户导出也不太实际,给客户写个简单的...【详细内容】
2024-03-20  dbaplus社群    Tags:日志   点击:(4)  评论:(0)  加入收藏
Kubernetes 究竟有没有 LTS?
从一个有趣的问题引出很多人都在关注的 Kubernetes LTS 的问题。有趣的问题2019 年,一个名为 apiserver LoopbackClient Server cert expired after 1 year[1] 的 issue 中提...【详细内容】
2024-03-15  云原生散修  微信公众号  Tags:Kubernetes   点击:(5)  评论:(0)  加入收藏
站内最新
站内热门
站内头条