Bootstrap

vue项目中配置eslint和prettier

一,现象

新建vue项目后,写完代码后,希望能保存ctrl+s之后,自动格式化代码

目前只是使用vue-cli脚手架创建的项目,虽然引入了eslint,但要想在vscode中保存代码时,实现自动格式化,还需要做以下的配置。

二.安装vscode插件

🏭首先要确保已经安装了 ESLlint、Vetur、Prettier 这三个插件。

1,eslint

在团队协作中,为避免低级 Bug、产出风格统一的代码,会预先制定编码规范。使用 Lint 工具和代码风格检测工具,则可以辅助编码规范执行,有效控制代码质量。

ESLint 由 JavaScript 红宝书 作者 Nicholas C. Zakas 编写, 2013 年发布第一个版本。 NCZ 的初衷不是重复造一个轮子,而是在实际需求得不到 JSHint 团队响应的情况下做出的选择:以可扩展、每条规则独立、不内置编码风格为理念编写一个 lint 工具。

🌾安装之后:

image-20211023165716073

2,Vetur

这个插件主要作用就是让vscode识别.vue文件,实现语法高亮。

image-20211023170326874

3,Prettier

它的作用是将我们散漫的风格迥异的代码格式化为符合规范的代码。

🌾安装之后:

image-20211023194428837

三,具体使用

团队开发写代码,通常会有以下这么两个问题存在:

☔️代码质量问题:使用方式有可能有问题

☔️代码风格问题:风格不符合一定规则

1,先谈代码质量问题

当你安装了插件之后,你的代码不符合它设定的默认规范的时候,就会出现波浪线提示

npx eslint --init

image-20211024150622321

这时候打开项目代码,就会报错了:

image-20211024152243459

这是因为我们选择了:“eslint-config-airbnb-base”作为eslint的默认配置,而它设定了字符串使用单引号的。

具体的默认配置可以查看:GitHub - airbnb/javascript: JavaScript Style Guide

打开.eslintrc.js文件,我们可以在rules处增加一个规则,这些规则会覆盖刚刚我们采用的airbnb默认配置,也就是说,我们可以在这里自定义:

  rules: {
    quotes: [2, 'double'], //错误,必须双引号
    semi: [  //错误,必须有分号结尾
      2,
      'always',
      {
        omitLastInOneLineBlock: true,
      },
    ],
  },

于是代码又恢复了正常:

image-20211024153012001

像这样,统一代码书写的配置,是eslint的主要用途,可以统一代码,减少bug的出现。

但是又出现了新的问题,如果出现这个eslint配置,使用者就必须写成这种形式,那刚开始可能会很抵触,毕竟习惯起来需要一个过程。所以应该在使用eslint时,按ctrl+s保存代码,能够自动转化为符合要求的代码!

🏷保存代码时自动规范格式​

这个需要在vscode中进行配置,按住ctrl+shift+p,输入setting,打开setting.json,完成以下配置:

{ 
  // 保存时 prettier 自动格式化
  "editor.formatOnSave": true,
  // 保存时自动启用 eslint --fix 自动修复
  "editor.codeActionsOnSave": {
    "source.fixAll": true,
    "eslint.autoFixOnSave" : true,
  },
  //   配置eslint适用于vue代码
  "eslint.validate": [
    "vue",
    "javascript",
    // "typescript",
    // "typescriptreact",
    // "html",
    // {
    //     "language":"vue",
    //     "autoFix":true
    // }
  ],
}

这时候,回到代码中ctrl+s,就会发现逗号的问题被自动修复了。

11.gif

🎍另外,有些文件,我们并不需要用eslint作为校验,就可以在根目录下新建一个.eslintignore文件来管理:

.eslintrc.js
.prettierrc.js
babel.config.js
vetur.config.js
/node_modules/
2,再来谈代码风格问题

