(四)Vue3-huohuo-admin 工程配置(ESlint+Prettier+Stylelint)

451 阅读9分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路

上一篇,我们在项目中创建了一个.husky文件,主要利用了husky hooksgit hooks的部分功能为我们的项目做提交校验与格式化。其中我们使用到了两个非常核心的lint插件,也就是ESlintPrettier。这两篇内容实质上属于同一个前端工程化的模块--代码规范化。在这一篇,我将会深入讲一下如何利用一些插件来对我们的代码进行统一的规范化管理,帮助我们在开发时写出风格统一、质量更好的代码(同时让上一篇所做的项目配置更加有效)。

一般针对代码规则的定制分为两部分:格式规则(代码的格式风格)、质量规则(代码是否可能因为语法使用不当引起潜在的 Bug)。

本项目将会使用ESlint + Prettier + Stylelint来制定我们的代码规则,为我们的代码做统一的检查与修正。

Prettier

官网入口

最好的代码格式化工具之一——Prettier。它可以帮助我们统一开发团队的代码风格,并提供非常好的IDE支持,方便我们在开发时能够及时检查并修改代码格式。

在上一篇,我们已经安装了Prettier,这里我们再创建它的配置文件.prettierrc.js(我们只需要接受Prettier定好的一套格式规则,只做少量的规则修改)。比如:

module.exports = {
  bracketSpacing: true,
  singleQuote: false,
  arrowParens: "avoid",
  trailingComma: "none",
};

ESlint

初学者面对的最痛苦的地方肯定少不了ESlint这个插件(记得在VSCode中安装该插件),相信曾经很多人都跟我一样,“给你卸了!给你关了!”,但正是这个强大的插件,为我们的JS代码提供了大量的检测规则,帮助我们写出质量更高的代码。关于ESlint的详细介绍,我们可以阅读官网

ESlint 插件

ESlint 辅助插件

在上一篇,我们已经安装好了ESlintpnpm install -D eslint),并且配置了一个简单的.eslintrc.js文件(当然也可以是.eslintrc文件)。但实际上每个项目的ESlint配置并不简单,你不仅需要根据项目框架的需求自己制定检测规则,还需要安装各种各样开发时需要的的ESlint插件依赖,以此来帮助你获得更加友好的代码提示。

比如如下两种开发依赖:

  • Vue.js 的官方ESLint插件:eslint-plugin-vue
  • PrettierESlint整合的包:eslint-config-prettier(配置ESLint以关闭与Prettier冲突的规则)、eslint-plugin-prettier(让Prettier作为一个插件运行在ESLint中,通过ESLint统一检查修改双方规则)

注:eslint-config-prettier插件本项目并未安装,替代插件见下边。当然你也可以直接安装这个插件。

考虑到我们的项目使用的是Vue3 + TS,所以我们需要针对Vue3 + TS项目的基本配置做一个规则集。我们可以安装下面两个插件,他们的配置专为@vue/cli& create-vue setups使用而设计,解决一些ESlinct中已知的限制。

安装完成后需要在.eslintrc.js文件的扩展选项中做配置

module.exports = {
  extends: [
    "eslint:recommended",
    "plugin:vue/vue3-essential",
    "@vue/eslint-config-prettier"
    "@vue/eslint-config-typescript", // 放在末尾,以让其关闭eslint:recommended部分规则集的功能生效
  ],
};

官方建议:为了解决ESLint中的一个已知限制,我们建议您与此包一起使用@rushstack/eslint-patch,这样您就不必安装太多依赖项。

ESlint 解析器

如果我们在template模板中使用复杂的指令和表达式,是很容易出错的,怎么办呢?

vue-eslint-parser 解析器可以配合我们的eslint-plugin-vue捕捉可能出现的错误。

pnpm install -D vue-eslint-parser

用法:在.eslintrc.js文件中加入parser选项指定解析器

{
  parser: "vue-eslint-parser";
}

