写点什么

ReactJS 中的 CSS 架构

  • 2017-08-15
  • 本文字数:3253 字

    阅读完需:约 11 分钟

我们生活在一个新的时代,每一天都充满了各种各样的新工具和范式。我们总是试图将旧有的架构应用在新技术上,而那样极可能以失败告终。 其中的一个例子便是 BEM—— 一个 CSS 命名约定,它解决的是那些可能不会再次出现的问题。

先来讲一讲重要的背景知识。

BEM**** 是什么?

BEM 是 CSS 的一个命名约定,遵从简单而直接的哲学:代码的一致性、伸缩性和可重用性。这个方法论正是源于它的名字:Block-Element-Modifier。这意味着所有的类会被拆分成三个实体,每一个实体在架构和代码组织上都具有不同的目的和角色。

  • 块(Block)****: 具备独立逻辑和功能的组件。
  • 元素(Element)****: 块中没有独立意义的部分。
  • 修改器(Modifier)****: 定义块或元素的行为和外观。

当讨论遵从 BEM 规范的选择器时,有以下三条规则:

  • 只使用 CSS 类选择器。
  • 类名可以包含数字但不应该包含特殊字符。
  • 使用连字符来分隔单词。

BEM 实体应该是这样的:

复制代码
/* 块的名字,也是块中元素和修改器的命名空间 */
.block
/* 块的命名空间后面紧跟下划线和元素名字 */
.block__element
/* 块或者元素的命名空间后紧跟连字符和修改器的名字 */
.block--modifier,
.block__element--modifier

为什么要使用BEM

BEM 哲学的提出基于以下几个前提:代码的一致性、代码的伸缩性、代码的可重用性、生产力和团队协作。从 BEM 的哲学出发,不得不说 BEM 是非常好的范例,并且已经在全球范围内被来自大公司(如 Google 和 Twitter)的许多开发者所采用。

如果想要了解关于这个方法论更多的信息,可以阅读这篇文章: CSS Architectures

让我们开始吧

当谈到代码的命名规范和代码结构时,不得不提到 BEM。BEM 很简单并且功能完善。然而,当谈论到 CSS 架构时还有另外一点必须考虑,那就是目录结构。于是我们不得不提到另一种架构:ITCSS

ITCSS 是由 Harry Roberts 提出的一个方法论,用于创建、管理和衡量大型的 CSS 项目。他曾说:

ITCSS__ 是一种方法论,旨在将整个 __CSS__ 项目可视化为一个分层的倒置三角形。这种分层架构代表了一个模型,可以帮助你以最有效、最节省资源的方式使用 __CSS_。_

本文不会深入探讨这个方法论,不过我们推荐阅读 Harry 的文章以掌握其背后蕴含的概念。简而言之,ITCSS 主张以分层的方式组织代码,以倒三角的方式组织结构,这样我们就可以在顶层定义通用元素,在底部定义特定元素。

这里的关键在于我们可以使用分层的方式作为一种有效的目录结构来组织代码。在我之前的经验中,我所使用的方法与其提出的原始目的有一点不同,我会用以下的方式对代码进行分层:

  • Settings: 基本配置和变量。
  • Tools: 函数和 mixins。
  • Base: 应用到每一个页面的基本样式。在这里放置 reset/normalize 的代码最好不过了。
  • Components: 组成界面的 UI 组件。
  • Utilities: 非常具体的规则,这是唯一可以使用!important 的层。

现在我们需要做的事情是混合这两个方法,然后一个强大且简单的 CSS 架构便唾手可得。

ReactJS**** 的方式

在过去几年中,BEM+ITCSS架构满足了我们所有的需求,直到一切都开始发生变化。当 ReactJS 成为主要的单页面应用库,以组件化进行思考的方式较以前便有些不同。现在,每一个组件是一个 JS 模块,并且每个模块中的 HTML 结构完全关联该组件,并且通过自定义属性可以动态变更组件状态和变量。不再需要将样式标签与语义化的 HTML 结构关联起来,现在你可以创建完全逻辑化的动态功能性组件。

