使用Webpack搭建React脚手架(下篇)

一套完善的开发环境配置可以极大的提升开发效率,提高代码质量,方便多人合作,以及后期的项目迭 代和维护。所以说,前端代码格式规范和语法检测的工具可以提高代码的质量和可读性,减少低级错误 和维护成本,提高团队的协作效率和开发效率,是非常有必要的。本篇我们将完善脚手架的搭建。

代码格式规范和语法检测工具

1,EditorConfig: EditorConfig 是一个用于统一编辑器和 IDE 的配置文件的工具。它可以帮助团队协作 中的开发人员保持一致的编码风格,无论他们使用的是哪个编辑器或 IDE。 EditorConfig 支持配置文 件的语法规则、缩进、换行符、字符编码等,可以通过在项目中添加一个 .editorconfig 文件来使 用。 

2,ESLint: ESLint 是一个广泛使用的 JavaScript 代码检查工具,它可以帮助开发人员遵循代码风格规 范,并发现代码中潜在的问题。 ESLint 有很多可定制的规则,可以根据团队的代码风格和项目的要求 进行配置。ESLint 还支持集成到许多编辑器和 IDE 中,如 Visual Studio Code、Sublime Text、Atom、 WebStorm 等,以提供实时的语法和格式错误检查。

3,Prettier: Prettier 是一个代码格式化工具,它可以自动化地将代码格式化为一致的风格。与 ESLint 不同的是, Prettier 不关心代码的语义或质量,只关心代码的外观。 Prettier 与 ESLint 集成使用可以让代码保持一致性和规范性。

4,Stylelint: Stylelint 是一个 CSS 样式检查工具,它可以帮助开发人员遵循 CSS 代码风格规范,并发 现代码中潜在的问题。 Stylelint 有很多可定制的规则,可以根据团队的代码风格和项目的要求进行配 置。 Stylelint 也支持集成到许多编辑器和 IDE 中,如 Visual Studio Code、Sublime Text、Atom、 WebStorm 等,以提供实时的语法和格式错误检查。

 这些工具都有相似的目标:

使代码本身和团队成员之间更加一致

检测可能导致潜在错误的有问题的代码

总的来说:

EditorConfig : 跨编辑器和IDE编写代码,保持一致的简单编码风格;

Prettier : 专注于代码格式化的工具,美化代码;

ESLint :专注于代码质量检测、编码风格约束等;

Stylelint :专注于样式代码语法检查和格式错误检查; 

 代码提交规范工具

1. Commitizen: Commitizen 是一个 Git 提交信息格式化工具,可以通过命令行交互式地创建符合 规范的 Git 提交信息。 Commitizen 支持自定义提交信息的格式,并且可以与其他规范工具(如 ESLint 和 Prettier )集成使用

2. Conventional Commits: Conventional Commits 是一个 Git 提交信息规范标准,规定了 Git 提交信息的格式和内容,包括提交类型、作用域、描述、消息体和消息页脚等部分。 Conventional Commits 规范可以帮助团队协作中的开发人员更好地理解和管理代码变更历史。

3. Husky 和 Commitlint: Husky 和 Commitlint 是两个工具,可以结合使用来规范 Git 提交信 息。 Husky 可以在 Git 提交前执行一些预定义的钩子(如 pre-commit 和 pre-push ),而 Commitlint 可以检查 Git 提交信息是否符合规范。

4. Git Hooks: Git Hooks 是 Git 自带的一个钩子系统,可以在 Git 的各个生命周期(如 precommit 和 post-commit )执行自定义的脚本。开发人员可以使用 Git Hooks 来规范化 Git 提交 信息,例如在 pre-commit 钩子中执行提交信息的格式检查。

代码 Git 提交规范工具是非常有用的,可以帮助开发人员更好地管理代码变更历史,提高代码可维护性 和协作效率。开发人员可以根据自己的需求和团队的规范选择合适的工具和标准来使用

 editorconfig

在项目中引入 editorconfig 是为了在多人协作开发中保持代码的风格和一致性。不同的开发者使用不 同的编辑器或IDE,可能会有不同的缩进(比如有的人喜欢4个空格,有的喜欢2个空格)、换行符、编码 格式等。甚至相同的编辑器因为开发者自定义配置的不同也会导致不同风格的代码,这会导致代码的可 读性降低,增加代码冲突的可能性,降低了代码的可维护性。

EditorConfig 使不同编辑器可以保持同样的配置。因此,我们得以无需在每次编写新代码时,再 依靠 Prettier 来按照团队约定格式化一遍(出现保存时格式化突然改变的情况) 。当然这需要在 你的 IDE 上安装了必要的 EditorConfig 插件或扩展。

安装 EditorConfig for VS Code 

 目前主流的编辑器或者 IDE 基本上都有对应的 EditorConfig 插件,有的是内置支持的(比如, WebStorm 不需要独立安装 EditorConfig 的插件),有的需要独立安装。

需要注意的是,不同的插件对 EditorConfig 属性的支持度不一样,笔者使用的是 VS Code 。

然后在插件的介绍页上点击设置的齿轮,并且选择Add to Workspace Recommendations,就可以将 其加入清单。也可以直接开启 .vscode/extensions.json 进行编辑:

{
	"recommendations": ["editorconfig.editorconfig"]
}

为什么要做这个操作? 假如哪天项目新来一个协同开发的同学,当他拉取取项目,用 vscode 打 开项目的时候,编辑器就会自动提醒他安装这个插件,并将相关的配置做设定。下面的 eslint 和 prettier 插件也是类似。

新建 .editorconfig 

在根目录新建 .editorconfig 文件: 

