万字长文详解react项目使用craco进行配置并集成Prettier、Eslint、husky、lint-staged

10,949 阅读1分钟

项目搭建

为什么使用craco

使用create-react-app 创建的项目默认是无法修改其内部的webpack配置的,不像vue-cli那样可以通过一个配置文件修改。 虽然有一个eject 命令可以是将配置完全暴露出来,但这是一个不可逆的操作,同时也会失去CRA带来的便利和后续升级。 如果想要无 eject 重写 CRA 配置,目前成熟的是下面这几种方式

  • 通过 CRA 官方支持的 --scripts-version 参数,创建项目时使用自己重写过的 react-scripts
  • 使用 react-app-rewired + customize-cra 组合覆盖配置
  • 使用 craco 覆盖配置

这里我们选择的是使用craco覆盖配置

搭建步骤

  • 使用 create-react-app 创建一个项目,这里我们命名为 react-app
npx create-react-app react-app --template typescript
  • 进入项目,安装craco依赖
npm install --save @craco/craco
  • 在项目根目录下创建配置文件craco.config.js,并根据实际情况完善配置
const path = require('path');
const { name } = require('./package.json');

const pathResolve = pathUrl => path.join(__dirname, pathUrl);

module.exports = {
  reactScriptsVersion: 'react-scripts' /* (default value) */,
  webpack: {
    alias: {
      '@': pathResolve('src'),
      '@assets': pathResolve('src/assets'),
      '@components': pathResolve('src/components'),
      '@constants': pathResolve('src/constants'),
      '@containers': pathResolve('src/containers'),
      '@hooks': pathResolve('src/hooks'),
      '@mocks': pathResolve('src/mocks'),
      '@routes': pathResolve('src/routes'),
      '@services': pathResolve('src/services'),
      '@styles': pathResolve('src/styles'),
      '@types': pathResolve('src/types'),
      '@utils': pathResolve('src/utils'),
      '@contexts': pathResolve('src/contexts'),
    },
    configure(webpackConfig) {
      // 配置扩展扩展名
      webpackConfig.resolve.extensions = [...webpackConfig.resolve.extensions, ...['.scss', '.css']];
      // 接入微前端框架qiankun的配置,不接入微前端可以不需要
      webpackConfig.output.library = `${name}-[name]`;
      webpackConfig.output.libraryTarget = 'umd';
      webpackConfig.output.globalObject = 'window';
      return webpackConfig;
    },
  },
  devServer: {
    // 本地服务的端口号
    port: 3001,
    // 本地服务的响应头设置
    headers: {
      // 允许跨域
      'Access-Control-Allow-Origin': '*',
    },
  },
};

详细配置请参考官网文档

  • 修改 package.json 中的 scripts
"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
  },

集成Eslint

什么是Eslint

ESLint 是一个在 JavaScript 代码中通过规则模式匹配作代码识别和报告的插件化的检测工具,它的目的是保证代码规范的一致性和及时发现代码问题、提前避免错误发生。 ESLint 的关注点是代码质量,检查代码风格并且会提示不符合风格规范的代码。除此之外 ESLint 也具有一部分代码格式化的功能。

Eslint的作用及优势

  • 检查语法错误,避免低级bug

比如:api语法错误、使用了未定义的变量、修改const变量

  • 统一团队代码风格

比如:使用tab还是空格,使用单引号还是双引号等

  • 确保代码遵循最佳实践

比如:可以借助eslint-config-standard配置包扩展社区中流行的最佳实践的风格指南。

这样就能极大提高项目中多人协作开发时的效率、代码的可读性以及可维护性

支持的配置文件格式

ESLint 支持几种格式的配置文件:

  • JavaScript -- 使用 .eslintrc.js 然后输出一个配置对象。
  • YAML -- 使用 .eslintrc.yaml.eslintrc.yml 去定义配置的结构。
  • JSON -- 使用 .eslintrc.json 去定义配置的结构,ESLint JSON 文件允许 JavaScript 风格的注释。
  • (弃用) -- 使用 .eslintrc,可以使 JSON 也可以是 YAML。
  • package.json -- 在 package.json 里创建一个 eslintConfig属性,在那里定义你的配置。