所以在新的场景下我们怎样处理样式和代码结构呢?保持样式和 React 组件相分离,使用 BEM+ITCSS 方法,或者彻底改为使用CSS-in-JS方式?我会建议介于这两者之间,并且利用我们之前所积累的知识。

Think-Adapt-First**** 方式

使用这些方法论和命名约定的想法源于以下原因:保持样式隔离、创建具有唯一性的选择器并在组件作用域内理清选择器之间的关系。人们使用BEM约定并不是因为他们喜欢使用双下划线或双连字符,而是因为 BEM 有助于提升代码的一致性、伸缩性、可重用性、生产力和可预测性。这些都是可行性的前提,所以为什么不一直用它呢?

因为我们不能。当我们决定在项目中引入React时架构发生了改变。于是,一些限制、模式和新的可能性出现了。这是将我们以前的规则应用于新环境的最佳时机。下面是我强烈推荐给你的工具。

CSS**** 模块

为了说明 CSS 模块的所有优势和功能,我想引用其作者 Glen Maddern 的话。除了它能够带来成堆的好处,最主要的就是CSS**** 本地作用域。现今,所有的 React 组件都可以在逻辑和呈现状态上进行完全的隔离。下面是代码结构的直观显示:

在 style.css 中不再需要保持经典的 BEM 约定。依据 BEM 约定,我们应该将第一个实体作为组件的命名空间,但是现在有了 CSS 模块,这种关系会基于 JS 组件名进行动态的创建。

