当前位置:   article > 正文

前端开发规范性利器系列之:ESLint

eslint

前言

我是一名从事低代码平台研发的前端CV程序猿有几十名像我一样的小伙伴协同研发在长期的多人协作和滚动迭代中,不出意外,代码中会充斥各种“坏味道”如代码风格不统一、扩展性和灵活性降低等问题。我们是如何解决这些问题的呢今天介绍的法宝之一就是大名鼎鼎的ESLint

ESlint是何方神圣?

ESLint 是一个用于 JavaScript 代码静态分析的工具,它可以帮助开发团队遵循一致的代码风格和最佳实践。ESLint 解决了以下几个痛点:

1.代码风格统一:在多人协作的项目中,不同的开发者可能有不同的编码风格习惯,导致代码风格不一致。ESLint 可以定义代码规则,并对代码进行检查和修复,以确保整个项目的代码风格保持一致,提高代码的可读性和可维护性。

2.发现潜在的错误和问题:ESLint 通过静态分析代码,可以发现潜在的错误、漏洞和常见的编码问题。例如,未声明的变量、不推荐使用的语法、潜在的逻辑错误等。通过及早发现这些问题,可以提高代码质量和可靠性。

3.支持最佳实践:ESLint 提供了一系列的规则,可以帮助开发者遵循最佳实践和行业标准。例如,强制使用严格模式、禁止使用已废弃的 API、强制使用代码块等。这些规则可以帮助开发者编写更健壮、可靠的代码。

4.可扩展性和灵活性:ESLint 具有高度的可配置性和可扩展性。开发者可以根据项目的需求,自定义规则和配置,以适应不同的开发环境和项目要求。此外,ESLint 还支持插件系统,可以集成其他工具或规则集,以满足特定的需求。

ESLint的安装使用

ESLint在以下情况下特别有用:

1.项目初始化: 在创建新项目时,使用ESLint 可以确保从一开始就保持良好的代码质量。

2.团队协作: 当多个开发者合作时,ESLint 有助于维持一致的代码风格,减少代码审查时的冲突。

3.持续集成: 将ESLint集成到持续集成工具中,可以确保每次提交都符合项目的代码规范。

1.拓展安装:vscode编辑器

在编辑器内安装ESLint插件,并且确保项目路径下.vscode/settings.json中 "editor.codeActionsOnSave"."source.fixAll.eslint" 已设置为true,具体效果为在vue或者js中没对齐js代码时,保存可以自动完成对齐格式化

2.拓展安装:非vscode工具

如idea,内部提供了根据eslint格式化的功能,在完成了编码后,可以自行使用该功能进行格式化,或者尝试设置为保存,即马上格式化

3.项目安装

npm i eslint -D 

4.创建规则配置文件-.eslintrc.js

文章最后附带彩蛋!!!!

off/0:关闭规则

warn/1: 打开规则,并且作为一个警告(不影响exit code)

error/2:打开规则,并且作为一个错误(exit code将会是1)

  1. // 也可通过 npx eslint --init 来自动化生成对应配置文件
  2. // 根目录创建.eslintrc.js
  3. module.exports = {
  4. root: true,
  5. parserOptions: {
  6. parser: 'babel-eslint'
  7. },
  8. env: {
  9. browser: true
  10. },
  11. extends: ['plugin:vue/essential','standard'
  12. ],
  13. // required to lint *.vue files
  14. plugins: ['vue'],
  15. // add your custom rules here
  16. rules: {
  17. // 禁止函数圆括号之前有一个空格
  18. 'space-before-function-paren': ['error', 'never'],
  19. ......
  20. }
  21. }

5.按需使用.eslintignore

  1. // 根目录创建.eslintignore
  2. /build/
  3. /config/
  4. /dist/

6.检查本地项目代码

  1. // pacakage.json
  2. "scripts": {
  3. "lint": "eslint --ext .js,.vue src ..."
  4. }
  5. // 控制台
  6. npm run lint
  7. // 批量规范格式化的指令为npm run lintFix
  8. // 如果当前需求修改内容较多,且此前没有进行过格式化的话,需要执行此指令进行全量格式化

