赞
踩
我是一名从事低代码平台研发的前端CV程序猿,有几十名像我一样的小伙伴协同研发。在长期的多人协作和滚动迭代中,不出意外,代码中会充斥各种“坏味道”,如代码风格不统一、扩展性和灵活性降低等问题。我们是如何解决这些问题的呢?今天介绍的法宝之一就是大名鼎鼎的ESLint。
ESLint 是一个用于 JavaScript 代码静态分析的工具,它可以帮助开发团队遵循一致的代码风格和最佳实践。ESLint 解决了以下几个痛点:
1.代码风格统一:在多人协作的项目中,不同的开发者可能有不同的编码风格习惯,导致代码风格不一致。ESLint 可以定义代码规则,并对代码进行检查和修复,以确保整个项目的代码风格保持一致,提高代码的可读性和可维护性。
2.发现潜在的错误和问题:ESLint 通过静态分析代码,可以发现潜在的错误、漏洞和常见的编码问题。例如,未声明的变量、不推荐使用的语法、潜在的逻辑错误等。通过及早发现这些问题,可以提高代码质量和可靠性。
3.支持最佳实践:ESLint 提供了一系列的规则,可以帮助开发者遵循最佳实践和行业标准。例如,强制使用严格模式、禁止使用已废弃的 API、强制使用代码块等。这些规则可以帮助开发者编写更健壮、可靠的代码。
4.可扩展性和灵活性:ESLint 具有高度的可配置性和可扩展性。开发者可以根据项目的需求,自定义规则和配置,以适应不同的开发环境和项目要求。此外,ESLint 还支持插件系统,可以集成其他工具或规则集,以满足特定的需求。
ESLint在以下情况下特别有用:
1.项目初始化: 在创建新项目时,使用ESLint 可以确保从一开始就保持良好的代码质量。
2.团队协作: 当多个开发者合作时,ESLint 有助于维持一致的代码风格,减少代码审查时的冲突。
3.持续集成: 将ESLint集成到持续集成工具中,可以确保每次提交都符合项目的代码规范。
在编辑器内安装ESLint插件,并且确保项目路径下.vscode/settings.json中 "editor.codeActionsOnSave"."source.fixAll.eslint" 已设置为true,具体效果为在vue或者js中没对齐js代码时,保存可以自动完成对齐格式化
如idea,内部提供了根据eslint格式化的功能,在完成了编码后,可以自行使用该功能进行格式化,或者尝试设置为保存,即马上格式化
npm i eslint -D
文章最后附带彩蛋!!!!
off/0:关闭规则
warn/1: 打开规则,并且作为一个警告(不影响exit code)
error/2:打开规则,并且作为一个错误(exit code将会是1)
- // 也可通过 npx eslint --init 来自动化生成对应配置文件
- // 根目录创建.eslintrc.js
- module.exports = {
- root: true,
- parserOptions: {
- parser: 'babel-eslint'
- },
- env: {
- browser: true
- },
- extends: ['plugin:vue/essential','standard'
- ],
- // required to lint *.vue files
- plugins: ['vue'],
- // add your custom rules here
- rules: {
- // 禁止函数圆括号之前有一个空格
- 'space-before-function-paren': ['error', 'never'],
- ......
- }
- }

