react18项目搭建代码规范eslint+stylelint+prettier+husky+lint-staged

327 阅读10分钟

网上有很多教程是有关搭建react项目工程化代码规范的文章,自己跟着敲一遍会出现各种问题,大部分是依赖包版本问题,深受其扰,所以抽空整理出自己总结的一份模板搭建教程

node版本:18.20.0

1.项目初始化

  • vite快速搭建react
npm create vite

image.png

  • 目录结构

image.png

  • 删除eslint.config.js

2.代码规范

prettier

  • vscode安装

image.png

  • 安装
npm i -D prettier@2.6.2
  • 根目录新建文件 .prettierrc.cjs 并复制以下内容
// @see: https://www.prettier.cn

module.exports = {
  // 超过最大值换行
  printWidth: 130,
  // 缩进字节数
  tabWidth: 2,
  // 使用制表符而不是空格缩进行
  useTabs: true,
  // 结尾不用分号(true有,false没有)
  semi: true,
  // 使用单引号(true单双引号,false双引号)
  singleQuote: false,
  // 更改引用对象属性的时间 可选值"<as-needed|consistent|preserve>"
  quoteProps: 'as-needed',
  // 在对象,数组括号与文字之间加空格 "{ foo: bar }"
  bracketSpacing: true,
  // 多行时尽可能打印尾随逗号。(例如,单行数组永远不会出现逗号结尾。) 可选值"<none|es5|all>",默认none
  trailingComma: 'none',
  // 在JSX中使用单引号而不是双引号
  jsxSingleQuote: false,
  //  (x) => {} 箭头函数参数只有一个时是否要有小括号。avoid:省略括号 ,always:不省略括号
  arrowParens: 'avoid',
  // 如果文件顶部已经有一个 doclock,这个选项将新建一行注释,并打上@format标记。
  insertPragma: false,
  // 指定要使用的解析器,不需要写文件开头的 @prettier
  requirePragma: false,
  // 默认值。因为使用了一些折行敏感型的渲染器(如GitHub comment)而按照markdown文本样式进行折行
  proseWrap: 'preserve',
  // 在html中空格是否是敏感的 "css" - 遵守CSS显示属性的默认值, "strict" - 空格被认为是敏感的 ,"ignore" - 空格被认为是不敏感的
  htmlWhitespaceSensitivity: 'css',
  // 换行符使用 lf 结尾是 可选值"<auto|lf|crlf|cr>"
  endOfLine: 'auto',
  // 这两个选项可用于格式化以给定字符偏移量(分别包括和不包括)开始和结束的代码
  rangeStart: 0,
  rangeEnd: Infinity,
  // Vue文件脚本和样式标签缩进
  vueIndentScriptAndStyle: false
}

  • 为了不让本地的配置文件影响prettier和eslint,在根目录下新建 .vscode 文件夹,并且创建settings.json,复制内容
{
  "search.exclude": {
    "/node_modules": true,
    "dist": true,
    "pnpm-lock.sh": true
  },
  "editor.formatOnSave": true,
  "[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"
  },
  "[css]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[less]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[scss]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
}
  • 现在一保存代码就自动格式化

GIF.gif

  • 根目录新建文件 .prettierignore 忽略格式化的文件,一般内容是
