对现有React项目进行规范及优化(涉及使用Typescript、eslint+Prettier规范)(一)

2,438 阅读41分钟

最近项目组不是很忙,也为了实践自己学习的知识,进行吸收优化,决定对项目组现有项目进行规范优化,也是对项目代码的负责,在此做个记录。

备注:搭建react项目参考文章# 从零开始撸一套react项目模板

概论

此文章记录的是基于线上项目代码的规范优化升级,也是自己对经手项目的代码的补救,针对所要优化的项目,涉及以下几点知识点:

  • 使用Typescript开发(都说ts很香,也看过一些相关文档,但一直没有结合具体项目去实践,确实体验不到他是怎么个香)
  • ESLint+Prettier规范编码(确实是我的责任,在项目初期,因为报错的eslint规范太多,很烦,就把eslint规范关了,现在想想,这不仅仅是对项目的不负责,也是对自己的不负责,对团队的不负责,对未来接受的猿们的不负责,所以,即使现在使用很痛苦,也一定要把这个规范建立起来)
  • 编码规范及规范的文档说明、相关配置文件的文档说明
  • 单元测试(核心业务模块)
  • 高阶组件的编写规范,对于高阶组件的使用(规范化与合理化,例如使用装饰器的写法、根据业务合理使用高阶组件)
  • 项目中错误异常(包括组件的错误异常、错误边界的优化,属于优化范畴),针对渲染异常(组件渲染异常的处理)的“错误边界”的相关内容在项目中的应用(如果渲染异常,在没有任何降级保护措施的情况下,页面会直接显示白屏。在组件内部可以使用getDerivedStateFromError 与 componentDidCatch 两个函数,还有基于整体性考量,针对错误边界的预防与兜底)
  • 引入mockjs
  • 根据以上优化,自定义编写前端脚手架
  • 搭建前端公共组件库(一直以来,我们的项目针对本来可以复用的组件跟业务边界切割不清、可以复用的组件没有根据组件的功能分类清晰、在工程实践即项目工程目录构建中,没有清晰安排构建,这些等等的问题,早就在我心目中所嫌弃与诟病,但一直没有时间去重新规划整理,趁着这段闲暇时间,也趁着自己在这段提升时间段,把这块自己一直嫌弃的模块去重新规划与规范化)
  • Sentry报错收集平台接入(开源的 Sentry 平台)
  • Sonar 搭建(不管是对项目负责、还是为了团队成长,项目代码交付也好、自己的代码质量规范也好,确实需要一个代码质量分析工具,于是选择了sonarqube)
  • 性能优化方向在项目中的实践 以上几点相关知识点,就是我要针对我们项目的优化点,本文章会持续书写,毕竟项目会一直持续优化。 下图是我整个项目规范化与优化下来的大概实行步骤,如下: 未命名文件 (2).png 废话少说,接下来开始表演:

一. 项目中引入Typescript

引入Typescript借鉴相关文章:

1. 全局安装ts

安装命令如下:

npm i -g typescript

2. 创建tsconfig.json

命令如下:

tsc --init

如下图:

1625710508(1).png

tsconfig.json的配置参数如下:

ULOT}U$3UN9QZG6GGH9X@1A.png

3. 安装开发环境依赖

1. 比较旧的开发环境

1. 依赖如下:

npm install --save-dev typescript @types/react @types/react-dom @types/react-router-dom ts-loader

2. webpack配置文件示例如下:

module.exports = {
    context: ...,
    entry: ...,
    output: ...,

    // 添加resolve
    resolve: {
        extensions: ['.ts', '.tsx', '.js']
    },

    module: {
        loaders: [

            // 增加新的loader
            {
                test: /\.tsx?$/,
                loaders: ['babel-loader', 'ts-loader']
            },
            ...
        ]
    },
    plugins: ...
};

运行项目时发现会报如下错误:

1625750688(1).png 安装的相关依赖的版本如下:

1625751055(1).png

1625751002(1).png 推测是由于ts-loader的版本过高导致的这个报错,由于不使用ts-loader方案,所以没有继续探究。

2. 新的开发环境

1. 依赖如下:

npm install --save-dev typescript @types/react @types/react-dom @babel/preset-typescript @babel/plugin-proposal-class-properties @babel/plugin-proposal-object-rest-spread @babel/core @babel/preset-env

2. package.json的项设置如下:

{
  "name": "inforserver",
  "version": "1.0.0",
  "description": "信息服务平台",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1",
    "dev": "webpack-dev-server --config ./config/webpack.dev.config.js --host 0.0.0.0",
    "build": "webpack -p --config ./config/webpack.prod.config.js"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "antd": "^3.22.2",
    "axios": "^0.19.0",
    "crypto-js": "^4.0.0",
    "echarts": "4.9.0",
    "happypack": "^5.0.1",
    "hard-source-webpack-plugin": "^0.13.1",
    "immutable": "^4.0.0-rc.12",
    "js-cookie": "^2.2.1",
    "moment": "^2.24.0",
    "prop-types": "latest",
    "qs": "^6.8.0",
    "react": "^16.9.0",
    "react-cookies": "^0.1.1",
    "react-dom": "^16.9.0",
    "react-loadable": "^5.5.0",
    "react-redux": "^7.1.1",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.1",
    "redux": "^4.0.4",
    "redux-persist": "^6.0.0",
    "redux-persist-transform-immutable": "^5.0.0",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/plugin-proposal-class-properties": "^7.14.5",
    "@babel/plugin-proposal-object-rest-spread": "^7.14.7",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-transform-runtime": "^7.14.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.14.5",
    "@svgr/webpack": "^5.3.1",
    "@types/react": "16.9.23",
    "@types/react-dom": "16.9.7",
    "@types/react-router-dom": "5.1.3",
    "babel-loader": "^8.0.6",
    "babel-plugin-import": "^1.13.3",
    "babel-polyfill": "^6.26.0",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^6.0.2",
    "css-loader": "^3.2.0",
    "file-loader": "^4.2.0",
    "html-webpack-plugin": "^3.2.0",
    "less": "^3.10.3",
    "less-loader": "^5.0.0",
    "mini-css-extract-plugin": "^0.8.0",
    "node-sass": "^4.12.0",
    "react-countup": "^4.3.3",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.0",
    "terser-webpack-plugin": "^4.2.0",
    "typescript": "^4.3.5",
    "uglifycss-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.7",
    "webpack-dev-server": "^3.8.0",
    "webpack-merge": "^4.2.2",
    "webpack-theme-color-replacer": "^1.3.7"
  }
}

其中:

基于Babel 7 下配置 TypeScript 支持,使用 @babel/preset-typescript 和 @babel/preset-env 配置TypeScript的编译环境,安装 Babel 基础如下:

有 5 个包需要下载安装,它们分别是:

  • @babel/core
  • @babel/preset-env
  • @babel/preset-typescript
  • @babel/plugin-proposal-class-properties
  • @babel/plugin-proposal-object-rest-spread 其中包含了 2 个插件 plugin-proposal-class-properties 和 plugin-proposal-object-rest-spread,分别用于转换语法特性“类属性”、“对象展开”。
  1. 配置 babel 注意:我们的项目没有创建文件 .babelrc

配置内容如下:

1625755077(1).png

我们项目是使用HappyPack插件提升webpack构建时间的,所以webpack配置文件的具体内容如下:

1625755906(1).png HappyPack内的配置内容如下:

1625755077(1).png 注意:完成后运行项目Typescript如果报如下错误:

1625729793(1).png 即报如下错误:

Typescript error “Cannot write file xxx because it would overwrite input file

出现这个问题基本是因为开启了allowJs。

  • 因为allowJs即允许Typescript编译器去编译js。而编译之后的输出文件也就是xxx.js与源文件是一样的。
  • 所以就会报出“会覆盖输入文件”这样的错误。 解决思路如下:

11432478-e4fd00419e43a760.png 我是设定outDir参数,指向相关位置。

二. ESLint+Prettier统一代码风格

1. 背景

在一个多人协作的项目中,不同的开发人员写的代码的风格不太一样,比如是否需要在行末加分号,换行、空格、缩紧、项目中散落的console处理方法、单行代码最大长度等等,如果项目中没有统一的规范就会导致代码风格的五花八门,不利于代码的阅读和维护。

为了项目中有统一的编码规范,我们使用eslint + prettier 来进行约束。

  • 使用 eslint + prettier添加统一代码规范
  • 格式化现有项目下的不符合规范的文件
  • 配置编辑器,自动检测新增或修改的代码的规范合法性

引入ESLint+Prettier借鉴相关文章:

2. 配置相关配置依赖环境及配置文件

1. 安装eslint

ESLint 主要有以下特点:

  • 默认规则包含所有 JSLint、JSHint 中存在的规则,易迁移;
  • 规则可配置性高:可设置「警告」(warning)、「错误」(error)两个 error 等级,或者直接禁用;
  • 包含代码风格检测的规则(可以丢掉 JSCS 了);
  • 支持插件扩展、自定义规则。 ESLint配置方式,可以通过以下三种方式配置 ESLint:
  • 使用 .eslintrc 文件(支持 JSON 和 YAML 两种语法);
  • 在 package.json 中添加 eslintConfig 配置块;
  • 直接在代码文件中定义。 我们选择使用 .eslintrc 文件方式!!!

规则的严重性(rule severity)

  • "off"
  • or 0 - turn the rule off 不验证 "warn"
  • or 1 - turn the rule on as a warning(doesn’ t affect exit code) 警告 "error"
  • or 2 - turn the rule on as an error(exit code is 1 when triggered) 错误( 如有) 规则的选项(additional options) "quotes": [2, "double"]

1. 安装依赖如下:

npm i -D eslint babel-eslint eslint-plugin-import eslint-plugin-jsx-a11y eslint-plugin-react eslint-plugin-react-hooks @typescript-eslint/parser @typescript-eslint/eslint-plugin

其中,相关插件如下:

  • eslint-plugin-jsx-a11y:检验jsx语法
  • eslint-plugin-react:设定react组件的相关规范
  • eslint-plugin-import:在使用 import 的时候,一些 rules 规范
  • @typescript-eslint/parser:eslint 默认使用 Espree 进行解析,无法识别 ts 的一些语法,所以需要安装一个 ts 的解析器 @typescript-eslint/parser,用它来代替默认的解析器
  • @typescript-eslint/eslint-plugin:来提供有关 ts 的规则补充

2. 添加.eslintrc配置文件

配置文件内容如下:

{
  //  ESLint默认使用Espree作为其解析器
  //  此项是用来指定eslint解析器的,解析器必须符合规则
  //  官网没有对其他解析器进行说明,但是有提示,使用其他解析器的时候,注意确认它是不是和 ESLint 兼容。
  //  至于咋确认。。你指定一下这个解析器,看看 eslint 会不会对你想检查的代码正常报错,就行了。
  // 以下解析器与 ESLint 兼容:
  //  1、Esprima
  //  2、Babel-ESLint:一个对Babel解析器的包装,使其能够与 ESLint 兼容(如果你想使用一些 先进的语法,比如es6789...)。
  //  3、@typescript-eslint/parser:将 TypeScript 转换成与 estree 兼容的形式,以便在ESLint中使用(如果你想使用typescript)。
  "parser": "@typescript-eslint/parser",
  //  解析器选项,与parser同时使用
  //  在使用自定义解析器时,
  //  为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作,
  //  配置属性 parserOptions 仍然是必须的
  //  解析器会被传入parserOptions,
  //  但是不一定会使用它们来决定功能特性的开关。
  "parserOptions": {
    // 默认是 script。模块化的代码要写:module(当前最常见做法,如果你的代码是 ECMAScript 模块)
    "sourceType": "module",
    //emcaVersion用来指定你想要使用的 ECMAScript 版本
    //注意,使用对于 ES6 语法,使用"ecmaVersion": 6时,不自动启用es6全局变量
    //自动启用es6语法和全局变量,需要搭配env使用"env": { "es6": true }
    "ecmaVersion": 6,
    //想使用额外的语言特性,一个配置对象,可配置项如下(value 均为 true/false)
    "ecmaFeatures": {
      "jsx": true,//启用jsx
      "globalReturn":true, //在全局作用域下使用return语句
      "impliedStrict":true, //启用全局strict mode (如果 ecmaVersion 是 5 或更高)
      "experimentalObjectRestSpread": false //启用实验性的object rest/spread properties支持(不建议开启)
    },
    "useJSXTextNode": true
  },
  //  要在配置文件中指定环境,请使用env键并通过将每个设置为来指定要启用的环境true。
  "env": {
    "browser": true, //开启浏览器全局变量
    "es6": true, //启用除模块以外的所有ECMAScript 6功能(这会自动将ecmaVersion解析器选项设置为6)
    "node": true //Node.js全局变量和Node.js作用域
  },
  // 此项是用来配置标准的js风格,就是说写代码的时候要规范的写
  // 指定配置的字符串(配置文件的路径,可共享配置的名称eslint:recommended或eslint:all)。
  // 字符串数组:每个其他配置都扩展了前面的配置
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended"
  ],
  // 此项是用来提供插件的,插件名称省略了eslint-plugin-
  "plugins": [
    "react",
    "@typescript-eslint"
  ],
  /*
  * "off" 或 0 - 关闭规则
  * "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  * "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
  */
  "rules": {
    "semi": 2, // 行末分号,根据编码习惯选择添加,这里配置的加分号
    "no-console": 0, // 禁用 console
    "comma-dangle": [2,"never"], //对象字面量项尾不能有逗号(always-multiline是有逗号)
    "max-len": 0, // 强制一行的最大长度,可以是[1, 200]限制长度
    "max-lines": 0, // 强制最大行数
    "max-params": 0, // 强制 function 定义中最多允许的参数数量,可以用[1, 7]限制数量
    "max-statements": 0, // 强制 function 块最多允许的的语句数量,可以用[1, 200]限制数量
    "max-statements-per-line": 0, // 强制每一行中所允许的最大语句数量
    "space-before-function-paren": [0,"always"], // 强制在 function的左括号之前使用一致的空格
    // 禁止出现未使用过的表达式
    "no-unused-expressions": [
      0,
      {
        "allowShortCircuit": true,
        "allowTernary": true
      }
    ],
    "arrow-body-style": [0, "never"], // 要求箭头函数体使用大括号
    "func-names": 0, // 强制使用命名的 function 表达式
    "prefer-const": 0, // 要求使用 const 声明那些声明后不再被修改的变量
    "no-extend-native": 2, // 禁止扩展原生类型
    "no-param-reassign": 0, // 不允许对 function 的参数进行重新赋值
    "no-restricted-syntax": 0, // 禁止使用特定的语法
    "no-eval": 0, // 禁用 eval(),eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
    "no-continue": 0, // 禁用 continue 语句
    "global-require": 1, // 要求 require() 出现在顶层模块作用域中
    "camelcase": 2, //强制驼峰法命名
    "import/no-extraneous-dependencies": 0,
    "import/prefer-default-export": 0,
    "import/no-unresolved": 0,
    "import/extensions": 0,
    // react
    "react/jsx-first-prop-new-line": 0,
    "react/jsx-filename-extension": 0,
    "react/require-default-props": 0,
    "react/no-string-refs": 0,
    "react/no-find-dom-node": 0,
    "react/display-name": 0, //防止在React组件定义中丢失displayName
    "react/forbid-prop-types": [2, {"forbid": ["any"]}], //禁止某些propTypes
    "react/jsx-boolean-value": 2, //在JSX中强制布尔属性符号
    "react/jsx-closing-bracket-location": 1, //在JSX中验证右括号位置
    "react/jsx-curly-spacing": [2, {"when": "never", "children": true}], //在JSX属性和表达式中加强或禁止大括号内的空格。
    "react/jsx-indent-props": [2, 4], //验证JSX中的props缩进
    "react/jsx-key": 2, //在数组或迭代器中验证JSX具有key属性
    "react/jsx-max-props-per-line": [1, {"maximum": 1}], // 限制JSX中单行上的props的最大数量
    "react/jsx-no-bind": 0, //JSX中不允许使用箭头函数和bind
    "react/jsx-no-duplicate-props": 2, //防止在JSX中重复的props
    "react/jsx-no-literals": 0, //防止使用未包装的JSX字符串
    "react/jsx-no-undef": 1, //在JSX中禁止未声明的变量
    "react/jsx-pascal-case": 0, //为用户定义的JSX组件强制使用PascalCase
    "react/jsx-sort-props": 2, //强化props按字母排序
    "react/jsx-uses-react": 1, //防止反应被错误地标记为未使用
    "react/jsx-uses-vars": 2, //防止在JSX中使用的变量被错误地标记为未使用
    "react/no-danger": 0, //防止使用危险的JSX属性
    "react/no-did-mount-set-state": 0, //防止在componentDidMount中使用setState
    "react/no-did-update-set-state": 1, //防止在componentDidUpdate中使用setState
    "react/no-direct-mutation-state": 2, //防止this.state的直接变异
    "react/no-multi-comp": 2, //防止每个文件有多个组件定义
    "react/no-set-state": 0, //防止使用setState
    "react/no-unknown-property": 2, //防止使用未知的DOM属性
    "react/prefer-es6-class": 2, //为React组件强制执行ES5或ES6类
    "react/prop-types": 0, //防止在React组件定义中丢失props验证
    "react/react-in-jsx-scope": 2, //使用JSX时防止丢失React
    "react/self-closing-comp": 0, //防止没有children的组件的额外结束标签
    "react/sort-comp": 2, //强制组件方法顺序
    "react/no-array-index-key": 0, //防止在数组中遍历中使用数组key做索引
    "react/no-deprecated": 1, //不使用弃用的方法
    "react/jsx-equals-spacing": 2, //在JSX属性中强制或禁止等号周围的空格
    "no-extra-boolean-cast": 0, //禁止不必要的bool转换
    "no-unreachable": 1, //不能有无法执行的代码
    "no-mixed-spaces-and-tabs": 0, //禁止混用tab和空格
    "prefer-arrow-callback": 0, //比较喜欢箭头回调
    "arrow-parens": 0, //箭头函数用小括号括起来
    "arrow-spacing": 0, //=>的前/后括号
    "jsx-a11y/href-no-hash": 0,
    "jsx-a11y/no-static-element-interactions": 0,
    "quotes": [2, "single"], //单引号
    "no-debugger": 2, //禁用debugger
    "no-var": 2, //对var警告
    "no-irregular-whitespace": 0, //不规则的空白不允许
    "no-trailing-spaces": 1, //一行结束后面有空格就发出警告
    "eol-last": 0, //文件以单一的换行符结束
    "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], //不能有声明后未被使用的变量或参数
    "no-underscore-dangle": 0, //标识符不能以_开头或结尾
    "no-alert": 2, //禁止使用alert confirm prompt
    "no-lone-blocks": 0, //禁止不必要的嵌套块
    "no-class-assign": 2, //禁止给类赋值
    "no-cond-assign": 2, //禁止在条件表达式中使用赋值语句
    "no-const-assign": 2, //禁止修改const声明的变量
    "no-delete-var": 2, //不能对var声明的变量使用delete操作符
    "no-dupe-keys": 2, //在创建对象字面量时不允许键重复
    "no-duplicate-case": 2, //switch中的case标签不能重复
    "no-dupe-args": 2, //函数参数不能重复
    "no-empty": 2, //块语句中的内容不能为空
    "no-func-assign": 2, //禁止重复的函数声明
    "no-invalid-this": 0, //禁止无效的this,只能用在构造器,类,对象字面量
    "no-redeclare": 2, //禁止重复声明变量
    "no-spaced-func": 2, //函数调用时 函数名与()之间不能有空格
    "no-this-before-super": 0, //在调用super()之前不能使用this或super
    "no-undef": 2, //不能有未定义的变量
    "no-use-before-define": 0, //未定义前不能使用
    "jsx-quotes": [2, "prefer-double"], //强制在JSX属性(jsx-quotes)中一致使用双引号
    "@typescript-eslint/no-unused-vars": 0,
    "@typescript-eslint/no-explicit-any": 0,
    "@typescript-eslint/no-empty-interface": 0,
    "@typescript-eslint/explicit-function-return-type": 0,
    "@typescript-eslint/camelcase": ["off", {"properties":"always"}], //强制驼峰法命名(ts)
    "@typescript-eslint/no-use-before-define": 0
  },
  /*
  *ESLint支持将共享设置添加到配置文件中。
  *您可以将settings对象添加到ESLint配置文件,并将其提供给将要执行的每个规则。
  *如果您要添加自定义规则,并希望它们能够访问相同的信息并且易于配置,这可能会很有用。
  */
  "settings": {
    "import/resolver": "node",
    "react": {
      "version": "detect"
    }
  }
}

//环境定义了预定义的全局变量。可用环境为:
//browser -浏览器全局变量。
//node -Node.js全局变量和Node.js作用域。
//commonjs -CommonJS全局变量和CommonJS作用域(将其用于使用Browserify / WebPack的仅浏览器代码)。
//shared-node-browser -Node.js和浏览器通用的全局变量。
//es6-启用除模块以外的所有ECMAScript 6功能(这会自动将ecmaVersion解析器选项设置为6)。
//es2017-添加所有ECMAScript 2017全局变量并自动将ecmaVersion解析器选项设置为8。
//es2020-添加所有ECMAScript 2020全局变量并将ecmaVersion解析器选项自动设置为11。
//worker -网络工作者的全局变量。
//amd- 根据amd规范定义require()和define()作为全局变量。
//mocha -添加所有Mocha测试全局变量。
//jasmine -添加了版本1.3和2.0的所有Jasmine测试全局变量。
//jest -开玩笑的全局变量。
//phantomjs -PhantomJS全局变量。
//protractor -量角器全局变量。
//qunit -QUnit全局变量。
//jquery -jQuery全局变量。
//prototypejs -Prototype.js全局变量。
//shelljs -ShellJS全局变量。
//meteor -流星全局变量。
//mongo -MongoDB全局变量。
//applescript -AppleScript全局变量。
//nashorn -Java 8 Nashorn全局变量。
//serviceworker -服务人员全局变量。
//atomtest -Atom测试助手全局变量。
//embertest -灰烬测试助手全局变量。
//webextensions -WebExtensions全局变量。
//greasemonkey -GreaseMonkey全球。
//这些环境不是互斥的,因此您一次可以定义多个环境。

3. 在package.json中添加脚本

脚本内容如下:

{
  scripts: {
    "lint-fix": "eslint --fix .js src"
  }
}
  • 执行这个命令,这个命令会检测src目录下的所有的js文件。
  • 如果发现在src目录下有不需要检测的文件夹,比如vendors文件夹下有三方开源的代码,不需要做格式的校验,可以通过配置下.eslintignore,忽略此文件夹目录。
  • 执行完lint-fix命令后,eslint会根据配置文件标注出不符合规范的地方,并且自动添加一些修改,比如去掉行末添加分号。
  • 但是对于一些其它的代码格式的设置,比如缩进是使用空格还是tab,大括号是否要添加空格等等,而这些正是prettier插件擅长做的。 注意:我上面scripts脚本写的有问题,如果按照上面写,执行后,会出现如下报错: 111.png 所以脚本内容改为如下:
{
  scripts: {
    "lint-fix": "eslint --fix --ext .js --ext .tsx src"
  }
}

