家好,很高兴又见面了,我是"高级前端分享",由我带着大家一起关注前端前沿、深入前端底层技术,大家一起进步,也欢迎大家关注、点赞、收藏、转发!
最近读了一篇抵制 css-in-js 的文章,虽然大部分观点都有道理,但部分存在可商榷之处,让我们分析一下这篇文章,了解 css 还做了哪些努力,以及 css-in-js 会如何发展。
作者认为,模块化 jsx 让 html 结构与行为耦合在一起是很有价值的,然而样式却不应该与模块耦合起来,因为样式是一种全局行为。许多时候需要对网站进行全局的设计,将样式分散到模块中会导致更多的理解成本。
将样式与模块松耦合,系统会获得更大的自由度与拓展性。如果样式与结构松耦合,一套看似相似的的元素,可能拥有完全不同的底层结构。然而交互必须与结构紧耦合,因为交互依赖于结构。
局部样式会阻碍视觉一致性,只有全局化样式才能保证视觉一致性。
如果每个组件维护自己的样式,那么会存在许多样式代码复制粘贴的问题,复制粘贴的代码可维护性极低。
无论是 css-in-js 还是 css 预编译的尝试,各自都具有强大优点,本文对 css-in-js 提出的质疑我认为是欠妥当的,下面谈谈 css-in-js 如何解决作者提出的问题,以及简单介绍 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 的思路。
文中提出,网站样式要从全局考虑,模块化样式行为的优点是解决了样式冲突问题,但因此也削弱了对全局样式的把控。
开发单个组件的样式分为两种情况,分别是明确风格的组件与样式独立的组件,在样式独立组件中,由于不确定会被哪些主题的网站所引用,因此无论是全局 css 还是局部 css,都无法控制样式。在明确风格的情况下,可以先把此风格的基色确定下来,无论是抽成 sass 变量还是 js 变量,都具有可复用性。
全局 css 的开发,适合自上而下控制,组件通过定义 class 而不需要关心具体样式,通过全局 class 统一调控整体风格。而 css-in-js 是自下而上的,但需要预先抽出整体风格的样式模块,其效果与全局 css 是等价的。
全局 css 控制风格:
<style>
.container{}
.list-item{}
.submit-button{}
</style>
<div className="container">
<div className="list-item"></div>
<div className="list-item"></div>
<div className="submit-button"></div>
</div>
css-in-js 风格:
const CommonContainer = styled.div``
const CommonListItem = styled.div``
const CommonSubmitButton = css``
export const Container = styled(CommonContainer)``
export const ListItem = styled(CommonListItem)``
export const CommonSubmitButton = styled.div`
${CommonSubmitButton}
`
而 css-in-js 运行时的样式解析,让我们更轻易的切换主题。比如我们抽出一个公共样式包,业务代码中的色值都从此样式包中引用,那么在不同的环境下,公共样式包可能通过所在宿主环境的判断,返回给业务代码不同的色值,甚至与宿主环境配合,从宿主环境拿到注入的颜色,实现一套代码在运行时轻松换肤。
文中观点提出,css-in-js 这种局部样式行为,会导致公共样式、方法难以复用,导致各个模块参杂着大量重复代码。因为 sass 通过定义全局变量、mixins 方法让样式更具有复用性。
我觉得这是一种误解,在 css-in-js 模式中,通过全局合理的设计,使用 js 文件存放颜色变量、公共方法、可能会复用的 css 代码块,其复用能力远大于 sass。
OOCSS 成为 css 的面向对象加强版,每个 class 只处理一件事:
.size {width: 25%;}
.bgBlue {background:blue}
.g-bd2{margin:0 0 10px;}
网易 NEC 就大量使用了这种思想。
这样的好处在于避免了 class 之间的冗余,让我们更容易创建可复用的 class,也不会在命名上纠结。
然而,先不说 oocss 带来的巨大零散 class 导致的维护成本,以及修改 class 导致的巨大风险,class 的本意是语义化,如果让 class 使用一堆对象描述堆砌,我们将很难定位一个元素,也很难描述这个元素的含义。
SMACSS 认为 css 有 5 个类别:
我们通过这 5 种类别来拼凑出完整的 class,我感觉就是对 OOCSS 的进一步规范和约束。
对这 5 种类别,在命名时要加上对应前缀,分别是:
我觉得这样在语义化的基础上,拆分了状态、主题、布局,着实增强了 css 可读性。
尽可能减少适配层级,虽然增加适配层级会减少冲突发生率,但是会增加额外的阅读负担,以及一些 bug(旧版 ie 层级超过 255 导致样式失效)。
像 css-modules 这种解决方案恰恰反其道而行之,通过层级避免冲突,通过预编译解决阅读负担,然而在没有预编译的情况下,最小化适配深度原则依然是最有效的。
BEM 规范更像是 SMACSS 分类的加强版,通过 __element 表述后代,--modifier 表述状态,比如:
.article {}
.article__label {} /* label 元素 */
.article__label--selected {} /* label 元素处于被选中状态 */
类似 SMACSS 对 css 元素进行了分层:
ITCSS 的分层是非常有借鉴意义的,即便在 css-in-js 设计中,也可以参考此模式定义结构。
ECSS 的规范是这样的:
.nsp-Component_ChildNode-variant
例子:
<div class="tl-MediaObject">
<a href="#" class="tl-MediaObject_Link">
<img class="tl-MediaObject_Media" src="mini.jpg" alt="User">
</a>
<div class="tl-MediaObject_Attribution">@BF 14 minutes ago</div>
</div>
更多细节可以看此 PPT
虽然我认为这篇文章提出的 css-in-js 缺点大部分存在漏洞,但它警示了我们,css 设计的初衷是全局化控制样式,即便产生了样式冲突、混乱的问题,但我们仍要记住,在模块化开发的今天,仍要保持网站风格的整体性,即便使用了 css-in-js 的开发方式。
虽然作者呼吁我们不要只顾着 css-in-js,要放眼看看 OOCSS, SMACSS, BEM, ITCSS, 和 ECSS 等基于原生 css 的解决方案,但我觉得把这些思想运用到 css-in-js 是个不错的选择 :p
原文链接:
https://github.com/ascoders/weekly/blob/master/%E5%89%8D%E6%B2%BF%E6%8A%80%E6%9C%AF/27.%E7%B2%BE%E8%AF%BB%E3%80%8Acss-in-js%20%E6%9D%80%E9%B8%A1%E7%94%A8%E7%89%9B%E5%88%80%E3%80%8B.md
CSS-in-JS是什么,看到这个词就能大概猜到是在JavaScript里写CSS,那为什么要在JavaScript里写CSS呢,像之前一样写在css文件里哪里不好么?
在介绍这个概念之前,先来回顾一下在日常编写CSS代码时都有哪些痛点:
全局污染 – CSS的选择器是全局生效的,所以在class名称比较简单时,容易引起全局选择器冲突,导致样式互相影响。
命名混乱 – 因为怕全局污染,所以日常起class名称时会尽量加长,这样不容易重复,但当项目由多人维护时,很容易导致命名风格不统一。
样式重用困难 – 有时虽然知道项目上已有一些相似的样式,但因为怕互相影响,不敢重用。
代码冗余 – 由于样式重用的困难性等问题,导致代码冗余。
在CSS的进化历史上,出现过各种各样的框架致力于解决以上的问题:
SASS, LESS – 提供了变量、简单函数、运算、继承等,扩展性、重用性都有了很大的提升,解决了一些样式重用冗余的问题,但是对于命名混乱问题的效果不大。
BEM (.block__element–modifier) – 比较流行的class命名规则,部分解决了命名混乱和全局污染的问题,但class定义起来还是不太方便,比较冗长,而且和第三方库的命名还是有可能冲突。
CSS Modules – 模块化CSS,将CSS文件以模块的形式引入到JavaScript里,基本上解决了全局污染、命名混乱、样式重用和冗余的问题,但CSS有嵌套结构的限制(只能一层),也无法方便的在CSS和JavaScript之间共享变量。
可以看一个简单的CSS Modules例子了解一下:
生成的dom结构如下图,基于css文件中的class名称生成了唯一的class名称,样式会定义到生成的class上。
styles打印出来如下图,定义了css中的class名字和生成的唯一class名字的对应关系。
可以看出,以上框架都解决了不少痛点,但也还是各有一些不足,当然CSS-in-JS也并不是完美的解决了所有问题,我们先来详细介绍一下。
现在随着组件化概念的流行,对从组件层面维护CSS样式的需求日益增大,CSS-in-JS就是在组件内部使用JavaScript对CSS进行了抽象,可以对其声明和加以维护。这样不仅降低了编写CSS样式带来的风险,也让开发变得更加轻松。它和CSS Modules的区别是不再需要CSS样式文件。
来看一下几个流行的CSS-in-JS框架六个月内的下载趋势:
我们来看看几个下载量靠前的框架的风格是什么样的:
styled-components
先来看看下载量最高的styled-component的代码风格:
从上图可以看出,Title和Wrapper都是框架包装好的component,可以直接在react的jsx语法中使用,在包装component的时候还定义了标签分别是h1和section。此段代码产生的html dom如下图所示:
可以看到section和h1上分别生成了唯一的class名称,样式也对应的定义在生成的class上了。
这样就可以解决命名混乱和全局污染的问题。组件相关的代码都在一起,可以统一查看,也可以方便的重用样式。
glamorous
再来看看glamorous,这个框架是PayPal开发的。(前两个logo看下来,恍惚间感觉进了化妆品专柜)。
和styled-component不同的是,glamorous的样式直接以attribute的形式定义在了dom上,之后虽然也为其生成了class名称及样式,但这种以attribute定义的方式对伪类选择符(如 :hover)支持的不好,会带来一些不方便,而且需要再记住一套attributes名称和值与真正的css样式代码的对应关系。
JSS
和上面两个框架类似,jss也是会定义styles对象,并附到component上,最后生成的dom也是会有生成的唯一class名称,并有对应的样式,但样式并不是真正的css语法,而是对象的属性和值,这样也是对伪类选择符支持的不好,而且也需要记住属性和css样式代码之间的对应关系。
Radium
Radium在定义样式对象上看似和其他相似,但在生成dom结构的时候并没有生成唯一的class名称,而是直接把样式放到了style属性上,这样会带来诸如可读性差、CSS权重过大、不支持伪类选择符等问题。
下面再来看一个styled-component提供的基于jest的测试框架:
jest-styled-components
这个框架主要是通过生成Snapshot并比较的方式来保证component样式的每次更改都会被检测到,并且样式是期望的样式。这样就又降低了重构CSS样式带来的风险。
看了这些框架后,可以发现CSS-in-JS的优势还是挺多的:
因为有了生成的唯一class名称,避免了全局污染的问题
唯一的class名称也解决了命名规则混乱的问题
JavaScript和CSS之间可以变量共享,比如一些基础的颜色和尺寸,这样再当需要在JavaScript里计算一些高度的时候,可以取到和dom相关的一些padding,margin数值,统一管理
只生成页面需要用到的代码,缩减了最终包的大小,提升了性能
CSS的单元测试增加了样式重构的安全性
但是CSS-in-JS也存在着一些不足和争议:
有些观点觉得JS和CSS的关系没这么近,把CSS写进JS里引入了新的一套依赖,增加了复杂度,新人加入项目后需要学习的东西就更多了,也让学习曲线更加陡了
对前端框架确实有些依赖性,更适合于组件化的框架,如React等
Debug的时候需要花更多的功夫才能找到对应的样式代码
覆盖第三方插件样式时会有权重不够的问题
Lint工具对于JavaScript内部的CSS代码样式支持的还不够
在ThoughtWorks最新一期的技术雷达(CSS-in-JS | Technology Radar | ThoughtWorks)里,它的等级是Assess,表示的是:“值得追求。重要的是理解如何建立这种能力。企业应该在风险可控的项目中尝试此技术。” 所以最后想说的是,虽然它还是有些不足和争议,在应用之前需要多角度衡量一下对项目的适合度。但它的优点也很多,确确实实解决了很多痛点,而且与web组件化的方向高度一致,希望大家在条件合适的情况下多多尝试,多多反馈,这样也能促进整个CSS编码体验的继续进化~
文/ThoughtWorks张霄翀
原文链接:https://insights.thoughtworks.cn/css-in-js/
趣是最好的老师,HelloGitHub 就是帮你找到兴趣!
分享 GitHub 上有趣、入门级的开源项目。
这是一个面向编程新手、热爱编程、对开源社区感兴趣 人群的月刊,月刊的内容包括:各种编程语言的项目、让生活变得更美好的工具、书籍、学习笔记、教程等,这些开源项目大多都是非常容易上手,而且非常 Cool。主要是希望大家能动手用起来,加入到开源社区中。
在浏览、参与这些项目的过程中,你将学习到更多编程知识、提高编程技巧、找到编程的乐趣。
最后 HelloGitHub 这个项目就诞生了
以下为本期内容|每个月 28 号发刊
1、ngx_waf:一个 Nginx 防火墙模块。我差点就错过了的宝藏项目,它使用简单不需要复杂的配置,支持的功能直戳我的痛点。你看:
2、fast-cpp-csv-parser:读取 CSV 文件的 C++ 库(仅头文件)。示例代码:
# include "csv.h"
int main(){
io::CSVReader<3> in("ram.csv");
in.read_header(io::ignore_extra_column, "vendor", "size", "speed");
std::string vendor; int size; double speed;
while(in.read_row(vendor, size, speed)){
// 对 ram.csv 文件中的数据,做你想做的事情吧!
}
}
3、UNO:使用 C++ 编写的命令行 UNO 纸牌游戏。操作方便支持人机或联机对战,游戏基于 Asio 网络库和现代 C++ 开发,也有对 C++17 的尝试。分别实现了服务端、客户端,代码简单对 C++ 新手友好,UNO 的爱好者快来玩一玩吧!
4、godot:一款功能丰富的开源游戏引擎。最初它只是一款 2D 引擎,近期拓展了 3D 部分的能力。相较于 UE4 或者 Unity 这样的成熟商业引擎来说,Godot 还很年轻不够成熟,尤其 3D 方面的能力。但它拥有简易的开发方式,上手简单。而且社区活跃、文档覆盖全面、有较为丰富的示例代码,对于刚入门的游戏开发者友好。同时开源引擎底层代码完全开源,开发者可以阅读和贡献代码,而不是只停留在游戏逻辑开发层面。总而言之 Godot 是一个极有潜力的游戏引擎,推荐给想学习游戏开发的同学
5、water.css:一个专门为简单页面和示例网页准备的 CSS 框架
6、fyne:一款 Go 语言跨平台 UI 库。想用 Go 写图形界面应用的小伙伴,快速上手:
安装
$ go get fyne.io/fyne
运行一个 demo
$ go get fyne.io/fyne/cmd/fyne_demo/
$ fyne_demo
7、golearn:Go 写的机器学习框架。来,跑个模型试试吧:
cd $GOPATH/src/github.com/sjwhitworth/golearn/examples/knnclassifier
go run knnclassifier_iris.go
8、keepass2android:一个开源的 Android 密码管理器。下载地址,功能:
9、PrettyZoo:一款 Java 写的高颜值 ZooKeeper 客户端桌面应用。该项目使用了 JDK11 以及 JavaFX 编写的 GUI 客户端,代码量适中适合想学习 JavaFX 编写应用的朋友。需要连接 ZK 服务端查看数据的话,手边有这么个工具还是挺方便的。实用和颜值集一身的项目
10、vueblog:一款轻量级 Java 博客项目。基于 SpringBoot+Vue 实现并附有详细开发文档和讲解视频,让刚学会 Java 的同学也能搞定。每个体面的技术人员可能都有一个自己说了算的博客吧
11、x-spreadsheet:基于 JavaScript 实现的轻量级 Web 电子表格库。它功能齐全,包含表格的基本操作和函数等,还有详细的中文文档,在线尝试
12、h5-Dooring:一款功能齐全的 H5 页面可视化配置平台。让你通过可视化的方式制作出 H5 页面,技术栈以 React 为主,后台采用 Node.js 实现。虽然网上有很多这种工具,但本项目免费开源、功能齐全值得一试
13、Ant-Forest:基于 Auto.js 的蚂蚁森林能量自动收获脚本。它是个“绿色环保”的项目,我能从中感受到满满的爱和想把它做好的决心!来看看作者开发 Ant-Forest 时解决了哪些难题:
14、tui.image-editor:功能齐全的图片编辑器。支持图片剪裁、旋转、涂鸦等功能,实现了 Vue 和 React 封装的组件,便于整合进你的项目
15、windows95:基于 Electron 实现的 Windows 95 操作系统。它实现了该操作系统下的所有东西,对!所有!想体验下 Windows 95 版的扫雷吗?下载安装即可
16、LuLu:免费开源的 macOS 防火墙软件
17、humhub:用 PHP 写的开源社交平台。看过《社交网络》的小伙伴,都知道大名鼎鼎的 Facebook 最早就是扎克伯格用 PHP 语言写出来的,humhub 能够让不会编程的小伙伴也可以用创建出一个社交平台啦。跟着提示一步步操作,不到 1 分钟我的社交平台就建好了
18、phpbrew:一个编译、安装、管理多版本 PHP 的工具。有了它就可以方便地在不同 PHP 版本之间自由切换啦,特性:
19、python-patterns:Python 设计模式和使用场景的集合
20、pgcli:支持语法高亮和自动补全的 Postgres 数据库客户端命令行工具。它安装简单上手快速,如果你用过 Postgres 数据库自带的命令行工具,就一定能感受到 pgcli 的迷人之处
21、15-minute-apps:基于 PyQt 框架写的小型桌面应用程序的集合。想用 Python 写桌面应用的小伙伴,这个项目应该可以帮到你。比如写个扫雷游戏:
22、Pine:一个免费、轻量、简洁的 macOS Markdown 编辑器。功能:
23、Publish:专为 Swift 开发人员准备的静态网站生成器。让你实现整个网站都是用 Swift 构建的工具,支持多种主题、插件以及更多强大的自定义选项。示例网站,安装和快速开始:
$ git clone https://github.com/JohnSundell/Publish.git
$ cd Publish
$ make
$ mkdir MyWebsite
$ cd MyWebsite
$ publish new
24、open-source-rover:NASA 面向科技爱好者开源的火星漫游车设计方案和代码。通过该项目你可以使用便宜的树莓派做出自己的火星漫游车,所需的零件很容易就可以买到,遥控部分是使用现成的 Xbox 手柄或者手机,减少花销。喜欢动手和硬件的小伙伴们,这个东西够酷吗?
25、bat:替代 cat 的命令行工具。你还在命令行用 cat 查看文件吗?那你就 out 啦!今天推荐的 bat 它不仅支持语法高亮,还能展示 Git 的改动。macOS 下安装命令:brew install bat 相信你用过 bat 后就不会再想用回 cat 了
26、Web-Dev-For-Beginners:微软开源的 Web 开发教程。该教程共有 24 节课,但目前只有英文版
27、neofetch:展示操作系统信息的命令行工具,支持将近 150 种操作系统
28、jpeg_tutorial:教你编写 JPEG 解码器的教程,示例为 Rust 代码
29、sql-style-guide:一份 SQL 语句编写风格建议。比如:
-- Good
select *
from users
where email = 'example@domain.com'
-- Bad
select *
from users
where email = "example@domain.com"
30、pure-bash-bible:该书有好多复制就能用的 bash 函数,我愿称其为 bash 的“奇技淫巧”。比如把字母转为大写的函数:
upper() {
# Usage: upper "string"
printf '%s\n' "${1^^}"
}
$ upper "hello"
HELLO
31、pulse:根据包含马赛克的人脸图像,生成一张相似容貌的结果。注意不是复原哦,仅可用于人脸
32、Surface-Defect-Detection:该项目整理了目前大量靠谱的表面缺陷检测数据集,还有最新的顶会论文以及作者的解读笔记。从事视觉方向的小伙伴,心动了吗?
迎留言告诉我本期你最喜欢那个项目,如果觉得本文还不错的话,就点赞、转发一波吧~
*请认真填写需求信息,我们会在24小时内与您取得联系。