还可以使用parserOptions.parser属性来指定自定义解析器来解析script标签(除了解析器之外的其他属性将被赋予指定的解析器),这里我推荐使用 TypeScript ESLint,我们继续安装一下。

pnpm install -D @typescript-eslint/parser
pnpm install -D @typescript-eslint/eslint-plugin
{
  parser: "vue-eslint-parser",
  parserOptions: {
    parser: "@typescript-eslint/parser",
    ecmaVersion: 2020,
    sourceType: "module",
    jsxPragma: "React", // 如果你用了jsx写法的话(本项目中会使用到)
    ecmaFeatures: {
      jsx: true
    }
  },
}

自定义一些 ESlint 规则集

自定义规则集主要在两部分:globalsrules选项

globals 选项

写 TS 的时候,我们会创建一些类型文件(xxx.d.ts),里面都是我们定义的TS类型。为了能够让ESlint检查到它们并给我良好的提示,我们可以在这个选项中也做相应的配置。

比如我们自定义了一个类型Fn(定义在types文件夹下的index.d.ts文件)

declare interface Fn<T = any, R = T> {
  (...arg: T[]): R;
}

我们需要在.eslintrc文件中也配置一下

{
  globals: {
    Fn"readonly"
  }
}

rules 选项

推荐规则

{
    rules: {
      "vue/no-v-html": "off",
      "vue/require-default-prop": "off",
      "vue/require-explicit-emits": "off",
      "vue/multi-word-component-names": "off",
      "@typescript-eslint/no-explicit-any": "off", // any
      "no-debugger": "off",
      "@typescript-eslint/explicit-module-boundary-types": "off", // setup()
      "@typescript-eslint/ban-types": "off",
      "@typescript-eslint/ban-ts-comment": "off",
      "@typescript-eslint/no-empty-function": "off",
      "@typescript-eslint/no-non-null-assertion": "off",
      "vue/html-self-closing": [
        "error",
        {
          html: {
            void: "always",
            normal: "always",
            component: "always"
          },
          svg: "always",
          math: "always"
        }
      ],
      "@typescript-eslint/no-unused-vars": [
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_"
        }
      ],
      "no-unused-vars": [
        "error",
        {
          argsIgnorePattern: "^_",
          varsIgnorePattern: "^_"
        }
      ],
      "prettier/prettier": [
        "error",
        {
          endOfLine: "auto"
        }
      ]
  }
}

温馨提示:如果你遇到了某个语法检测警告,并对其进行了处理,发现仍然无效。请记得重启一下 VSCode!!!重启一下 VSCode!!!重启一下 VSCode!!!

到这里,我们的ESLint配置已经基本成形(汗~真不容易啊!)。

Stylelint

Stylelint

官网入口

Style,样式,懂了,用来规范CSS代码的--样式格式规则。先安装依赖。

pnpm install -D stylelint stylelint-config-html stylelint-config-prettier stylelint-config-recommended stylelint-config-standard stylelint-order

image-20220505163839576

注:stylelint-config-html需要配合postcss-html使用

在项目的根目录中创建一个配置文件stylelint.config.js,推荐配置如下