像缩进是用空格还是用tab啦,使用单引号还是双引号啦,句末需不需要分号啦,都属于代码风格问题,他们影响的更多的是代码的统一风格。而这些,prettier也可以进行规范。

在项目根目录下新建.prettierrc文件,然后配置:

{
  "semi": false,
  "singleQuote": true
}

意思是不需要句尾分号,字符串需要单引号。可以注意到,我这里特地写地和上文eslint地配置冲突了。

🍃解决配置的冲突问题

冲突的本质在于 eslint既负责了代码质量检测,又负责了一部分的格式美化工作,格式化的部分规则和 prettier不兼容。

  extends: [
    'plugin:vue/essential',
    'airbnb-base',
    "plugin:prettier/recommended"//这样写,让pettier的规则,放在eslint规则扩展中,于是会经由eslint报错
  ],

网上的一些文章配置的这两个地方的代码,实现的效果和这一个extend是一样的。

image-20211024132704060

这样设置之后,prettier的规则会放置到eslint的扩展中,报错会经由eslint报错。在VSCode中配置了保存自动格式化后,eslint中将代码进行格式化后,会重新被prettier再次格式化。因此最终的格式化效果是Prettier提供的,然而我们代码校验使用的是ESLint,因此往往会出现冲突,最常见的冲突就是:eslint fix后变成单引号,保存(prettier)自动格式化后变成双引号,导致代码校验异常。

🍦接下来举例两者的冲突:

.eslint文件:必须分号结尾且双引号
"rules": {
    "quotes": [2, "double"], //警告,必须双引号
    semi: [
        2,
       	"always",
        {
       	 	 omitLastInOneLineBlock: true
        }
    ],
}
.prettierr文件:必须无分号结尾,且单引号
{
    "semi": false,
    "singleQuote": true
}

于是会出现按ctrl+S,第一次报eslint配置的错,第二次报prettier的错,循环往复,不断冲突。

image-20211024120955447

网上说的让prettier的规则覆盖eslint的,我并没有生效,而是像上文这样不断冲突。最后的解决是把eslint和prettier中的配置弄成一致:

🎁.eslint.js配置:

module.exports = {
  env: {
    browser: true,
    es6: true,
    node: true,
  },
  extends: [
    'plugin:vue/essential',
    'airbnb-base',
    "plugin:prettier/recommended"//这样写,让pettier的规则,放在eslint规则扩展中,于是会经由eslint报错
  ],
  globals: {
    Atomics: 'readonly',
    SharedArrayBuffer: 'readonly',
  },
  parserOptions: {
    ecmaVersion: 2018,
    sourceType: 'module',
  },
  plugins: [
    'vue',
  ],
  "rules": {
    "quotes": [2, "double"], //警告,必须双引号
    "no-compare-neg-zero": 2, //禁止与 -0 进行比较
    "no-cond-assign": [
      2,
      "except-parens"
    ], //禁止条件语句中出现赋值操作符
    "no-console": 0, //允许出现console
    "no-constant-condition": 2, //禁止在条件中使用常量表达式
    "no-control-regex": 2, //禁止在正则表达式中使用控制字符
    "no-debugger": 0, //可以使用debugger
    "no-dupe-args": 2, //禁止 function 定义中出现重名参数
    "no-dupe-keys": 2, //禁止对象字面量中出现重复的 key
    "no-duplicate-case": 2, //禁止出现重复的 case 标签
    "no-empty": 2, //禁止出现空语句块
    "no-empty-character-class": 2, //禁止在正则表达式中使用空字符集
    "no-ex-assign": 2, //禁止对 catch 子句的参数重新赋值
    "no-extra-boolean-cast": 2, //禁止不必要的布尔转换
    "no-extra-parens": [
      "error",
      "functions"
    ], //禁止不必要的括号
    "no-extra-semi": 2, //禁止不必要的分号
    "no-func-assign": 2, //禁止对 function 声明重新赋值
    "no-inner-declarations": 0, //禁止在嵌套的块中出现变量声明或 function 声明
    "no-invalid-regexp": 2, //禁止 RegExp 构造函数中存在无效的正则表达式字符串
    "no-irregular-whitespace": 2, //禁止不规则的空白
    "no-obj-calls": 2, //禁止把全局对象作为函数调用
    "no-prototype-builtins": 2, //禁止直接调用 Object.prototypes 的内置属性
    "no-regex-spaces": 2, //禁止正则表达式字面量中出现多个空格
    "no-sparse-arrays": 2, //禁用稀疏数组
    "no-template-curly-in-string": 0, //禁止在常规字符串中出现模板字面量占位符语法
    "no-unexpected-multiline": 2, //禁止出现令人困惑的多行表达式
    "no-unreachable": 2, //禁止在 return、throw、continue 和 break 语句之后出现不可达代码
    "no-unsafe-finally": 2, //禁止在 finally 语句块中出现控制流语句
    "no-unsafe-negation": 2, //禁止对关系运算符的左操作数使用否定操作符
    "use-isnan": 2, //要求使用 isNaN() 检查 NaN
    "valid-jsdoc": "off", //
    "valid-typeof": 2, //强制 typeof 表达式与有效的字符串进行比较

    "curly": 2, //强制所有控制语句使用一致的括号风格
    "consistent-return": [
      2,
      {
        treatUndefinedAsUnspecified: true
      }
    ],
    "default-case": 2,
    eqeqeq: "off",
    "guard-for-in": 0,
    "no-case-declarations": 0,
    "no-empty-pattern": 2,
    "no-fallthrough": 2,
    "no-global-assign": [
      2,
      {
        exceptions: []
      }
    ],
    "no-octal": 2,
    "no-redeclare": 2,
    "no-self-assign": 2,
    "no-unused-labels": 2,
    "no-useless-escape": 2,
      
    strict: 2,
      
    "no-delete-var": 2,
    "no-undefined": 0,
    "no-undef": 2,
    "no-use-before-define": 2,
      
    "array-bracket-spacing": [
      2,
      "never"
    ],
    "block-spacing": [
      2,
      "always"
    ],
    "brace-style": [
      2,
      "1tbs"
    ],
    "comma-dangle": [
      "error",
      "never"
    ],
    "comma-spacing": [
      2,
      {
        before: false,
        after: true
      }
    ],
    "comma-style": [
      2,
      "last"
    ],
    "computed-property-spacing": [
      "error",
      "never"
    ],
    "eol-last": [
      2,
      "always"
    ],
    "func-call-spacing": [
      "error",
      "never"
    ],
    indent: [
      "error",
      2,
      {
        SwitchCase: 1
      }
    ],
    "jsx-quotes": [
      "error",
      "prefer-double"
    ],
    "key-spacing": [
      "error",
      {
        beforeColon: false,
        afterColon: true
      }
    ],
    "new-cap": [
      "error",
      {
        newIsCap: true,
        capIsNewExceptionPattern: "(Type[a-zA-Z0-9]+|Deco[a-zA-Z0-9]+)+"
      }
    ],
    "new-parens": "error",
    "no-mixed-spaces-and-tabs": 2,
    "no-multi-assign": "error",
    "no-multiple-empty-lines": "error",
      
      
    semi: [
      2,
      "always",
      {
        omitLastInOneLineBlock: true
      }
    ],
      
    "constructor-super": 2,
    "no-class-assign": 0,
    "no-const-assign": 2,
    "no-this-before-super": 2,
    "no-var": 2,
    "no-dupe-class-members": 2,
    "no-new-symbol": 2,
    "require-yield": 2,
    "prefer-const": 0,
  }
};

🎁.prettierrc.js的配置