- // 根目录创建.eslintignore
- /build/
- /config/
- /dist/
- // pacakage.json
- "scripts": {
- "lint": "eslint --ext .js,.vue src ..."
- }
-
- // 控制台
- npm run lint
-
- // 批量规范格式化的指令为npm run lintFix
- // 如果当前需求修改内容较多,且此前没有进行过格式化的话,需要执行此指令进行全量格式化
-
当前eslint会通过git提交前校验执行,即当使用git commit 命令时,【husk】就会自动调用eslint的全局检查(后面会出文章会讲解如何配置),出现问题时会抛出异常,此时需要各位开发者认真检查下报错信息,如果是规范问题的话,可以直接根据报错信息定位到对应的文件。原则上不允许随意使用跳过命令来躲避检查,如果有不知道如何解决的情况,请咨询你的导师,旨在通过完善编码规范和知识库来统一思想,并通过自动化手段来约束团队。
- rules: {
- // 关闭有关生成器 空格的规则
- 'generator-star-spacing': 'off',
- // 关闭消除未使用的变量,函数和函数的参数
- 'no-unused-vars': 0,
- // 不允许在对象文字中的键和冒号之间使用空格
- 'key-spacing': ['error', { beforeColon: false }],
-
- // 括号内的空格限制
- 'array-bracket-spacing': ['error', 'never'],
- ### 正确示例
- let a,
- b
- ### 错误示例
- let a, b
-
- // 声明变量时强制换行
- 'one-var-declaration-per-line': ['error', 'always'],
-
- // 禁止函数圆括号之前有一个空格
- 'space-before-function-paren': ['error', 'never'],
- ### 正确示例
- function() {}
- ### 错误示例
- function () {}
-
- // 除了生产环境,允许debugger
- 'no-debugger': process.env.NODE_ENV === 'production' ? 'error' : 'off',
-
- // 关闭禁用不必要的return await
- 'no-return-await': 0,
-
- // 限制最大长度不超过140个字符
- **目前注释的原因是:在设置限制行数140临近几个数字,项目运行时候会卡在70%进度不动。设置在145以后就没有问题,错误数不多**
- // 'max-len': [
- // 'error',
- // {
- // code: 140,
- // ignoreComments: true, // 忽略所有拖尾注释和行内注释
- // ignoreUrls: true, // 忽略含有链接的行
- // ignoreStrings: true, // 不忽略字符串的行
- // ignoreTemplateLiterals: true // 忽略模板字符串的行
- // }
- // ],
-
- // 强制限制驼峰法
- camelcase: ['error', { properties: 'never' }],
- ### 正确示例
- let isExamle = ''
- ### 错误示例
- let is_example = ''
-
- // 关闭callback必须有参数,关闭回调报错(callback(true/false))
- 'standard/no-callback-literal': 'off',
-
- // 允许-在字符串中出现了Control的字符
- 'vue/no-parsing-error': 'off',
-
- // 关闭计算的属性括号内强制执行一致的间距
- 'standard/computed-property-even-spacing': 'off',
-
- // 运算符放到后面
- 'operator-linebreak': [
- 'error',
- 'after',
- { overrides: { '?': 'before', ':': 'before' } }
- ],
- ### 正确示例
- let a +
- b +
- c
- ### 错误示例
- let a
- + b
- + c
-
- // 逗号放在后面
- 'comma-style': ['error', 'last'],
- ### 正确示例
- let a,
- b,
- c
- ### 错误示例
- let a
- ,b
- ,c
-
- // 要求条件语句需要大括号
- curly: ['error', 'all'],
- ### 正确示例
- if() {return}
- ### 错误示例
- if() return
-
- // 句末不出现分号
- semi: ['error', 'never'],
-
- // 行注释必须另起一行
- // 'line-comment-position': ['error', { position: 'above' }],
- ### 正确示例
- // 引入font-icon
- @import '~@/assets/icon/iconfont';
- ### 错误示例
- @import '~@/assets/icon/iconfont'; // 引入font-icon
-
- // 要求在注释之前有一个空格
- 'spaced-comment': ['error', 'always'],
- ### 正确示例
- // 引入font-icon
- ### 错误示例
- //引入font-icon
-
- // 强制单引号和反勾号
- quotes: ['error', 'single', { allowTemplateLiterals: true }],
-
- // 建议使用let代替var(由于是建议,用警告)
- 'no-var': ['warn'],
-
- // $emit不校验kebab-case,自定义事件名使用短横线方式
- 'vue/custom-event-name-casing': 0,
- ### --->vue /对自定义事件名称强制使用特定的大小写:https://eslint.vuejs.org/rules/custom-event-name-casing.html
-
- // prop类型不能被假定为构造函数
- 'vue/require-prop-type-constructor': 0,
- ### --->vue /要求prop类型是构造函数:
- https://eslint.vuejs.org/rules/require-prop-type-constructor.html
-
- // 对象字面值属性名称引号
- 'quote-props': 0,
-
- // 不要在子组件内部改变 prop
- 'vue/no-mutating-props': 0,
- ### --->vue /无变异道具:
- https://eslint.vuejs.org/rules/no-mutating-props.html#rule-details
-
- // 不允许v-if和v-for一起用
- 'vue/no-use-v-if-with-v-for': 0,
- ### --->vue /禁止在同一元素上使用v-if和v-for:
- https://eslint.vuejs.org/rules/no-use-v-if-with-v-for.html
-
- // 强制 计算属性(computed)有return
- 'vue/return-in-computed-property': 0,
- ### --->vue /计算属性返回-->强制在计算属性中存在return语句:
- https://eslint.vuejs.org/rules/return-in-computed-property.html
-
- // 禁止直接使用 Object.prototypes 的内置属性
- 'no-prototype-builtins': 0,
- ### 正确示例
- var hasBarProperty = Object.prototype.hasOwnProperty.call(foo, "bar");
- ### 错误示例
- let hasBarProperty = foo.hasOwnProperty("bar");
-
- // 在promise使用async
- 'no-async-promise-executor': 0,
- ### --->禁止将异步功能用作Promise执行器:
- https://cn.eslint.org/docs/rules/no-async-promise-executor
-
- // 检查每个prop的默认值对于给定类型是否有效
- 'vue/require-valid-default-prop': 0,
- ### --->vue /要求有效默认属性:
- https://eslint.vuejs.org/rules/require-valid-default-prop.html
- // 禁用不必要的转义字符
- 'no-useless-escape': 0,
- // 禁止使用未注册的组件
- 'vue/no-unused-components': 0,
- ### ---> Vue /没有使用的组件:https://eslint.vuejs.org/rules/no-unused-components.html
-
- // 未使用的scope等var
- 'vue/no-unused-vars': 0,
- ### ---> Vue /没有使用过的变量:https://eslint.vuejs.org/rules/no-unused-vars.html
-
- // 缩进代码 2
- "vue/html-indent": 2,
- // 强制每行的最大属性数,默认是 1
- "vue/max-attributes-per-line": 2
-
- // 执行完当前判断里的内容,立马执行紧跟着的下一个里面的内容 例如switch的 case 没有break,紧接着执行下一个case
- 'no-fallthrough': 'off',
-
- // 禁止在强制数组方法的回调函数中要return, 因为大部分时候使用循环是执行其它操作
- 'array-callback-return': 0
- }