// https://editorconfig.org
root = true //设置为true表示根目录,控制配置文件 .editorconfig 是否生效的字段
[*] // 匹配全部文件,匹配除了 `/` 路径分隔符之外的任意字符串
charset = utf-8 //设置字符编码,取值为 latin1,utf-8,utf-8-bom,
//utf-16be 和 utf-16le,当然 utf-8-bom 不推荐使用
end_of_line = lf //设置使用的换行符,取值为 lf,cr 或者 crlf
indent_size = 2 // 设置缩进的大小,即缩进的列数,当 indexstyle 取值 tab时,indentsize 会使用 tab_width 的值
indent_style = space // 缩进风格,可选space|tab
insert_final_newline = true // 设为true表示使文件以一个空白行结尾
trim_trailing_whitespace = true // 删除一行中的前后空格
[*.md] // 匹配全部 .md 文件
trim_trailing_whitespace = false

上面的配置可以规范本项目中文件的缩进风格,和缩进空格数等,会覆盖 vscode 的配置,来达到不同 编辑器中代码默认行为一致的作用。

VS Code 的 EditorConfig 目前支持下列属性:

indent_style

indent_size

tab_width

end_of_line (on save)

insert_final_newline (on save)

trim_trailing_whitespace (on save) 

prettier

每个人写代码的风格习惯不一样,比如代码换行,结尾是否带分号,单双引号,缩进等,而且不能只靠 口头规范来约束,项目紧急的时候可能会不太注意代码格式,这时候需要有工具来帮我们自动格式化代 码,而prettier就是帮我们做这件事的。

安装 VS Code 插件和 prettier 

安装 prettier :

pnpm i prettier -D 

新建 .prettierrc.js 

在根目录下新建 .prettierrc.js 和 .prettierignore 文件:

// .prettierrc.js
module.exports = {
tabWidth: 2, // 一个tab代表几个空格数,默认就是2
useTabs: false, // 是否启用tab取代空格符缩进,.editorconfig设置空格缩进,所以设置为
false
printWidth: 100, // 一行的字符数,如果超过会进行换行
semi: false, // 行尾是否使用分号,默认为true
singleQuote: true, // 字符串是否使用单引号
trailingComma: 'all', // 对象或数组末尾是否添加逗号 none| es5| all
jsxSingleQuote: true, // 在jsx里是否使用单引号,你看着办
bracketSpacing: true, // 对象大括号直接是否有空格,默认为true,效果:{ foo: bar }
arrowParens: 'avoid' // 箭头函数如果只有一个参数则省略括号
}
// .prettierignore
node_modules
dist
env
.gitignore
pnpm-lock.yaml
README.md
src/assets/*
.vscode
public
.github
.husky

配置 .vscode/settings.json 

配置前两步后,虽然已经配置 prettier 格式化规则,但还需要让 vscode 来支持保存后触发格式化, 在项目根目录新建 .vscode 文件夹,内部新建 settings.json 文件配置文件,代码如下:

{
	"search.exclude": {
	"/node_modules": true,
	"dist": true,
	"pnpm-lock.yaml": true
	},
	"files.autoSave": "onFocusChange",
	"editor.formatOnSave": true,
	"editor.formatOnType": true,
	"editor.defaultFormatter": "esbenp.prettier-vscode",
	"[javascript]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"[javascriptreact]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"[typescript]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"[typescriptreact]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"[json]": {
	"editor.defaultFormatter": "vscode.json-language-features"
	},
	"[html]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"[markdown]": {
	"editor.defaultFormatter": "esbenp.prettier-vscode"
	},
	"javascript.validate.enable": false,
}

若配置 "prettier.requireConfig": true 则要求根目录下有 Prettier 的配置文件,它会覆 盖 Prettier扩展 中的默认配置,否则保存时不会自动格式化。 

若配置 "prettier.requireConfig": false 则不要求根目录下有配置文件,若有的话也会被感 知到并以配置文件的内容为准。

这些代码的作用是:在编辑后保存 [xxx] 文件时,自动应用 Prettier 进行格式化,以确保代码 风格的一致性。 

先配置了忽略哪些文件不进行格式化,又添加了保存代码后触发格式化代码配置,以及各类型文件格式 化使用的格式。这一步配置完成后,修改项目代码,把格式打乱,点击保存时就会看到编辑器自动把代 码格式规范化了。

若设置需要配置文件,则必须要求根目录下有配置文件 .prettierrc.js 或 .editorconfig 中的一个 或者两个同时存在,否则代码保存不会进行格式化。

可能你会对上面 .editorconfig 文件作为 Prettier 的配置文件感到疑惑, vscode 的 Prettier 插 件中有关配置文件有这样的一段描述

可以看出Prettier插件获取配置文件有一个优先级: .prettierrc > .editorconfig > vscode默 认配置 。

上面的前两者并不是说 .prettierrc 和 .editorconfig 同时存在时,后者的配置内容就被忽 略,实际的表现:

.prettierrc.js 和 .editorconfig 同时存在时,二者内容会进行合并,若配置项有冲突,这 .prettierrc 的优先级更高。

为了保证我们的编辑器是使用 prettier 作为默认格式化的工具,最好是手动配置一下默认的格式化插件。

脚本命令检查和修复格式 

在 package.json 的 "scripts" 中加入以下脚本命令:

"lint:prettier": "prettier --write --loglevel warn \"src/**/*. {js,ts,json,tsx,css,less,scss,stylus,html,md}\"" 

这段代码是一个脚本命令,用于运行 Prettier 工具来格式化指定目录下的文件。具体解释如下:

"--write" : 表示将格式化后的结果直接写回原文件中,而不是输出到控制台。

"--loglevel warn" : 表示只输出警告级别的日志信息。

"src/**/*.{js,ts,json,tsx,css,less,scss,stylus,html,md}" : 是要格式化的文件的路径, 这里指定了在 src 目录下,所有扩展名为 .js ,.ts, .json, .tsx ,.css ,.less, .scss, .stylus, .html , .md 的文件。 

