从0到1搭建react组件库-样式方案(一)

301 阅读6分钟

前言:

Vue采用单文件组件(SFC)模式,将模版、逻辑和样式组织在一个文件中。这种结构天然支持样式的模块化,使得传统CSS方案以及CSS预处理方案在Vue生态中一直占据主导地位。

相比之下,React起初没有官方的样式解决方案,这种情况激发了社区的创新,催生了多种多样的样式解决方案,从css-in-js 到 css module等各种方案并存发展(Vue中也有对应的方案)。

(文中如果有错误,欢迎交流指正。)

样式方案

接下来就来简单介绍一下这几种方案的特点,

CSS

传统CSS方案的优势就是简单直接,易于使用,并且浏览器原生支持,无需在编译时做出特殊处理,但是它不支持嵌套、混合等高级特性,导致开发体验不佳,并且容易造成全局污染。

CSS预处理器 (LESS、SCSS)

CSS预处理器在传统CSS的基础上进行拓展,支持变量、嵌套、混合等高级特性,增加了样式的可复用性,提升了开发体验。但是其在编译时需要转化为传统CSS,并且可能会导致CSS文件体积增大。依然存在全局污染的问题。

CSS In JS (styled components)

用JS来写样式,避免了全局污染,可以利用JS的特性灵活的处理动态样式的需求,便于实现主题化和样式的复用,但是也难以避免增加JavaScript的运行时开销,并且无法利用好浏览器的缓存特性。

Atom css(Tailwind)

原子化CSS的方案,提高了样式的复用性,减少了重复的CSS代码,并且一致性强,易于维护统一的风格,减少了样式冲突的可能性,但是HTML就会变得冗长,并且不适用于复杂的自定义设计。

上面看待这几种的方案视角是通过开发者的开发体验来进行评判的,各有优劣,没有绝对的好与不好,选择自己擅长,合适的解决方案即可。

开发范式:

组件库最终提供给开发者的使用体验也不尽相同。大致可以分为以下三种,详细内容查看React组件库CSS样式方案分析, 这篇文章对这几种情况进行了详细的介绍。

这里我们选择样式与JS分离的开发方案,也即用户需要单独引入JS和CSS文件的方式。

样式逻辑分离:

这种情况下,CSS文件是独立出来的,并且在组件的JS中不直接引入CSS文件。往往需要使用者在开发的过程中手动引入组件与对应的CSS文件。在样式逻辑分离的组件库中的组件打包后的目录结构类似下图    使用js作为最终导出的文件是为了方便在打包时能够进行tree sharking。

样式与逻辑关联

与样式逻辑分离的方案基本一致,但是其不同之处在于,组件的JS中会直接引入组件对应的样式文件,无需用户再次进行引入。但是需要在组件的打包结果中保留正确的样式引入语句(打包后的样式路径)。

样式与逻辑结合

通过css-in-js的方案,或者是通过打包过程中使用打包工具将CSS打包进 JS中。

浅析组件库插件如何实现按需加载样式:

在使用样式逻辑分离的开发范式中,不支持直接按需加载样式文件。

如果需要实现按需加载样式的功能时,可以通过在代码中引入指定的组件样式文件,取代入口文件中引入全局样式的方案。但是如果用户需要自己在引入组件的同时还需要引入当前组件的样式文件,无疑为用户带来了较差的用户体验。

因此现代组件库在使用样式与逻辑分离的范式时,往往会为用户提供一个对应打包工具的插件来便捷的实现按需加载的需求,这样开发者的使用体验与上面直接按需加载的方案开发体验一致,其背后的实现的大致原理如下:

  1. 首先从打包工具的提供的钩子中获取到文件的ast抽象语法树, 并且游历ast语法树中的节点。
  2. 解析语法树中的节点,如果当前节点是引入声明(import语句)就继续向下执行。
  3. 判断导入节点中的文件路径是否对应组件库的路径文件名。(例如arco就是判断是否等于"@arco-desgin/web-react")如果相等继续向下执行。
  4. 根据导入的组件名称,组装出样式文件的路径。
  5. 判断当前样式文件是否存在,如果存在于项目中,则调用babel的库 @babel/helper-module-imports 向抽象语法树中添加样式的路径导入。

最终的效果会类似下面的内容:

// 原始导入语句
import { Button } from "@arco-design/web-react"
// 转换后效果
import { Button } from "@arco-design/web-react"
import "@arco-design/web-react/es/Button/style/index.js"

色值体系(与arco组件库颜色一致):

整体色彩样式的架构遵循了一个层级分明的色彩管理系统。颜色这里为了省事借鉴了arco的组件库,但是对色值的实现进行了简化。

  • 色值体系: 建立一个全面的色彩体系,定义不同色阶的色卡,确保视觉的一致性。
  • 全局色值变量: 从色值体系中提取常用的关键色值,定义为全局变量。这些变量通常会包含企业的主题色,功能色(成功、警告、错误)和中性色。
  • 组件色值变量: 基于全局色值变量,为每个组件的状态定义特定的色值变量。
  • 组件状态匹配: 在组件的样式文件中,根据组件的不同状态(默认、悬停、激活、禁用等)匹配相应的色值变量。 在这里通过less的混合(mixins)和函数功能来动态的计算和应用这些哦颜色。

  色值体系 -> 全局色值变量 -> 组件色值变量 -> 组件状态匹配色值变量。

packages/ui/src下新建一个styles文件夹,用来管理组件库中的色值体系、全局色值变量内容、一些变量的声明。

在styles下新建一个index.less用来声明全局的色值文件。

packages/ui/src/styles/下新建一个theme文件夹, 然后新建global.css, variable.css分别用来存储css变量(色值体系)和全局色值变量。

由于都是配置,大量且繁琐,这里就不展示代码,详情查看代码仓库。

packages/ui/src/components/Button/style/index.less按照下面代码修改。

@import "../../../styles/index";
.mini-btn {
  color: #1a1a1a;
  background-color: @color-primary-1;
}

在website(文档)中查看具体的效果, 如果色值体系生效,即配置成功。

仓库地址

未完待续

下面的一章中,会补全组件色值变量以及组件状态匹配色值变量部分逻辑。

往期文章

从0到1搭建react组件库-项目规范篇

从0到1搭建react组件库-开发方案篇

从0到1搭建react组件库-文档篇