前端工程自动化代码质量检查

lxf2023-02-17 01:51:28

随着团队的扩张和业务的发展,代码量越来越大,也不再是一个人一个项目,一个项目往往要多人协作, 需要有一个良好的技术选型和成熟的架构做支撑,也需要团队中每一个开发者都能用心维护项目, 除了人工code review以外,就需要一些规范工具来帮我们提升代码质量,做到编码统一

一、Prettier

译为 “漂亮、美化”,即美化我们的代码,或者说格式化、规范化代码,使其更加工整

一般不会检查我们代码具体的写法,而是在“可读性”上做文章

支持包括多种语言、数据交换格式、语法规范扩展

如:javascript、html、vue、json、yaml、css、less、sass等

官方文档:prettier.io/

特点:

  • 构建井统一代码风格
  • 帮助团队新成员快速融入团队
  • 开发者可以完全聚焦业务开发,不必在代码整理上花费过多心思
  • 方便,低成本灵活接入,并快速发挥作用
  • 清理井规范已有代码
  • 减少潜在 Bug
  • 丰富强大的社区支持

配置文件

安装依赖:

pnpm install prettier pretty-quick husky -D

.editorconfig -> .prettierrc.js 、 .prettierrc.js -> VsCode用户设置

.prettierrc

{
  "semi": false,
  "singleQuote": true,
  "overrides": [
    {
      "files": ".prettierrc",
      "options": {
        "parser": "json"
      }
    }
  ]
}

.prettierignore

dist
node_modules
coverage
pnpm-lock.yaml

项目配置

{
  "scripts":{
    "lint:prettier": "prettier --write  "src/**/*.{js,json,tsx,css,less,scss,vue,html,md}"",
  },
  "husky":{
    "hooks":{
      "pre-commit":"pretty-quick --staged"
    }
  }
}

二、Eslint

官方文档:eslint.org/docs/latest…

  • 为什么要使用Eslint?

因为JavaScript不具备先天编译流程,往往在运行时暴露错误

  • Eslint是做什么的?

Eslint,允许开发者在执行前发现代码错误,或不合理的写法

特点:

  • 所有规则都插件化
  • 所有规则都可插拔(随时开关)
  • 所有设计都透明化
  • 使用Espree进行JavaScript解析
  • 使用AST分析语法

配置文件

安装依赖:

pnpm install eslint @wang-qing/eslint-config eslint-staged -D

.eslintrc.json

前端工程自动化代码质量检查

说明:

  • off/0:关闭规则
  • warn/1:以warning形式打开规则
  • error/2:以error形式打开规则

.eslintignore

node_modules
dist
pnpm-lock.yaml
!.*

配置文件说明

前端工程自动化代码质量检查

配置文件优先级

  • .eslintrc.js
  • .eslintrc.cjs
  • .eslintrc.yaml
  • .eslintrc.yml
  • .eslintrc.json
  • package.json

eslint.config.js(实验功能)

export default [    {        rules: {            indent: ["error", 4],
            quotes: ["error", "double"]
        }
    }
]

项目配置

{
  "scripts":{
    // 遍历所有文件,并在每个找到错误的文件中提供详细日志,但需要开发者手动打开这些文件并更正错误
    "lint":"eslint --debug src/* ",
    // --fix 可以自动修复错误
    "lint:write":"eslint --debug src/ --fix",
    "lint:eslint": "eslint --cache --max-warnings 0  "{src,mock}/**/*.{vue,ts,tsx}" --fix",
    "lint:lint-staged": "lint-staged"
  },
  "lint-staged": {
    "*.{js,jsx,ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ],
    "{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": [
      "prettier --write--parser json"
    ],
    "package.json": [
      "prettier --write"
    ],
    "*.vue": [
      "eslint --fix",
      "prettier --write",
      "stylelint --fix"
    ],
    "*.{scss,less,styl,html}": [
      "stylelint --fix",
      "prettier --write"
    ],
    "*.md": [
      "prettier --write"
    ]
  }
}

三、husky

husky就是git的钩子,在GIt进行到某一时段时,可以交给开发者完成某些特定的操作

例:在整个项目上运行lint会很慢,我们一般只想对更改的文件进行检查,此时就需要使用到lint-staged

官方文档:git-scm.com/book/zh/v2/…

husky和lint-staged结合使用