这个脚本命令的作用是:运行 Prettier 工具来格式化指定目录下的文件,并将格式化后的结果直接 写回原文件中。同时,只输出警告级别的日志信息。

现在可以测试一下,将 .prettierrc.js 中的 tabWidth 值修改为 4(缩进为 4 个空格),然后运行 pnpm run lint:prettier ,随便打开一个 src 下的文件,你就会发现缩进从 2 变成 4 了。

stylelint 

安装 stylelint 插件和依赖 

pnpm i stylelint stylelint-config-css-modules stylelint-config-prettier
stylelint-config-standard stylelint-order -D

新建 .stylelintrc.js 和 .stylelintignore 

// @see: https://stylelint.io
module.exports = {
  extends: [
    "stylelint-config-standard",
    "stylelint-config-prettier",
    // "stylelint-config-css-modules",
    // "stylelint-config-recess-order" // 配置stylelint css属性书写顺序插件,
  ],
  plugins: ["stylelint-order"],
  rules: {
    /**
	* indentation: null, // 指定缩进空格
	"no-descending-specificity": null, // 禁止在具有较高优先级的选择器后出现被其覆盖的
	较低优先级的选择器
	"function-url-quotes": "always", // 要求或禁止 URL 的引号 "always(必须加上引
	号)"|"never(没有引号)"
	"string-quotes": "double", // 指定字符串使用单引号或双引号
	"unit-case": null, // 指定单位的大小写 "lower(全小写)"|"upper(全大写)"
	"color-hex-case": "lower", // 指定 16 进制颜色的大小写 "lower(全小写)"|"upper(全
	大写)"
	"color-hex-length": "long", // 指定 16 进制颜色的简写或扩写 "short(16进制简
	写)"|"long(16进制扩写)"
	"rule-empty-line-before": "never", // 要求或禁止在规则之前的空行 "always(规则之前
	必须始终有一个空行)"|"never(规则前绝不能有空行)"|"always-multi-line(多行规则之前必须始终有
	一个空行)"|"never-multi-line(多行规则之前绝不能有空行。)"
	"font-family-no-missing-generic-family-keyword": null, // 禁止在字体族名称列表
	中缺少通用字体族关键字
	"block-opening-brace-space-before": "always", // 要求在块的开大括号之前必须有一
	个空格或不能有空白符 "always(大括号前必须始终有一个空格)"|"never(左大括号之前绝不能有空
	格)"|"always-single-line(在单行块中的左大括号之前必须始终有一个空格)"|"never-singleline(在单行块中的左大括号之前绝不能有空格)"|"always-multi-line(在多行块中,左大括号之前必须
	始终有一个空格)"|"never-multi-line(多行块中的左大括号之前绝不能有空格)"
	"property-no-unknown": null, // 禁止未知的属性(true 为不允许)
	"no-empty-source": null, // 禁止空源码
	"declaration-block-trailing-semicolon": null, // 要求或不允许在声明块中使用尾随
	分号 string:"always(必须始终有一个尾随分号)"|"never(不得有尾随分号)"
	"selector-class-pattern": null, // 强制选择器类名的格式
	"value-no-vendor-prefix": null, // 关闭 vendor-prefix(为了解决多行省略 -
	webkit-box)
	"at-rule-no-unknown": null,
	"selector-pseudo-class-no-unknown": [
	true,
	{
	ignorePseudoClasses: ["global", "v-deep", "deep"]
	}
	]
	}
	*/
    "selector-class-pattern": [
      // 命名规范 -
      "^([a-z][a-z0-9]*)(-[a-z0-9]+)*$",
      {
        message: "Expected class selector to be kebab-case",
      },
    ],
    "string-quotes": "double", // 单引号
    "at-rule-empty-line-before": null,
    "at-rule-no-unknown": null,
    "at-rule-name-case": "lower", // 指定@规则名的大小写
    "length-zero-no-unit": true, // 禁止零长度的单位(可自动修复)
    "shorthand-property-no-redundant-values": true, // 简写属性
    "number-leading-zero": "always", // 小数不带0
    "declaration-block-no-duplicate-properties": true, // 禁止声明快重复属性
    "no-descending-specificity": true, // 禁止在具有较高优先级的选择器后出现被其覆盖的较低优先级的选择器。
    "selector-max-id": null, // 限制一个选择器中 ID 选择器的数量
    "max-nesting-depth": 10,
    "declaration-block-single-line-max-declarations": 1,
    "block-opening-brace-space-before": "always",
    // 'selector-max-type': [0, { ignore: ['child', 'descendant', 'compounded']
  },
  indentation: [
    2,
    {
      // 指定缩进 warning 提醒
      severity: "warning",
    },
  ],
  "order/order": [
    "custom-properties",
    "dollar-variables",
    "declarations",
    "rules",
    "at-rules",
  ],
  "order/properties-order": [
    // 规则顺序
    "position",
    "top",
    "right",
    "bottom",
    "left",
    "z-index",
    "display",
    "float",
    "width",
    "height",
    "max-width",
    "max-height",
    "min-width",
    "min-height",
    "padding",
    "padding-top",
    "padding-right",
    "padding-bottom",
    "padding-left",
    "margin",
    "margin-top",
    "margin-right",
    "margin-bottom",
    "margin-left",
    "margin-collapse",
    "margin-top-collapse",
    "margin-right-collapse",
    "margin-bottom-collapse",
    "margin-left-collapse",
    "overflow",
    "overflow-x",
    "overflow-y",
    "clip",
    "clear",
    "font",
    "font-family",
    "font-size",
    "font-smoothing",
    "osx-font-smoothing",
    "font-style",
    "font-weight",
    "line-height",
    "letter-spacing",
    "word-spacing",
    "color",
    "text-align",
    "text-decoration",
    "text-indent",
    "text-overflow",
    "text-rendering",
    "text-size-adjust",
    "text-shadow",
    "text-transform",
    "word-break",
    "word-wrap",
    "white-space",
    "vertical-align",
    "list-style",
    "list-style-type",
    "list-style-position",
    "list-style-image",
    "pointer-events",
    "cursor",
    "background",
    "background-color",
    "border",
    "border-radius",
    "content",
    "outline",
    "outline-offset",
    "opacity",
    "filter",
    "visibility",
    "size",
    "transform",
  ],
};

