CSS 属性的排序对浏览器的渲染性能影响很小,几乎可以忽略不计。浏览器在解析 CSS 时会创建内部的数据结构,不会被属性顺序影响。
不过,保持一致的 CSS 属性排序仍然有其他好处:
- 提高代码可读性
- 方便团队协作
- 减少合并冲突
目前的经济大环境比较差,互联网公司新项目逐渐变少,维护项目的任务逐渐增多,而维护别人写的代码就可能就是常态性的任务,快速阅读和理解别人写的代码比自己写代码对开发者要求会更高,而统一整洁的代码风格更有利于维护和排查代码问题。
继上一篇《Vue开发时让Prettier与Eslint和谐相处》之后,唯独留下了css没有规范起来,这篇就来详细说一说。
1. 安装依赖
npm install --save-dev stylelint stylelint-config-standard stylelint-order
stylelint
是一个用于检测CSS代码风格的工具,不仅能处理style标签内的css,还可以处理template模板中的行内样式stylelint-config-standard
是一种预设的stylelint配置规则集stylelint-order
是一个用于规定CSS属性顺序的插件
2. 创建 Stylelint 配置文件
在项目根目录创建一个 Stylelint 配置文件 .stylelintrc.js
:
module.exports = {
extends: ['stylelint-config-standard'],
plugins: ['stylelint-order'],
rules: {
'order/properties-alphabetical-order': true, //按照字母顺序排列
// 或者使用更复杂的自定义排序规则:
// 'order/properties-order': [
// 'position',
// 'top',
// 'right',
// 'bottom',
// 'left',
// 'display',
// 'align-items',
// 'justify-content',
// 'float',
// 'clear',
// 'overflow',
// 'overflow-x',
// 'overflow-y',
// 'margin',
// 'margin-top',
// 'margin-right',
// 'margin-bottom',
// 'margin-left',
// 'padding',
// 'padding-top',
// 'padding-right',
// 'padding-bottom',
// 'padding-left',
// 'width',
// 'min-width',
// 'max-width',
// 'height',
// 'min-height',
// 'max-height',
// 'font-size',
// 'font-family',
// 'font-weight',
// 'text-align',
// 'text-transform',
// 'text-decoration',
// 'line-height',
// 'letter-spacing',
// 'color',
// 'background',
// 'background-color',
// 'background-image',
// 'background-repeat',
// 'background-position',
// 'border',
// 'border-top',
// 'border-right',
// 'border-bottom',
// 'border-left',
// 'border-radius',
// 'opacity',
// 'filter',
// 'list-style',
// 'outline',
// 'visibility',
// 'z-index',
// 'box-shadow',
// 'text-shadow',
// 'resize',
// 'transition'
// ],
},
overrides: [
{
files: ['*.vue', '**/*.vue'],
customSyntax: 'postcss-html'
},
{
files: ['*.scss', '**/*.scss'],
customSyntax: 'postcss-scss'
}
]
};
如果你对自定义排序规则觉得太麻烦,没关系,Stylelint 确实有内置的 CSS 属性排序规则,但它并不是默认启用的。你可以使用 stylelint-config-recess-order
插件来实现这个功能。这个插件提供了一个预定义的 CSS 属性排序顺序,基于 Twitter 的 RECESS 项目。 在使用的过程中,它不仅仅会对css属性排序,他对vue中的方法也会按照一定规则进行排序。
以下是如何在你的项目中设置它:
- 首先,安装 stylelint-config-recess-order 插件:
npm install --save-dev stylelint-config-recess-order
还要安装必要的格式化插件
npm i postcss-html postcss-scss -D
- 然后,在你的 .stylelintrc.js 文件中添加这个配置。如果你还没有这个文件,可以创建一个。以下是一个基本的配置示例:
module.exports = {
extends: ['stylelint-config-standard', 'stylelint-config-recess-order'],
plugins: ['stylelint-order'],
rules: {
// 'order/properties-alphabetical-order': true
// 或者使用更复杂的排序规则:
// 'order/properties-order': ['position', 'top', 'right', 'bottom', 'left', 'display', 'align-items', 'justify-content', 'float', 'clear', 'overflow', 'overflow-x', 'overflow-y', 'margin', 'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'padding', 'padding-top', 'padding-right', 'padding-bottom', 'padding-left', 'width', 'min-width', 'max-width', 'height', 'min-height', 'max-height', 'font-size', 'font-family', 'font-weight', 'text-align', 'text-transform', 'text-decoration', 'line-height', 'letter-spacing', 'color', 'background', 'background-color', 'background-image', 'background-repeat', 'background-position', 'border', 'border-top', 'border-right', 'border-bottom', 'border-left', 'border-radius', 'opacity', 'filter', 'list-style', 'outline', 'visibility', 'z-index', 'box-shadow', 'text-shadow', 'resize', 'transition']
},
overrides: [
{
files: ['*.vue', '**/*.vue'],
customSyntax: 'postcss-html'
},
{
files: ['*.scss', '**/*.scss'],
customSyntax: 'postcss-scss'
}
]
};
3. 在 package.json 中添加 script:
{
"scripts": {
"format:stylelint": "stylelint --fix src/**/*.{vue,css,scss}"
}
}
现在,你可以运行 npm run format:stylelint
来排序 CSS 属性。
4. 集成到 VS Code
如果你使用 VS Code,你可以安装 Stylelint 扩展,并在 settings.json 中添加以下配置:
{
"editor.codeActionsOnSave": {
"source.fixAll.stylelint": "explicit"
},
"stylelint.validate": ["css", "less", "scss", "vue"]
}
这样,每次保存文件时都会自动排序 CSS 属性。
如果提示一些错误信息,并且不想修复这些无关紧要的错误信息,我们可以在rules中配置为禁用
rules: {
'selector-pseudo-element-no-unknown': null
}
配置完毕后,需要重启vscode才能生效,目前我这是这个情况
如果你只想要排序,不想要它的全部校验规则,我这里借助AI工具整理了所有属性规则,并全部置为null,需要的话,可以粘贴到规则里面
rules: {
"no-descending-specificity": null, // 禁止选择器的特异性递减
"declaration-block-no-duplicate-custom-properties": null, // 禁止在声明块中出现重复的自定义属性
"declaration-block-no-duplicate-properties": null, // 禁止在声明块中出现重复的属性
"font-family-no-duplicate-names": null, // 禁止在字体族中使用重复的名字
"keyframe-block-no-duplicate-selectors": null, // 禁止在关键帧块中出现重复的选择器
"no-duplicate-at-import-rules": null, // 禁止重复的 @import 规则
"no-duplicate-selectors": null, // 禁止重复的选择器
"block-no-empty": null, // 禁止空块
"comment-no-empty": null, // 禁止空注释
"no-empty-source": null, // 禁止空源文件
"color-no-invalid-hex": null, // 禁止无效的十六进制颜色
"function-calc-no-unspaced-operator": null, // 禁止在 calc 函数中使用无空格的运算符
"keyframe-declaration-no-important": null, // 禁止在关键帧声明中使用 !important
"media-query-no-invalid": null, // 禁止无效的媒体查询
"named-grid-areas-no-invalid": null, // 禁止无效的命名网格区域
"no-invalid-double-slash-comments": null, // 禁止无效的双斜线注释
"no-invalid-position-at-import-rule": null, // 禁止 @import 规则的无效位置
"string-no-newline": null, // 禁止字符串中的换行符
"no-irregular-whitespace": null, // 禁止不规则的空白
"custom-property-no-missing-var-function": null, // 禁止缺少 var 函数的自定义属性
"font-family-no-missing-generic-family-keyword": null, // 禁止缺少通用字体族关键字的字体族
"function-linear-gradient-no-nonstandard-direction": null, // 禁止线性渐变函数中的非标准方向
"declaration-block-no-shorthand-property-overrides": null, // 禁止在声明块中简写属性覆盖
"selector-anb-no-unmatchable": null, // 禁止不可匹配的 :nth-child 和 :nth-last-child 选择器参数
"annotation-no-unknown": null, // 禁止未知的注释
"at-rule-no-unknown": null, // 禁止未知的 at 规则
"declaration-property-value-no-unknown": null, // 禁止声明中未知的属性值
"function-no-unknown": null, // 禁止未知的函数
"media-feature-name-no-unknown": null, // 禁止未知的媒体特性名称
"media-feature-name-value-no-unknown": null, // 禁止未知的媒体特性名称和值
"no-unknown-animations": null, // 禁止未知的动画
"no-unknown-custom-media": null, // 禁止未知的自定义媒体
"no-unknown-custom-properties": null, // 禁止未知的自定义属性
"property-no-unknown": null, // 禁止未知的属性
"selector-pseudo-class-no-unknown": null, // 禁止未知的伪类选择器
"selector-pseudo-element-no-unknown": null, // 禁止未知的伪元素选择器
"selector-type-no-unknown": null, // 禁止未知的类型选择器
"unit-no-unknown": null, // 禁止未知的单位
"at-rule-allowed-list": null, // 指定允许的 at 规则列表
"at-rule-disallowed-list": null, // 指定不允许的 at 规则列表
"at-rule-no-vendor-prefix": null, // 禁止 at 规则中的厂商前缀
"at-rule-property-required-list": null, // 指定 at 规则所需的属性列表
"color-hex-alpha": null, // 指定十六进制颜色中的 alpha 通道
"color-named": null, // 指定颜色的命名方式
"color-no-hex": null, // 禁止十六进制颜色
"comment-word-disallowed-list": null, // 指定注释中不允许的词列表
"declaration-no-important": null, // 禁止使用 !important
"declaration-property-unit-allowed-list": null, // 指定属性允许的单位列表
"declaration-property-unit-disallowed-list": null, // 指定属性不允许的单位列表
"declaration-property-value-allowed-list": null, // 指定属性允许的值列表
"declaration-property-value-disallowed-list": null, // 指定属性不允许的值列表
"function-allowed-list": null, // 指定允许的函数列表
"function-disallowed-list": null, // 指定不允许的函数列表
"function-url-no-scheme-relative": null, // 禁止相对协议的 URL
"function-url-scheme-allowed-list": null, // 指定允许的 URL 协议列表
"function-url-scheme-disallowed-list": null, // 指定不允许的 URL 协议列表
"length-zero-no-unit": null, // 禁止为零长度指定单位
"media-feature-name-allowed-list": null, // 指定允许的媒体特性名称列表
"media-feature-name-disallowed-list": null, // 指定不允许的媒体特性名称列表
"media-feature-name-no-vendor-prefix": null, // 禁止媒体特性名称中的厂商前缀
"media-feature-name-unit-allowed-list": null, // 指定媒体特性名称允许的单位列表
"media-feature-name-value-allowed-list": null, // 指定媒体特性名称允许的值列表
"property-allowed-list": null, // 指定允许的属性列表
"property-disallowed-list": null, // 指定不允许的属性列表
"property-no-vendor-prefix": null, // 禁止属性中的厂商前缀
"rule-selector-property-disallowed-list": null, // 指定规则选择器属性不允许的列表
"selector-attribute-name-disallowed-list": null, // 指定选择器属性名称不允许的列表
"selector-attribute-operator-allowed-list": null, // 指定选择器属性运算符允许的列表
"selector-attribute-operator-disallowed-list": null, // 指定选择器属性运算符不允许的列表
"selector-combinator-allowed-list": null, // 指定允许的选择器组合器列表
"selector-combinator-disallowed-list": null, // 指定不允许的选择器组合器列表
"selector-disallowed-list": null, // 指定不允许的选择器列表
"selector-no-qualifying-type": null, // 禁止限定类型选择器
"selector-no-vendor-prefix": null, // 禁止选择器中的厂商前缀
"selector-pseudo-class-allowed-list": null, // 指定允许的伪类选择器列表
"selector-pseudo-class-disallowed-list": null, // 指定不允许的伪类选择器列表
"selector-pseudo-element-allowed-list": null, // 指定允许的伪元素选择器列表
"selector-pseudo-element-disallowed-list": null, // 指定不允许的伪元素选择器列表
"unit-allowed-list": null, // 指定允许的单位列表
"unit-disallowed-list": null, // 指定不允许的单位列表
"value-no-vendor-prefix": null, // 禁止值中的厂商前缀
"function-name-case": null, // 指定函数名称的大小写
"selector-type-case": null, // 指定选择器类型的大小写
"value-keyword-case": null, // 指定值关键字的大小写
"at-rule-empty-line-before": null, // 在 at 规则之前需要空行
"comment-empty-line-before": null, // 在注释之前需要空行
"custom-property-empty-line-before": null, // 在自定义属性之前需要空行
"declaration-empty-line-before": null, // 在声明之前需要空行
"rule-empty-line-before": null, // 在规则之前需要空行
"declaration-block-single-line-max-declarations": null, // 指定单行声明块中的最大声明数
"declaration-property-max-values": null, // 指定声明属性的最大值数
"max-nesting-depth": null, // 指定最大嵌套深度
"number-max-precision": null, // 指定数值的最大精度
"selector-max-attribute": null, // 指定选择器中的最大属性数
"selector-max-class": null, // 指定选择器中的最大类数
"selector-max-combinators": null, // 指定选择器中的最大组合器数
"selector-max-compound-selectors": null, // 指定选择器中的最大复合选择器数
"selector-max-id": null, // 指定选择器中的最大 ID 数
"selector-max-pseudo-class": null, // 指定选择器中的最大伪类数
"selector-max-specificity": null, // 指定选择器的最大特异性
"selector-max-type": null, // 指定选择器中的最大类型数
"selector-max-universal": null, // 指定选择器中的最大通用选择器数
"time-min-milliseconds": null, // 指定时间的最小毫秒数
"alpha-value-notation": null, // 指定 alpha 值的表示法
"color-function-notation": null, // 指定颜色函数的表示法
"color-hex-length": null, // 指定十六进制颜色的长度
"font-weight-notation": null, // 指定字体粗细的表示法
"hue-degree-notation": null, // 指定色相角度的表示法
"import-notation": null, // 指定 @import 的表示法
"keyframe-selector-notation": null, // 指定关键帧选择器的表示法
"lightness-notation": null, // 指定亮度的表示法
"media-feature-range-notation": null, // 指定媒体特性范围的表示法
"selector-not-notation": null, // 指定 :not() 选择器的表示法
"selector-pseudo-element-colon-notation": null, // 指定伪元素选择器的冒号表示法
"comment-pattern": null, // 指定注释的模式
"custom-media-pattern": null, // 指定自定义媒体查询的模式
"custom-property-pattern": null, // 指定自定义属性的模式
"keyframes-name-pattern": null, // 指定关键帧名称的模式
"selector-class-pattern": null, // 指定类选择器的模式
"selector-id-pattern": null, // 指定 ID 选择器的模式
"selector-nested-pattern": null, // 指定嵌套选择器的模式
"font-family-name-quotes": null, // 指定字体族名称是否使用引号
"function-url-quotes": null, // 指定 URL 函数是否使用引号
"selector-attribute-quotes": null, // 指定选择器属性是否使用引号
"declaration-block-no-redundant-longhand-properties": null, // 禁止声明块中不必要的长写属性
"shorthand-property-no-redundant-values": null, // 禁止简写属性中不必要的值
"comment-whitespace-inside": null // 指定注释内的空白
}
5. Git 钩子
你可以使用 husky 和 lint-staged 在提交代码前自动运行 Stylelint:
{
"husky": {
"hooks": {
"pre-commit": "lint-staged"
}
},
"lint-staged": {
"src/**/*.{css,less,scss,vue}": ["stylelint --fix"]
}
}