{
	"husky". {
		"hooks":{
			"pre-commit", "lint-staged"
		}
		"lint-staged": {
			"*.(jslisx)": ["npm run lint:eslint","npm run lint:prettier","git add"]
		}
	}

四、stylelint

官方文档:stylelint.io/

安装stylelint依赖

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

postcss依赖

pnpm install postcss postcss-less postcss-html -D

package.json

{
	"lint:stylelint": "stylelint --cache --fix "**/*.{vue,less,postcss,css,scss}" --cache --cache-location node_modules/.cache/stylelint/"
}

stylelint.config.cjs

module.exports = {
  root: true,
  plugins: ['stylelint-order'],
  // customSyntax: 'postcss-less',
  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',
    // 'declaration-block-trailing-semicolon': 'always',
    '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'],
  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'],
          },
        ],
      },
    },
  ],
};

五、.editorconfig

官方文档:editorconfig.org/

root = true

[*]
charset = utf-8
indent_style = space
indent_size = 2
end_of_line = lf
insert_final_newline = true
trim_trailing_whitespace = true
quote_type = single

六、jsconfig.json

官方文档:code.visualstudio.com/docs/langua…

{
	"compilerOptions": {
		"baseUrl": ".",
		"paths": {
			"@/*": ["src/*"],
			"#/*": ["types/*"]
		},
		"jsx": "preserve"
	},
	"include": ["src/**/*.d.ts", "src/**/*.js"],
	"exclude": ["node_modules", "**/node_modules", "dist"]
}

七、Vscode

推荐扩展

名称描述
Vue Language Features为Vue,Vitepress和petite-vue构建的语言支持扩展
PrettierPrettier是一个固执己见的代码格式化程序
Eslint将 ESLint 集成到 VS Code 中
StylelintStylelint的扩展
Sasssass的扩展插件
Editor for VS Code此插件尝试使用 .editorconfig 文件中的设置覆盖用户/工作区设置

extensions.json

extensions.json在项目下的.vscode文件夹中

{
  "recommendations": [
    "Vue.volar",
    "esbenp.prettier-vscode",
    "dbaeumer.vscode-eslint",
    "stylelint.vscode-stylelint",
    "syler.sass-indented",
		"EditorConfig.EditorConfig"
  ]
}

settings.json

settings.json在项目下的.vscode文件夹中

个人设置参考

{
  //===========================================
  //============= Editor ======================
  //===========================================
  "editor.tabSize": 2,
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "editor.fontSize": 15,
  // "editor.formatOnSave": true,
  "editor.inlineSuggest.enabled": true,
  "editor.accessibilitySupport": "off",
  "editor.wordWrap": "on",
  "editor.cursorSmoothCaretAnimation": true,
  "editor.snippetSuggestions": "top",
    // 保存后自动修复格式
    "editor.codeActionsOnSave": {
      "source.fixAll.eslint": true
    },
  //===========================================
  //============= workbench =======================
  //===========================================
  "workbench.iconTheme": "vscode-great-icons",
  "workbench.colorTheme": "Vitesse Dark Soft",
  "workbench.tree.indent": 16,
  //===========================================
  //============= files =======================
  //===========================================
  "files.autoSave": "onFocusChange",
  "files.eol": "\n",
  "search.exclude": {
    "**/node_modules": true,
    "**/*.log": true,
    "**/*.log*": true,
    "**/bower_components": true,
    "**/dist": true,
    "**/elehukouben": true,
    "**/.git": true,
    "**/.gitignore": true,
    "**/.svn": true,
    "**/.DS_Store": true,
    "**/.idea": true,
    "**/.vscode": false,
    "**/yarn.lock": true,
    "**/tmp": true,
    "out": true,
    "dist": true,
    "node_modules": true,
    "CHANGELOG.md": true,
    "examples": true,
    "res": true,
    "screenshots": true,
    "yarn-error.log": true,
    "**/.yarn": true
  },
  "files.exclude": {
    "**/.cache": true,
    "**/.editorconfig": true,
    "**/.eslintcache": true,
    "**/bower_components": true,
    "**/.idea": true,
    "**/tmp": true,
    "**/.git": true,
    "**/.svn": true,
    "**/.hg": true,
    "**/CVS": true,
    "**/.DS_Store": true
  },
  "files.watcherExclude": {
    "**/.git/objects/**": true,
    "**/.git/subtree-cache/**": true,
    "**/.vscode/**": true,
    "**/node_modules/**": true,
    "**/tmp/**": true,
    "**/bower_components/**": true,
    "**/dist/**": true,
    "**/yarn.lock": true
  },
  "files.associations": {
    "*.cjson": "jsonc",
    "*.wxss": "css",
    "*.wxs": "javascript"
  },
  "emmet.includeLanguages": {
    "wxml": "html"
  },
  "[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.codeActionsOnSave": {
  //     "source.fixAll.eslint": false
  //   }
  // },
  "fileheader.configObj": {
    "createFileTime": true,
    "language": {
      "languagetest": {
        "head": "/$$",
        "middle": " $ @",
        "end": " $/",
        "functionSymbol": {
          "head": "/** ",
          "middle": " * @",
          "end": " */"
        },
        "functionParams": "js"
      }
    },
    "autoAdd": false,
    "autoAddLine": 100,
    "autoAlready": false,
    "annotationStr": {
      "head": "/*",
      "middle": " * @",
      "end": " */",
      "use": false
    },
    "headInsertLine": {
      "php": 2,
      "sh": 2
    },
    "beforeAnnotation": {
      "文件后缀": "该文件后缀的头部注释之前添加某些内容"
    },
    "afterAnnotation": {
      "文件后缀": "该文件后缀的头部注释之后添加某些内容"
    },
    "specialOptions": {
      "特殊字段": "自定义比如LastEditTime/LastEditors"
    },
    "switch": {
      "newlineAddAnnotation": true
    },
    "supportAutoLanguage": [],
    "prohibitAutoAdd": ["json"],
    "folderBlacklist": ["node_modules", "文件夹禁止自动添加头部注释"],
    "prohibitItemAutoAdd": [
      "项目的全称, 整个项目禁止自动添加头部注释, 可以使用快捷键添加"
    ],
    "moveCursor": true,
    "dateFormat": "YYYY-MM-DD HH:mm:ss",
    "atSymbol": ["@", "@"],
    "atSymbolObj": {
      "文件后缀": ["头部注释@符号", "函数注释@符号"]
    },
    "colon": [": ", ": "],
    "colonObj": {
      "文件后缀": ["头部注释冒号", "函数注释冒号"]
    },
    "filePathColon": "路径分隔符替换",
    "showErrorMessage": false,
    "writeLog": false,
    "wideSame": false,
    "wideNum": 13,
    "functionWideNum": 0,
    "CheckFileChange": false,
    "createHeader": false,
    "useWorker": false,
    "designAddHead": false,
    "headDesignName": "random",
    "headDesign": false,
    "cursorModeInternalAll": {},
    "openFunctionParamsCheck": true,
    "functionParamsShape": ["{", "}"],
    "functionBlankSpaceAll": {},
    "functionTypeSymbol": "*",
    "typeParamOrder": "type param",
    "customHasHeadEnd": {},
    "throttleTime": 60000,
    "functionParamAddStr": "",
    "NoMatchParams": "no show param",
    "search.followSymlinks": false,
    "files.exclude": {
      "**/.git": true,
      "**/.svn": true,
      "**/.hg": true,
      "**/CVS": true,
      "**/.DS_Store": true,
      "**/tmp": true,
      "**/node_modules": true,
      "**/bower_components": true,
      "**/dist": true
    },
    "files.watcherExclude": {
      "**/.git/objects/**": true,
      "**/.git/subtree-cache/**": true,
      "**/node_modules/**": true,
      "**/tmp/**": true,
      "**/bower_components/**": true,
      "**/dist/**": true
    }
  },
  //===========================================
  //============= terminal =======================
  //===========================================
  "terminal.integrated.defaultProfile.osx": "zsh",
  "terminal.integrated.fontSize": 14,
  "terminal.integrated.env.osx": {
    "FIG_NEW_SESSION": "1"
  },
  //===========================================
  //============= volar =======================
  //===========================================
  "volar.autoCompleteRefs": true,
  "volar.tsPlugin": true,
  "volar.tsPluginStatus": false,
  //===========================================
  //============= eslint =======================
  //===========================================
  "eslint.codeActionsOnSave.rules": null,
  // 添加vue支持
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "json",
    "json5",
    "jsonc",
    "yaml"
  ],
  "eslint.probe": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact",
    "html",
    "vue",
    "json",
    "json5",
    "jsonc",
    "yaml"
  ],
  //===========================================
  //============= stylelint =======================
  //===========================================
  "css.validate": false,
  "scss.validate": false,
  "less.validate": false,
  "stylelint.enable": true,
  "stylelint.packageManager": "yarn",
  //===========================================
  //============= other =======================
  //===========================================
  "window.nativeTabs": true,
  "errorLens.enabledDiagnosticLevels": [
    "error"
  ],
  "window.zoomLevel": 1,
  "path-intellisense.mappings": {
    "@/": "${workspaceRoot}/src"
  },

}

八、其他配置文件

.npmrc(使用pnpm)

shamefully-hoist=true
strict-peer-dependencies=false

.nvmrc

v18

.browserslistrc

# Browsers that we support

last 1 version
> 1%
IE 10 # sorry

.markdownlint.json

{
  "MD033": false,
  "MD013": false
}