Vue开发时让CSS属性按规则排序

553 阅读10分钟

CSS 属性的排序对浏览器的渲染性能影响很小,几乎可以忽略不计。浏览器在解析 CSS 时会创建内部的数据结构,不会被属性顺序影响。

不过,保持一致的 CSS 属性排序仍然有其他好处:

  1. 提高代码可读性
  2. 方便团队协作
  3. 减少合并冲突

目前的经济大环境比较差,互联网公司新项目逐渐变少,维护项目的任务逐渐增多,而维护别人写的代码就可能就是常态性的任务,快速阅读和理解别人写的代码比自己写代码对开发者要求会更高,而统一整洁的代码风格更有利于维护和排查代码问题。

继上一篇《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中配置为禁用

image.png

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"]
  }
}