.stylelintignore 文件:

*.json
*.png
*.eot
*.ttf
*.woff
src/styles/antd-overrides.less
node_modules
dist
env
.gitignore
pnpm-lock.yaml
README.md
src/assets/*
.vscode
public
.github
.husky

配置 .vscode/settings.json 

最后记得在 .vscode/settings.json 中加入:

{
	// ...
	"editor.codeActionsOnSave": {
	"source.fixAll.stylelint": true
	},
	"stylelint.validate": [
	"css",
	"less",
	"sass",
	"stylus",
	"postcss"
	]
}

eslint 

ESLint 是什么呢?一个开源的 JavaScript 的 linting 工具,是一个在 JavaScript 代码中通过规 则模式匹配作代码识别和报告的插件化的检测工具,它的目的是保证代码规范的一致性和及时发现代码 问题、提前避免错误发生。它使用 espree 将 JavaScript 代码解析成抽象语法树 (AST),然后通过AST 来分析我们代码,从而给予我们两种提示:

1. 代码质量问题:使用方式有可能有问题,eslint 可以发现代码中存在的可能错误,如使用未声明变 量、声明而未使用的变量、修改 const 变量、代码中使用debugger等等 

2. 格问题代码风:风格不符合一定规则,eslint 也可以用来统一团队的代码风格,比如加不加分号、 使用 tab 还是空格、字符串使用单引号 等等

ESLint 的关注点是代码质量,检查代码风格并且会提示不符合风格规范的代码。除此之外 ESLint 也 具有一部分代码格式化的功能。

ESLint 最初是由 Nicholas C. Zakas ( JavaScript 高级程序设计 作者)于2013年6月创建的开源 项目。它的目标是提供一个插件化的javascript代码检测工具。 

JavaScript发展历程中出现的Lint工具: JSLint -> JSHint -> ESLint/TSLint ;

1,JSLint 是最早出现的 Lint 工具,不支持灵活拓展及配置,必须接受它所有规则; 

2,JSHint 在 JSLint 的基础上提供了一定的配置项,给了开发者较大的自由,但无法添加自定义规 则;

3,Zakas 创建 ESLint 的初衷就是觉得当时的JSHint存在局限性,无法添加自定义规则。

4,ES6 的出现后则让 ESLint 迅速大火。 因为ES6新增了很多语法, JSHint 短期内无法提供支持,而 ESLint 只需要有合适的解析器以及拓 展校验规则 就能够进行 Lint 检查。此时 babel 就为兼容 ESLint 开发了 babel-eslint 解析 器,提供支持的同时也让 ESLint 成为最快支持 ES6 语法的 Lint 工具。

安装 eslint 插件和包

安装 eslint :

pnpm i eslint eslint-config-airbnb eslint-config-standard eslint-friendlyformatter eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-node eslintplugin-promise eslint-plugin-react-hooks eslint-plugin-react @typescripteslint/eslint-plugin @typescript-eslint/parser eslint-plugin-prettier eslintconfig-prettier -D

 新建 .eslintrc.js

在根目录新建一个 .eslintrc.js 文件:

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  extends: [
    "airbnb-base",
    "eslint:recommended",
    "plugin:import/recommended",
    "plugin:@typescript-eslint/recommended",
    "prettier",
    "plugin:prettier/recommended",
  ],
  parser: "@typescript-eslint/parser",
  parserOptions: {
    ecmaFeatures: {
      jsx: true,
    },
    ecmaVersion: "latest",
    sourceType: "module",
  },
  plugins: ["react", "@typescript-eslint"],
  rules: {
    // eslint (http://eslint.cn/docs/rules)
    "react/jsx-filename-extension": [
      "error",
      { extensions: [".js", ".jsx", ".ts", ".tsx"] },
    ],
    "class-methods-use-this": "off",
    "no-param-reassign": "off",
    "no-unused-expressions": "off",
    "no-plusplus": 0,
    "no-restricted-syntax": 0,
    "consistent-return": 0,
    "@typescript-eslint/ban-types": "off",
    // "import/no-extraneous-dependencies": "off",
    "@typescript-eslint/no-non-null-assertion": "off",
    "import/no-unresolved": "off",
    "import/prefer-default-export": "off", // 关闭默认使用 export default 方式导出
    "import/no-extraneous-dependencies": ["error", { devDependencies: true }],
    "@typescript-eslint/no-use-before-define": 0,
    "no-use-before-define": 0,
    "@typescript-eslint/no-var-requires": 0,
    "@typescript-eslint/no-explicit-any": "off",
    "@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间。
    "no-shadow": "off",
    // "@typescript-eslint/no-var-requires": "off"
    "import/extensions": [
      "error",
      "ignorePackages",
      {
        "": "never",
        js: "never",
        jsx: "never",
        ts: "never",
        tsx: "never",
      },
    ],
    // "no-var": "error", // 要求使用 let 或 const 而不是 var
    // "no-multiple-empty-lines": ["error", { max: 1 }], // 不允许多个空行
    // "no-use-before-define": "off", // 禁止在 函数/类/变量 定义之前使用它们
    // "prefer-const": "off", // 此规则旨在标记使用 let 关键字声明但在初始分配后从未重新分配的变量,要求使用 const
    // "no-irregular-whitespace": "off", // 禁止不规则的空白
    // // typeScript (https://typescript-eslint.io/rules)
    // "@typescript-eslint/no-unused-vars": "error", // 禁止定义未使用的变量
    // "@typescript-eslint/no-inferrable-types": "off", // 可以轻松推断的显式类型可能会增加不必要的冗长
    // "@typescript-eslint/no-namespace": "off", // 禁止使用自定义 TypeScript 模块和命名空间。
    // "@typescript-eslint/no-explicit-any": "off", // 禁止使用 any 类型
    // "@typescript-eslint/ban-ts-ignore": "off", // 禁止使用 @ts-ignore
    // "@typescript-eslint/ban-types": "off", // 禁止使用特定类型
    // "@typescript-eslint/explicit-function-return-type": "off", // 不允许对初始化为数字、字符串或布尔值的变量或参数进行显式类型声明
    // "@typescript-eslint/no-var-requires": "off", // 不允许在 import 语句中使用require 语句
    // "@typescript-eslint/no-empty-function": "off", // 禁止空函数
    // "@typescript-eslint/no-use-before-define": "off", // 禁止在变量定义之前使用它们
    // "@typescript-eslint/ban-ts-comment": "off", // 禁止 @ts- 使用注释或要求在指令后进行描述
    // "@typescript-eslint/no-non-null-assertion": "off", // 不允许使用后缀运算符的非空断言(!)
    // "@typescript-eslint/explicit-module-boundary-types": "off", // 要求导出函数和类的公共类方法的显式返回和参数类型
    // // react (https://github.com/jsx-eslint/eslint-plugin-react)
    // "react-hooks/rules-of-hooks": "error",
    // "react-hooks/exhaustive-deps": "off"
  },
  settings: {
    "import/extensions": [".js", ".jsx", ".ts", ".tsx"],
    "import/parsers": {
      "@typescript-eslint/parser": [".ts", ".tsx"],
    },
    "import/resolver": {
      node: {
        paths: ["src"],
        extensions: [".js", ".jsx", ".ts", ".tsx"],
        moduleDirectory: ["node_modules", "src/"],
      },
    },
    react: {
      version: "detect",
    },
  },
};

 有时候,我们需要在代码中忽略ESLint的某些规则检查,此时我们可以通过加入代码注释的方式解 决:可以指定整个文件、某一行、某一区块开启/关闭 某些或全部规则检查;

/* eslint-disable */      --禁用全部规则 放在文件顶部则整个文件范围都不检查

