自界说 ESLint 规矩,让代码继续美丽

这是第 60 篇不掺水的原创,想获取更多原创好文,请扫 上方二维码重视咱们吧~
本文首发于政采云前端团队博客:自界说 ESLint 规矩,让代码继续美丽

自界说 ESLint 规矩,让代码继续美丽

背景

一段实在的代码开展历E P u N 6 c U

良久良久以前,有一个$ g U @ ; . n需求,然后产出了一段代码,代码典雅而简练

exp@ + ^ g 8 ) ] &ort const getConf%  j d ) r S [ ig =d o 5 K ? e q (param1, param2) => {
return ...
}o A q J;

不久又来了个需求,加个参数扩展,so easy!

export conQ 9 ]st getConfig = (param1, parame @ K # ] / G v2, param3) => {
return ...
};

通过多次产品需求迭代后,现在的代码

自界说 ESLint 规矩,让代码继续美丽
export const getConfig = (- 3 M cparam1, param2, param3, param4, param5, param6, para! : }m7……) => {
return ...
};

在产品迭代进程中,上面的 case 一个函数的参数从 2 个开展到了 7 个,典雅的代码逐渐变为不行维护。
这是什么问题?这归咎于日益增长的需求,快速响应和代码质量之间的敌对。

那怎^ Y @ C G I 0 (么避免| b `呢?

  • 拟定代码标准
  • 靠开发同学的自我涵养
  • 进行 Code Review
  • 东西提示
  • 发版控制,不允许发版

拟定代码标准肯定是需求的,那怎样束缚代码呢?标准文档宣讲,再凭仗开发同学的F m ? V 9自我涵养?答案是:无法保证。

Code Review ?但难免也有被捕之鱼。发版控制?能有用处理但是开发领会欠好。

假设咱们在开发者写代码的时候就及时给到提示和建议,那开发领会就很棒了,而 ESL6 [ ( { U Kint 的自界说规矩就可以实现在开发进程中$ V z ^ 1 4 `给开发同学和睦的提示。

ESLint 原理

ESLint 是一个代码检查东西,通过静态的分析,寻觅有问题的方式或许代码。默许运用 Espree 解析器将代码解析为 AST 抽象语法树,然后再对代码进行检查。

看下最简略的一段代码运用 espree 解析器转换成的抽象语法树结构,此处可以运用 astexplorer 快速便利检查解析成 AST 的结构:

代码片段:

var a = 1;

转换出的成果:

{
"type": "Program",
"start": 0,
"end": 10,
"range": [
0,
10
],
"body": [
{
"type": "Variaq # E o j 0bc Z 8leDeclaration",
"start": 0,
"end": 10,
"range":= % v [
0,
10
],
"declarations": [
{
"type": "VariableDeclarator",
"start": 4= Z 1,i [ ( U w e # d
"end": 9,
"range": [
4,
9
],
"id": {
"type": "Identifier",
"start": 4,
"end": 5,
"range": [
4,
5
],
"name": "a"
},
"init": {
"type": v a % !"Literal",P 3 T R (
"start": 8,
"end": 9,
"range": [
8,
9
],
"value": 1,
"raw": "1"
}
}
],
"kind": "var"
}
],
"so/ u ^ i v VurcM 5 K ( q h l ceType": "module"
}

代码转换为 AST 后,可以很便利的对代码的每个节点对代码进行检查。

自界说 ESLint 规矩开发

怎样自界说

语法树分析

对方针代码进行语法树解析,可运用 astexplorer

自界说 ESLint 规矩,让代码继续美丽

编写规矩

下面是一个规矩简略的结构(官方API文档说明)

module.exports = {
meta: {
docs: {
description: "最多参数允许参数",
},
},
create: function (context) {
return {
FunctionDecla8 w J T { b a B ration: (node) =&gB j A 4 O _t; {
if (node.params.length > 3) {
context.report({
node,
message:b W S Z R O k b d U o"参数最多不能超过3个",
});
}
},
};
},
};
  • meta g j Z k Ta(方针)包含规矩的元数据
  • crD L g l veate ( function ) 返回一个方针,其间包含了 ESLint 在遍历 JavaScript 代码的抽象语法树 AST ( ESTree 界说的 AST ) 时,用来访问节点的办法
  • context# W ].report ( ) 用来发布警告或过错,并能供给自动批改功用(取决于你所运用的配备)

最简略的示例(只运用 node 和 mel k U B 5 T J w ussage 参数):

context.report({
node,
meW c I R i - y b Hssage: "参数最多不能u h A } ; G F U超过3个",
});

运用m !上面的这个规i B { P ` t = z矩,结合编辑器就有了对整个 node 节点的提示,假设需求更精确的过错? w f c r B或警告提示,咱们可以运用 loc 参数, APk _ Y ~ *I 文档说明 。

自界说 ESLint 规矩,让代码继续美丽

怎样运用自界说规矩

运用自界说的 ESLint 规矩,你需求自界说一个 ESLintN / e ` , X C 2 的插件,然后将规矩写到自界说的 ESLint 插件中,然后在业务代码中增加 ESLint 配备,引? X m f 6 dESLint 插件。

ESLint 插件

创建

f , 9 ) I X R S R立一个 ESLint2 } u h D c O j plugin,并创建 一个 ESLiH 8 ^ N Rnt rule

依据 Yeoman generator ,可以快速创建 ESLint plugin 项目。

npm i -g yo
npm i -g generator-eslint
// 创建一个plugin
yo eslint:plugin
// 创建一个规矩
yo eslint:rule

创建好的项目目录结构:

  • rules 文件夹寄存E { q – q e ? T p的是各个规矩文件
  • tests 文件夹寄存单元测试文件
  • package.json 是你的 ESLint 插件 npm 包的说明文件,其间的 name 特色就是你的 ESLint 插件的名称,命名规矩:带前缀# R w c R q i d eslint-plugin-
自界说 ESLint 规矩,让代码继续美丽

示例代码:

lib/rules/m_ Q Nax-params.js

module.e0 5 e ^ # Oxports = {
meta: {
docs5 i ! i O W ) s m: {
descripJ . U Mtion: "最多参数",
},
},
create: function (context) {
/**
* 获取函数的参数的初步、结束位置
* @param {node} node AST Node
*/
function getFunctionParamsLoc(node) {
const paramsLength = nodeS Q b Z.params.length;
return {
start:= K n 4 @ I ] node.params[0].loc.start,
end: noF p x D $de.params[paramsLength -1 . 1 ] k y = 1].loc.end,
};
}
return {
FunctionDeclaration: (node) => {
if (node.params.length > 3) {
context.r U 8 i S f 4 }eport({
loc: getFunctionParamsLoc(nu 6 h k q 1ode),
node,
message: "] % P t g 6 i参数最多不能超过3个",
});
}
},
};
},
};