module.exports = {
  root: true,
  plugins: ["stylelint-order"],
  customSyntax: "postcss-html",
  extends: ["stylelint-config-standard", "stylelint-config-prettier"],
  rules: {
    "selector-class-pattern": null,
    "selector-pseudo-class-no-unknown": [
      true,
      {
        ignorePseudoClasses: ["global"],
      },
    ],
    "selector-pseudo-element-no-unknown": [
      true,
      {
        ignorePseudoElements: ["v-deep"],
      },
    ],
    "at-rule-no-unknown": [
      true,
      {
        ignoreAtRules: [
          "tailwind",
          "apply",
          "variants",
          "responsive",
          "screen",
          "function",
          "if",
          "each",
          "include",
          "mixin",
        ],
      },
    ],
    "no-empty-source": null,
    "named-grid-areas-no-invalid": null,
    "unicode-bom": "never",
    "no-descending-specificity": null,
    "font-family-no-missing-generic-family-keyword": null,
    "declaration-colon-space-after": "always-single-line",
    "declaration-colon-space-before": "never",
    "rule-empty-line-before": [
      "always",
      {
        ignore: ["after-comment", "first-nested"],
      },
    ],
    "unit-no-unknown": [true, { ignoreUnits: ["rpx"] }],
    "order/order": [
      [
        "dollar-variables",
        "custom-properties",
        "at-rules",
        "declarations",
        {
          type: "at-rule",
          name: "supports",
        },
        {
          type: "at-rule",
          name: "media",
        },
        "rules",
      ],
      { severity: "warning" },
    ],
  },
  ignoreFiles: ["**/*.js", "**/*.jsx", "**/*.tsx", "**/*.ts", "**/*.json"],
  overrides: [
    {
      files: ["*.vue", "**/*.vue", "*.html", "**/*.html"],
      extends: ["stylelint-config-recommended", "stylelint-config-html"],
      rules: {
        "keyframes-name-pattern": null,
        "selector-pseudo-class-no-unknown": [
          true,
          {
            ignorePseudoClasses: ["deep", "global"],
          },
        ],
        "selector-pseudo-element-no-unknown": [
          true,
          {
            ignorePseudoElements: ["v-deep", "v-global", "v-slotted"],
          },
        ],
      },
    },
  ],
};

postcss

针对CSS的插件,我们非常常见的还有postcss,它可以:

  • 增强代码的可读性
  • 自动转换助你支持最新的CSS语法
  • CSS模块化解决命名冲突问题
  • 配合stylelint避免CSS代码中的错误

用法看这里。又到了安装插件环节。。。

pnpm install -D postcss postcss-html postcss-import postcss-scss

image-20220505160902054

本项目使用的是SASS,如果你使用的是less,把postcss-scss换成postcss-less即可

创建postcss.config.js配置文件

module.exports = {
  plugins: [require("autoprefixer"), require("postcss-import")],
};

注:这里还使用了autoprefixer插件(pnpm install -D autoprefixer),它可以自动在样式中添加浏览器厂商前缀,避免手动处理样式兼容问题。

忽略文件

有些文件,我们不希望他们遵循我们定义的规则集,我们可以让他们跳过检测。

.eslintignore

public
dist
*.d.ts
package.json

.stylelintignore

/dist/*
/public/*
public/*

成形与使用

先看一下现在的目录结构跟开发依赖 image-20220505163804923

tsconfig.json 配置

别忘了我们还有个tsconfig.json文件,根据我们项目的需要,给它也配置一下。

{
  "compilerOptions": {
    "target": "esnext",
    "module": "esnext",
    "moduleResolution": "node",
    "strict": false,
    "jsx": "preserve",
    "importHelpers": true,
    "experimentalDecorators": true,
    "strictFunctionTypes": false,
    "skipLibCheck": true,
    "esModuleInterop": true,
    "isolatedModules": true,
    "allowSyntheticDefaultImports": true,
    "forceConsistentCasingInFileNames": true,
    "sourceMap": true,
    "baseUrl": ".",
    "allowJs": false,
    "resolveJsonModule": true,
    "lib": ["dom", "esnext"],
    "incremental": true,
    "paths": {
      // 别名配置
      "/@/*": ["src/*"],
      "@build/*": ["build/*"],
      "/#/*": ["types/*"]
    },
    "types": ["node", "vite/client"], // 如果你使用element-plus,记得在这里添加"element-plus/global"
    "typeRoots": ["./node_modules/@types/", "./types"]
  },
  "include": [
    "src/**/*.ts",
    "src/**/*.tsx",
    "src/**/*.vue",
    "types/*.d.ts",
    "mock/*.ts",
    "vite.config.ts"
  ],
  "exclude": ["node_modules", "dist", "**/*.js"],
  "references": [{ "path": "./tsconfig.node.json" }] // 工程引用
}