经过一段时间的实践,我们总结出了一套完整的编码规范,下面介绍与主题相关的部分实践。
● 属性名全小写,用中划线做分隔符
● 不要在自动闭合标签结尾处使用斜线(HTML5 规范 指出他们是可选的)
● 不要忽略可选的关闭标签
减少标签数量
● 在编写HTML代码时,需要尽量避免多余的父节点
● 例如:在书写vue的template不书写多余的div
● 很多时候,需要通过迭代和重构来使HTML变得更少
● 任何时候都要用尽量小的复杂度和尽量少的标签来解决问题
1.【强制】使用2个空格做为一个缩进层级
2.【强制】switch 下的 case 和 default 必须增加一个缩进层级。
3. 空格类部分已经由工程中eslint规则进行定义
如:二元操作符左右两边都必须存在一个空格
一元操作符与变量名间不允许存在空格
定义对象时,属性名和属性值之间的冒号后必须存在一个空格
运算符处换行时,运算符必须处于行末尾
注释符号//与注释内容之间必须存在一个空格
.......
4.在if / else / for / do / while语句中,{}内部语句必须另起一行
5.语句末尾一律省略分号
6.注释必须另起一行
7.【强制】函数/方法注释必须包含函数说明,有参数和返回值时必须使用注释标注,参考下面样例:
- 正例:
- /**
- * 函数描述 *
- * @param {string} p1 参数1的说明
- * @param {string} p2 参数2的说明,比较长
- * 那就换行了.
- * @param {number=} p3 参数3的说明(可选)
- * @return {Object} 返回值描述
- */
- function foo(p1, p2, p3) {
- var p3 = p3 || 10
- return {
- p1: p1,
- p2: p2,
- p3: p3
- }
- }