7.异常处理

当前eslint会通过git提交前校验执行,即当使用git commit 命令时,【husk】就会自动调用eslint的全局检查(后面会出文章会讲解如何配置),出现问题时会抛出异常,此时需要各位开发者认真检查下报错信息,如果是规范问题的话,可以直接根据报错信息定位到对应的文件。原则上不允许随意使用跳过命令来躲避检查,如果有不知道如何解决的情况,请咨询你的导师,旨在通过完善编码规范和知识库来统一思想,并通过自动化手段来约束团队。

8.可参考的配置文件(实践中)

  1. rules: {
  2. // 关闭有关生成器 空格的规则
  3. 'generator-star-spacing': 'off',
  4. // 关闭消除未使用的变量,函数和函数的参数
  5. 'no-unused-vars': 0,
  6. // 不允许在对象文字中的键和冒号之间使用空格
  7. 'key-spacing': ['error', { beforeColon: false }],
  8. // 括号内的空格限制
  9. 'array-bracket-spacing': ['error', 'never'],
  10. ### 正确示例
  11. let a,
  12. b
  13. ### 错误示例
  14. let a, b
  15. // 声明变量时强制换行
  16. 'one-var-declaration-per-line': ['error', 'always'],
  17. // 禁止函数圆括号之前有一个空格
  18. 'space-before-function-paren': ['error', 'never'],
  19. ### 正确示例
  20. function() {}
  21. ### 错误示例
  22. function () {}
  23. // 除了生产环境,允许debugger
  24. 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
  25. // 关闭禁用不必要的return await
  26. 'no-return-await': 0,
  27. // 限制最大长度不超过140个字符
  28. **目前注释的原因是:在设置限制行数140临近几个数字,项目运行时候会卡在70%进度不动。设置在145以后就没有问题,错误数不多**
  29. // 'max-len': [
  30. // 'error',
  31. // {
  32. // code: 140,
  33. // ignoreComments: true, // 忽略所有拖尾注释和行内注释
  34. // ignoreUrls: true, // 忽略含有链接的行
  35. // ignoreStrings: true, // 不忽略字符串的行
  36. // ignoreTemplateLiterals: true // 忽略模板字符串的行
  37. // }
  38. // ],
  39. // 强制限制驼峰法
  40. camelcase: ['error', { properties: 'never' }],
  41. ### 正确示例
  42. let isExamle = ''
  43. ### 错误示例
  44. let is_example = ''
  45. // 关闭callback必须有参数,关闭回调报错(callback(true/false))
  46. 'standard/no-callback-literal': 'off',
  47. // 允许-在字符串中出现了Control的字符
  48. 'vue/no-parsing-error': 'off',
  49. // 关闭计算的属性括号内强制执行一致的间距
  50. 'standard/computed-property-even-spacing': 'off',
  51. // 运算符放到后面
  52. 'operator-linebreak': [
  53. 'error',
  54. 'after',
  55. { overrides: { '?': 'before', ':': 'before' } }
  56. ],
  57. ### 正确示例
  58. let a +
  59. b +
  60. c
  61. ### 错误示例
  62. let a
  63. + b
  64. + c
  65. // 逗号放在后面
  66. 'comma-style': ['error', 'last'],
  67. ### 正确示例
  68. let a,
  69. b,
  70. c
  71. ### 错误示例
  72. let a
  73. ,b
  74. ,c
  75. // 要求条件语句需要大括号
  76. curly: ['error', 'all'],
  77. ### 正确示例
  78. if() {return}
  79. ### 错误示例
  80. if() return
  81. // 句末不出现分号
  82. semi: ['error', 'never'],
  83. // 行注释必须另起一行
  84. // 'line-comment-position': ['error', { position: 'above' }],
  85. ### 正确示例
  86. // 引入font-icon
  87. @import '~@/assets/icon/iconfont';
  88. ### 错误示例
  89. @import '~@/assets/icon/iconfont'; // 引入font-icon
  90. // 要求在注释之前有一个空格
  91. 'spaced-comment': ['error', 'always'],
  92. ### 正确示例
  93. // 引入font-icon
  94. ### 错误示例
  95. //引入font-icon
  96. // 强制单引号和反勾号
  97. quotes: ['error', 'single', { allowTemplateLiterals: true }],
  98. // 建议使用let代替var(由于是建议,用警告)
  99. 'no-var': ['warn'],
  100. // $emit不校验kebab-case,自定义事件名使用短横线方式
  101. 'vue/custom-event-name-casing': 0,
  102. ### --->vue /对自定义事件名称强制使用特定的大小写:https://eslint.vuejs.org/rules/custom-event-name-casing.html
  103. // prop类型不能被假定为构造函数
  104. 'vue/require-prop-type-constructor': 0,
  105. ### --->vue /要求prop类型是构造函数:
  106. https://eslint.vuejs.org/rules/require-prop-type-constructor.html
  107. // 对象字面值属性名称引号
  108. 'quote-props': 0,
  109. // 不要在子组件内部改变 prop
  110. 'vue/no-mutating-props': 0,
  111. ### --->vue /无变异道具:
  112. https://eslint.vuejs.org/rules/no-mutating-props.html#rule-details
  113. // 不允许v-if和v-for一起用
  114. 'vue/no-use-v-if-with-v-for': 0,
  115. ### --->vue /禁止在同一元素上使用v-if和v-for:
  116. https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html
  117. // 强制 计算属性(computed)有return
  118. 'vue/return-in-computed-property': 0,
  119. ### --->vue /计算属性返回-->强制在计算属性中存在return语句:
  120. https://eslint.vuejs.org/rules/return-in-computed-property.html
  121. // 禁止直接使用 Object.prototypes 的内置属性
  122. 'no-prototype-builtins': 0,
  123. ### 正确示例
  124. var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
  125. ### 错误示例
  126. let hasBarProperty = foo.hasOwnProperty("bar");
  127. // 在promise使用async
  128. 'no-async-promise-executor': 0,
  129. ### --->禁止将异步功能用作Promise执行器:
  130. https://cn.eslint.org/docs/rules/no-async-promise-executor
  131. // 检查每个prop的默认值对于给定类型是否有效
  132. 'vue/require-valid-default-prop': 0,
  133. ### --->vue /要求有效默认属性:
  134. https://eslint.vuejs.org/rules/require-valid-default-prop.html
  135. // 禁用不必要的转义字符
  136. 'no-useless-escape': 0,
  137. // 禁止使用未注册的组件
  138. 'vue/no-unused-components': 0,
  139. ### ---> Vue /没有使用的组件:https://eslint.vuejs.org/rules/no-unused-components.html
  140. // 未使用的scope等var
  141. 'vue/no-unused-vars': 0
  142. ### ---> Vue /没有使用过的变量:https://eslint.vuejs.org/rules/no-unused-vars.html
  143. // 缩进代码 2
  144. "vue/html-indent": 2,
  145. // 强制每行的最大属性数,默认是 1
  146. "vue/max-attributes-per-line": 2
  147. // 执行完当前判断里的内容,立马执行紧跟着的下一个里面的内容 例如switch的 case 没有break,紧接着执行下一个case
  148. 'no-fallthrough': 'off',
  149. // 禁止在强制数组方法的回调函数中要return, 因为大部分时候使用循环是执行其它操作
  150. 'array-callback-return': 0
  151. }

