本文包含以下子部分,因涉及内容较多,遂单开讲述,对应部分会再次提及相关链接
- 2020年的css周边之sass使用指南
- 2020年的css周边之styled-components原理及使用指南
本文内容是,通过css开发过程中遇到的问题,引出以下解决方案,并对每种解决方案举例并详细解读:
- preprocessor
- post-css
- css-in-js
- css-module
然后推荐几个编写css时的风格指南(style guides),以更好的组织代码:
- BEM
- OOCSS
最后结合vue-cli和create-react-app,对其预设的css方案进行分析,推荐出我们在日常项目中可以采用的较好方案。
正文开始了 ?
css作为前端三件套之一,我们在实际的开发使用中会遇到一些问题:
- css简单的语法难以实现代码复用和复杂逻辑;
- 不同浏览器对新的css特性的实现不一;
- 缺少模块化,全局的选择器因其级联特性会造成样式映射不易维护。
解决方案
- 对第一个问题,可以用
preprocessor
、post-css
等方式先用另一种语法编写再编译成原生css的方式解决,也可以用css-in-js
直接用js控制样式的生成。 - 为了兼容最新特性,我们可以使用
post-css
或css-in-js
为样式自动添加浏览器前缀或添加polyfill。 - 为css添加模块化特性,本质上是对不同模块的选择器添加独一无二的标记,可以使用
css-module
或css-in-js
自动为各种元素添加唯一的class name。
解决方案
preprocessor
css预处理器是在css基础上扩展而来的各种新语言,为在css原有基础上添加了变量、嵌套、mixins和函数等功能,通过编译生成css。特定的预处理器比如sass,其之于css,就相当于ts)(想了解ts更多可以参考按照新的思路再学一遍typescript)之于js。
常见的css预处理器包括
- sass官网
- less官网
- stylus官网
三种预处理器的功能重复度很高,相关语法的区别(具体区别请参考再谈 CSS 预处理器),本质是一样的,学会了其中一种可以向其他预处理器无痛切换。
关于sass的使用参考这篇文章 2020年的css周边之sass使用指南
postcss
postcss经常也被归为预处理器,但这里并不是。它和预处理器的本质区别是不会引入第三方类型的文件,输入css文件输出的依然是css文件。其之于css相当于babel(想了解babel更多可以参考babel是怎么解决我们问题的)之于js。
更准确得讲,postcss像babel中的@babel/core,接收一个css文件将其转化成抽象语法树,这时候可以引入一些插件做任何想做的事(比如验证语法或增加浏览器特定的前缀),再根据修改后的抽象语法树生成新的css。没有插件的postcss可以表示为
const postcss = code => code
插件
要想实现postcss的价值最终要看使用的插件,可以在插件列表中挑选,同时也可以根据提供的api自定义插件,实现自己定制化的功能,更多细节请参考开发 PostCSS 插件。
这里介绍几种比较常用的。
- Autoprefixer 即使没用过postcss这个插件恐怕也听过,用来为相关属性添加不同浏览器推荐的前缀。比如
//处理前
::placeholder {
color: gray;
}
//处理后
::-moz-placeholder {
color: gray;
}
:-ms-input-placeholder {
color: gray;
}
::placeholder {
color: gray;
}
- cssnext和postcss-preset-env 前面提到babel的时候不知道大家有没有想到css中能不能将浏览器未实现的新版本写法转化为旧版本写法,没错,这里介绍的插件就是可以转换新语法和提供polyfill。这两个插件中后者替代了前者,前者已废弃。
- precss 可以实现sass等预处理器的功能(语法和sass很像),并且依赖了postcss-preset-env,因此一定程度上可以替代预处理器。
想了解postcss更多,可以参考PostCSS Deep Dive
css-in-js
说起css-in-js,有人可能会想到html-in-js,前端三大件此刻都归于js,即all-in-js。
在js中写html就是我们经常用的jsx,jsx是ECMAScript的语法扩展,需要一个编译过程才能成为标准的ECMAScript语法,然后进一步生成dom。
而css-in-js虽然也实现了在js中写其他语言的功能,但是它的思路只是一层包装,即在jsx的基础上(比如styled-component使用React.createElement方法)将接收到的元素或组件进一步添加常规属性和独特的className,并且生成对应的stylesheet,以达到模块化等效果。
css-in-js的解决方案也有若干轮子可以开箱即用,比如
- styled-components
- styled-jsx
- emotion
还是和预处理器一样,这里以styled-components为例对其详细介绍,其他实现类似。
关于styled-components的进一步讲解参考这篇文章 2020年的css周边之styled-components原理及使用指南
和jsx一样,css-in-js主要和react一起使用。
css-module
产生背景
css-in-js将部分或者所有样式代码放进了js,在实现了代码复用的同时,也造成了css和js的强耦合,有什么实现css模块化更好的办法呢?
先看js是怎么解决模块化问题的,其中一个思路就是使用命名空间(可以参考ts中namespace的用法),即将一系列命名空间内部属性等挂载到同一个全局变量上,使项目方便维护也减少了命名冲突的概率。从各种第三方实现(比如AMD)发展到现在,js已经有了语言层面的模块化:esModule。
css的模块化是要解决全局作用域下的选择器冲突问题,在有第三方实现之前,我们可以通过后面介绍的一系列命名规范(比如BEM)来保证选择器指代的唯一性,但随之带来的是因遵守严格的规范造成的开发成本的提升。
直到在css社区有了自己的模块化规范:CSS Modules(css module的相关背景可以参考The End of Global CSS)。
基本用法
编写正常的css样式
/* style.css */
.className {
color: green;
}
然后从js中将其导入
import styles from "./style.css";
// import { className } from "./style.css";
element.innerHTML = '<div class="' + styles.className + '">';
也可以通过composes
复用其他样式声明或者使用:global
和:local
切换作用域,具体用法参考官方文档和Interoperable CSS。
原理
css module处理下的css文件会被编译成一种标准的低级可交换格式文件:ICSS
比如
:export {
className: _className_97fd867fsfg;
}
._className_97fd867fsfg {
color: green;
}
该文件会被其他cssmodule文件或者js文件引入,其中本地的className
被映射成全局的_className_97fd867fsfg
,实现了模块化。另外导入等实现也类似,这里不赘述。
实现情况
webpack的css-loader和browserify的css-modulesify等对其进行了实现,通过相关配置可以实现相关特性。
methodology
css方法论指的是为了提高开发效率在组织代码时推荐遵守的一些规范,比如
BEM
BEM 是一种class命名规范,bem分别代表Blocks, Elements and Modifiers,class name命名规则为block--modifier-value
- block代表一个独立的实体,可以理解为一个模块,比如header, container, menu, checkbox, input
- Elements指的是块中的一部分,比如menu item, list item, checkbox caption, header title
- Modifier指的是block或Elements的一种展示或行为,比如disabled, highlighted, checked, fixed, size big, color yellow
OOCSS
OOCSS,意为面向对象的css(Object Oriented CSS) ,一个css对象是指可重复使用的视觉模式,可以抽象为一块独立的代码块(特定结构的html元素和对应类名),会在整个网站重复使用。比如一个图片旁边有一些描述,其中包含固定(或可选的)的结构。
<div class="media">//media是对象最外层class
<a href="http://twitter.com/stubbornella" class="img">
<img src="https://img.qiyuandi.com/images/5/2021nuyqycfkykrb.jpg" />
</a>
<div class="bd">
<a href="http://twitter.com/stubbornella">@Stubbornella</a> <span class="detail">14 miniutes ago</span>
</div>
</div>
两个设计原则
- Separate structure and skin 将重复的视觉定义成皮肤,结构要用通用的class name表示,而不是特定的某类元素,比如img,方便将结构对应部分替换。
- Separate container and content 将容器和内容分开,即内容的一个部分无论放在哪里都是一样的,具体区别用class name来区分,比如不能给
.myObject>h2
设置样式,否则其他位置h2将有不同效果。
其他
其他常见的还有SMACSSMACSS、SUITCSS和Atomic等。
总结
就像在css module部分说的,这些规则会增大开发成本,在有众多更好用的解决方案的情况下,这些规则可以作用辅助手段为我们使用。
最佳实践
我们现在来到了最后一部分,看过了这么多理论,在项目中怎么使用呢?
在回答这个问题之前,我们先看两个比较优秀的项目采用了哪些方案。
vue-cli
vue-cli是vue官方的脚手架
- 使用使用了CSS Modules解决模块问题
- 采用了PostCSS,并默认开启autoprefixer,添加css属性浏览器特定前缀
- 可以在初始化项目时选用 Sass、Less、Stylus任何一种预处理器
create-react-app
create-react-app是react官方的脚手架
- 使用使用了CSS Modules解决模块问题
- react推荐不要跨组件复用class,这样会使预处理器用处不大,即可以用预处理器但不推荐
- 使用PostCSS Normalize对浏览器默认样式进行重置,避免跨浏览器的差异
- 采用了PostCSS,并默认开启autoprefixer,添加css属性浏览器特定前缀
总结
这里大概做一下推荐
- 对于解决模块化问题使用CSS Modules
- 兼容问题使用PostCSS
- 代码复用和复杂逻辑选择一个喜欢的预处理器,比如sass
具体怎么选择可以根据自身情况,并可以添加适当的约定。
当我们对css相关方案有了全局的了解后,这应该已经不是问题了,对吧。?
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!