/* eslint-disable no-alert, no-console */        --禁用某些规则

// eslint-disable-line          --当前行上禁用规则

// eslint-disable-next-line           --下一行上禁用规则

新建 .eslintignore 

在根目录新建一个 .eslintignore 文件:

node_modules
dist
env
.gitignore
pnpm-lock.yaml
README.md
src/assets/*
.vscode
public
.github
.husky

.eslintignore 用于指定 ESLint 工具在检查代码时要忽略的文件和目录。具体来说, .eslintignore 文件中列出的文件和目录将被 ESLint 忽略,不会对其进行代码检查和报告。这个文件中的每一行都是一 个文件或目录的模式,支持使用通配符( * )和正则表达式来匹配多个文件或目录。

通常情况下, .eslintignore 文件中会列出一些不需要检查的文件或目录,比如第三方库、测试 代码、构建输出等,以减少 ESLint 的检查范围,提高代码检查的效率。 

添加eslint语法检测脚本 

前面的eslint报错和警告都是我们用眼睛看到的,有时候需要通过脚本执行能检测出来,在 package.json 的 scripts 中新增:

// --fix:此项指示 ESLint 尝试修复尽可能多的问题。这些修复是对实际文件本身进行的,只有剩余的未
修复的问题才会被输出。
"lint:eslint": "eslint --fix --ext .js,.ts,.tsx ./src",

现在执行 pnpm run lint:eslint ,控制台将会爆出一系列 warning ;

除此之外再解决一个问题就是 eslint 报的警告:

React version not specified in eslint-plugin-react settings

需要告诉 eslint 使用的 react版本 ,在 .eslintrc.js 和 rules 平级添加 settings 配置,让 eslint 自己检测 react 版本,对应 issuse:

settings: {
	"react": {
	"version": "detect"
	}
}

再执行 pnpm run lint:eslint 就不会报这个未设置 react 版本的警告了。

eslint 与 prettier 冲突 

安装两个依赖:

pnpm i eslint-config-prettier eslint-plugin-prettier -D

在 .eslintrc.js 的 extends 中加入:

module.exports = {
  // ...
  extends: [
    // ...
    "plugin:prettier/recommended", // <==== 增加一行
  ],
  // ...
};

最后再配置一下 .vscode/settings.json :

{
	// ...
	"eslint.enable": true,
	"editor.codeActionsOnSave": {
	"source.fixAll.eslint": true,
	},
}

husky + lint-statged 

使用lint-staged优化eslint检测速度 

在上面配置的 eslint 会检测 src 文件下所有的 .ts, .tsx 文件,虽然功能可以实现,但是当项目文件 多的时候,检测的文件会很多,需要的时间也会越来越长,但其实只需要检测提交到暂存区,就是 git add 添加的文件,不在暂存区的文件不用再次检测,而 lint-staged 就是来帮我们做这件事情的。

在 package.json 添加 lint-staged 配置 

"lint-staged": {
	"src/**/*.{ts,tsx}": [
	"pnpm run lint:eslint",
	"pnpm run lint:prettier"
	]
},
	

因为要检测 git 暂存区代码,所以如果你的项目还没有使用 git 来做版本控制,需要执行 git init 初始化一下 git :         

git init

初始化 git 完成后就可以进行测试了,先提交一下没有语法问题的 App.tsx