团队编码规范

经过一段时间的实践,我们总结出了一套完整的编码规范,下面介绍与主题相关的部分实践。

HTML

● 属性名全小写,用中划线做分隔符

● 不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的)

● 不要忽略可选的关闭标签

减少标签数量

● 在编写HTML代码时,需要尽量避免多余的父节点

● 例如:在书写vue的template不书写多余的div

● 很多时候,需要通过迭代和重构来使HTML变得更少

● 任何时候都要用尽量小的复杂度和尽量少的标签来解决问题

JavaScript

1.【强制】使用2个空格做为一个缩进层级

2.【强制】switch 下的 case 和 default 必须增加一个缩进层级。

3. 空格类部分已经由工程中eslint规则进行定义

如:二元操作符左右两边都必须存在一个空格

一元操作符与变量名间不允许存在空格

定义对象时,属性名和属性值之间的冒号后必须存在一个空格

运算符处换行时,运算符必须处于行末尾

注释符号//与注释内容之间必须存在一个空格

.......

4.在if / else / for / do / while语句中,{}内部语句必须另起一行

5.语句末尾一律省略分号

6.注释必须另起一行

7.【强制】函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标注,参考下面样例:

  1. 正例:
  2. /**
  3. * 函数描述 *
  4. * @param {string} p1 参数1的说明
  5. * @param {string} p2 参数2的说明,比较长
  6. * 那就换行了.
  7. * @param {number=} p3 参数3的说明(可选)
  8. * @return {Object} 返回值描述
  9. */
  10. function foo(p1, p2, p3) {
  11. var p3 = p3 || 10
  12. return {
  13. p1: p1,
  14. p2: p2,
  15. p3: p3
  16. }
  17. }