如果同一个目录下有多个配置文件,ESLint 只会使用一个。优先级顺序如下: .eslintrc.js > .eslintrc.yaml > .eslintrc.yml > .eslintrc.json > .eslintrc > package.json

遇到项目内有多个层叠配置时,依然采用就近原则作为高优先级;

配置文件说明

Rules-启用的规则及其各自的错误级别

ESLint 附带有大量的规则。你可以使用注释或配置文件修改你项目中要使用的规则。要改变一个规则设置,你必须将规则 ID 设置为下列值之一:

  • "off"0 - 关闭规则
  • "warn"1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
  • "error"2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)

例如:

rules: {
    'eqeqeq': 2,
    'no-alert': 2,
    'no-undef': 2,
    'no-use-before-define': 2,
    'react-hooks/exhaustive-deps': 2,
    '@typescript-eslint/no-explicit-any': 0,
    '@typescript-eslint/no-non-null-assertion': 0,
    '@typescript-eslint/no-var-requires': 0,
  },

Globals-配置额外的全局变量

启用ESLint规则后,当访问当前源文件内未定义的变量时,no-undef 规则将发出警告。 而有时候,我们是需要在其他文件访问一些全局变量的,且保证能正常取到值。这时可以在 ESLint 中定义这些全局变量,这样 ESLint 就不会发出警告了。

  • 用注释指定全局变量,格式如下:
/* global var1, var2 */

这定义了两个全局变量,var1var2。如果你想选择性地指定这些全局变量可以被写入(而不是只被读取),那么你可以用一个 "writable" 的标志来设置它们:

/* global var1:writable, var2:writable */
  • 配置文件中通过globals 配置属性设置,对于每个全局变量键,将对应的值设置为 "writable" 以允许重写变量,或 "readonly" 不允许重写变量。例如:
// .eslintrc.js
"globals": {
  "var1": "writable",
  "var2": "readonly"
}

Environments - 指定脚本的运行环境

每种环境都有一组特定的预定义全局变量。如brower、node环境变量、es2021环境变量等。

env: {
    browser: true,
    es2021: true,
    node: true,
  },

Plugins - 第三方插件

ESLint 支持使用第三方插件,先在项目中下载安装要引入的插件,配置文件中使用 plugins 关键字来存放插件名字的列表。插件名称可以省略 eslint-plugin- 前缀。

plugins: ['react', 'babel'], // eslint-plugin-react eslint-plugin-babel

Extends - 继承

一个配置文件可以被基础配置中的已启用的规则继承。

 extends: ["eslint:recommended","plugin:prettier/recommended"],

使用Eslint

安装Eslint

ESLint 可以安装在当前项目中或全局环境下,但因项目间存在的差异性,我们一般会将它安装在当前项目中。

npm install eslint --save-dev

安装插件和解析器

假如项目中使用了TypeScript和React,则安装

// 安装eslint-plugin-react配置包扩展支持React语法
// 安装 @typescript-eslint/parser,替代掉默认的Espree解析器
// 安装@typescript-eslint/eslint-plugin提供额外的ts 语法的规则
npm install --save-dev eslint-plugin-react @typescript-eslint/parser @typescript-eslint/eslint-plugin

其他的插件和解析器请根据实际项目需要安装。

创建配置文件

创建配置文件有以下两种方式

  • 在根目录新建 .eslintrc.js文件,并自己完善该配置文件
  • 在终端中输入如下命令npx eslint--init并根据提示自动创建配置文件

文件配置大致如下:

module.exports = {
  env: {
    browser: true,
    es2021: true,
    node: true,
  },
  root: true,
  extends: ['eslint:recommended', 'react-app', 'plugin:prettier/recommended', 'plugin:@typescript-eslint/recommended'],
  overrides: [],
  parser: '@typescript-eslint/parser',
  parserOptions: {
    ecmaVersion: 'latest',
    sourceType: 'module',
    ecmaFeatures: {
      jsx: true,
    },
  },
  plugins: ['react', '@typescript-eslint'],
  rules: {
    eqeqeq: 2,
    'no-alert': 2,
    'no-undef': 2,
    'no-use-before-define': 2,
    'react-hooks/exhaustive-deps': 2,
    '@typescript-eslint/no-explicit-any': 0,
    '@typescript-eslint/no-non-null-assertion': 0,
    '@typescript-eslint/no-var-requires': 0,
  },
};