git add src/App.tsx

把 src/App.tsx 提交到暂存区后,执行 npx lint-staged ,会顺利通过检测。

假如我们现在把 package.json 中的 "lint:eslint" 改一下,加一个 --max-warnings=0 ,表示允 许最多 0 个警告,就是只要出现警告就会报错:

"lint:eslint": "eslint --fix --ext .js,.ts,.tsx ./src --max-warnings=0",

然后在 App.tsx 中加入一个未使用的变量:

// ...
const a = 1
// ...

然后执行: 

git add src/App.tsx

npx lint-staged 

你就会发现控制台出现了 warning

这就是 lint-staged 的作用。

使用tsc检测类型和报错 

需要注意的是,执行 tsc 命令可能会生成一个编译后的产物文件,需想要避免这个问题,需要在 tsconfig.json 中加入 "noEmit": true :

{
	"compilerOptions": {
	"target": "es2016", // 编译输出的ECMAScript目标版本为ES2016
	"esModuleInterop": true, // 允许默认导入与CommonJS模块操作
	"module": "commonjs", // 使用CommonJS模块系统
	"forceConsistentCasingInFileNames": true, // 强制文件名大小写一致
	"strict": true, // 启用所有严格的类型检查选项
	"skipLibCheck": true, // 跳过所有声明文件(.d.ts)的类型检查
	"resolveJsonModule": true, // 允许从.json文件中导入数据
	"allowJs": false, // 不允许在项目中包含JavaScript文件
	"noEmit": true, // 不生成输出文件(例如.js文件)
	"noImplicitAny": false, // 在表达式和声明上有隐含的any类型时,不发出警告
	"experimentalDecorators": true, // 启用实验性的装饰器
	"baseUrl": ".", // 用于解析非相对模块名称的基目录
	"paths": {
	"@/*": ["./src/*"] // 将路径别名“@”映射到“./src/”
	},
	"typeRoots": ["./typings/*.d.ts", "node_modules/@types"], // 指定类型声明文件的查找路径
	"jsx": "react-jsx" // react18这里改成react-jsx,就不需要在tsx文件中手动引入React了
	},
	"include": ["./src/*", "./src/**/*.ts", "./src/**/*.tsx", "./typings/*.d.ts"],
	// 指定需要包含的文件和目录
	"exclude": ["node_modules", "dist"] // 指定需要排除的文件和目录
}	

 更多的配置参考:

{
	"compilerOptions": {
	/* 基本选项 */
	"target": "es5", // 指定 ECMAScript 目标版本: 'ES3'
	(default), 'ES5', 'ES6'/'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'
	"module": "commonjs", // 指定使用模块: 'commonjs', 'amd',
	'system', 'umd' or 'es2015'
	"lib": [], // 指定要包含在编译中的库文件
	"allowJs": true, // 允许编译 javascript 文件
	"checkJs": true, // 报告 javascript 文件中的错误
	"jsx": "preserve", // 指定 jsx 代码的生成: 'preserve',
	'react-native', or 'react'
	"declaration": true, // 生成相应的 '.d.ts' 文件
	"sourceMap": true, // 生成相应的 '.map' 文件
	"outFile": "./", // 将输出文件合并为一个文件
	"outDir": "./", // 指定输出目录
	"rootDir": "./", // 用来控制输出目录结构 --outDir.
	"removeComments": true, // 删除编译后的所有的注释
	"noEmit": true, // 不生成输出文件
	"importHelpers": true, // 从 tslib 导入辅助工具函数
	"isolatedModules": true, // 将每个文件作为单独的模块 (与类似).
	'ts.transpileModule' 
	/* 严格的类型检查选项 */
	"strict": true, // 启用所有严格类型检查选项
	"noImplicitAny": true, // 在表达式和声明上有隐含的 any类型时报错
	"strictNullChecks": true, // 启用严格的 null 检查
	"noImplicitThis": true, // 当 this 表达式值为 any 类型的时候,生成一个错误
	"alwaysStrict": true, // 以严格模式检查每个模块,并在每个文件里加入
	'use strict'
	/* 额外的检查 */
	"noUnusedLocals": true, // 有未使用的变量时,抛出错误
	"noUnusedParameters": true, // 有未使用的参数时,抛出错误
	"noImplicitReturns": true, // 并不是所有函数里的代码都有返回值时,抛出错误
	"noFallthroughCasesInSwitch": true, // 报告 switch 语句的 fallthrough 错误。(即,不允许 switch 的 case 语句贯穿)
	/* 模块解析选项 */
	"moduleResolution": "node", // 选择模块解析策略: 'node' (Node.js) or(TypeScript pre-1.6)
	'classic' 
"baseUrl": "./", // 用于解析非相对模块名称的基目录
	"paths": {}, // 模块名到基于 baseUrl 的路径映射的列表
	"rootDirs": [], // 根文件夹列表,其组合内容表示项目运行时的结构内容
	"typeRoots": [], // 包含类型声明的文件列表
	"types": [], // 需要包含的类型声明文件名列表
	"allowSyntheticDefaultImports": true, // 允许从没有设置默认导出的模块中默认导入。
	/* Source Map Options */
	"sourceRoot": "./", // 指定调试器应该找到 TypeScript 文件而不是源文件的位置
	"mapRoot": "./", // 指定调试器应该找到映射文件而不是生成文件的位置
	"inlineSourceMap": true, // 生成单个 soucemaps 文件,而不是将sourcemaps 生成不同的文件
	"inlineSources": true, // 将代码与 sourcemaps 生成到一个文件中,要求同时设置了 --inlineSourceMap 或 --sourceMap 属性
	/* 其他选项 */
	"experimentalDecorators": true, // 启用装饰器
	"emitDecoratorMetadata": true // 为装饰器提供元数据的支持
	}
}
		

