Skip to main content

前端工程化实践

前言本

本人现为一个学生身份, 美梦是作为基础建设的架构师, 本文意为编写一个通用的前端工程的模板, 没有经过一个长久的实践, 本文可能写的比较粗糙, 有错误和改进请友善提出, 编写与审核均为本人, 可能有文字描述错误, 不清晰等问题

说明

使用Husky + Oxlint + ESLint + StyleLint + Prettier + Commitlint 作为基础设施库

  • Husky: Git hooks工具,通过配置一系列钩子,可以在git操作的不同阶段执行相应的命令, 这里使用它在代码提交到仓库前的检查, 使用使用Oxlint快速检索错误,然后执行ESLint检查代码规范与Prettier进行代码格式化输出, 统一代码规范
  • Oxlint: 现阶段只有Lint功能开发比较能用, 对于大型项目, 在CI时使用它会运行的比较快
  • ESLint: TS/TSX/Vue等代码的代码样式限制与格式化与修复
  • Prettier: TS/TSX/Vue/Markdown等文件的代码样式格式化
  • StyleLint: CSS/SCSS/Less等文件的排序,格式化
  • Commitlint: git commit时的规则校验

本文均使用pnpm包管理器, 但是部分包含npm的使用方法, 在这里它们的区别可能就在于指令的用法不同

Husky

作用

Git hooks工具,通过配置一系列钩子,可以在git操作的不同阶段执行相应的命令

官方文档

  1. 安装

如果.git目录不是在前端项目里,如:

. 
├── .git/
├── backend/ # No package.json
└── frontend/ # Package.json with husky

则需要修改prepare的内容, 不使用pnpm exec husky init, 需要手动在package.json修改为:

"prepare": "cd .. && husky frontend/.husky"

如果手动修改了scriptsprepare,需要重新执行一次

pnpm prepare

注意, 如果.git目录不是在前端项目里, 那么pre-commit也要修改进入到对应的目录, 例如:

有这样的目录结构:

. 
├── .git/
├── backend/ # No package.json
└── frontend/ # Package.json with husky

那么, 对应的pre-commit文件内容就应该这样写:

cd frontend

pnpm test

Oxlint

定义

检查文件是否按照定义的规则进行编写

在工程化的作用(扮演的角色)

它现阶段不能直接代替ESLint, 它没有修复代码的功能, 只是它使用了Rust编写的工具, 运行很快, 现阶段, 作者推荐在CI阶段时运行在ESLint之前,这样,大部分常见问题还没走到 Eslint 这一步就被 Oxlint拦截, 大大减少大型项目的CI流水线的运行时间

https://oxc-project.github.io/docs/guide/usage/linter.html

pnpm add -D oxlint

Usages  用法

  • npx oxlint@latest --rules 以获取规则列表。
  • npx oxlint@latest --help 以获取配置说明