复制代码
/*
* Reset
*/
.button {
appearance: none;
border: 0;
background-color: white;
cursor: pointer;
color: #333;
font-size: .9rem;
font-weight: bold;
}
.button:focus,
.button:focus:hover {
border-color: #51a7e8;
box-shadow: 0 0 5px rgba(81, 167, 232, .5);
outline: 0;
}
/*
* Sizes
*/
.small {
padding: .5em 1.4em;
}
.medium {
padding: .8em 1.4em;
}
.large {
padding: 1.2em 2.2em;
}
/*
* Themes
*/
.default {
border: 1px solid #ddd;
border-radius: 3px;
background: linear-gradient(to bottom, #fefefe 0%, #ddd 100%);
}
.default:hover {
border-color: #bbb;
background: #ddd;
}
.default:active {
border-color: #aaa;
background: linear-gradient(to bottom, #bbb 0%, #ddd 44%);
}

按照规则,你的组件中应只包含需要的类。我们可以通过下面的方式来描述一个按钮:

复制代码
import PropTypes from 'prop-types'
import React from 'react'
import styles from './styles.css'
export const ButtonType = {
BUTTON: 'button',
RESET: 'reset',
SUBMIT: 'submit',
}
export const ButtonTheme = {
DEFAULT: 'default',
POSITIVE: 'positive',
DANGER: 'danger',
}
export const ButtonSize = {
SMALL: 'small',
MEDIUM: 'medium',
LARGE: 'large',
}
const Button = ({ type, onClick, children, theme, size }) => {
const className = `${styles.button} ${styles[theme]} ${styles[size]}`
return ({children})
}
Button.propTypes = {
type: PropTypes.oneOf(Object.keys(ButtonType)),
theme: PropTypes.oneOf(Object.keys(ButtonTheme)),
size: PropTypes.oneOf(Object.keys(ButtonSize)),
onClick: PropTypes.func.isRequired,
children: PropTypes.node.isRequired,}
Button.defaultProps = {
type: ButtonType.BUTTON,
theme: ButtonTheme.DEFAULT,
size: ButtonSize.MEDIUM,
}
export default Button

好的,下面我们给出组件分层的解决方案。那么如何定义全局样式呢,比如设置、规范化和重置?

ITCSS**** 复活

既然基于 ITCSS 的分层目录结构仍然可以完美匹配我们的需求,那么为什么不继续使用它呢?

上面的想法是在src**** 目录中创建与 ITCSS 同风格的样式文件目录,除了组件的文件目录外,因为 React 已经默认定义了组件的文件目录结构。

现在,我们会得到下面的目录结构:

我们可以使用:global标签在主应用程序中导入样式文件,样式可以作为全局的类来使用。

再应用Think-Adapt-First方式

该结构背后的理念是通过以一种可伸缩的方式保持CSS**** 架构创建更好的 ReactJS 项目,可以支持成千上万的组件和开发人员协同工作。

然而本文的真正关键点在于打开你的思维,去适应新事物!有时候适应是很困难的,因为对于难以掌控的事物,你必须放手,但是请不要忘记,你已经解决的问题可能将以一去不复返。

所以,年轻的朝圣者们,请持续探索知识。

查看英文原文 CSS Architecture with ReactJS


感谢薛命灯对本文的审校。

给InfoQ 中文站投稿或者参与内容翻译工作,请邮件至 editors@cn.infoq.com 。也欢迎大家通过新浪微博( @InfoQ @丁晓昀),微信(微信号: InfoQChina )关注我们。

2017-08-15 19:004833

评论

发布
暂无评论
发现更多内容

Django笔记三十四之分页操作

Hunter熊

Python django 分页

2023-05-21:给定一个字符串 s 和一个整数 k 。你可以从 s 的前 k 个字母中选择一个, 并把它加到字符串的末尾。 返回 在应用上述步骤的任意数量的移动后,字典上最小的字符串。 输入:s

福大大架构师每日一题

Go 算法 福大大

如何通过Makefile优化加速编译过程提高开发效率

小万哥

c++ 程序员 面试 后端 开发

如何借助Kafka持久化存储K8S事件数据?

SEAL安全

kafka 云原生 k8s Seal软件 企业号 5 月 PK 榜

敏捷开发:新一代软件开发模式的优越性与挑战

xfgg

Java 架构 开发效率

软件测试/测试开发丨Python基本数据类型之字符串

测试人

Python 软件测试 自动化测试 测试开发

C语言编程—存储知识

芯动大师

使用 OpenAPI 构建 API 文档

江湖十年

golang Web 后端 swagger OpenAPI

我出版了一本关于TikTok电商运营的书

博文视点Broadview

三周年连更活动结果已出炉,快来看看有没有你

InfoQ写作社区官方

热门活动 三周年连更

从热爱到深耕,在开发路上的他们勇敢逐梦

HarmonyOS SDK

HMS Core

非常实验——在SSH下通过终端浏览网页

吴脑的键客

浏览器 终端工具

为什么MySQL单表不能超过2000万行?

华为云开发者联盟

数据库 后端 华为云 华为云开发者联盟 企业号 5 月 PK 榜

靠AI自动生成视频撸自媒体收益,月入5000+

派大星

ChatGPT4

Ribbon默认负载均衡规则替换为NacosRule

越长大越悲伤

Spring Cloud

建造者模式:将对象的构建与表示分离,提高程序的可维护性和可扩展性

Jack

golang 设计模式

面试复盘:哈希冲突的常见解决方案?

javacn.site

如何列举测试点

FunTester

三周年主题征文活动结果已出炉,快来看看有没有你

InfoQ写作社区官方

热门活动 三周年征文

菜单栏应用管理:Bartender 4 激活中文版

真大的脸盆

Mac Mac 软件 菜单栏管理工具 菜单栏软件

HashTable 在蚂蚁转化归因中的极致运用

阿里云大数据AI技术

大数据 开发者 企业号 5 月 PK 榜

抠图党福音:教你一键分割图像

华为云开发者联盟

华为云 华为云开发者联盟 企业号 5 月 PK 榜 人工资高hi嗯呢该 分割图像

深度学习基础入门篇[8]::计算机视觉与卷积神经网络、卷积模型CNN综述、池化讲解、CNN参数计算

汀丶人工智能

人工智能 深度学习 计算机视觉 池化 CNN网络

国内AI大模型 VS chatgpt (20)-- H265/H264有何不同,h265有什么优势?

福大大架构师每日一题

福大大 ChatGPT 文心一言

From Java To Kotlin:空安全、扩展、函数、Lambda很详细,这次终于懂了

Seachal

Java android Lambda kotlin 函数式编程

ReactJS中的CSS架构_语言 & 开发_Douglas Gimli_InfoQ精选文章