阅读 165

对现有React项目进行规范及优化(涉及使用Typescript、eslint规范、Sonar 搭建、React组件库搭建、性能优化)(持续更新ing...)

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

概论

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

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

以上几点相关知识点,就是我要针对我们项目的优化点,本文章会持续书写,毕竟项目会一直持续优化。 下图是我整个项目规范化与优化下来的大概实行步骤,如下: 未命名文件.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
复制代码

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添加统一代码规范
  • 格式化现有项目下的不符合规范的文件
  • 配置编辑器,自动检测新增或修改的代码的规范合法性

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支持(不建议开启)
    }
  },
  //  要在配置文件中指定环境,请使用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": 0, //对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": 2, //未定义前不能使用
    "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": 0
  },
  /*
  *ESLint支持将共享设置添加到配置文件中。
  *您可以将settings对象添加到ESLint配置文件,并将其提供给将要执行的每个规则。
  *如果您要添加自定义规则,并希望它们能够访问相同的信息并且易于配置,这可能会很有用。
  */
  "settings": {
    "import/resolver": "node"
  }
}

//环境定义了预定义的全局变量。可用环境为:
//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

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

4. 安装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 标志传递配置文件

2. Prettier是什么?

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

文章分类
前端
文章标签