有用的选项和示例

  • --deny-warnings 将警告转换为错误,对于退出代码为 1 的 CI 失败很有用。
  • -D all 拒绝(打开)所有规则。
  • -A all -D eqeqeq 运行单个规则。
  • -D correctness -D perf -A debugger -A eqeqeq 拒绝(打开)和规则,并允许(关闭) correctness debugger 和 eqeqeq perf 规则。
  • -c ./eslintrc.json 使用该 rules 字段配置规则,如 ESLint 中所述。仅 json` 支持格式

在package.json添加:

pnpm pkg set scripts.lint-check="oxlint"

如下效果:

{
...
"scripts": {
...
"lint-check": "oxlint",
...
},
...
}

.husky/pre-commit添加

cat >> .husky/pre-commit <<EOF
pnpm lint-check
EOF

检查:

pnpm lint-check

lint-staged

作用

在提交代码前进行lint检查时,可以让lint只检查git暂存区(staged)的文件,而不会检查所有文件

pnpm add --save-dev lint-staged

package.json

{
"lint-staged": {
"src/**/*.{ts,tsx,js,jsx}": [
"oxlint"
]
},
}

ESLint

  1. 安装
pnpm create @eslint/config

or

npm init @eslint/config
  1. 添加配置
  • --cache:该参数启用了 ESLint 的缓存功能,以提高重复运行 ESLint 时的性能。ESLint 将会缓存 lint 结果和文件状态,从而避免不必要的重新检查。
  • --max-warnings 0:此参数规定了允许的最大警告数量。在这个例子中,设置为 0 表示如果有任何警告产生,将被视为错误而导致命令失败。
  • src:指定需要进行 ESLint 检查的目录或文件。在这里,它表示对 "src" 目录进行检查。
  • --ext ts,tsx:这个参数指定了 ESLint 将要检查的文件扩展名。在这里,它表示 ESLint 将检查扩展名为 ".ts" 和 ".tsx" 的文件。
  • --fix:该参数告诉 ESLint 尝试自动修复发现的问题。如果某些问题可以被自动修复,ESLint 将会尝试修改源代码以修复这些问题。
pnpm pkg set scripts.eslint="eslint src/**/**{.ts,.tsx}"

pnpm pkg set scripts.eslint:fix="eslint --cache --max-warnings 0 src --ext ts,tsx --fix"
  1. 添加忽略格式化的目录
echo node_modules > .eslintignore 
  1. 检查是否正常运行
pnpm eslint
pnpm eslint:fix

添加扩展插件, 对库进行更严格的限制, 我使用React, 需要再在.eslintrc.js加上配置,指定自动检测选择当前安装的版本:

js

复制代码

例如

在`.eslintrc.js`加上配置,指定自动检测选择当前安装的版本:

js

复制代码

`react: { pragma: 'React', version: 'detect' }`

Prettier

  1. 安装
  • --save-exact 标志用于确保安装的包版本是精确匹配的
pnpm install -D --save-exact prettier
  1. 创建配置文件 创建一个prettier配置文件,并写入基本的配置:
cat > .prettierrc.json << EOF
{
"semi": true,
"tabWidth": 2,
"singleQuote": true,
"printWidth": 80,
"trailingComma": "all",
"endOfLine": "lf"
}
EOF
  1. 创建prettier忽略文件(eslint 默认会忽略 node_modules):
echo node_modules > .prettierignore
  1. 解决prettier规则与ESLint规则的冲突
pnpm add -D eslint-config-prettier

因为ESLint支持多种配置文件, 本文使用yml形式来保存配置, 编辑.eslintrc.yml: 在extends添加prettier

extends:
- prettier

安装和配置eslint-plugin-prettier插件以便将Prettier作为ESLint规则运行,并将差异报告为单个ESLint问题:

pnpm add -D eslint-plugin-prettier

.eslintrc.yml文件中添加配置:

rules: {
prettier/prettier: error
}
  1. package.jsonlint-staged选择一个规则进行添加:
  • "**/*": "prettier --write --ignore-unknown": 让可识别的文件都使用prettier进行格式化
  • "*.{ts,tsx,js,jsx,cjs,mjs,vue,css,scss,less,md}": "prettier --write": 针对特定的文件类型进行格式化

示例:

pnpm pkg set lint-staged."**/*"="prettier --write --ignore-unknown"

如下效果:

{
"lint-staged": {
"*.{ts,tsx,js,jsx,cjs,mjs,vue,css,scss,less,md}": "prettier --write --ignore-unknown",
"**/*": "prettier --write --ignore-unknown"
},
}

Commitlint

  1. 安装
  • mac:
pnpm add -D @commitlint/{cli,config-conventional}
  • windows
npm install --save-dev @commitlint/config-conventional @commitlint/cli
  1. 创建配置
  • 官方默认配置:
echo "export default { extends: ['@commitlint/config-conventional'] };" > commitlint.config.js
  • 本人使用的配置:
cat > commitlint.config.js <<EOF
export default {
extends: ['@commitlint/config-conventional'],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release'
],
],
}
EOF

commitlint.config.js文件内容如下:

export default {
extends: ['@commitlint/config-conventional'],
'type-enum': [
2,
'always',
[
'feat',
'fix',
'perf',
'style',
'docs',
'test',
'refactor',
'build',
'ci',
'chore',
'revert',
'wip',
'workflow',
'types',
'release'
],
],
}
  1. 添加git commit hook钩子

注意, 如果.git目录不是在前端项目里, 那么commit-msg也要修改进入到对应的目录, 例如:

有这样的目录结构:

. 
├── .git/
├── backend/ # No package.json
└── frontend/ # Package.json with husky

那么, 对应的commit-msg文件内容就应该这样写:

cd frontend

pnpm commitlint ${1}
  • pnpm:
echo "cd frontend && pnpm commitlint --edit \$1" > .husky/commit-msg
  • npm:
echo "cd frontend && npx --no -- commitlint --edit \$1" > .husky/commit-msg

或者: 作为替代方法,在package.json 创建脚本

npm pkg set scripts.commitlint="commitlint --edit"
echo "pnpm commitlint \${1}" > .husky/commit-msg

还是那句话, 如果.git目录不是在前端项目里, 那么commit-msg也要修改进入到对应的目录

默认生成的.husky/commit-msg文件是这样:

pnpm commitlint ${1}

你需要添加:

cd frontend

...
  1. 测试

测试失败示例:

git commit -m "foo: add commit-msg hook"

预期会输出错误:

> [email protected] commitlint /Users/lisa/Public/Golang/src/2024/edu-system/frontend
> commitlint --edit ".git/COMMIT_EDITMSG"

⧗ input: foo: add commit-msg hook
✖ type must be one of [build, chore, ci, docs, feat, fix, perf, refactor, revert, style, test] [type-enum]

✖ found 1 problems, 0 warnings
ⓘ Get help: https://github.com/conventional-changelog/commitlint/#what-is-commitlint

 ELIFECYCLE  Command failed with exit code 1.
husky - commit-msg script failed (code 1)

Pasted image 20240317024548.png

测试成功示例:

git commit -m "feat: add commit-msg hook"

Pasted image 20240317024605.png

配置参考

package:

pnpm add -D husky 
pnpm add -D lint-staged
pnpm add -D oxlint
pnpm create @eslint/config
pnpm add -D eslint-config-prettier eslint-plugin-prettier
pnpm add -D --save-exact prettier
pnpm add -D @commitlint/{cli,config-conventional}

package.json:

{
"lint-staged": {
"*.{ts,tsx,js,jsx,cjs,mjs,vue}": "eslint --fix",
"**/*": "prettier --write --ignore-unknown"
},
"scripts": {
"prepare": "cd .. && husky frontend/.husky",
"prettier:check": "prettier --check src/**/**{.cjs,.mjs,.ts,.tsx,.html,.md}",
"prettier:write": "prettier --write src/**/**{.cjs,.mjs,.ts,.tsx,.html,.md}",
"lint": "eslint src/**/**{.ts,.tsx}",
"lint:fix": "eslint --cache --max-warnings 0 src --ext ts,tsx --fix",
"lint:check": "oxlint",
"lint-staged": "lint-staged",
"commitlint": "commitlint --edit"
},
}

.husky/pre-commit:

cd frontend

pnpm lint-staged

.husky/commit-msg:

cd frontend

pnpm commitlint ${1}

.vscode/settings.json:

{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file"
}

IDE支持

VSCode

为了简化代码格式化操作,需要在VSCode保存时自动格式化代码,可以在.vscode/settings.json文件添加如下配置

mkdir -p .vscode && cat > .vscode/settings.json << EOF
{
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"editor.formatOnSaveMode": "file"
}
EOF

后续文章

  1. 霞的云原生之旅: 后端工程化
  2. 霞的云原生之旅: 运维工程化

参考

  1. commitlint
  2. prettier
  3. eslint
  4. oxc