硬核干货——《中小企业 AI 实战指南》免费下载! 了解详情
写点什么

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:005073

评论

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

React源码解读之任务调度

flyzz177

React

React-Hook最佳实践

xiaofeng

React

6个步骤强化 CI/CD 安全

SEAL安全

Paddle Graph Learning (PGL)图学习之图游走类node2vec模型[系列四]

汀丶人工智能

图神经网络 11月月更

React源码解读之更新的创建

flyzz177

React

通俗易懂的React事件系统工作原理

夏天的味道123

React

React-diff原理及应用

xiaofeng

React

火山引擎 DataTester 首推A/B实验经验库,帮助企业高效优化实验设计能力

字节跳动数据平台

大数据 A/B测试

华为云Astro的前世今生:用7年时间革新低代码开发观念

华为云开发者联盟

低代码 华为云

查看、校验、归档…带你掌握openGauss账本数据库

华为云开发者联盟

数据库 后端 华为云

微博:公布热搜算法!

博文视点Broadview

彻底搞懂Vue虚拟Dom和diff算法

yyds2026

Vue

测试大咖漫谈如何搞定软件质量?

测吧(北京)科技有限公司

软件测试

「Go工具箱」推荐一个轻量级、语义化的时间处理库:carbon

Go学堂

golang 开源 程序员 carbon 日期时间转换

React核心技术浅析

夏天的味道123

React

浪潮信息工程师:谈一谈设备透传虚拟机启动慢背后的原因及其优化方法 | 第 51 期

OpenAnolis小助手

Linux 系统运维 内核 龙蜥大讲堂 浪潮

使用EasyCV Mask2Former轻松实现图像分割

阿里云大数据AI技术

深度学习 计算机视觉 图像处理 图像分割 企业号十月 PK 榜

计算机网络:以太网与IEEE 802.3

timerring

计算机网络 11月月更

HummerRisk V0.5.1 发布:新增对象存储、优化K8s 资源态势和资源拓扑等

HummerCloud

Kubernetes 云原生 云安全 云原生安全

【LeetCode】字符串相加Java题解

Albert

算法 LeetCode 11月月更

虚拟机、沙箱和容器之间的区别

Onegun

容器 虚拟机 沙箱

聊聊Vuex原理

yyds2026

Vue

count(*)查询性能很差?用这5招轻松优化

小小怪下士

Java 程序员 后端

数据库独角兽SingleStore:没有HTAP,机器学习和人工智能都是不切实际的

StoneDB

数据库 开源 HTAP StoneDB SingleStore

React源码解读之React Fiber

flyzz177

React

移动前端的安全管理方案

Onegun

前端 安全

这可能是你需要的React实战技巧

夏天的味道123

React

读懂React原理之调和与Fiber

xiaofeng

React

技术界中的虚拟机、容器和沙箱的关系

FinFish

容器 虚拟机 安全沙箱

vue的几个提效技巧

yyds2026

Vue

多视角碰撞,探索 Serverless 企业落地更多可能性丨阿里云用户组厦门站

云布道师

阿里云 云原生

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