8.【强制】使用类型严格的===进行条件判断

9.【建议】若条件表达式过长,建议使用定义布尔值变量代替

  1. 正例:
  2. var condition1 = arr.indexOf(a) >= -1
  3. var condition2 = b === 2 || c === 3
  4. var condition3 = b !== 2 || d === 4
  5. if (condition1 && condition2 && condition3) {
  6. // …
  7. }
  8. 反例:
  9. if (arr.indexOf(a) >= -1 && (b === 2 || c === 3) && (b !== 2 || d === 4)) {
  10. // …
  11. }

常量命名

  1. const CON_NUM = 10
  2. let row = Math.ceil(num/CON_NUM)

变量命名

● 命名控件使用lowerCamelCase风格

● 标准变量:采用小写驼峰式命名,前缀应当是名词。(函数的名字前缀为动词,以此区分变量和函数)

● 'ID'在变量名中全大写

● 'URL'在变量名中全大写

● 'Android'在变量名中大写第一个字母

● 'iOS'在变量名中小写第一个,大写后两个字母

● 组件变量:大驼峰式命名法,首字母大写,例:TableComponent

● 常量:名称全大写,用下划线连接,例:MAX_COUNT

● 命名建议:尽量在变量名字中体现所属类型,如:length、count等表示数字类型;而包含name、title表示为字符串类型,布尔型变量使用is、has开头命名。

● 【强制】字符串类型变量必须使用单引号,不允许使用双引号

● 变量尽量即声明即用,而不是统一声明所有变量

● 在字符串中引用变量时,使用模板字符串代替使用+拼接。

函数命名

常规函数

● 小驼峰式命名法,前缀应当为动词,call或者是handle等。

● 事件函数:以on开头,例:onClickonConfirm

(注:事件包括click事件,change事件,emit自定义事件,页面跳转事件)

命名建议:可使用常见动词约定如下:

**动词**
**含义**
**返回值**
can
判断是否可执行某个动作(权限)
函数返回一个布尔值。true:可执行;false:不可执行
has
判断是否含有某个值
函数返回一个布尔值。true:含有此值;false:不含有此值
is
判断是否为某个值
函数返回一个布尔值。true:为某个值;false:不为某个值
get
获取某个值
函数返回一个非布尔值
set
设置某个值
无返回值、返回是否设置成功或者返回链式对象
load
加载某些数据
无返回值或者返回是否加载完成的结果
类&构造函数

● 大驼峰式命名法,首字母大写,前缀为名称

类的成员