在项目中使用了 ts ,但一些类型问题,现在配置的 eslint 是检测不出来的,需要使用 ts 提供的 tsc 工具进行检测, 

在 index.tsx 定义了函数 hello ,参数 name 是 string 类型,当调用传 number 类型参数时,页面有了 明显的ts报错,但此时提交 index.tsx 文件到暂存区后执行 npx lint-staged

发现没有检测到报错,所以需要配置下 tsc 来检测类型,在 package.json 添加脚本命令

"pre-check": "tsc && npx lint-staged"

执行 pnpm run pre-check ,发现已经可以检测出类型报错了。

配置 husky 

为了避免把不规范的代码提交到远程仓库,一般会在git提交代码时对代码语法进行检测,只有检测通过 时才能被提交,git提供了一系列的 githooks ,而我们需要其中的 pre-commit 钩子,它会在 git commit 把代码提交到本地仓库之前执行,可以在这个阶段检测代码,如果检测不通过就退出命令行进程 停止 commit 。

代码提交前husky检测语法 

而 husky 就是可以监听 githooks 的工具,可以借助它来完成这件事情。

安装husky 
pnpm i husky -D
配置husky的pre-commit钩子 

生成 .husky 配置文件夹(如果项目中没有初始化 git ,需要先执行 git init )

npx husky install

 会在项目根目录生成 .husky 文件夹,生成文件成功后,需要让 husky 支持监听 pre-commit 钩子,监 听到后执行上面定义的 pnpm run pre-check 语法检测。

npx husky add .husky/pre-commit 'pnpm run pre-check'

 会在 .husky 目录下生成 pre-commit 文件,里面可以看到我们设置的 npm run pre-check 命令。

然后提交代码进行测试

git add .
git commit -m "feat: add code validate"

会发现监听 pre-commit 钩子执行了 pnpm run pre-check , 使用 eslint 检测了 git 暂存区的两个文 件,并且发现了 index.tsx 的警告,退出了命令行,没有执行 git commit 把暂存区代码提交到本地仓库。 

 Commit 信息的 Linter - Commitlint

Commitlint 是个 npm 包,它使用 commit conventions 规范来检查 commit 的信息是否符合我们约 定好的提交规范。 

 通过配置 commitlint.config.js , Commitlint 可以知道要使用哪些规则规范 commit 信息,并输 出相对的提示供使用者作为修改的依据。

 使用 Commitlint 规范项目的 commit ,可以让所有人的代码提交保持一致的格式,这样做会有下列好 处:

容易检索:利用定义的关键字可以轻松地找到想要找的 commit