这里面有一个references选项(工程引用),本项目并未使用该功能,将其删除,并删除 tsconfig.node.json文件。官网对此选项描述的并不太直白,可以参考这里

执行 lint

前面我们配置好了一整套的ESlint + Prettier + Stylelint代码规则,我们可以使用VSCode的格式化快捷键对我们的代码进行格式化,还可以使用lint命令来手动让代码跑一次校验并格式化。

所以我们需要配置一些脚本命令(关于命令的配置,建议根据项目需要查阅官网进行配置,这里仅作推荐使用):

{
  "scripts": {
    "lint:eslint": "eslint --cache --max-warnings 0  \"{src,mock}/**/*.{vue,ts,tsx}\" --fix",
    "lint:prettier": "prettier --write  \"src/**/*.{js,json,tsx,css,less,scss,vue,html,md}\"",
    "lint:stylelint": "stylelint --cache --fix \"**/*.{vue,css,scss,postcss,less}\" --cache --cache-location node_modules/.cache/stylelint/",
    "lint": "pnpm lint:eslint && pnpm lint:prettier && pnpm lint:stylelint"
  }
}

在你提交代码前,pnpm lint一下即可

image-20220505164130419

关于 .eslintcache 文件

我们发现,每当我们pnpm lintlint:eslint命令)后,就会在根目录下生成一个.eslintcache文件,这个文件有什么用?

image-20220506175128957

当我们lint代码的时候,ESLint会帮我们检查全部代码。检查代码是需要时间的,如果我们只更改了一小部分文件,就没有必要再全部检查一遍了。这个时候,.eslintcache出现了。

官方解释.eslintcache文件能帮助我们存储有关已处理文件的信息,以便仅对更改的文件进行操作。.eslintcache 默认情况下存储在缓存中。启用此选项可以通过确保仅对更改的文件进行lint来显着提高ESLint的运行时间。

所以我在命令"lint:eslint"后加了eslint --cache

另外的,--max-warnings允许我们指定警告阈值,控制ESLint退出状态。我们设为0,这样的话,如果ESLint检查我们代码中出现了语法警告,就会报错,即无法正常通过。--fix选项指示ESLint尝试帮助我们修复尽可能多的问题,输出剩余的未修复问题。

VSCode 整合

关于项目的规范化都配置好了,还有一个重要的角色就是我们所使用的IDE工具--VSCode

我们发现在项目的根目录创建一个.vscode文件夹,为了保证我们代码格式风格的严格统一,通常会在这个文件夹下创建两个配置文件。

extensions.json 扩展配置文件

其实就是为了让你更快乐地进行项目的开发,帮你提升开发效率个插件工具集。

这里就做个示例(随便截了个图),具体看项目情况而定!

image-20220505171546269

{
  "recommendations": [
    "voorjaar.windicss-intellisense",
    "vscode-icons-team.vscode-icons",
    "davidanson.vscode-markdownlint",
    "stylelint.vscode-stylelint",
    "dbaeumer.vscode-eslint",
    "esbenp.prettier-vscode",
    "johnsoncodehk.volar",
    "mikestead.dotenv",
    "eamodio.gitlens",
    "antfu.iconify"
    // ......
  ]
}

关于VSCode有哪些非常好用的插件,网上有很多推荐,请自行搜索自己需要的。

settings.json VSCode 配置文件

示例:

{
  "editor.formatOnType": true,
  "editor.formatOnSave": true,
  "editor.tabSize": 2,
  "editor.formatOnPaste": true,
  "files.autoSave": "afterDelay",
  "git.confirmSync": false,
  "workbench.startupEditor": "newUntitledFile",
  "editor.suggestSelection": "first",
  "editor.acceptSuggestionOnCommitCharacter": false,
  "css.lint.propertyIgnoredDueToDisplay": "ignore",
  "editor.quickSuggestions": {
    "other": true,
    "comments": true,
    "strings": true
  },
  "files.associations": {
    "editor.snippetSuggestions": "top"
  },
  "[javascriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[typescriptreact]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[html]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[markdown]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }
}