/dist/*
.local
/node_modules/**

**/*.svg
**/*.sh
/public/*

eslint

eslint是用来检查代码中的语法错误、代码风格问题以及可能的逻辑错误。

  • vscode安装

image.png

  • 安装
npm i eslint@8.17.0  -D 
  • 安装插件
npm i @typescript-eslint/eslint-plugin@8.15.0 @typescript-eslint/parser@8.15.0 -D
  • 根目录新建 .eslintrc.js 复制内容
module.exports = {
	root: true, // 指定这是根配置文件,用于定义javascript或者Typescript项目中的代码质量和风格的规则。
	env: { browser: true, es2020: true, node: true }, // 指定代码的运行环境
	extends: [
		// 这里是越靠后的插件配置优先级越高
		"eslint:recommended", // eslint默认规则
		"plugin:@typescript-eslint/recommended", // Typescript推荐规范
		"plugin:react-hooks/recommended", // React Hooks推荐规范
	],
	ignorePatterns: ["dist", ".eslintrc.cjs"], // 忽略文件
	parser: "@typescript-eslint/parser", // 指定的解析器
	plugins: ["react-refresh"], // 支持React的热刷新
	rules: {
		"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-<directive> 使用注释或要求在指令后进行描述
		"@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": "off",
		"react-hooks/exhaustive-deps": "off"
	}
};

  • 新建.eslintignore 忽略文件
*.sh
node_modules
*.md
*.woff
*.ttf
.vscode
.idea
dist
/public
/docs
.husky
.local
/bin
.eslintrc.js
.prettierrc.cjs
/src/mock/*

发现eslint并没有生效,直接重启vscode后会生效,后面如果有遇到插件没有生效问题,可以重启vscode试试

  • 验证下

GIF.gif

  • 保存自动修复错误,需要在./vscode/settings.json增加内容
  "eslint.validate": [
    "javascript",
    "javascriptreact",
    "typescript",
    "typescriptreact"
  ],
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
  },
  • 验证保存,不规范代码自动修复

GIF.gif

  • 解决eslint和prettier冲突

eslint既负责了代码质量检测,又负责了一部分的格式美化工作,格式化部分的部分规则和prettier不兼容,所以让eslint只负责代码质量检测而让prettier负责美化,要用上 eslint-config-prettier + eslint-plugin-prettier。

  • eslint-config-prettier 的作用是关闭eslint中与prettier相互冲突的规则。
  • eslint-plugin-prettier 的作用是赋予eslint用prettier格式化代码的能力。 安装依赖并修改.eslintrc文件
  • 安装
npm i eslint-config-prettier@8.5.0 eslint-plugin-prettier@4.0.0 -D
  • .eslintrc.js 添加
extends: [
	// 这里是越靠后的插件配置优先级越高
	"eslint:recommended", // eslint默认规则
	"plugin:@typescript-eslint/recommended", // Typescript推荐规范
	"plugin:react-hooks/recommended", // React Hooks推荐规范
	"plugin:prettier/recommended" // 集成Prettier插件规范
],

stylelint

  • vscode安装插件

image.png

  • 安装
  • stylelint:styleLint 的核心模块
  • stylelint-config-standard:styleLint 官方推荐的配置规则
  • stylelint-config-prettier:该配置用于解决 StyleLint 和 Prettier 之间的规则冲突问题
npm i stylelint@14.9.1 stylelint-config-standard@26.0.0 stylelint-config-prettier@9.0.5 -D
  • 根目录新建 .stylelintrc.js .stylelintignore
//.stylelintrc.js
module.exports = {
	// 从标准配置中继承规则
	extends: ["stylelint-config-standard", "stylelint-config-prettier"],
	// 规则配置
	rules: {
		// 禁用注释前的空行规则
		"comment-empty-line-before": null,
		// 禁用声明前的空行规则
		"declaration-empty-line-before": null,
		// 指定函数名的大小写为小写
		"function-name-case": "lower",
		// 禁用选择器特异性递减规则
		"no-descending-specificity": null,
		// 禁用无效的双斜杠注释规则
		"no-invalid-double-slash-comments": null,
		// 指定规则前需要空行
		"rule-empty-line-before": "never"
	},

	// 忽略检查的文件或文件夹
	ignoreFiles: ["node_modules/**/*", "build/**/*"]
};