具体配置可根据项目实际需要进行配置

添加命令进行eslint检测

package.json中添加如下命令:

"scripts": {
    "start": "craco start",
    "build": "craco build",
    "test": "craco test",
    // 新添加的lint命令,意思是使用.eslintrc.js检测src文件夹下的后缀为.ts,.tsx,.js,.jsx的所有文件,并对可自动修复的eslint报错进行修复
    "lint": "eslint -c .eslintrc.js src --ext .ts,.tsx,.js,.jsx --fix"
  },

之后就可以运行npm run lint这个命令进行eslint校验;

比如,我们在项目内任意一个ts文件内输入如下代码:

console.log('jump', age);

然后运行npm run lint 就会发现终端报如下错误: 在这里插入图片描述 这就说明,我们的Eslint配置成功了

集成Prettier

什么是Prettier

Prettier是一个诞生于2016年就迅速流行起来的专注于代码格式化的工具。出道即巅峰啊-.- Prettier只关注格式化,并不具有lint检查语法等能力。它通过解析代码并匹配自己的一套规则,来强制执行一致的代码展示格式。 它在美化代码方面有很大的优势,配合ESLint可以对ESLint格式化基础上做一个很好的补充。

使用Prettier

安装Prettier

npm install prettier --save-dev

配置Prettier规则

在项目根目录新建.prettierrc.js,并完善配置 大致配置如下:

module.exports = {
    // 1.一行代码的最大字符数,默认是80(printWidth: <int>)
    printWidth: 120,
    // 2.tab宽度为2空格(tabWidth: <int>)
    tabWidth: 2,
    // 3.是否使用tab来缩进,我们使用空格(useTabs: <bool>)
    useTabs: false,
    // 4.结尾是否添加分号,false的情况下只会在一些导致ASI错误的其工况下在开头加分号,我选择无分号结尾的风格(semi: <bool>)
    semi: true,
    // 5.使用单引号(singleQuote: <bool>)
    singleQuote: true,
    // 6.object对象中key值是否加引号(quoteProps: "<as-needed|consistent|preserve>")as-needed只有在需求要的情况下加引号,consistent是有一个需要引号就统一加,preserve是保留用户输入的引号
    quoteProps: 'as-needed',
    // 7.在jsx文件中的引号需要单独设置(jsxSingleQuote: <bool>)
    jsxSingleQuote: false,
    // 8.尾部逗号设置,es5是尾部逗号兼容es5,none就是没有尾部逗号,all是指所有可能的情况,需要node8和es2017以上的环境。(trailingComma: "<es5|none|all>")
    trailingComma: 'es5',
    // 9.object对象里面的key和value值和括号间的空格(bracketSpacing: <bool>)
    bracketSpacing: true,
    // 10.jsx标签多行属性写法时,尖括号是否另起一行(jsxBracketSameLine: <bool>)
    jsxBracketSameLine: false,
    // 11.箭头函数单个参数的情况是否省略括号,默认always是总是带括号(arrowParens: "<always|avoid>")
    arrowParens: 'avoid',
    // 12.range是format执行的范围,可以选执行一个文件的一部分,默认的设置是整个文件(rangeStart: <int>  rangeEnd: <int>)
    rangeStart: 0,
    rangeEnd: Infinity,
    // 18. vue script和style标签中是否缩进,开启可能会破坏编辑器的代码折叠
    vueIndentScriptAndStyle: false,
    // 19.    endOfLine: "<lf|crlf|cr|auto>" 行尾换行符,默认是lf,
    endOfLine: 'auto',
    // 20.embeddedLanguageFormatting: "off",默认是auto,控制被引号包裹的代码是否进行格式化
    embeddedLanguageFormatting: 'off',
};

如何解决Prettier与ESLint的配置冲突问题?

在代码格式化时采用Perttier规则,而我们代码校验使用的是ESLint,如果同一个规则配置不一致,往往就会出现冲突问题;

比如:字符串单、双引号的配置,eslint fix后把字符串变成单引号,再次编辑文件后,保存(Prettier)自动格式化后却又变成双引号,导致代码校验异常。