● 公共属性和方法:跟变量和函数的命名一样

● 私有属性和方法:前缀为_(下划线),后面跟公共属性和方法一样的命名方式

注意事项:

1.【强制】不要在内置对象的原型上添加方法,如Array, Date

2.【强制】不要在循环内部声明函数

3.【强制】不要有空的代码块

CSS

【强制】class 必须单词全字母小写,单词间以 _ 分隔。

  1. /* 正例 */
  2. .sidebar {}
  3. .sidebar_left {}
  4. .sidebar_left_xx_xx {}
  5. /* 反例 */
  6. .Sidebar {}
  7. .sidebarLeft {}
【强制】class 必须代表相应模块或部件的内容或功能,不得以样式信息进行命名。
  1. /* 正例 */
  2. .foreign_dialog_footer {}
  3. /* 反例 */
  4. .footer {}

编码顺序【可使用stylelint】

[  // 布局属性  'display',  'overflow',  'visibility',  'scroll-behavior',  'scroll-snap-align',  // 布局属性:定位  'position',  'top',  'right',  'bottom',  'left',  'z-index',  // 布局属性:浮动  'float',  'clear',  // 布局属性:列表  'list-style',  'list-style-type',  'list-style-position',  'list-style-image',  // 布局属性:表格  'table-layout',  'border-collapse',  'border-spacing',  'caption-side',  'empty-cells',  // 布局属性:弹性  'flex-flow',  'flex-direction',  'flex-wrap',  'justify-content',  'align-content',  'align-items',  'align-self',  'flex',  'flex-grow',  'flex-shrink',  'flex-basis',  'order',  // 布局属性:多列  'columns',  'column-width',  'column-count',  'column-gap',  'column-rule',  'column-rule-width',  'column-rule-style',  'column-rule-color',  'column-span',  'column-fill',  'column-break-before',  'column-break-after',  'column-break-inside',  // 布局属性:格栅  'grid-columns',  'grid-rows',  // 尺寸属性  'box-sizing',  'width',  'min-width',  'max-width',  'height',  'min-height',  'max-height',  'margin',  'margin-left',  'margin-right',  'margin-top',  'margin-bottom',  'padding',  'padding-left',  'padding-right',  'padding-top',  'padding-bottom',  'border',  'border-width',  'border-style',  'border-color',  'border-colors',  'border-left',  'border-left-width',  'border-left-style',  'border-left-color',  'border-left-colors',  'border-right',  'border-right-width',  'border-right-style',  'border-right-color',  'border-right-colors',  'border-top',  'border-top-width',  'border-top-style',  'border-top-color',  'border-top-colors',  'border-bottom',  'border-bottom-width',  'border-bottom-style',  'border-bottom-color',  'border-bottom-colors',  'border-radius',  'border-top-left-radius',  'border-top-right-radius',  'border-bottom-left-radius',  'border-bottom-right-radius',  'border-image',  'border-image-source',  'border-image-slice',  'border-image-width',  'border-image-outset',  'border-image-repeat',  'overflow-x',  'overflow-y',  // 界面属性  'appearance',  'outline',  'outline-width',  'outline-style',  'outline-color',  'outline-offset',  'outline-radius',  'outline-radius-topleft',  'outline-radius-topright',  'outline-radius-bottomleft',  'outline-radius-bottomright',  'background',  'background-color',  'background-image',  'background-repeat',  'background-repeat-x',  'background-repeat-y',  'background-position',  'background-position-x',  'background-position-y',  'background-size',  'background-origin',  'background-clip',  'background-attachment',  'bakground-composite',  'mask',  'mask-mode',  'mask-image',  'mask-repeat',  'mask-repeat-x',  'mask-repeat-y',  'mask-position',  'mask-position-x',  'mask-position-y',  'mask-size',  'mask-origin',  'mask-clip',  'mask-attachment',  'mask-composite',  'mask-box-image',  'mask-box-image-source',  'mask-box-image-width',  'mask-box-image-outset',  'mask-box-image-repeat',  'mask-box-image-slice',  'box-shadow',  'box-reflect',  'backdrop-filter',  'mix-blend-mode',  'filter',  'opacity',  'object-fit',  'clip',  'clip-path',  'resize',  'zoom',  'cursor',  'pointer-events',  'touch-callout',  'user-modify',  'user-focus',  'user-input',  'user-select',  'user-drag',  // 文字属性  'font',  'font-family',  'font-style',  'font-stretch',  'font-weight',  'font-variant',  'font-size',  'font-size-adjust',  'line-height',  'line-clamp',  'vertical-align',  'direction',  'unicode-bidi',  'writing-mode',  'ime-mode',  'text-overflow',  'text-decoration',  'text-decoration-line',  'text-decoration-style',  'text-decoration-color',  'text-decoration-skip',  'text-underline-position',  'text-align',  'text-align-last',  'text-justify',  'text-indent',  'text-stroke',  'text-stroke-width',  'text-stroke-color',  'text-shadow',  'text-transform',  'text-size-adjust',  'src',  'color',  // 内容属性  'tab-size',  'overflow-wrap',  'word-wrap',  'word-break',  'word-spacing',  'letter-spacing',  'white-space',  'caret-color',  'quotes',  'content',  'content-visibility',  'counter-reset',  'counter-increment',  'page',  'page-break-before',  'page-break-after',  'page-break-inside',  // 交互属性  'will-change',  'perspective',  'perspective-origin',  'backface-visibility',  'transform',  'transform-origin',  'transform-style',  'transition',  'transition-property',  'transition-duration',  'transition-timing-function',  'transition-delay',  'animation',  'animation-name',  'animation-duration',  'animation-timing-function',  'animation-delay',  'animation-iteration-count',  'animation-direction',  'animation-play-state',  'animation-fill-mode',  // Webkit专有属性  '-webkit-overflow-scrolling',  '-webkit-text-fill-color',  '-webkit-tap-highlight-color']
【强制】编写组件样式时候必须使用scoped,vue2.7 中不再推荐 /deep/ 或者::v-deep 这种写法,建议使用 :deep() 方法来替换
  1. &:deep(.el-input) {
  2. width: 60px;
  3. }

Vue

Template

参考<VUE风格指南>

关于$refs的使用

为了维护的方便,防止出现修改原先属性致出现全局属性的错误的情况, 编码过程中不允许出现使用$refs方式去引用变量的情况

  1. // 组件A
  2. export default {
  3. data() {
  4. return {
  5. propTest: {}
  6. }
  7. },
  8. methods: {
  9. getPropTest() {
  10. return this.propTest
  11. }
  12. }
  13. }
  14. // 组件B
  15. export default {
  16. mounted() {
  17. console.log(this.$refs.propTest) // X 不允许使用
  18. console.log(this.$refs.getPropTest()) // √
  19. }
  20. }

小结

总结起来,使用 ESLint 的步骤包括安装 ESLint、初始化配置文件、运行 ESLint 来检查代码,并根据需要修复问题。通过配置和使用规则,你可以定制 ESLint 来确保项目中的代码始终保持一致和高质量,并遵循最佳实践。在持续集成和团队协作中,ESLint是一个不可或缺的工具,助力开发者提升代码的质量和效率。

作者介绍:
道一云,成立于2004年,是中国低代码领域的领导厂商、腾讯战略投资企业、腾讯生态核心合作伙伴。拥有自主知识产权管理软件产品百余项,涵盖数字化应用构建低代码平台-七巧、全场景智能业务分析BI-七析、千人千面、数智化办公企业级门户-七星以及30多款开箱即用的场景应用。

欢迎关注:
公众号:道一云低代码(do1info)
官网:道一云七巧 - 可视化、智能化、数字化应用构建
免费体验:道一云产品免费试用

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/小舞很执着/article/detail/924282
推荐阅读
相关标签
  

闽ICP备14008679号