自动输出 Changelog :固定的讯息格式可以藉由changelog 工具的帮助输出 `Changelog

 安装Commitlint

首先安装 Commitlint : 

pnpm i @commitlint/cli -D

 使用Commitlint

安装完成后,由于 Commitlint 的配置档是必要的,因此要建立配置档 commitlint.config.js : 

module.exports = {
  rules: {
    "header-min-length": [2, "always", 10],
  },
};

 配置属性 rules 可以设定规则,规则列表请参考 Commitlint 的官方页面。范例中设定讯息标头的最小 长度要大于10。

接着执行 commitlint :

> echo 'foo' | npx commitlint
⧗ input: foo
✖ header must not be shorter than 10 characters, current length is 3 [headermin-length]
✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-iscommitlin

当 message 信息为 foo 时,由于长度只有3,因此 Commitlint 会视为违规而输出错误讯息。

配置规则包 

为了节省配置规则的时间, Commitlint 可以使用预先配置的规则包来设定多项规则。使用前须要先安装: 

pnpm i @commitlint/config-conventional -D

 这里使用 @commitlint/config-conventional 是 Commitlint 提供的规则包。安装完成后,要在配 置中设定使用规则包:

module.exports = {
  extends: ["@commitlint/config-conventional"],
  // ...
};

这样一来 Commitlint 就会将 @commitlint/config-conventional所配置的规则都纳入并对讯息做相应 的检查。

使用 Husky 为 Commitlint 注册 Git Hooks 

到目前为止,我们都必须自己去手动调用 Commitlint 才能作用,使用起来的步骤较原本多,也不直观,容易被忽略。

接下来我们使用 Husky 将 Commitlint 融入Git flow 中,让其更加的易用。使用 husky add 将指 令加入Git hooks :

npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

修改完后,要重新注册 Git hooks :

npx husky install

这会触发相关的初始化工作。完成设定后,当你输入指令 git commit ,在完成编辑讯息后会启动 Commitlint 检查讯息。

Commitizen 

为了避免写出不符规范的 commit message 而提交失败, Commitizen 使用问答的方式,让使用者在 完成问答时就可以边写出符合规范的信息,以减少来回的次数。 

Commitizen 是个指令式的工具,使用 Commitizen 来 commit 代码时会启动设定的 adapter ,使用 adapter 提供的问题一一询问开发者,每个问题都会确认一部分的 commit message ,到最后将所有的 回答组合起来,变成一个完整并符合规范的 commit message 。

cz-git

指定提交文字规范,一款工程性更强、高度自定义、标准输出格式的 commitizen 适配器:

pnpm i commitizen cz-git -D -g

配置 package.json : 

"config": {
	"commitizen": {
	"path": "node_modules/cz-git"
	}
}
	

 配置 commitlint.config.js 文件

// @see: https://cz-git.qbenben.com/zh/guide
/** @type {import('cz-git').UserConfig} */
module.exports = {
	ignores: [commit => commit.includes('init')],
	extends: ['@commitlint/config-conventional'],
	rules: {
	// @see: https://commitlint.js.org/#/reference-rules
	'body-leading-blank': [2, 'always'],
	'footer-leading-blank': [1, 'always'],
	'header-max-length': [2, 'always', 108],
	'subject-empty': [2, 'never'],
	'type-empty': [2, 'never'],
	'subject-case': [0],
	'type-enum': [
	2,
	'always',
	[
	'feat',
	'fix',
	'docs',
	'style',
	'refactor',
	'perf',
	'test',
	'build',
	'ci',
	'chore',
	'revert',
	'wip',
	'workflow',
	'types',
	'release'
	]
	]
	},
	prompt: {
	messages: {
	type: "Select the type of change that you're committing:",
	scope: 'Denote the SCOPE of this change (optional):',
	customScope: 'Denote the SCOPE of this change:',
	subject: 'Write a SHORT, IMPERATIVE tense description of the change:\n',
	body: 'Provide a LONGER description of the change (optional). Use "|" to
	break new line:\n',
	breaking: 'List any BREAKING CHANGES (optional). Use "|" to break new
	line:\n',
	footerPrefixsSelect: 'Select the ISSUES type of changeList by this change
	(optional):',
	customFooterPrefixs: 'Input ISSUES prefix:',
	footer: 'List any ISSUES by this change. E.g.: #31, #34:\n',
	confirmCommit: 'Are you sure you want to proceed with the commit above?'
	// 中文版
	// type: "选择你要提交的类型 :",
	// scope: "选择一个提交范围(可选):",
	// customScope: "请输入自定义的提交范围 :",
	// subject: "填写简短精炼的变更描述 :\n",
	// body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
	// breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
	// footerPrefixsSelect: "选择关联issue前缀(可选):",
	// customFooterPrefixs: "输入自定义issue前缀 :",
	// footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
	// confirmCommit: "是否提交或修改commit ?"
	},
	types: [
	{
	value: 'feat',
	name: 'feat:  A new feature',
	emoji: ''
	},
	{
	value: 'fix',
	name: 'fix:  A bug fix',
	emoji: ''
	},
	{
	value: 'docs',
	name: 'docs:  Documentation only changes',
	emoji: ''
	},
	{
	value: 'style',
	name: 'style:  Changes that do not affect the meaning of the code',
	emoji: ''
	},
	{
	value: 'refactor',
	name: 'refactor: ♻️ A code change that neither fixes a bug nor adds a
	feature',
	emoji: '♻️'
	},
	{
	value: 'perf',
	name: 'perf: ⚡️ A code change that improves performance',
	emoji: '⚡️'
	},
	{
	value: 'test',
	name: 'test: ✅ Adding missing tests or correcting existing tests',
	emoji: '✅'
	},
	{
		value: 'build',
		name: 'build:  Changes that affect the build system or external
		dependencies',
		emoji: ''
		},
		{
		value: 'ci',
		name: 'ci:  Changes to our CI configuration files and scripts',
		emoji: ''
		},
		{
		value: 'chore',
		name: "chore:  Other changes that don't modify src or test files",
		emoji: ''
		},
		{
		value: 'revert',
		name: 'revert: ⏪️ Reverts a previous commit',
		emoji: '⏪️'
		}
		// 中文版
		// { value: "特性", name: "特性:  新增功能", emoji: "" },
		// { value: "修复", name: "修复:  修复缺陷", emoji: "" },
		// { value: "文档", name: "文档:  文档变更", emoji: "" },
		// { value: "格式", name: "格式:  代码格式(不影响功能,例如空格、分号等格式修正)", emoji: "" },
		// { value: "重构", name: "重构: ♻️ 代码重构(不包括 bug 修复、功能新增)",emoji: "♻️" },
		// { value: "性能", name: "性能: ⚡️ 性能优化", emoji: "⚡️" },
		// { value: "测试", name: "测试: ✅ 添加疏漏测试或已有测试改动", emoji: "✅"
		},
		// { value: "构建", name: "构建:  构建流程、外部依赖变更(如升级 npm 包、修改webpack 配置等)", emoji: "" },
		// { value: "集成", name: "集成:  修改 CI 配置、脚本", emoji: "" },
		// { value: "回退", name: "回退: ⏪️ 回滚 commit", emoji: "⏪️" },
		// { value: "其他", name: "其他:  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: "" }
		],
		useEmoji: true,
		themeColorCode: '',
		scopes: [],
		allowCustomScopes: true,
		allowEmptyScopes: true,
		customScopesAlign: 'bottom',
		customScopesAlias: 'custom',
		emptyScopesAlias: 'empty',
		upperCaseSubject: false,
		allowBreakingChanges: ['feat', 'fix'],
		breaklineNumber: 100,
		breaklineChar: '|',
		skipQuestions: [],
		issuePrefixs: [{ value: 'closed', name: 'closed: ISSUES has been processed'
		}],
		customIssuePrefixsAlign: 'top',
		emptyIssuePrefixsAlias: 'skip',
		customIssuePrefixsAlias: 'custom',
		allowCustomIssuePrefixs: true,
		allowEmptyIssuePrefixs: true,
		confirmColorize: true,
		maxHeaderLength: Infinity,
		maxSubjectLength: Infinity,
		minSubjectLength: 0,
		scopeOverrides: undefined,
		defaultBody: '',
		defaultIssues: '',
		defaultScope: '',
		defaultSubject: ''
		}
	}				

 然后测试:

git add .
git-cz

 一键提交

我们还可以通过一个script来集成之前所有的这些步骤:

"scripts": {
	// ...
	"commit": "git pull && git add -A && git-cz && git push",
}
	

 现在,我们只需要执行 pnpm run commit 即可完成代码的质量检测、 style format 、代码提交规范等一系列流程了。

至此,我们就完成了脚手架的代码质量和 git 提交规范的配置。就当前的脚手架已具备基本的 React 项目的配置,可以作为大多数项目的基础架子了~

你可能感兴趣的:(webpack,react.js,前端)