通常有以下两种解决方法:

  • 要么修改 eslintrc,要么修改 prettierrc 配置,让它们配置保持一致
  • 禁用 ESLint中和Prettier配置有冲突的规则;再使用 Prettier 来替代 ESLint 的格式化功能

安装eslint-config-prettier插件配置集,把其配置到eslintrc规则的尾部。执行eslint命令,会禁用那些和Prettier配置有冲突的规则。 安装eslint-plugin-prettier插件,先使用Prettier对代码进行格式化,再并对不一致的地方进行标记;

这两个包配合使用,可以达到运行 eslint --fix 时,采用Prettier的配置规则 来格式化文件。

集成husky

什么是husky

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

husky的作用

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

也可以在commit-msg钩子中结合commitlint实现提交信息的检查

了解githooks

Git Hooks 就是在 Git 执行特定事件(如commitpushreceive等)时触发运行的脚本,类似于“钩子函数”,没有设置可执行的钩子将被忽略。 git hook 的作用是在 git 动作发生前后触发自定义脚本。这些动作包括提交,合并,推送等,我们可以利用这些钩子在 git流程的各个环节实现自己的业务逻辑。

git hook 分为客户端 hook服务端 hook

客户端 hook 主要有四个:

  • pre-commit:提交信息前运行,可检查暂存区的代码
  • prepare-commit-msg:不常用
  • commit-msg:非常重要,检查提交信息就用这个钩子
  • post-commit:提交完成后运行

服务端 hook 包括:

  • pre-receive:非常重要,推送前的各种检查都在这
  • post-receive:不常用
  • update:不常用

自动配置husky

初始化husky

npx husky-init install // npm
yarn dlx husky-init  //  Yarn 2+ 
pnpm dlx husky-init  // pnpm

husky-init命令主要做了以下的事情

  • 在项目目录下创建.husky文件夹,并且默认创建了一个pre-commit的钩子,钩子中内容为
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npm run test
  • 在package.json中添加脚本: "prepare":"husky install"npm install时默认会执行这个脚本

配置pre-commit钩子

修改pre-commit钩子内容为:

#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "pre-commit";
npm run lint;

这样我们每次执行git commit命令的时候都会触发pre-commit钩子,从而在控制台先打印pre-commit,然后在执行一遍eslint校验,如果eslint校验失败就会中止git commit

集成lint-staged

什么是lint-staged

之前我们的配置是每次git commit的时候都执行一次npm run lint,这个命令会把src文件夹下的所有符合文件格式的文件都走一遍eslint校验,这显然是不太合理的;尤其是项目越来越大的时候这更是一个很耗时的操作。所以,我们需要过滤出git暂存区里的文件,仅对暂存区的文件做eslint校验即可。 lint-staged就是这样一个工具,它可以帮我们过滤出 Git 代码暂存区文件(被 git add 的文件)。这个很实用,因为我们如果对整个项目的代码做一个检查,可能耗时很长,如果是老项目,要对之前的代码做一个代码规范检查并修改的话,这可能就麻烦了呀,可能导致项目改动很大。 lint-staged 总是将所有暂存文件的列表传递给任务。

使用lint-staged

  • 安装lint-staged
npm install lint-staged --save-dev
  • 配置lint-stagedpackage.json中新增如下命令:
// package.json

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

这条命令的意思是,对于暂存区中src文件夹下后缀名为ts,tsx,js,jsx的文件执行eslint校验并自动修复可修复的eslint报错。当然,也可以根据项目需要加入其他任务,比如:

// package.json

"lint-staged": {
  "src/**/*.{js,vue}": [
    "prettier --write",
    "eslint --cache --fix",
    "git add"
  ]
}

这里 lint-staged 的配置是:在 git 的待提交的文件中,在 src 目录下的所有 .js .vue 都要执行三条命令。使用prettier格式化文件样式;校验eslint并自动修复;将处理过的代码重新 add 到 暂存区 中。

  • 修改pre-commit钩子 将pre-commit钩子的内容修改为如下:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

echo "pre-commit";
npx lint-staged;

这样每次提交的时候就只会对暂存区中的文件进行eslint校验了。

以上就是react项目使用craco进行配置并集成PrettierEslinthuskylint-staged的详细内容,详细代码可查看我的代码仓库