补偿测试用例

/tests/lib/rul9 % N 5 ) 5 I !es/max-params.js

var ruleTester = new RuleTester();
rul: 7 | + TeTester.run("max-params", rule, {
valid: ["function test(d, e, f) {}"],
invalid: [
{
code: "function test(a, b, c. w o 5 @, d) {}",
errors: [{
message: "参数最多不能超过3个",
}]
},
],
});

ESLint 插件装置

在需求的业务代码中装置你的 ESLiJ j _nt 插件。(eslint-plugin-my-eslist-plugin 是你的 ESLint 插件 npm 包的包名)

ne ! I I L ` O qpm install eslint-plugin-my-esl g jlist-plugin

假设你的 npm 包还未发布,需求进行本地调试:

可运用 npmh P 2 ` } n link 本地调试,npm link 的运用。

配备

增加你的 plu{ C N c } xgin 包名(eslint-plugin-E * $ 9 6 m 前缀可忽略) 到 .eW [ } ] : :slintrc 配备文件的 plugins 字段。

.esliR 4 A R V tntrY W - 9c 配备文件示例:

{
"pP 3 I . 3 9 N .luH $ sgins": [
"zoo. S X G [ $" // 你的 ESlint pluH v r 9 Bgin 的名字
]
}

rules 中再将 plugin 中的规矩导入。
⚠️ ESLint更新后,需求重启 vscode,才能收效。( vscode 重启方便办法:CTRL +SHITx l j ! + . $ I 5F + P,输入 Reload Window

此处触及 ESLint 的规矩设置(参看说明)

{
"rules": {
"zoo/rule-name": 2
}
}

效果

自界说 ESLint 规矩,让代码继续美丽

实践运用事Q 1 W9 z V e

函数、办法的入参个数控制,其实已经在 ESLint 的规矩中了。在业务场景中,咱们需求对咱们的业务规矩编写自界说的 ESLint 规矩。

一个简略的业务场景:业务中通常会呈现跳转到许多不同的业务域名的操作,不同的环境有不同的域名,咱们需求从配备中取出A ~ a W ^ 3 I v (域名u ^ G 4 ! _运用,而不是采纳硬编码域名的方案。

由此咱们发生出了一个规矩:禁止硬编码业务域名。

规矩为:

module.exports = {
meta: {
type: "suggestion",
docs: {| $ V
description: "不允许硬编码业务域名",
},
fixable: "code",
},
create: functioX , o G % _ Kn (context) {
const sourceCode = context.getSourceCode();
function checkDomain(node) {
// 匹配硬编码的业务域名的正则
const Reg7 ^ c 5 9 X = /^(http:\/\/|https:\/\/|7 , _ V  } 0 T/\/)(.*.){0,1}zcygov(.com|cn)(.*)/;
const content =
(node.type === "Lite| q D r S 8 nralD [ Z g E b" && node.value) ||
(node[ w { e {.type === "TemplateLiteral" && node.quasis[0].value.cooked);
const domainNode =
(node.type === "Literal" &&aD t @mp; node) ||
(node.type === "TemplateLiteral9 X a p" && n@ Q k ? ~ x ~ a Iode.quasis[0]);
if (Reg.test(content)) {
context.report({
nf M ( ;ode,
// 过错/警告提示信息
message: "不允许硬编码业务域名",
// 批改
fix(fixer) {
const fixes = [];
let domainKey = cont3 z [ l @ M eent.match(Reg)[2];
domainKey = domainKey
? domainKey.suO r 6 V Qbstr(0, domainKey.length - 1)
: "";
if (node.type === "Literal") {
fixes.push(
fixer.replaceTextRange(
[domainNode.start + 1, domainNode.end -! 9 o p - ~ ) 1],
content.replace(Reg, `$4`)
)
);
}
if (node.type === "TemplateLiteral") {
fixes.push(
fixer.replZ z b y AaceTextRaY P t z 7 e -  Jnge(
[domainNode.start, domainNode.end],
content.replace(Reg, `$4`)
)
);
}
if (
node.type === "Literal" &&
node.parent.type === "JSXAttriv @ ^ 2 a s FbuteA . ~ T 7 L b"
) {
fixes.push(fixer.insertTextBefore(node, "{")), E u d X 3 g;
fixes.push(fixer.insertTextAfter(node, v f b s Z ? 4"}"));
}
fixes.pH r S r : Aush(3 ( V O
fixer.insertTextBefore(
node,
`window.getDomain('${domainKey}') + ` V F b ; ^
)
);
return fixes;
},
});
}
}
return {
// 文本
Liter) o H W * p Gal: checkDomain,
// 模板字符串
TemplateLiteral: checkDomain,
};
},
};

补偿测试用例

/tests/lib/rules/no-zcy-domain.je $ P c N – ) Es

var rule = require("../../..& T @ s a/lib/rules/no-zcy-domain"),
RuleTester = require("eslint").RuleTes0 % L W 1ter;
var ruleTester = new RuleTester();
ruleTester.run("no-p Y [ 4 + q ( T &zcy-domain", rule, {
valid:F 1 b j D a  [
"bark ^ b $ c  # . ?",
"baz",
`
var s = {
x: "zcygov"
};1 0 y z & @ t ^
`,z | w u B & ]
],
invalid: [
{
code: `
var s = "/} s a g */zcygov.cn"
`,
errors: [
{
message: "不允许硬编码业务域名",
},
],
},
{
co r 6 _ Yode: `
var s = {
x: "http://bidding.zcygov.cn"
};
`,
errors: [
{
message: "不允r s c C : 9 F许硬编码业务域名",
},
],
},
],
});

结合{ } , vscode 保存自动批改 ESLint 过错的功用,效果如下:

自界说 ESLint 规矩,让代码继续美丽

更多的运用场景

除了上面说的硬编码的场景,还可以将沉积出的最佳实践和业务标准通过自界说 ESLint 的办法来提示开发者,这关于多人帮忙、代码维护、代码风格的一致性都会有很大的帮助。

更多的运用场景有:

  • Input 有必要要有 maxk f 4 - A ) Oleng{ | O A . ,th 特色,避免请f ] W求的后端接口数据库反常
  • 代码中不能呈现加减乘除等核算,假设需求核T p + ( l s = f p算应该引入东西函数,来控制由于前端浮点数核算引起的 Bug
  • 标准束缚,单位= V Y N p @ S元的两边的括号要用英文括号,不能用中文括号,来抵达交互展现统一的效果
  • 代码中不能运用 OSS 地址的静态资源途径,应该运用 CDN 地址的资源途径

参看文献

  • developer.mozi~ u L O =lla.org/zh-CN/docs/…
  • eslint.org/docs/deR j @ jvelo…

引荐阅读

q y 5 ! M分钟教会你树立企业级的 npm 私有库房

一份值得保藏的 Git 反常处理清单

招贤纳士

政采云前端团队(ZooTeam),一个年青赋有热情和创造力的前端团队,隶属于政采云产品研发部,Base 在风景如画的杭州。团队现有 50 余个前端小伙伴,平均年龄 27 岁,近 3 成是全栈工程师,妥妥的% { m S 5 F { 4青年风暴团。成员构成既有来自于阿里、网易的“老”兵,也有浙大、中科大、8 ? ^杭电等校的应届新人。团队在日常的业务对接之外,还在物料体系、工程渠道、树立渠道3 % ! = 3、功能领会、云端运用、数据分析及可视化等方向进行技能探求和实战e B . | ? =,推动并落地了一系列的内部技能产品,继续探求m M % { – w前端技能体系的新鸿沟。

假设你想改动一向被事折腾,希望初步能折腾事;假设你想改动一c V o 2 u向被告诫需求多些主意,却无从破局;假设你想改动你有才能去做成那个成果,却不需求你;假设你想改动你想做成的事需求一个团队去支撑,但没你带人的位置;假设你想改动既定的节奏,将会是“ 5 年作业时间 3 年作业经验”~ , r =;假设你想改动原本领会不错,但y R ( c H S e v总是有那一层窗户纸的含糊… 假设你信赖信赖的力量^ H u ] p % i b,信赖平凡人能成果非凡事,信赖能遇到更好的自己。假设你希望参与到跟着业务腾飞的进程,亲手推动一个有着深入的业务了解、完善的技能体系、技能创造价值、影响力外溢的前端团队的成长进程,我觉得咱们该聊聊。任何时间,等着你写点什么,发给 ZooTeam@cF 1 N v ] D [ & hai-inc.c, ` ` 1om

自界说 ESLint 规矩,让代码继续美丽