module.exports = {
    "arrowParens": "avoid",
    "bracketSpacing": true,
    "cursorOffset": -1,
    "insertPragma": false,
    "jsxBracketSameLine": false,
    "plugins": [],
    "printWidth": 80,
    "proseWrap": "preserve",
    "rangeStart": 0,
    "requirePragma": false,
    "semi": true,
    "singleQuote": false,
    "tabWidth": 2,
    "trailingComma": "none",
    "useTabs": false
};

🎁vscode中的setting.json配置

当项目中没有.prettierrc文件时,就会让这里面的配置生效了:

{
  "editor.minimap.enabled": false,
  "diffEditor.ignoreTrimWhitespace": false,
  "javascript.updateImportsOnFileMove.enabled": "always",
  "http.proxyAuthorization": "false",
  "workbench.editor.enablePreview": false,
  "workbench.startupEditor": "newUntitledFile",
  "emmet.includeLanguages": {
      "vue-html": "html",
      "vue": "html"
  },
  "emmet.preferences": {
  
  },
  "emmet.showSuggestionsAsSnippets": true,
  "eslint.codeAction.showDocumentation": {
      "enable": true
  },
  //配置保存时按照eslint文件的规则来处理一下代码
  "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true,
      "eslint.autoFixOnSave" : true,
  },
  //配置eslint适用于vue代码
  "eslint.validate": [
      "vue",
      "javascript",
      // "typescript",
      // "typescriptreact",
      // "html",
      // {
      //     "language":"vue",
      //     "autoFix":true
      // }
  ],
  "window.zoomLevel": -1,
  "leek-fund.immersiveBackground": true,
  "leek-fund.statusBarStock": [
      "sh000001"
  ],
  "interview.workspaceFolder": "C:\\Users\\10594\\.FEInterview",
  "eslint.codeActionsOnSave.mode": "problems",
  "eslint.format.enable": true,
  "interview.updateNotification": true,
  "leek-fund.fundSort": -1,
  "leek-fund.funds": [
      "008888",
      "008087",
      "161725",
      "006600",
      "163115",
      "001576",
      "160225",
      "519674",
      "005311"
  ],
  "leek-fund.showEarnings": 0,
  
  // 使能每一种语言默认格式化规则
  "[html]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },

  "javascript.validate.enable": true,
  "emmet.excludeLanguages": [
      "markdown"
  ],
  /*  prettier的配置 */
  "prettier.printWidth": 80, // 超过最大值换行
  "prettier.tabWidth": 2, // 缩进字节数
  "prettier.useTabs": false, // 句尾添加分号
  "prettier.singleQuote": false, // 使用单引号代替双引号
  "prettier.proseWrap": "preserve", //  (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号
  "prettier.bracketSpacing": true, // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
  "prettier.endOfLine": "auto", // 结尾是 \n \r \n\r auto
  "prettier.eslintIntegration": false, //不让prettier使用eslint的代码格式进行校验
  "prettier.htmlWhitespaceSensitivity": "ignore",
  "prettier.ignorePath": ".prettierignore", // 不使用prettier格式化的文件填写在项目的.prettierignore文件中
  "prettier.jsxBracketSameLine": false, // 在jsx中把'>' 是否单独放一行
  "prettier.jsxSingleQuote": false, // 在jsx中使用单引号代替双引号
  "prettier.parser": "babylon", // 格式化的解析器,默认是babylon
  "prettier.requireConfig": false, // Require a 'prettierconfig' to format prettier
  "prettier.stylelintIntegration": false, //不让prettier使用stylelint的代码格式进行校验
  "prettier.trailingComma": "none", // 在对象或数组最后一个元素后面是否加逗号(在ES5中加尾逗号)
  "prettier.tslintIntegration": false,
  "[jsonc]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascriptreact]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  }, // 不让prettier使用tslint的代码格式进行校验
  "vetur.experimental.templateInterpolationService": false,
  "vetur.ignoreProjectWarning": true,
  "prettier.arrowParens": "avoid"
}
;