前言
你是否遇到过以下的问题:
- 维护老项目,格式化代码出现几千行变更?
- 不能自动格式化,保存代码需要手动操作?
- commit时不能自动格式化,代码CR时全是问题?
- 不知道如何配置代码检验、格式化代码规范?
- 团队commit信息不规范,经常看到 "fix" 这样的commit msg?
- commit时不知道开头有哪些,只会"feat"、"fix"?
本篇文章带大家全部搞定~
不管是多人合作还是个人项目,代码规范是很重要的。在项目初始化时配置好规范,这样做不仅可以很大程度地避免基本语法错误、保证代码的可读性,也会为后续省去很多的麻烦。
前端工程流配置是前端工程师必须掌握的一项技能,本篇文章详细讲述了一个项目从0开始搭建,会涉及到哪些规范,如何使用、配置、以及可能遇到的问题。
本文使用到的技术:
create-react-app
eslint
prettier
stylelint
tslint
commitlint
husky
lint-stage
\
使用create-react-app创建一个项目:
create-react-app Lint-Demo --template typescript
代码检查工具
Eslint
介绍:一个插件化的javascript代码检测工具;
功能:每次保存,vscode就能标红不符合ESLint规则的地方,同时还会做一些简单的自我修正;
安装:
- 编辑器安装eslint插件
- 安装eslint包(Node.js >=6.14, npm version 3+)
npm install eslint --save-dev
- 紧接着你应该设置一个配置文件:
./node_modules/.bin/eslint --init
或
npx eslint --init
注:npx是什么?
npx附带npm,它允许你运行本地安装的包(不是全局),它能让项目内部安装的模块用起来更方便;
这是全部的配置项:
接下来,eslint会帮我们在项目根目录创建一个.eslintrc.js 文件,eslint会帮我们完成一些基础的配置:
注:.eslintrc.js 与.eslintrc.json功能一致,二取一即可;
module.exports = {
"env": {
"browser": true,
"es2021": true
},
"extends": [
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended"
],
"overrides": [
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": "latest",
"sourceType": "module"
},
"plugins": [
"react",
"@typescript-eslint"
],
"rules": {
}
}
如果需要自定义校验规则,可以参考eslint.bootcss.com/docs/user-g…;
\
- 之后,你可以在任何文件或目录上运行ESLint,如下:
./node_modules/.bin/eslint yourfile.js
\
- 当然也可以放在package.json的script作为脚本来运行:
"scripts": {
"lint": "eslint --ext .js,.ts,.tsx src/"
},
代码风格工具
prettier
介绍:一个代码格式化工具;
功能: 按下ctrl+s,代码自动格式化;规范团队代码风格;
安装:
- 安装eslint包
npm install --save-dev --save-exact prettier
- 然后,创建一个空配置文件,让编辑器和其他工具知道你正在使用 Prettier:
echo {}> .prettierrc.json
- 接下来,创建一个.prettierignore文件,让 Prettier CLI 和编辑器知道哪些文件不能格式化。
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
pnpm-debug.log*
lerna-debug.log*
node_modules
dist
dist-ssr
*.local
stats.html
- 现在,可以使用命令格式化所有文件:
npx prettier --write .
- 设置编辑器
每次都通过命令行格式化会很麻烦,可以通过在编辑器中运行 Prettier 来充分利用 Prettier,无论是通过键盘快捷键还是在保存文件时自动运行。
-
- 安装vscode的prettier插件
- 首选项-设置,设定编辑器默认代码格式化的插件为Prettier
- 设定Prettier插件保存时自动格式化代码,搜索设置项贴入editor.formatOnSave,将搜索到的项目打钩即可
- 处理Eslint与prettier的冲突
具体配置原因请看这里:github.com/prettier/es…
安装eslint-config-prettier以使 ESLint 和 Prettier 相互配合。它会关闭所有不必要或可能与 Prettier 冲突的 ESLint 规则。Stylelint 有一个类似的配置:stylelint-config-prettier;
npm i eslint-config-prettier eslint-plugin-prettier -D
还要在.eslintrc 的 plugins中添加 "prettier" :
{
"extends": ["prettier"],
"plugins": ["prettier"],
"rules": {
"prettier/prettier": "error",
"arrow-body-style": "off",
"prefer-arrow-callback": "off"
}
}
虽然我们已经要求项目使用eslint了,但是不能保证组员提交代码之前都将eslint中的问题解决掉了,所以我们需要在组员执行 git commit 命令的时候对其进行校验,如果不符合eslint规范,那么自动通过规范进行修复。我们可以借助husky的能力做到这一点。
Git检查工具
husky
介绍:husky是一个为 git 客户端增加 hook 的工具。作用就是在git执行一些操作的时候触发一些钩子,在钩子处执行一些自己需要的命令,比如代码的eslint校验、格式化代码等。最佳实践一般是校验两处,eslint(代码规范)及commit message规范
安装:
- 安装依赖
npm i lint-staged husky -D
- 添加script
"scripts": {
"prepare": "husky install",
},
高版本的husky是需要你使用.husky下面的hooks脚本,不会在自己调用.git hooks脚本,生成.husky脚本很简单。执行命令husky install即可。但是由于husky是局部安装的,所以一般都是写一个script命令,然后执行npm run prepare如下:
- 执行prepare命令 ,会在项目跟目录下创建 .husky/文件夹,用来存放所有的git hooks。
npm run prepare
- 添加commit msg
npx husky add .husky/commit-msg 'npm run commitlint --edit "$1"'
- 自定义git提交规范
npm i @commitlint/cli @commitlint/config-conventional -D
项目根目录下创建 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", "cz"],
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 commit -m 'feat: 增加 xxx 功能' 错误type将无法提交。
\
正常情况下如果这么设置"pre-commit: eslint src/*.js",检查的是全部文件,有点浪费性能。所以出现了lint-staged:
lint-staged
介绍: 仅仅过滤出 Git 代码暂存区文件(被 git add 的文件)的工具;他只会检查我们这一次提交的文件,大大提高了性能,
项目集成:
- 安装依赖
npm i lint-staged -D
- 在package中添加命令
{
"script": {
"lint:lint-staged": "lint-staged"
}
}
- 添加lint-stage配置
方法一:在package.json中添加
方法二:创建.lintstagedrc 文件
方法三:创建lint-staged.config.js 文件并进行配置
这里以.lintstagedrc 为例:
{
"*.{js,jsx,ts,tsx}": ["npx prettier --write .", "eslint --fix"],
"*.md": ["prettier --write"]
}
\
- 修改husky的配置脚本
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
npm run lint:lint-staged
这样,在 commit 之前,会将暂存区的内容做一次 代码检查 和 代码美化,然后再添加到暂存区;然后再 commit。
通常我们的git commit会按照统一的风格来提交,这样可以快速定位每次提交的内容,方便之后对版本进行控制,我们可以使用commitizen辅助我们:
辅助提交工具
commitizen
介绍:
集成:
- 安装依赖
npm i commitizen cz-conventional-changelog -D
- 安装指令和命令行的展示信息
在package.json 中添加 commit 指令, 执行 git-cz 指令
npm set-script commit "git-cz"
- 编写commit指令,初始化命令行的选项信息,不然没有选项
npx commitizen init cz-conventional-changelog --save-dev --save-exact
- 增加 .cz-config.js
'use strict'
module.exports = {
types: [
{ value: '✨新增', name: '新增: 新的内容' },
{ value: '🐛修复', name: '修复: 修复一个Bug' },
{ value: '📝文档', name: '文档: 变更的只有文档' },
{ value: '💄格式', name: '格式: 空格, 分号等格式修复' },
{ value: '♻️重构', name: '重构: 代码重构,注意和特性、修复区分开' },
{ value: '⚡️性能', name: '性能: 提升性能' },
{ value: '✅测试', name: '测试: 添加一个测试' },
{ value: '🔧工具', name: '工具: 开发工具变动(构建、脚手架工具等)' },
{ value: '⏪回滚', name: '回滚: 代码回退' }
],
scopes: [
{ name: 'leetcode' },
{ name: 'javascript' },
{ name: 'typescript' },
{ name: 'Vue' },
{ name: 'node' }
],
// it needs to match the value for field type. Eg.: 'fix'
/* scopeOverrides: {
fix: [
{name: 'merge'},
{name: 'style'},
{name: 'e2eTest'},
{name: 'unitTest'}
]
}, */
// override the messages, defaults are as follows
messages: {
type: '选择一种你的提交类型:',
scope: '选择一个scope (可选):',
// used if allowCustomScopes is true
customScope: 'Denote the SCOPE of this change:',
subject: '短说明:\n',
body: '长说明,使用"|"换行(可选):\n',
breaking: '非兼容性说明 (可选):\n',
footer: '关联关闭的issue,例如:#31, #34(可选):\n',
confirmCommit: '确定提交说明?(yes/no)'
},
allowCustomScopes: true,
allowBreakingChanges: ['特性', '修复'],
// limit subject length
subjectLimit: 100
}
- package.json 中,将原来commit配置,变更为自定义配置
"config": {
"commitizen": {
"path": "./node_modules/cz-customizable"
}
}
- 然后提交会变成这样
\
附录
仓库地址:
\
\