//.stylelintignore
/dist/*
/public/*
public/*
  • 规范css书写顺序
  • stylelint-order 该插件的作用是强制你按照某个顺序编写 css。例如先写定位,再写盒模型,再写内容区样式,最后写 CSS3 相关属性。这样可以极大的保证我们代码的可读性。
  • stylelint-config-rational-order 一套常见的css书写顺序
npm i stylelint-order@6.0.4 stylelint-config-rational-order@0.1.2 -D

.stylelintrc.js添加

extends: ["stylelint-config-standard", "stylelint-config-rational-order", "stylelint-config-prettier"],
	plugins: ["stylelint-order", "stylelint-declaration-block-no-ignored-properties"],

如果没有生效,重启vscode

GIF.gif

  • 报错自动修复错误 在./vscode/settings.json添加
  // 使用 stylelint 自身的校验即可
  "css.validate": false,
  "less.validate": false,
  "scss.validate": false,
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": "explicit",
    "source.fixAll.stylelint": "explicit"
  },
  • 验证保存下

GIF.gif

EditorConfig

EditorConfig和Prettier一样,都是用来配置格式化你的代码的,这个格式化代码,要和你lint配置相符!否则会出现你格式化代码以后,却不能通过你的代码校验工具的检验

EditorConfig 文件中的设置用于在基本代码库中维持一致的编码风格和设置,例如缩进样式、选项卡宽度、行尾字符以及编码等,而无需考虑使用的编辑器或 IDE

  • vscode安装 image.png
  • 根目录新建 .editorconfig 复制内容
root = true # 控制配置文件 .editorconfig 是否生效的字段

[**] # 匹配全部文件
indent_style = space # 缩进风格,可选space|tab
indent_size = 2 # 缩进的空格数
charset = utf-8 # 设置字符集
trim_trailing_whitespace = true # 删除一行中的前后空格
insert_final_newline = true # 设为true表示使文件以一个空白行结尾
end_of_line = lf

[**.md] # 匹配md文件
trim_trailing_whitespace = false

到此eslint+stylelint+prettier的代码规范,整理完成,我们继续

3.git规范

  • husky 操作 git 钩子的工具

  • lint-staged 本地暂存代码检查工具

  • commitlint commit 信息校验工具

  • commitizen 辅助 commit 信息 ,可以通过选择输入,规范提交信息

husky&lint-staged

  • 安装
npm i husky@8.0.1 lint-staged@13.0.2 -D
  • package.json添加
{
  "scripts": { 
    "prepare": "husky install" 
  },
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged"
    }
  },
}
  • 在根目录创建lint-staged.config.js,并添加内容
module.exports = {
	"*.{js,jsx,ts,tsx}": ["eslint --fix", "prettier --write"],
	"{!(package)*.json,*.code-snippets,.!(browserslist)*rc}": ["prettier --write--parser json"],
	"package.json": ["prettier --write"],
	"*.{scss,less,styl,css}": ["stylelint --fix", "prettier --write"],
	"*.md": ["prettier --write"]
};

  • 运行npm run prepare 会在根目录生成.husky目录
  • .husky目录下创建文件pre-commit,并添加以下内容
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx lint-staged

image.png

  • 提交下代码试试
git add -A
git commit -m '测试'

发现有个语句错误 image.png 变量a声明了未使用,删掉它并重新提交,最后push到远程仓库

commitlint

  • 安装
npm i -D @commitlint/cli@17.0.2 @commitlint/config-conventional@17.0.2
  • 根目录创建 commitlint.config.js
module.exports = {
	// 继承的规则
	extends: ["@commitlint/config-conventional"],
	// @see: https://commitlint.js.org/#/reference-rules
	rules: {
		"subject-case": [0], // subject大小写不做校验

		// 类型枚举,git提交type必须是以下类型
		"type-enum": [
			2,
			"always",
			[
				"feat", // 新增功能
				"fix", // 修复缺陷
				"docs", // 文档变更
				"style", // 代码格式(不影响功能,例如空格、分号等格式修正)
				"refactor", // 代码重构(不包括 bug 修复、功能新增)
				"perf", // 性能优化
				"test", // 添加疏漏测试或已有测试改动
				"build", // 构建流程、外部依赖变更(如升级 npm 包、修改 webpack 配置等)
				"ci", // 修改 CI 配置、脚本
				"revert", // 回滚 commit
				"chore" // 对构建过程或辅助工具和库的更改(不影响源文件、测试用例)
			]
		]
	}
};
  • .husky目录添加文件 commit-msg
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"

npx --no-install commitlint --edit $1
  • package.json添加
  "husky": {
    "hooks": {
      "pre-commit": "lint-staged",
      "commit-msg": "commitlint --config commitlint.config.js -E HUSKY_GIT_PARAMS"
    }
  },
  • 重新提交到git

image.png

  • 提交信息需要规范,type值需要commitlint.config.js定义的一种

image.png

commitizen&cz-git

  • 通过选择输入,并且自定义配置提交规范
  • 安装
npm i commitizen@4.2.4 cz-git@1.3.4 -D
  • package.json添加
  "config": {
    "commitizen": {
      "path": "node_modules/cz-git"
    }
  },
  • commitlint.config.js添加
module.exports = {
//...
	prompt: {
		messages: {
			type: "选择你要提交的类型 :",
			scope: "选择一个提交范围(可选):",
			customScope: "请输入自定义的提交范围 :",
			subject: "填写简短精炼的变更描述 :\n",
			body: '填写更加详细的变更描述(可选)。使用 "|" 换行 :\n',
			breaking: '列举非兼容性重大的变更(可选)。使用 "|" 换行 :\n',
			footerPrefixesSelect: "选择关联issue前缀(可选):",
			customFooterPrefix: "输入自定义issue前缀 :",
			footer: "列举关联issue (可选) 例如: #31, #I3244 :\n",
			generatingByAI: "正在通过 AI 生成你的提交简短描述...",
			generatedSelectByAI: "选择一个 AI 生成的简短描述:",
			confirmCommit: "是否提交或修改commit ?"
		},
		// prettier-ignore
		types: [
        { value: "feat",     name: "特性:     ✨  新增功能", emoji: ":sparkles:" },
        { value: "fix",      name: "修复:     🐛  修复缺陷", emoji: ":bug:" },
        { value: "docs",     name: "文档:     📝  文档变更", emoji: ":memo:" },
        { value: "style",    name: "格式:     💄  代码格式(不影响功能,例如空格、分号等格式修正)", emoji: ":lipstick:" },
        { value: "refactor", name: "重构:     ♻️  代码重构(不包括 bug 修复、功能新增)", emoji: ":recycle:" },
        { value: "perf",     name: "性能:     ⚡️  性能优化", emoji: ":zap:" },
        { value: "test",     name: "测试:     ✅  添加疏漏测试或已有测试改动", emoji: ":white_check_mark:"},
        { value: "build",    name: "构建:     📦️  构建流程、外部依赖变更(如升级 npm 包、修改 vite 配置等)", emoji: ":package:"},
        { value: "ci",       name: "集成:     🎡  修改 CI 配置、脚本",  emoji: ":ferris_wheel:"},
        { value: "revert",   name: "回退:     ⏪️  回滚 commit",emoji: ":rewind:"},
        { value: "chore",    name: "其他:     🔨  对构建过程或辅助工具和库的更改(不影响源文件、测试用例)", emoji: ":hammer:"},
      ],
		useEmoji: true,
		emojiAlign: "center",
		useAI: false,
		aiNumber: 1,
		themeColorCode: "",
		scopes: [],
		allowCustomScopes: true,
		allowEmptyScopes: true,
		customScopesAlign: "bottom",
		customScopesAlias: "custom",
		emptyScopesAlias: "empty",
		upperCaseSubject: false,
		markBreakingChangeMode: false,
		allowBreakingChanges: ["feat", "fix"],
		breaklineNumber: 100,
		breaklineChar: "|",
		skipQuestions: [],
		issuePrefixes: [{ value: "closed", name: "closed:   ISSUES has been processed" }],
		customIssuePrefixAlign: "top",
		emptyIssuePrefixAlias: "skip",
		customIssuePrefixAlias: "custom",
		allowCustomIssuePrefix: true,
		allowEmptyIssuePrefix: true,
		confirmColorize: true,
		maxHeaderLength: Infinity,
		maxSubjectLength: Infinity,
		minSubjectLength: 0,
		scopeOverrides: undefined,
		defaultBody: "",
		defaultIssues: "",
		defaultScope: "",
		defaultSubject: ""
	}
};

  • package.json 添加命令
    "commit": "git pull && git add -A && git-cz && git push"
  • 运行npm run commit,选择合适的选项,并填入信息,按回车并push到远程仓库

image.png

远程仓库显示对应的提交信息

image.png

4.完毕

代码在仓库