从组织管理的角度出发,如何管理项目中的CSS代码
组织样式文件?
管理样式文件的目的就是为了让开发人员更方便地维护代码
具体来说就是将文件进行分类,将相关的文件放在一起,方便开发在修改样式文件时,更容易找到目标文件,创建文件时,更容易找到对应的目录
CSS提供了import命令支持文件引用 但由于其存在一些问题(比如影响浏览器并行下载、加载顺序错乱等)导致使用率极低 更常见的做法是通过预处理器或编译工具插件来引入样式文件
开源项目中的样式文件
先看看热门的UI开源项目中都是如何管理样式文件的。以Bootstrap、ant-design、element为例
Bootstrap
根目录存放了组件样式文件和目录,其他样式文件放在不同的目录中。 目录中的文件分类清晰,但目录结构相对于大多数实际项目而言过于简单(只有样式文件)
forms/
:表单组件相关样式helpers/
:公共样式,包括定位、清除等mixins/
:可以理解为生成最终样式的函数utilities/
:媒体查询相关样式vendor/
:依赖的外部第三方样式
ant-design
该项目采用less预处理器,主要源码在
components/
目录下。各个组件按文件区分,查看其中的alert文件夹你会发现,组件相关代码,测试代码,demo示例、样式文件、描述文档都在其中
全局样式和公共样式在
components/
的子目录style/
目录下
color/
:颜色相关的变量与函数core/
:全局样式,根标签样式、字体样式等mixins/
:样式生成函数themes/
:主题相关的样式变量
组件代码及相关样式放在一起,开发的时候修改会很方便。 但在组件目录comnponents/
下设置style/
目录存放全局和公共样式,这些“样式”文件并不是一个单独的“组件”,再看style目录内部结构 相对于设置单独的color/
目录来管理样式中的颜色,更推荐像Bootstrap一样设立专门的目录或文件来管理变量
element
同样按组件划分目录,与ant-design不同的是组件样式文件,并没有和组件代码放在一起。
element将样式文件放入了
theme-chalk/
目录下
common/
:一些全局样式和公共变量date-picker/
:日期组件相关样式fonts/
:字体文件mixins/
:样式生成函数及相关变量
把样式当成“组件”看待,组件同级目录设立了theme-chalk/
目录存放样式文件。theme-chalk/
目录下的全局样式reset.scss
与组件样式同级,这也有些欠妥。
这种为了将样式打包成模块,在独立项目中直接嵌入另一个独立项目并不推荐。(可以简单理解为一个项目不要有多个pages.json文件) 更符合Git使用规范的做法,即是以子模块的方式引用进项目,而且将组件样式和源码分离这种方式开发的时候也不方便,经常需要跨多层目录查找和修改样式
样式文件管理模式
7-1模式
这种模式建议将目录结构划分为7个目录和1个文件
1个文件
样式的入口文件,它会将会用到的样式所有样式都引入进来 ,一般命名为
main.scss
7个目录
base/
:模板代码,比如默认标签样式重置components/
:组件相关样式layout/
:布局相关,包括头部、尾部、导航栏、侧边栏等pages/
:页面相关样式themes/
:主题样式,即使有的项目没有多个主题,也可以进行预留abstracts/
:其他样式文件生成的依赖函数及mixin,不能直接生成css样式vendors/
:第三方样式文件
目录结构示例
sass
|- abstracts/
|-|- _variables.scss
|-|- _functions.scss
|-|- _mixins.scss
|-|- _placeholders.scs
|- base/
|-|- _reset.scss
|-|- _typography.scss
|-|- ...
|- components/
|-|- buttons.scss
|-|- cover.scss
|-|- ...
|- Layout/
|-|- _navigation.scss
|-|- _grid.scss
|-|- _header.scss
|-|- ...
|- pages/
|-|- _home.scss
|-|- -contact.scss
|-|- ...
|- themes/
|-|- _theme.scss
|-|- _admin.scss
|-|- ...
|- vendors/
|-|- _bootstrap.scss
|-|- _jquery-ui.scss
|-|- ...
|-main.scss
由于这个划分模式是专门针对scss项目提出的,所以为了更加符合单页应用的项目结构,可以稍作优化 。
-
main.scss
文件存在意义不大页面样式、组件样式、布局样式都可以在页面和组件中引用,全局样式也可以在根组件中引用。 每次添加、修改样式文件都需要在
main.scss
文件中同步,这种过度中心化的配置方式也不方便 -
layout/
目录也可以去除因为像
footer、header
这些布局相关的样式,放入对应的组件中来引用会更好 至于不能被组件化的样式存在性也不大,因为对于页面布局,既可以通过下面介绍的方法来拆分成全局样式,也可以依赖第三方UI库来实现 -
themes/
目录也可以去除毕竟大部分前端项目是不需要设置主题的,即使有主题也可以新建一个样式文件来管理样式变量
-
vendors/
目录可以根据需求添加因为将外部样式复制到项目中的情况比较少,更多的是通过npm来安装引入UI库,或者通过webpack插件来写入对应的cdn地址
优化后的目录结构示例
这只是推荐的一种,具体使用可以根据实际情况进行调整。比如在项目的
src/
目录下,创建模块目录,按照模块拆分路由、页面以及组件,所以每个模块下都会有pages/、components/
来管理样式
src
|- abstracts
|-|- _variables.scss
|-|- _functions.scss
|-|- _mixins.scss
|-|- _placeholders.scss
|- base
|-|- reset.scss
|-|- typography.scss
|- components
|-|- buttons.scss
|-|- cover.scss
|-|- header/
|-|-|- header.tsx
|-|-|- header.sass
|-|- footer/
|-|-|- footer.tsx
|-|-|- footer.sass
|- pages
|-|- _home.scss
|-|- _contact.scss
避免样式冲突?
CSS的规则是全局的,任何一个样式规则,都对整个页面有效,如果不对选择器的命名加以管控会很容易产生冲突。
Vue和React是常见的前端框架,在处理样式冲突方面有不同的解决方法:
-
Vue中
Vue组件使用局部作用域的CSS,每个组件都有自己的作用域,避免了全局样式的冲突。
在Vue组件的
<style>
标签中添加scoped
属性,使样式仅适用于当前组件内的元素。Vue 编译过程中,会为每个组件生成一个唯一的哈希值,然后将该哈希值添加到组件中的每个选择器上,使得选择器具有唯一性。
-
React中
React通常使用CSS模块化技术来避免样式冲突。
使用类似于PostCSS的工具或CSS-in-JS库(如styled-components)将样式封装在组件内部,确保样式只适用于特定的组件。
手动命名
最简单有效的命名管理方式就是制定一些命名规则。比如oocss、BEM、AMCSS,其中推荐比较常用的BEM
BEM的命名具有语义,非常适合组件样式类
工具命名
规范约束也不能绝对保证样式名的唯一性,而且也没有有效的校验工具来保证命名正确无冲突,所以聪明的开发者想到了通过插件将原命名转化成不重复的随机命名,从根本上避免命名冲突,比较著名的解决方案就是CSS Modules
// css
.className{
color:green;
}
// HTML
<div class=${styles.className}></div>
// 编译之后的代码
//css
._3zyde4l1yATCOkgn-DBWEL{
color:green;
}
// HTML
<div class="_3zyde4l1yATCOkgn-DBWEL"></div>
因为这种方式编译后命名变得随机,所以会导致覆盖原来样式困难。
高效复用样式?
在开发中会发现有些样式会高度频繁使用,这很违背DRY(Don’t Repeat Yourself)原则,完全可以通过设置为全局公共样式来减少重复定义。
哪些可设置为公共样式?
枚举值的属性
在 CSS 中,有一些属性的取值是预先定义好的有限集合,也被称为枚举值(enumerated values)。
如:
display
属性的取值:`block、inline、inline-block“`position
属性的取值:static、relative、absolute、fixed、sticky
float
属性的取值:left、right、right、none
text-align
属性的取值:left、right、center、justify
特定数值的样式属性
特殊的固定值
如:
margin:0;
,left:0;
,height:l00%;
等
特殊的组合
如:
display: flex;justify-content: center;align-items: center;
display: flex;flex-direction: column;align-items: center;
等
设计规范所使用的属性
比如设计稿中规定的几种颜色
如何命名公共样式
全局样式是基于样式属性和值的,是无语义的。其次对于这种复用率很高的样式应该尽量保证命名简短方便记忆
我们团队所使用的就是属性名首字母 + 横线 + 属性值首字母的方式进行命名。
对于display:inline-block
的样式属性值,它的属性为display
缩写为d,值为inline-block
,缩写为ib,通过短横线连接起来就可以命名成d-ib
知识支撑
BEM
BEM是Block、Element、Modifier三个单词的缩写。
- Block:代表独立的功能组件
- Element:代表功能组件的一个组成部分
- Modifier:表示对应状态信息
.button{
...
}
.button-state-success{
...
}
.button-state-danger{
...
}
CSS in JavaScript
值得关注的CSS in JavaScript。其实Web标准,是提倡结构、样式、行为分离(分别对应HTML、CSS、JavaScript三种语言)。
但React.js的一出现就开始颠覆了这个原则。
在React.js中
- 通过JSX将HTML代码嵌入进JavaScript组件
- 通过CSS in JavaScript的方式将CSS代码也嵌入进JavaScript组件
这种all in JavaScript
的方式确实有悖Web标准,但这种编写方式和日益盛行的组件化概念非常契合,具有“高内聚”的特性
React.js这种方式有两个不那么明显的优势
- 可以通过随机命名解决作用域问题,但命名规则和CSS Modules都可以解决这个问题
- 样式可以使用JavaScript语言特性,比如函数、循环,实现元素不同的样式效果,可以通过新建不同样式类修改元素样式类来实现
当然styled-components
只是CSS in JavaScript
的一种解决方案,可以在GitHub上的资料学习其他解决方案
最后一句
学习心得!若有不正,还望斧正。