在要检测的文件格式前加 --ext 但执行该脚本又会报如下错误:

PT`O_N(7NPJ02(866V~TP.png

尝试把eslint相关的依赖都卸载,还是报这个错误,怀疑是.eslintrc配置文件配置有问题,如下:

23.png

把内容改成如下:

33.png

所以是配置书写错误,不应该像错误方式书写。

执行yarn run lint-fix,会报如下错误:

eslint-plugin-react报未指定react版本的Warning: React version not specified in eslint-plugin-react settings.

即要在eslintrc.js中指定react版本,在eslintrc.js中添加settings即可

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

4. 配置hooks

项目使用了hooks语法书写方式,就需要添加hooks的解析规则帮助我们修复hooks编写中的问题

1. 安装相关的依赖

安装命令如下:

// yarn add eslint-plugin-react-hooks
npm install eslint-plugin-react-hooks --save-dev
2. 在 .eslintrc.json 中补充相关的配置,配置如下
// .eslintrc.json
{
  "plugins": ["@typescript-eslint", "react-hooks"],
  "rules": {
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn"
  }
}

这样引入了hooks的校验之后就会帮我们提示一些类似于在非react组件中使用hooks语法这样的语法错误。

5. 安装huskylint-staged

在代码提交之前,进行代码规则检查能够确保进入git库的代码都是符合代码规则的。但是整个项目上运行lint速度会很慢,lint-staged能够让lint只检测暂存区的文件,所以速度很快。

1. husky

husky 是一个为 git 客户端增加 hook 的工具。安装后,它会自动在仓库中的 .git/ 目录下增加相应的钩子;比如 pre-commit 钩子就会在你执行 git commit 的触发。

那么我们可以在 pre-commit 中实现一些比如 lint 检查、单元测试、代码美化等操作。当然,pre-commit 阶段执行的命令当然要保证其速度不要太慢,每次 commit 都等很久也不是什么好的体验。

安装命令如下:

npm i husky -D
  • Git Hooks 就是在 Git 执行特定事件(如commit、push、receive等)时触发运行的脚本,类似于“钩子函数”,没有设置可执行的钩子将被忽略。

  • husky 在安装过程中会在 .git/hooks 文件夹中生成一系列的 git hook 脚本。

  • package.json 文件中配置 husky 的钩子需要执行的 命令 或 操作如下:

{
  "husky": {
    "hooks": {
      "pre-commit": "echo \"git commit trigger husky pre-commit hook\" "
    }
  }
}

钩子中执行多个命令如下:

  • 根据 npm script 的规则,使用 &&
{
  "husky": {
    "hooks": {
      "pre-commit": "echo \"git commit trigger husky pre-commit hook\" && npm run test"
    }
  }
}

从 1.0.0 开始,husky 的配置可以使用 .huskyrc.huskyrc.json.huskyrc.js 或 husky.config.js 文件

  • 如果您更喜欢使用数组,建议的方法是在 .huskyrc.js 中定义它们
const tasks = arr => arr.join(' && ')

module.exports = {
  'hooks': {
    'pre-commit': tasks([
      'npm run lint',
      'npm run test'
    ])
  }
}
2. lint-staged

lint-staged,一个仅仅过滤出 Git 代码暂存区文件(被 git add 的文件)的工具;这个很实用,因为我们如果对整个项目的代码做一个检查,可能耗时很长,如果是老项目,要对之前的代码做一个代码规范检查并修改的话,这可能就麻烦了呀,可能导致项目改动很大。

所以这个 lint-staged,对团队项目和开源项目来说,是一个很好的工具,它是对个人要提交的代码的一个规范和约束。lint-staged 总是将所有暂存文件的列表传递给任务。

安装命令如下:

npm i lint-staged -D

配置方式如下:package.json文件中

{
  "lint-staged": {
    "*.js": "eslint --fix"
  }
}

或者

{
  "src/**/*.{js,jsx,ts/tsx}": [
    "prettier --write",
    "eslint --cache --fix",
    "git add"
  ]
}

这里 lint-staged 的配置是:在 git 的待提交的文件中,在 src 目录下的所有 .js .jsx`` .ts ``.tsx都要执行三条命令。前两条一会儿说,后一条是将处理过的代码重新 add 到 git 中。

结合我们前面介绍的 husky,配合 husky 的 pre-commit 钩子,将会形成一个自动化工具链

结合方式如下:

"husky": {
  "hooks": {
    "pre-commit": "lint-staged"
  }
},
"lint-staged": {
  "src/**/*.{js,jsx,ts,tsx}": ["prettier --write", "eslint --cache --fix", "git add"]
}

在 commit 之前,将暂存区的内容做一次 代码检查 和 代码美化,然后再添加到暂存区;然后再 commit,ok了!!!

从 v3.1 开始,您现在可以使用不同的方式进行 lint-staged 配置:

  • lint-staged 在你的对象 package.json
  • .lintstagedrc JSON或YML格式的文件
  • lint-staged.config.js JS格式的文件
  • 使用 --config 或 -c 标志传递配置文件

只是道路道路往往是曲折的,这也是程序猿的乐趣所在吧,以上#### huskylint-staged的安装配置是有问题的你敢信???简直把之前配置推翻重来,经过一番查看才知道原来最新版本的husky(6.0.0)已经做了破坏性的变更,之前的设置方式已经失效了!!!

src=http___file1.renrendoc.com_fileroot_temp2_2020-8_9_24fa6c9c-52af-4751-97ff-3bc7b5c162d6_24fa6c9c-52af-4751-97ff-3bc7b5c162d63.gif&refer=http___file1.renrendoc.jpg

3. husky为什么放弃了之前的配置方式

根据官方的说法,之前husky的工作方式是这样的,为了能够让用户设置任何类型的git hooks都能正常工作,husky不得不创建所有类型的git hooks。这样在git 工作的每个阶段都会调用husky所设置的脚本,在这个脚本中husky会检查用户是否配置该hook,如果有就运行用户配置的命令,如果没有就继续往下执行。

这样做的好处就是无论用户设置什么类型的git hook husky都能确保其正常运行。但是缺点也是显而易见的,即使用户没有设置任何git hook,husky也向git中添加了所有类型的git hook。

那有没有可能让husky只添加我们需要的git hook呢?作者尝试过解决这个问题,但是失败了。究其失败的根本原因,就是因为husky需要在两个地方进行配置才能完成一个完整的git hook功能。一个是在package.json中配置git hook所要执行的真正命令,一个是在.git/hooks/中配置相对应的git hook。也就是说无论是添加还是删除git hook就要保证在这两个地方同步执行对应的操作。作者无法找到一个可靠的方法来同步这两个地方的配置,因此失败了。

作者认为这个问题是由husky工作模型的自身缺陷导致的,如果想要解决就不得不另辟蹊径采用一种新的工作模型。因此新版husky做了破坏性的变更。

而这个变更是从husky6.0.0开始的,而我安装的版本是7.0.1,显而易见,需要破坏性的用新的方式配置。

4. 新版husky的工作原理

新版的husky使用了从git 2.9开始引入的一个新功能core.hooksPath。core.hooksPath可以让你指定git hooks所在的目录而不是使用默认的.git/hooks/。

这样husky可以使用husky install将git hooks的目录指定为.husky/,然后使用husky add命令向.husky/中添加hook。通过这种方式我们就可以只添加我们需要的git hook,而且所有的脚本都保存在了一个地方(.husky/目录下)因此也就不存在同步文件的问题了。

5. 新版husky实践

接下来的前提是已经执行过下面的安装命令:

npm install husky lint-staged -D

Husky支持的Git hooks还是很全面的,如常用的pre-commit、commit-msg。这样我们就能再一些特定的时间点做一些事情。

1. 判断 .git 文件是否在项目根目录下

23514145-3d7f02a986e51ae2.webp

2. 修改项目根目录下的 package.json 中 "scripts" 字段
  1. 如果 .git 文件在你当前项目的根目录下,对 package.json 中 "scripts" 字段 做如下更改
//  .git  文件在你当前项目的根目录下时,修改项目根目录下的 package.json
  "scripts": {
    "prepare": "husky install"
  },
  1. 如果 .git 文件不在你项目的根目录下,则需要自定义目录

23514145-e5584b92de91ed55.webp 在 .git主目录下的每个子项目,可以依照每个项目特有的规则执行钩子,特别在一个仓库管理多个项目时,在不影响各项目独自的 lint 配置的情况下,可以实现对子项目独立配置的自动化提交管理,互不影响。情况如下:

23514145-b8460d42f421eb74.webp

如图中所述,.git 文件不在当前项目的根目录下,这时对当前项目根目录中的 package.json 中 "scripts" 字段 做如下更改

// .git  文件不在在你当前项目的根目录下时,修改项目根目录下的 package.json
  "scripts": {
    "prepare": "cd .. && husky install 当前项目的名称/.husky"
  },
3. 执行npm run prepare生成.husky文件夹

此时package.json文件里的scripts的prepare命令,可以在npm install或者yarn install后执行,也可以通过npm run prepare命令执行,执行后此时在当前项目的根目录下会出现一个 .husky 的文件夹

23514145-1c31dcd4f4f9df57.webp

4. 执行命令创建 pre-commit 钩子

执行命令 npx husky add .husky/pre-commit 在 .husky文件夹中创建 pre-commit 文件 命令如下:

npx husky add .husky/pre-commit

1627553165(1).png

注意:为什么不使用官网中提供的命令呢?因为使用官网中提供的命令在某些情况下会出现始终创建不成功的情况,详情查看 Issues

5. 修改步骤五中创建的 pre-commit 文件
  1. .git 文件在你当前项目的根目录下

23514145-8ef26605138b5fe0.webp 即改成后文件内容如下:

 // 将 pre-commit 文件修改为如下代码
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npx lint-staged
  1. .git 文件不在你当前项目的根目录下

23514145-b09827125e13d32c.webp 即改成后文件内容如下:

 // 将 pre-commit 文件修改为如下代码
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

cd projectName
npx lint-staged
6. 安装 lint-staged 安装包

首先明确一下,Lint-staged仅仅是文件过滤器,不会帮你格式化任何东西,所以没有代码规则配置文件,需要自己配置一下,package.json内的配置内容如下:

"lint-staged": {
  "src/**/*.{js,jsx,ts,tsx}": [
    "eslint --fix",
    "git add"
  ]
}

如下图:

1627554174(1).png

7. 测试 Husky + Lint-staged 配置是否生效

使用var定义的a变量,ESLint 规范报错,这时我们不去修改这个错误直接按照正常流程使用git提交代码,会发现git 不允许我们提交如下图:

1627554603(1).png

2. Prettier是什么?

  • Prettier是一个能够完全统一团队代码风格的利器,支持jsx、css。

  • Prettier 的中文意思是“漂亮的、机灵的”,也是一个流行的代码格式化工具的名称,它能够解析代码,使用你自己设定的规则来重新打印出格式规范的代码。它和ESLint的区别在于Prettier是一个专注于代码格式化的工具,对代码不做质量检查。使用Prettier在code review时不需要再讨论代码样式,节省了时间与精力。

  • 比如缩进是使用空格还是tab,大括号是否要添加空格等等,而这些正是prettier插件擅长做的。

  • prettier专门做代码格式化的事情,包括缩进、换行、添加空格等等,有了它可以让我们写的代码规范统一,增加代码的可读性、美观性。prettier可以作为eslint的一个插件使用。

Prettier的安装步骤如下(前提,项目中已经引入了ESLint):

1. 安装Prettier相关依赖

安装命令如下:

npm i -D prettier stylelint-config-prettier eslint-config-prettier eslint-plugin-prettier

23514145-cb415f6a1037d41d.webp 注意:有的依赖安装命令是如下方式:

npm install --save-dev --save-exact prettier
//--save-exact 安装精确版本

或者安装后在package.json中是如下显示方式:

55.png

//安装本地依赖包的意思

2. 使用ESLint运行Prettier

  1. 修改项目根目录中的 .eslintrc.js 文件 extends 字段,修改为如下内容:
"extends": [
  "eslint:recommended",
  "plugin:react/recommended",
  "plugin:@typescript-eslint/recommended",
  "prettier"
]

字段内容添加'prettier'

  1. 项目根目录中的 .eslintrc.js 文件 rules 中添加一项 "prettier/prettier":"2"

1627612899(1).png

  1. 增加.prettierrc配置文件, 设定代码格式化的规则: 在项目根目录下创建 .prettierrc.js 配置文件,在该文件中可以按照团队偏好自定义配置

23514145-8d68f1bbbc047d2b.webp 配置内容如下:

module.exports = {
  "printWidth": 100, // 指定代码长度,超出换行
  "tabWidth": 2, // tab 键的宽度
  "useTabs": false, // 不使用tab
  "semi": false, // 结尾加上分号
  "singleQuote": true, // 使用单引号
  "quoteProps": "as-needed", // 要求对象字面量属性是否使用引号包裹,(‘as-needed’: 没有特殊要求,禁止使用,'consistent': 保持一致 , preserve: 不限制,想用就用)
  "jsxSingleQuote": false, // jsx 语法中使用单引号
  "trailingComma": "es5", // 确保对象的最后一个属性后有逗号
  "bracketSpacing": true, // 大括号有空格 { name: 'rose' }
  "jsxBracketSameLine": false, // 在多行JSX元素的最后一行追加 >
  "arrowParens": "always", // 箭头函数,单个参数添加括号
  "requirePragma": false, // 是否严格按照文件顶部的特殊注释格式化代码
  "insertPragma": false, // 是否在格式化的文件顶部插入Pragma标记,以表明该文件被prettier格式化过了
  "proseWrap": "preserve", // 按照文件原样折行
  "htmlWhitespaceSensitivity": "ignore", // html文件的空格敏感度,控制空格是否影响布局
  "endOfLine": "auto" // 结尾是 \n \r \n\r auto
}

有了这个配置文件,再执行lint命令,eslint会按照这个prettier配置文件格式化我们的文件。

到这里,我们的项目中的eslint + prettier 的规则配置基本就完成了。

3. 相关配置文件的最终内容如下:

涉及package.json.eslintrc.prettierrctsconfig.json等文件的相关配置内容

1. package.json配置文件内容

//package.json
{
  "name": "inforserver",
  "version": "1.0.0",
  "description": "信息服务平台",
  "main": "index.js",
  "scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "dev": "webpack-dev-server --config ./config/webpack.dev.config.js --host 0.0.0.0",
    "build": "webpack -p --config ./config/webpack.prod.config.js",
    "lint-fix": "eslint --fix --ext .js --ext .tsx src",
    "prepare": "husky install"
  },
  "lint-staged": {
    "src/**/*.{js,jsx,ts,tsx}": [
      "prettier --write",
      "eslint --cache --fix",
      "git add"
    ]
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "dependencies": {
    "antd": "^3.22.2",
    "axios": "^0.19.0",
    "crypto-js": "^4.0.0",
    "echarts": "4.9.0",
    "happypack": "^5.0.1",
    "hard-source-webpack-plugin": "^0.13.1",
    "immutable": "^4.0.0-rc.12",
    "js-cookie": "^2.2.1",
    "moment": "^2.24.0",
    "prop-types": "latest",
    "qs": "^6.8.0",
    "react": "^16.9.0",
    "react-cookies": "^0.1.1",
    "react-dom": "^16.9.0",
    "react-loadable": "^5.5.0",
    "react-redux": "^7.1.1",
    "react-router": "^5.0.1",
    "react-router-dom": "^5.0.1",
    "redux": "^4.0.4",
    "redux-persist": "^6.0.0",
    "redux-persist-transform-immutable": "^5.0.0",
    "redux-thunk": "^2.3.0"
  },
  "devDependencies": {
    "@babel/core": "^7.14.6",
    "@babel/plugin-proposal-class-properties": "^7.14.5",
    "@babel/plugin-proposal-object-rest-spread": "^7.14.7",
    "@babel/plugin-syntax-dynamic-import": "^7.8.3",
    "@babel/plugin-transform-runtime": "^7.14.5",
    "@babel/preset-env": "^7.5.5",
    "@babel/preset-react": "^7.0.0",
    "@babel/preset-typescript": "^7.14.5",
    "@svgr/webpack": "^5.3.1",
    "@types/react": "16.9.23",
    "@types/react-dom": "16.9.7",
    "@types/react-router-dom": "5.1.3",
    "@typescript-eslint/eslint-plugin": "^4.28.4",
    "@typescript-eslint/parser": "^4.28.4",
    "babel-eslint": "^10.1.0",
    "babel-loader": "^8.0.6",
    "babel-plugin-import": "^1.13.3",
    "babel-polyfill": "^6.26.0",
    "clean-webpack-plugin": "^3.0.0",
    "copy-webpack-plugin": "^6.0.2",
    "css-loader": "^3.2.0",
    "eslint": "^7.31.0",
    "eslint-config-prettier": "^8.3.0",
    "eslint-plugin-import": "^2.23.4",
    "eslint-plugin-jsx-a11y": "^6.4.1",
    "eslint-plugin-prettier": "^3.4.0",
    "eslint-plugin-react": "^7.24.0",
    "eslint-plugin-react-hooks": "^4.2.0",
    "file-loader": "^4.2.0",
    "html-webpack-plugin": "^3.2.0",
    "husky": "^7.0.1",
    "less": "^3.10.3",
    "less-loader": "^5.0.0",
    "lint-staged": "^11.1.1",
    "mini-css-extract-plugin": "^0.8.0",
    "node-sass": "^4.12.0",
    "prettier": "^2.3.2",
    "react-countup": "^4.3.3",
    "sass-loader": "^8.0.0",
    "style-loader": "^1.0.0",
    "stylelint-config-prettier": "^8.0.2",
    "terser-webpack-plugin": "^4.2.0",
    "typescript": "^4.3.5",
    "uglifycss-loader": "^1.0.2",
    "url-loader": "^3.0.0",
    "webpack": "^4.39.3",
    "webpack-cli": "^3.3.7",
    "webpack-dev-server": "^3.8.0",
    "webpack-merge": "^4.2.2",
    "webpack-theme-color-replacer": "^1.3.7"
  }
}

2. .eslintrc配置文件内容如下:

//.eslintrc
{
  //  ESLint默认使用Espree作为其解析器
  //  此项是用来指定eslint解析器的,解析器必须符合规则
  //  官网没有对其他解析器进行说明,但是有提示,使用其他解析器的时候,注意确认它是不是和 ESLint 兼容。
  //  至于咋确认。。你指定一下这个解析器,看看 eslint 会不会对你想检查的代码正常报错,就行了。
  // 以下解析器与 ESLint 兼容:
  //  1、Esprima
  //  2、Babel-ESLint:一个对Babel解析器的包装,使其能够与 ESLint 兼容(如果你想使用一些 先进的语法,比如es6789...)。
  //  3、@typescript-eslint/parser:将 TypeScript 转换成与 estree 兼容的形式,以便在ESLint中使用(如果你想使用typescript)。
  "parser": "@typescript-eslint/parser",
  //  解析器选项,与parser同时使用
  //  在使用自定义解析器时,
  //  为了让 ESLint 在处理非 ECMAScript 5 特性时正常工作,
  //  配置属性 parserOptions 仍然是必须的
  //  解析器会被传入parserOptions,
  //  但是不一定会使用它们来决定功能特性的开关。
  "parserOptions": {
    // 默认是 script。模块化的代码要写:module(当前最常见做法,如果你的代码是 ECMAScript 模块)
    "sourceType": "module",
    //emcaVersion用来指定你想要使用的 ECMAScript 版本
    //注意,使用对于 ES6 语法,使用"ecmaVersion": 6时,不自动启用es6全局变量
    //自动启用es6语法和全局变量,需要搭配env使用"env": { "es6": true }
    "ecmaVersion": 6,
    //想使用额外的语言特性,一个配置对象,可配置项如下(value 均为 true/false)
    "ecmaFeatures": {
      "jsx": true,//启用jsx
      "globalReturn":true, //在全局作用域下使用return语句
      "impliedStrict":true, //启用全局strict mode (如果 ecmaVersion 是 5 或更高)
      "experimentalObjectRestSpread": false //启用实验性的object rest/spread properties支持(不建议开启)
    },
    "useJSXTextNode": true
  },
  //  要在配置文件中指定环境,请使用env键并通过将每个设置为来指定要启用的环境true。
  "env": {
    "browser": true, //开启浏览器全局变量
    "es6": true, //启用除模块以外的所有ECMAScript 6功能(这会自动将ecmaVersion解析器选项设置为6)
    "node": true //Node.js全局变量和Node.js作用域
  },
  // 此项是用来配置标准的js风格,就是说写代码的时候要规范的写
  // 指定配置的字符串(配置文件的路径,可共享配置的名称eslint:recommended或eslint:all)。
  // 字符串数组:每个其他配置都扩展了前面的配置
  "extends": [
    "eslint:recommended",
    "plugin:react/recommended",
    "plugin:@typescript-eslint/recommended",
    "plugin:prettier/recommended"
  ],
  // 此项是用来提供插件的,插件名称省略了eslint-plugin-
  "plugins": [
    "react",
    "@typescript-eslint",
    "react-hooks"
  ],
  /*
  * "off" 或 0 - 关闭规则
  * "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  * "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
  */
  "rules": {
    "semi": 2, // 行末分号,根据编码习惯选择添加,这里配置的加分号
    "no-console": 0, // 禁用 console
    "comma-dangle": [2,"never"], //对象字面量项尾不能有逗号(always-multiline是有逗号)
    "max-len": 0, // 强制一行的最大长度,可以是[1, 200]限制长度
    "max-lines": 0, // 强制最大行数
    "max-params": 0, // 强制 function 定义中最多允许的参数数量,可以用[1, 7]限制数量
    "max-statements": 0, // 强制 function 块最多允许的的语句数量,可以用[1, 200]限制数量
    "max-statements-per-line": 0, // 强制每一行中所允许的最大语句数量
    "space-before-function-paren": [0,"always"], // 强制在 function的左括号之前使用一致的空格
    "prettier/prettier":2, // 这项配置 对于不符合prettier规范的写法,eslint会提示报错
    // 禁止出现未使用过的表达式
    "no-unused-expressions": [
      0,
      {
        "allowShortCircuit": true,
        "allowTernary": true
      }
    ],
    "arrow-body-style": [0, "never"], // 要求箭头函数体使用大括号
    "func-names": 0, // 强制使用命名的 function 表达式
    "prefer-const": 0, // 要求使用 const 声明那些声明后不再被修改的变量
    "no-extend-native": 2, // 禁止扩展原生类型
    "no-param-reassign": 0, // 不允许对 function 的参数进行重新赋值
    "no-restricted-syntax": 0, // 禁止使用特定的语法
    "no-eval": 0, // 禁用 eval(),eval() 函数可计算某个字符串,并执行其中的的 JavaScript 代码。
    "no-continue": 0, // 禁用 continue 语句
    "global-require": 1, // 要求 require() 出现在顶层模块作用域中
    "camelcase": 2, //强制驼峰法命名
    "import/no-extraneous-dependencies": 0,
    "import/prefer-default-export": 0,
    "import/no-unresolved": 0,
    "import/extensions": 0,
    // react
    "react/jsx-first-prop-new-line": 0,
    "react/jsx-filename-extension": 0,
    "react/require-default-props": 0,
    "react/no-string-refs": 0,
    "react/no-find-dom-node": 0,
    "react/display-name": 0, //防止在React组件定义中丢失displayName
    "react/forbid-prop-types": [2, {"forbid": ["any"]}], //禁止某些propTypes
    "react/jsx-boolean-value": 2, //在JSX中强制布尔属性符号
    "react/jsx-closing-bracket-location": 1, //在JSX中验证右括号位置
    "react/jsx-curly-spacing": [2, {"when": "never", "children": true}], //在JSX属性和表达式中加强或禁止大括号内的空格。
    "react/jsx-indent-props": [2, 4], //验证JSX中的props缩进
    "react/jsx-key": 2, //在数组或迭代器中验证JSX具有key属性
    "react/jsx-max-props-per-line": [1, {"maximum": 1}], // 限制JSX中单行上的props的最大数量
    "react/jsx-no-bind": 0, //JSX中不允许使用箭头函数和bind
    "react/jsx-no-duplicate-props": 2, //防止在JSX中重复的props
    "react/jsx-no-literals": 0, //防止使用未包装的JSX字符串
    "react/jsx-no-undef": 1, //在JSX中禁止未声明的变量
    "react/jsx-pascal-case": 0, //为用户定义的JSX组件强制使用PascalCase
    "react/jsx-sort-props": 2, //强化props按字母排序
    "react/jsx-uses-react": 1, //防止反应被错误地标记为未使用
    "react/jsx-uses-vars": 2, //防止在JSX中使用的变量被错误地标记为未使用
    "react/no-danger": 0, //防止使用危险的JSX属性
    "react/no-did-mount-set-state": 0, //防止在componentDidMount中使用setState
    "react/no-did-update-set-state": 1, //防止在componentDidUpdate中使用setState
    "react/no-direct-mutation-state": 2, //防止this.state的直接变异
    "react/no-multi-comp": 2, //防止每个文件有多个组件定义
    "react/no-set-state": 0, //防止使用setState
    "react/no-unknown-property": 2, //防止使用未知的DOM属性
    "react/prefer-es6-class": 2, //为React组件强制执行ES5或ES6类
    "react/prop-types": 0, //防止在React组件定义中丢失props验证
    "react/react-in-jsx-scope": 2, //使用JSX时防止丢失React
    "react/self-closing-comp": 0, //防止没有children的组件的额外结束标签
    "react/sort-comp": 2, //强制组件方法顺序
    "react/no-array-index-key": 0, //防止在数组中遍历中使用数组key做索引
    "react/no-deprecated": 1, //不使用弃用的方法
    "react/jsx-equals-spacing": 2, //在JSX属性中强制或禁止等号周围的空格
    "react-hooks/rules-of-hooks": "error",
    "react-hooks/exhaustive-deps": "warn",
    "no-extra-boolean-cast": 0, //禁止不必要的bool转换
    "no-unreachable": 1, //不能有无法执行的代码
    "no-mixed-spaces-and-tabs": 0, //禁止混用tab和空格
    "prefer-arrow-callback": 0, //比较喜欢箭头回调
    "arrow-parens": 0, //箭头函数用小括号括起来
    "arrow-spacing": 0, //=>的前/后括号
    "jsx-a11y/href-no-hash": 0,
    "jsx-a11y/no-static-element-interactions": 0,
    "quotes": [2, "single"], //单引号
    "no-debugger": 2, //禁用debugger
    "no-var": 2, //对var警告
    "no-irregular-whitespace": 0, //不规则的空白不允许
    "no-trailing-spaces": 1, //一行结束后面有空格就发出警告
    "eol-last": 0, //文件以单一的换行符结束
    "no-unused-vars": [2, {"vars": "all", "args": "after-used"}], //不能有声明后未被使用的变量或参数
    "no-underscore-dangle": 0, //标识符不能以_开头或结尾
    "no-alert": 2, //禁止使用alert confirm prompt
    "no-lone-blocks": 0, //禁止不必要的嵌套块
    "no-class-assign": 2, //禁止给类赋值
    "no-cond-assign": 2, //禁止在条件表达式中使用赋值语句
    "no-const-assign": 2, //禁止修改const声明的变量
    "no-delete-var": 2, //不能对var声明的变量使用delete操作符
    "no-dupe-keys": 2, //在创建对象字面量时不允许键重复
    "no-duplicate-case": 2, //switch中的case标签不能重复
    "no-dupe-args": 2, //函数参数不能重复
    "no-empty": 2, //块语句中的内容不能为空
    "no-func-assign": 2, //禁止重复的函数声明
    "no-invalid-this": 0, //禁止无效的this,只能用在构造器,类,对象字面量
    "no-redeclare": 2, //禁止重复声明变量
    "no-spaced-func": 2, //函数调用时 函数名与()之间不能有空格
    "no-this-before-super": 0, //在调用super()之前不能使用this或super
    "no-undef": 2, //不能有未定义的变量
    "no-use-before-define": 0, //未定义前不能使用
    "jsx-quotes": [2, "prefer-double"], //强制在JSX属性(jsx-quotes)中一致使用双引号
    "@typescript-eslint/no-unused-vars": 0,
    "@typescript-eslint/no-explicit-any": 0,
    "@typescript-eslint/no-empty-interface": 0,
    "@typescript-eslint/explicit-function-return-type": 0,
    "@typescript-eslint/camelcase": ["off", {"properties":"always"}], //强制驼峰法命名(ts)
    "@typescript-eslint/no-use-before-define": 0
  },
  /*
  *ESLint支持将共享设置添加到配置文件中。
  *您可以将settings对象添加到ESLint配置文件,并将其提供给将要执行的每个规则。
  *如果您要添加自定义规则,并希望它们能够访问相同的信息并且易于配置,这可能会很有用。
  */
  "settings": {
    "import/resolver": "node",
    "react": {
      "version": "detect"
    }
  }
}

//环境定义了预定义的全局变量。可用环境为:
//browser -浏览器全局变量。
//node -Node.js全局变量和Node.js作用域。
//commonjs -CommonJS全局变量和CommonJS作用域(将其用于使用Browserify / WebPack的仅浏览器代码)。
//shared-node-browser -Node.js和浏览器通用的全局变量。
//es6-启用除模块以外的所有ECMAScript 6功能(这会自动将ecmaVersion解析器选项设置为6)。
//es2017-添加所有ECMAScript 2017全局变量并自动将ecmaVersion解析器选项设置为8。
//es2020-添加所有ECMAScript 2020全局变量并将ecmaVersion解析器选项自动设置为11。
//worker -网络工作者的全局变量。
//amd- 根据amd规范定义require()和define()作为全局变量。
//mocha -添加所有Mocha测试全局变量。
//jasmine -添加了版本1.3和2.0的所有Jasmine测试全局变量。
//jest -开玩笑的全局变量。
//phantomjs -PhantomJS全局变量。
//protractor -量角器全局变量。
//qunit -QUnit全局变量。
//jquery -jQuery全局变量。
//prototypejs -Prototype.js全局变量。
//shelljs -ShellJS全局变量。
//meteor -流星全局变量。
//mongo -MongoDB全局变量。
//applescript -AppleScript全局变量。
//nashorn -Java 8 Nashorn全局变量。
//serviceworker -服务人员全局变量。
//atomtest -Atom测试助手全局变量。
//embertest -灰烬测试助手全局变量。
//webextensions -WebExtensions全局变量。
//greasemonkey -GreaseMonkey全球。
//这些环境不是互斥的,因此您一次可以定义多个环境。

3. .prettierrc配置文件的相关内容如下:

// .prettierrc
module.exports = {
  "printWidth": 100, // 指定代码长度,超出换行
  "tabWidth": 2, // tab 键的宽度
  "useTabs": false, // 不使用tab
  "semi": true, // 结尾加上分号
  "singleQuote": true, // 使用单引号
  "quoteProps": "as-needed", // 要求对象字面量属性是否使用引号包裹,(‘as-needed’: 没有特殊要求,禁止使用,'consistent': 保持一致 , preserve: 不限制,想用就用)
  "jsxSingleQuote": false, // jsx 语法中使用单引号
  "trailingComma": "es5", // 确保对象的最后一个属性后有逗号
  "bracketSpacing": true, // 大括号有空格 { name: 'rose' }
  "jsxBracketSameLine": false, // 在多行JSX元素的最后一行追加 >
  "arrowParens": "always", // 箭头函数,单个参数添加括号
  "requirePragma": false, // 是否严格按照文件顶部的特殊注释格式化代码
  "insertPragma": false, // 是否在格式化的文件顶部插入Pragma标记,以表明该文件被prettier格式化过了
  "proseWrap": "preserve", // 按照文件原样折行
  "htmlWhitespaceSensitivity": "ignore", // html文件的空格敏感度,控制空格是否影响布局
  "endOfLine": "auto" // 结尾是 \n \r \n\r auto
}

4. tsconfig.json配置文件的相关内容如下:

{
  "compilerOptions": {
    /* Visit https://aka.ms/tsconfig.json to read more about this file */
    "baseUrl": "./",                             /* Base directory to resolve non-absolute module names. */
    "paths": {
      "@/*": ["./src/*"]
    },
    /* Basic Options */
    // "incremental": true,                         /* Enable incremental compilation */
    "target": "es2015",                                /* 输出代码 ES 版本,可以是: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', 'ES2018', 'ES2019', 'ES2020', 'ES2021', or 'ESNEXT'. */
    "module": "commonjs",                           /* 指定模块生成方式: 'none', 'commonjs', 'amd', 'system', 'umd', 'es2015', 'es2020', or 'ESNext'. */
    "allowJs": true,                             /* 编译时,允许有 js 文件 */
    "jsx": "react",                           /* jsx 的编译方式: 'preserve', 'react-native', 'react', 'react-jsx' or 'react-jsxdev'. */
    "sourceMap": true,                           /* 生成对应的 map 文件 */
    "outDir": "./dist/",                              /* 定义输出文件的文件夹 */
    "removeComments": true,                      /* 去除注释 */
    "strict": true,                                 /* 用于指定是否启用所有的类型检查. */
    "noImplicitAny": false,                       /*   不允许隐式 any,如果true,函数的形参必须带类型,如果叫不出class名的js对象,那就得any,比如(d:any)=>{}如果false,函数的样子更像js  (d)=>{} */
    "esModuleInterop": true,                        /* 通过导入内容创建命名空间,实现CommonJS和ES模块之间的互操作性 */
    "skipLibCheck": true,                           /* 对库定义文件跳过类型检查(2.0 以上) */
    "forceConsistentCasingInFileNames": true,        /* 不允许不同变量来代表同一文件 */
     "strictNullChecks": true,                    /* null 检查(2.0 以上) */
     "noFallthroughCasesInSwitch": true,          /* switch 语句中,每个 case 都要有 break */
    "allowUnreachableCode": true,                 /* 不报告执行不到的代码错误 */
    "allowUnusedLabels": false,                      /* 不报告未使用的标签错误 */
     "noImplicitThis": true,                      /* 不允许 this 为隐式 any */
     "noUnusedLocals": false,                      /* 未使用的本地变量将报错(2.0 以上) */
     "noUnusedParameters": false,                  /* 未使用的参数将报错(2.0 以上) */
     "typeRoots": [],                             /* 定义文件的文件夹位置(2.0 以上) */
     "types": [],                                 /* 设置引入的定义文件(2.0 以上) */
     "allowSyntheticDefaultImports": true        /* 允许引入没有默认导出的模块,即在tsx中使用js的模块引入方式来引入模块,例如引入react */
    // "lib": [],                                   /* Specify library files to be included in the compilation. */
    // "checkJs": true,                             /* Report errors in .js files. */
    // "declaration": true,                         /* Generates corresponding '.d.ts' file. */
    // "declarationMap": true,                      /* Generates a sourcemap for each corresponding '.d.ts' file. */
    // "outFile": "./",                             /* Concatenate and emit output to single file. */
    // "rootDir": "./",                             /* Specify the root directory of input files. Use to control the output directory structure with --outDir. */
    // "composite": true,                           /* Enable project compilation */
    // "tsBuildInfoFile": "./",                     /* Specify file to store incremental compilation information */
    // "noEmit": true,                              /* Do not emit outputs. */
    // "importHelpers": true,                       /* Import emit helpers from 'tslib'. */
    // "downlevelIteration": true,                  /* Provide full support for iterables in 'for-of', spread, and destructuring when targeting 'ES5' or 'ES3'. */
    // "isolatedModules": true,                     /* Transpile each file as a separate module (similar to 'ts.transpileModule'). */
    /* Strict Type-Checking Options */
    // "strictFunctionTypes": true,                 /* Enable strict checking of function types. */
    // "strictBindCallApply": true,                 /* Enable strict 'bind', 'call', and 'apply' methods on functions. */
    // "strictPropertyInitialization": true,        /* Enable strict checking of property initialization in classes. */
    // "alwaysStrict": true,                        /* Parse in strict mode and emit "use strict" for each source file. */
    /* Additional Checks */
    // "noImplicitReturns": true,                   /* Report error when not all code paths in function return a value. */
    // "noUncheckedIndexedAccess": true,            /* Include 'undefined' in index signature results */
    // "noImplicitOverride": true,                  /* Ensure overriding members in derived classes are marked with an 'override' modifier. */
    // "noPropertyAccessFromIndexSignature": true,  /* Require undeclared properties from index signatures to use element accesses. */
    /* Module Resolution Options */
    // "moduleResolution": "node",                  /* Specify module resolution strategy: 'node' (Node.js) or 'classic' (TypeScript pre-1.6). */
    // "paths": {},                                 /* A series of entries which re-map imports to lookup locations relative to the 'baseUrl'. */
    // "rootDirs": [],                              /* List of root folders whose combined content represents the structure of the project at runtime. */
    // "preserveSymlinks": true,                    /* Do not resolve the real path of symlinks. */
    // "allowUmdGlobalAccess": true,                /* Allow accessing UMD globals from modules. */
    /* Source Map Options */
    // "sourceRoot": "",                            /* Specify the location where debugger should locate TypeScript files instead of source locations. */
    // "mapRoot": "",                               /* Specify the location where debugger should locate map files instead of generated locations. */
    // "inlineSourceMap": true,                     /* Emit a single file with source maps instead of having a separate file. */
    // "inlineSources": true,                       /* Emit the source alongside the sourcemaps within a single file; requires '--inlineSourceMap' or '--sourceMap' to be set. */
    /* Experimental Options */
    // "experimentalDecorators": true,              /* Enables experimental support for ES7 decorators. */
    // "emitDecoratorMetadata": true,               /* Enables experimental support for emitting type metadata for decorators. */
    /* Advanced Options */
  },
  "include": ["src/**/*"]
  ,
  "exclude": [
    "node_modules"
  ]
}