8.【强制】使用类型严格的===进行条件判断
9.【建议】若条件表达式过长,建议使用定义布尔值变量代替
- 正例:
- var condition1 = arr.indexOf(a) >= -1
- var condition2 = b === 2 || c === 3
- var condition3 = b !== 2 || d === 4
- if (condition1 && condition2 && condition3) {
- // …
- }
- 反例:
- if (arr.indexOf(a) >= -1 && (b === 2 || c === 3) && (b !== 2 || d === 4)) {
- // …
- }
-
- const CON_NUM = 10
- let row = Math.ceil(num/CON_NUM)
● 命名控件使用lowerCamelCase风格
● 标准变量:采用小写驼峰式命名,前缀应当是名词。(函数的名字前缀为动词,以此区分变量和函数)
● 'ID'在变量名中全大写
● 'URL'在变量名中全大写
● 'Android'在变量名中大写第一个字母
● 'iOS'在变量名中小写第一个,大写后两个字母
● 组件变量:大驼峰式命名法,首字母大写,例:TableComponent
● 常量:名称全大写,用下划线连接,例:MAX_COUNT
● 命名建议:尽量在变量名字中体现所属类型,如:length、count等表示数字类型;而包含name、title表示为字符串类型,布尔型变量使用is、has开头命名。
● 【强制】字符串类型变量必须使用单引号,不允许使用双引号
● 变量尽量即声明即用,而不是统一声明所有变量
● 在字符串中引用变量时,使用模板字符串代替使用+拼接。
● 小驼峰式命名法,前缀应当为动词,call或者是handle等。
● 事件函数:以on开头,例:onClick,onConfirm
(注:事件包括click事件,change事件,emit自定义事件,页面跳转事件)
命名建议:可使用常见动词约定如下:
**动词**
|
**含义**
|
**返回值**
|
can
|
判断是否可执行某个动作(权限)
|
函数返回一个布尔值。true:可执行;false:不可执行
|
has
|
判断是否含有某个值
|
函数返回一个布尔值。true:含有此值;false:不含有此值
|
is
|
判断是否为某个值
|
函数返回一个布尔值。true:为某个值;false:不为某个值
|
get
|
获取某个值
|
函数返回一个非布尔值
|
set
|
设置某个值
|
无返回值、返回是否设置成功或者返回链式对象
|
load
|
加载某些数据
|
无返回值或者返回是否加载完成的结果
|
● 大驼峰式命名法,首字母大写,前缀为名称。
类的成员
● 公共属性和方法:跟变量和函数的命名一样
● 私有属性和方法:前缀为_(下划线),后面跟公共属性和方法一样的命名方式
注意事项:
1.【强制】不要在内置对象的原型上添加方法,如Array, Date
2.【强制】不要在循环内部声明函数
3.【强制】不要有空的代码块
【强制】class 必须单词全字母小写,单词间以 _ 分隔。
- /* 正例 */
- .sidebar {}
- .sidebar_left {}
- .sidebar_left_xx_xx {}
- /* 反例 */
- .Sidebar {}
- .sidebarLeft {}
- /* 正例 */
- .foreign_dialog_footer {}
- /* 反例 */
- .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']
- &:deep(.el-input) {
- width: 60px;
- }
参考<VUE风格指南>
为了维护的方便,防止出现修改原先属性致出现全局属性的错误的情况, 编码过程中不允许出现使用$refs方式去引用变量的情况
- // 组件A
- export default {
- data() {
- return {
- propTest: {}
- }
- },
- methods: {
- getPropTest() {
- return this.propTest
- }
- }
- }
-
-
- // 组件B中
- export default {
- mounted() {
- console.log(this.$refs.propTest) // X 不允许使用
- console.log(this.$refs.getPropTest()) // √
- }
- }
-
-

总结起来,使用 ESLint 的步骤包括安装 ESLint、初始化配置文件、运行 ESLint 来检查代码,并根据需要修复问题。通过配置和使用规则,你可以定制 ESLint 来确保项目中的代码始终保持一致和高质量,并遵循最佳实践。在持续集成和团队协作中,ESLint是一个不可或缺的工具,助力开发者提升代码的质量和效率。
作者介绍:
道一云,成立于2004年,是中国低代码领域的领导厂商、腾讯战略投资企业、腾讯生态核心合作伙伴。拥有自主知识产权管理软件产品百余项,涵盖数字化应用构建低代码平台-七巧、全场景智能业务分析BI-七析、千人千面、数智化办公企业级门户-七星以及30多款开箱即用的场景应用。
欢迎关注:
公众号:道一云低代码(do1info)
官网:道一云七巧 - 可视化、智能化、数字化应用构建
免费体验:道一云产品免费试用
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。