CSS Stylelint 自定义规则:代码规范与质量控制 (讲座模式)
各位观众老爷们,晚上好!我是你们的老朋友,代码界的段子手,今天跟大家聊聊 CSS Stylelint 自定义规则这档子事儿。保证让你听完之后,腰不酸了,腿不疼了,代码也更规范了!
1. 啥是 Stylelint?别跟我说你不知道!
首先,咱们得先明确一下,Stylelint 是个啥玩意?简单来说,它就是 CSS 代码的“质检员”,负责检查你的 CSS 代码是否符合规范。就像警察蜀黍查酒驾一样,逮住不规范的,直接给你开罚单(报错)。
Stylelint 默认提供了一大堆规则,比如:
color-hex-case
: 强制十六进制颜色值的大小写。font-family-name-quotes
: 强制字体族名称加引号。selector-class-pattern
: 限制类选择器的命名模式。
这些规则已经足够应付大部分场景了,但是,总有一些“奇葩”的需求,需要我们自己动手定制规则。 这时候,就轮到自定义规则登场了!
2. 为什么要自定义规则?难道默认的不好用?
默认规则当然好用,但是…
- 公司有自己的代码规范:每个公司都有自己的“家规”,比如命名规范、属性顺序、注释要求等等。默认规则可能无法完全满足这些需求。
- 项目有特殊的要求:有些项目比较特殊,需要一些特殊的规则来保证代码质量。
- 想提高团队效率:通过自定义规则,可以自动检查一些常见的错误,减少 code review 的时间和成本。
举个例子,假设我们公司规定,所有的类名都必须以 prefix-
开头,比如 prefix-button
、prefix-input
。默认的 Stylelint 规则就无法满足这个需求,这时候就需要自定义规则了。
3. 自定义规则的原理:AST 是个啥?
要自定义规则,我们得先了解一下 Stylelint 的工作原理。Stylelint 会把你的 CSS 代码解析成一个抽象语法树 (Abstract Syntax Tree, AST)。AST 就像一棵倒过来的树,每个节点代表一个 CSS 语法元素,比如选择器、属性、值等等。
举个例子,对于这段 CSS 代码:
.button {
color: red;
font-size: 16px;
}
Stylelint 会把它解析成这样的 AST(简化版):
{
"type": "stylesheet",
"stylesheet": {
"rules": [
{
"type": "rule",
"selectors": [
{
"type": "selector",
"value": ".button"
}
],
"declarations": [
{
"type": "declaration",
"property": "color",
"value": "red"
},
{
"type": "declaration",
"property": "font-size",
"value": "16px"
}
]
}
]
}
}
我们的自定义规则,其实就是在 AST 上进行遍历,找到符合特定条件的节点,然后进行检查和报错。
4. 如何编写自定义规则:手把手教你!
接下来,咱们就来手把手地教你如何编写自定义规则。
4.1 准备工作
首先,你需要安装 Stylelint 和 stylelint-webpack-plugin (如果你使用 webpack 的话)。
npm install stylelint stylelint-webpack-plugin --save-dev
然后,在你的项目中创建一个 stylelint.config.js
文件,这是 Stylelint 的配置文件。
module.exports = {
extends: [
'stylelint-config-standard', // 继承 Stylelint 官方推荐的规则
],
plugins: [
'./path/to/your/custom-rule.js', // 引入你的自定义规则
],
rules: {
// 这里可以覆盖或者添加额外的规则
},
};
4.2 编写规则代码
接下来,创建一个 JavaScript 文件,比如 custom-rule.js
,这就是你的自定义规则代码。
const stylelint = require('stylelint');
const ruleName = 'your-prefix/class-name-prefix'; // 规则名称,需要符合命名规范
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: '类名必须以 "prefix-" 开头',
});
module.exports = stylelint.createPlugin(ruleName, (primaryOption, secondaryOptions) => {
return (root, result) => {
const validOptions = stylelint.utils.validateOptions(
result,
ruleName,
{
actual: primaryOption,
possible: [true], // 允许 primaryOption 为 true
},
{
actual: secondaryOptions,
possible: {}, // 允许 secondaryOptions 为空
}
);
if (!validOptions) {
return;
}
root.walkRules((rule) => {
rule.selectors.forEach((selector) => {
// 忽略 :global 和 :local 选择器
if (selector.includes(':global') || selector.includes(':local')) {
return;
}
if (selector.startsWith('.')) {
const className = selector.slice(1); // 去掉 '.'
if (!className.startsWith('prefix-')) {
stylelint.utils.report({
message: messages.expected,
node: rule,
result,
ruleName,
});
}
}
});
});
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
代码解释:
stylelint.createPlugin(ruleName, (primaryOption, secondaryOptions) => { ... })
: 这是 Stylelint 创建插件的 API。ruleName
: 规则的名称,需要符合命名规范,比如your-prefix/class-name-prefix
。messages
: 错误提示信息。primaryOption
: 规则的主要选项,比如true
或者false
。secondaryOptions
: 规则的次要选项,比如一些配置参数。root.walkRules((rule) => { ... })
: 遍历所有的 CSS 规则。rule.selectors.forEach((selector) => { ... })
: 遍历规则中的所有选择器。stylelint.utils.report({ ... })
: 报错函数。
4.3 配置 Stylelint
在 stylelint.config.js
文件中,引入你的自定义规则,并启用它。
module.exports = {
extends: [
'stylelint-config-standard',
],
plugins: [
'./custom-rule.js',
],
rules: {
'your-prefix/class-name-prefix': true, // 启用自定义规则
},
};
4.4 运行 Stylelint
现在,你可以运行 Stylelint 来检查你的 CSS 代码了。
npx stylelint "**/*.css"
如果你的代码中有不符合规则的类名,Stylelint 就会报错。
5. 进阶技巧:更复杂的规则!
上面的例子只是一个简单的规则,用来检查类名的前缀。实际上,你可以编写更复杂的规则,比如:
- 检查属性的顺序:强制按照特定的顺序排列 CSS 属性。
- 限制颜色的使用:只允许使用指定的颜色变量。
- 检查注释的格式:强制按照特定的格式编写注释。
- 检查媒体查询的使用:限制媒体查询的嵌套层级。
要编写更复杂的规则,你需要更深入地了解 AST,以及 Stylelint 提供的 API。
5.1 检查属性顺序的例子
const stylelint = require('stylelint');
const ruleName = 'your-prefix/property-order';
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: (first, second) => `属性 ${first} 应该在 ${second} 之前`,
});
const propertyOrder = [
'position',
'top',
'right',
'bottom',
'left',
'display',
'width',
'height',
'margin',
'padding',
'border',
'font-size',
'color',
'background',
];
module.exports = stylelint.createPlugin(ruleName, (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkDecls((decl) => {
const property = decl.prop;
const index = propertyOrder.indexOf(property);
if (index === -1) {
return; // 忽略不在 propertyOrder 中的属性
}
let prevIndex = -1;
decl.prevAll().forEach((prev) => {
if (prev.type === 'decl') {
const prevProperty = prev.prop;
const prevPropertyIndex = propertyOrder.indexOf(prevProperty);
if (prevPropertyIndex !== -1) {
prevIndex = prevPropertyIndex;
return;
}
}
});
if (prevIndex > index) {
stylelint.utils.report({
message: messages.expected(property, propertyOrder[prevIndex]),
node: decl,
result,
ruleName,
});
}
});
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
这个规则会检查 CSS 属性的顺序,如果属性的顺序不符合 propertyOrder
数组中的顺序,就会报错。
5.2 使用 stylelint.utils.walkCompositions
处理 composes
如果你使用了 CSS Modules,并且使用了 composes
语法,那么你需要使用 stylelint.utils.walkCompositions
来处理 composes
引入的类名。
const stylelint = require('stylelint');
const ruleName = 'your-prefix/composes-class-name-prefix';
const messages = stylelint.utils.ruleMessages(ruleName, {
expected: 'Composes 引入的类名必须以 "prefix-" 开头',
});
module.exports = stylelint.createPlugin(ruleName, (primaryOption) => {
return (root, result) => {
if (!primaryOption) {
return;
}
root.walkDecls('composes', (decl) => {
stylelint.utils.walkCompositions(decl.value, (composition) => {
const className = composition.value;
if (!className.startsWith('prefix-')) {
stylelint.utils.report({
message: messages.expected,
node: decl,
result,
ruleName,
});
}
});
});
};
});
module.exports.ruleName = ruleName;
module.exports.messages = messages;
这个规则会检查 composes
引入的类名,如果类名不以 prefix-
开头,就会报错。
6. Stylelint 插件:偷懒的福音!
如果你不想自己编写自定义规则,也可以使用现成的 Stylelint 插件。有很多优秀的 Stylelint 插件,可以满足各种各样的需求。
常用的 Stylelint 插件:
插件名称 | 功能 |
---|---|
stylelint-config-standard |
Stylelint 官方推荐的规则集,包含了很多常用的规则。 |
stylelint-config-rational-order |
强制按照特定的顺序排列 CSS 属性。 |
stylelint-config-idiomatic-order |
另一种属性排序规则,更加符合语义化的顺序。 |
stylelint-declaration-block-no-redundant-longhand-properties |
避免使用冗余的长属性,比如 margin-top: 10px; margin-right: 10px; margin-bottom: 10px; margin-left: 10px; ,应该使用 margin: 10px; 。 |
stylelint-selector-bem-pattern |
强制使用 BEM 命名规范。 |
stylelint-scss |
支持 SCSS 语法,提供了一些 SCSS 相关的规则。 |
stylelint-less |
支持 LESS 语法,提供了一些 LESS 相关的规则。 |
使用插件非常简单,只需要安装插件,然后在 stylelint.config.js
文件中引入插件即可。
module.exports = {
extends: [
'stylelint-config-standard',
'stylelint-config-rational-order', // 引入属性排序插件
],
plugins: [
'stylelint-scss', // 引入 SCSS 插件
],
rules: {
// 这里可以覆盖或者添加额外的规则
'scss/dollar-variable-pattern': /^[a-z][a-zA-Z0-9]+$/, // 强制 SCSS 变量的命名规范
},
};
7. 总结:代码规范,人人有责!
好了,今天的讲座就到这里了。希望大家能够掌握 CSS Stylelint 自定义规则的编写方法,提高代码质量,规范团队协作,让我们的代码更加优雅、健壮!
记住,代码规范,人人有责!让我们一起努力,为构建更美好的代码世界贡献自己的力量!
散会!