Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

构建可靠的前端项目 - 少不了这些必备工具集 #53

Open
qufei1993 opened this issue Jul 9, 2023 · 0 comments
Open

构建可靠的前端项目 - 少不了这些必备工具集 #53

qufei1993 opened this issue Jul 9, 2023 · 0 comments

Comments

@qufei1993
Copy link
Owner

构建可靠的前端项目少不了这些必备工具集: ESLint、Prettier、Editorconfig、Husky、lint-staged、commitlint,它能帮助我们约束编码风格与提及规范,下文介绍这些工具集的使用,文末提供一些可参考的项目。

ESLint 代码质量检测

ESLint 是 JavaScript/TypeScript 编程中一个最受欢迎的代码规范检查工具,可避免一些由于低级的代码错误而导致的项目崩溃,另一方面也能规范我们的编码习惯

ESLint 基本使用

为项目开发时依赖添加 eslint 插件 npm i eslint -D,紧接着我们需要设置一个配置文件,如果没有全局安装 eslint 插件,可能就需要这样去做 ./node_modules/.bin/eslint --init,我不喜欢这样,好在还有 npx 这个工具,我们可以这样做 npx eslint --init

执行 --init 命令后,可以选择使用的框架、模块类型、语言(JS/TS)等,随后会生成一个 .eslint{yml|js|json} 文件。

$ npx eslint --init
✔ How would you like to use ESLint? · problems
✔ What type of modules does your project use? · esm
✔ Which framework does your project use? · react
✔ Does your project use TypeScript? · No / Yes
✔ Where does your code run? · browser
✔ What format do you want your config file to be in? · YAML

ESLint 支持 JavaScript、JSON 或者 YAML 等多种文件格式,这里使用的是 YAML 格式,相比于 JSON 可以写一些注释。

env:
  browser: true
  es2021: true
extends:
  - eslint:recommended
  - plugin:react/recommended
  - plugin:@typescript-eslint/recommended
parser: '@typescript-eslint/parser'
parserOptions:
  ecmaFeatures:
    jsx: true
  ecmaVersion: 13
  sourceType: module
plugins:
  - react
  - '@typescript-eslint'
rules: {}

对上述文件几个配置做一些简单介绍:

  • extends:这是一个扩展,可以集成一些社区的最佳实践,在其基础之上做一些自定义配置,下文我们介绍如何使用 airbnb。
  • **plugins:**这里是用来加载第三方插件,在使用之前必须 NPM 安装它。
  • parserOptions:指定想要支持的 JavaScript 语言
    • ecmaFeatures:想使用的额外的语言特性
      • jsx:启用 JSX
  • rules:指定了代码检查的规则,参考 ESLint 官网 Rules, 规则分为几个等级,用来表示严重程度:
    • 关闭:'off' 或 0,
    • 警告级别:'warn' 或 1,不会导致程序退出
    • 错误级别:'error' 或 2,会导致程序退出。

有了以上配置后,就可以执行 npx eslint ./your_file_or_directory 命令检测指定的文件或目录。

忽略特定文件或目录

在项目根目录创建 .eslintignore 文件告诉 ESLint 忽略掉某些特定的文件或目录。忽略模式可以参照 .gitignore 规范

# dependencies
/node_modules
# testing
/coverage
# production
/build

站在巨人的肩膀上 - 制定编码规范

怎么设计一套好的编码规范呢?也无需重头造轮子,借助一些社区开源的编码规范最佳实践,可以直接引入在我们的项目中,再根据自己的需求做一些定制。

我们这里以 Airbnb 为基础,做一些自定义的修改,参考 Airbnb JavaScript 风格指南

安装 eslint-config-airbnb npm i eslint-config-airbnb -D,如果你不需要 React,可以参考 eslint-config-airbnb-base 这个包,在 Node.js 服务端可以使用这个包。之后添加 "extends": "airbnb" 到你的 .eslintrc 文件。

env:
  browser: true
  es2021: true
extends:
  - airbnb
  - plugin:@typescript-eslint/recommended
  - plugin:react/jsx-runtime # React v17 需添加
parser: '@typescript-eslint/parser' # 解析 TS
parserOptions:
  ecmaFeatures:
    jsx: true
  ecmaVersion: 13
  sourceType: module
plugins:
  - react
  - '@typescript-eslint'
settings:
  import/resolver:
    node:
      # https://github.com/import-js/eslint-plugin-import#resolvers
      extensions:
        - ".js"
        - ".jsx"
        - ".ts"
        - ".tsx"
rules: {
  # "@typescript-eslint/explicit-module-boundary-types": 2,
  no-console: 0,

  # https://stackoverflow.com/questions/55614983/jsx-not-allowed-in-files-with-extension-tsxeslintreact-jsx-filename-extensio
  react/jsx-filename-extension: [
    2,
    {
      extensions: ['.js', '.jsx', '.ts', '.tsx']
    }
  ],

  import/extensions: 0,

  # https://stackoverflow.com/questions/69928061/struggling-with-typescript-react-eslint-and-simple-arrow-functions-components
  react/function-component-definition: 0,

  comma-dangle: 0
}

IDE 自动提示

每改动代码都运行 npx eslint ./ 命令执行代码检测,效率还是有点低效的,我们希望改动代码后,IDE 能够给我们自动提醒格式错误的代码片段。

在 VS Code 中,需要在应用商店安装 ESLint 插件。
image.png
当我没有遵守 ESLint 规范去修改代码后,编辑器会及时给我们一些错误提示。
image.png

Prettier 代码格式化

在使用 ESLint 时,另一个被搭配使用的工具是 Prettier,这是一个专业的代码格式化工具,支持 JS、TS、HTML、CSS 等众多语言 。

Prettier 基本使用

开发时依赖安装 Prettier,并创建一个空的配置文件,让编辑器和其它工具知道正在使用 Prettier。

$ npm install --save-dev --save-exact prettier
$ echo {}> .prettierrc.json

创建 .prettierignore 文件,让编辑器和 Prettier CLI 知道哪些文件不需要格式化,它建立在 .gitignore .eslintignore 之上。

$ touch .prettierignore

# Ignore artifacts:
build
coverage

手动触发

命令行执行 **npx prettier --write .** 手动触发,对于一个大项目来说可能会花费一些时间,因此还可以指定目录或文件来格式化。

npx prettier --write . # 格式化当前目录下的所有文件
npx prettier --write src/ # 格式化指定目录下的所有文件
npx prettier --write src/App.tsx # 格式化指定的文件

有了 ESLint 为什么还要使用 Prettier

ESLint 提供了 eslint --fix 命令用来自动修复代码格式问题,为什么还要用 Prettier 呢?

如果我说在代码格式化方面 Prettier 比 ESLint 更专业,你可能也不会信服,毕竟没有证据来证明这一点。下面我将通过一个例子来验证。

下面这段 JavaScript 代码片段,存在两个问题,一个是 console.log(...args) 没有缩进,另外一个是 createData() 这一行超出了最大长度限制。

const createData = (...args) => {
console.log(...args);
}
createData({ name: 'name', age: 'age', sex: 'sex', birthday: 'birthday', ctime: 'ctime', utime: 'utime' })

调整 ESLint 行的最大长度限制。

# .eslintrc.yml
rules: {
  max-len: [2, { code: 80 }]

随后控制台执行 eslint --fix 自动修复命令,发现虽然 console.log(...args) 这个缩进问题解决了,但是行的最大长度限制 max-len 还是未能解决。

image.png

接下来看看 Prettier 的效果如何呢,修改配置文件同 ESLint 保持一致。

// .prettierrc.json
{
  "printWidth": 80
}

运行 prettier --write 命令后,它格式化了我们的代码,结果是我们预期的。

image.png

解决 ESLint 与 Prettier 冲突

ESLint 与 Prettier 之间存在交集部分,一起使用就少不了冲突。例如,我的 ESLint 配置规则继承了 airbnb,而 airbnb 字符串默认使用单引号,而 Prettier 字符串默认使用双引号

// src/App.tsx
const str = "编程界";

// .eslintrc.yml
extends:
  - airbnb

// .prettierrc.json
{
	"singleQuote": false // 字符串默认使用双引号
}

在第一次执行 eslint --fix 格式化后的 str 为 const str = '编程界'; 帮我修复了这个格式问题,但是又运行了 Prettier 格式化后,ESLint 提示我们错误又出现了,这样就陷入了一个死循环。
image.png

正所谓术业有专攻,我们让 ESLint 专注于代码质量检测工作,Prettier 负责代码的格式化工作

解决方案第一步:开发时依赖安装 eslint-config-prettier **npm i eslint-config-prettier -D** 插件,目的是禁用掉 ESLint 插件与 Prettier 之间冲突的规则

# .eslintrc.yml
# extends 添加 prettier,此时冲突就已经解决了
extends:
  - airbnb
  - prettier # eslint-config-prettier

解决方案第二步:赋予 eslint --fixPrettier 规则格式化代码的能力,并以 ESLint 的方式报告错误。开发时依赖安装 eslint-plugin-prettier npm i eslint-plugin-prettier -D 插件,按照以下规则配置。

# // .eslintrc.yml
plugins:
  - prettier # eslint-plugin-prettier
rules: {
  prettier/prettier: 2, # 赋予 ESLint 以 Prettier 规则格式化代码的能力

上面的两步操作也可以写为一步,也是官方推荐的一个 https://github.com/prettier/eslint-plugin-prettier#recommended-configuration

extends:
  - plugin:prettier/recommended

Prettier 与 ESLint 结合在 VS Code 中发现的一个提示问题

当把 prettier 结合到 eslint 里时,usePrettierrc 这个参数表示是否使用 .prettierrc 文件中配置信息,默认是启用的,无需配置。

# .eslintrc.yml
prettier/prettier: ["error", {}, {
  "usePrettierrc": true # 默认值
}]

prettier 规则默认箭头函数如果只有一个参数也要加上 (),我想去掉这个规则,发现这个配置在 VSCode 展示有点问题无法读取到修改后的 arrowParens 这个配置(其它配置例如 singleQuote,在 IDE 中是生效的), 编辑器会提示错误,但 eslint --fix 时没有问题(此时证明 配置没问题,手动执行 eslint 还是生效的)

# .prettierrc
{
	"arrowParens": "avoid",
  "singleQuote": true
}

image.png

没有好的解决方案,为了让 IDE 不报错,首先尝试在 ESLint 配置里加上 arrowParens 这个配置,发现问题解决了,但是这样 ESLint prettier 配置中、.prettierrc 要写两遍总归不是太好

另外一种是不在 ESLint 配置里加上 arrowParens 这个配置,编辑器关闭重新打开也是没问题的,反复的试验了下,发现改完 .prettierrc 这个配置文件,VS Code 编辑器提示不会立即生效还默认是打开前的配置,如果有遇到类似问题,可以尝试关闭下再打开看看

prettier/prettier: ["error", {
	"arrowParens": "avoid"
}, {
  "usePrettierrc": true # 默认值
}]

Editorconfig 编辑器代码风格

Editorconfig 是用来维护不同编辑器之间代码风格,通过配置文件 .editorconfig 设置,该配置文件的优先级会更高,会覆盖任一编辑器的配置。

root = true

[*]
indent_style = space # 缩进使用空格
indent_size = 2 # 每一个缩进两个空格
end_of_line = lf # 使用 Unix 风格的换行符
insert_final_newline = true # 每个文件尾部都有一个新的换行符

# 为 JavaScript、Python 文件设置默认字符集
[*.{js,py}]
charset = utf-8

Husky 拦截 git 操作命令

团队中既然制定了规则,大家就要共同遵守,如果不加以限制,别人改动代码后不做 ESLint 检查,或者提交代码忘记执行代码检查命令,就会导致未经检测的代码被提交进项目仓库,代码规则的制定也就形同虚设了。

使用 git 版本控制的项目中,默认都有一个 .git/hooks 目录,包含了 git 在 commit、push 时的一些 hooks 脚本,结合这些可以做一些代码的校验操作。

$ ls .git/hooks
applypatch-msg.sample           pre-commit.sample               prepare-commit-msg.sample
commit-msg.sample               pre-merge-commit.sample         push-to-checkout.sample
fsmonitor-watchman.sample       pre-push.sample                 update.sample
post-update.sample              pre-rebase.sample
pre-applypatch.sample           pre-receive.sample

使用 husky 工具,可以将 git hooks 的使用变得更加简单,同时也能解决,.git 目录提交无法跟踪的问题。

在 husky 最新版本中,会生成一个 .husky/ 目录,以往的版本中是在 package.json 中配置一个 { "husky": {"hooks": {} } } 实现的。

Husky 推荐在 package.json 的 scripts 里设置 prepare,因为在执行 npm install 命令时,将会自动执行 prepare 脚本。

注意在 npm version < 7 时 npm set-script 命令不支持,需要手动在 package.json 设置 { "scripts": { "prepare": "husky install" } } 或升级 Node.js 版本为 v16.x LTS。

$ npm install husky -D
$ npm set-script prepare "husky install"
$ npm run prepare # 设置 .husky 目录

添加一个 hook,在 git commit 前执行 eslint 命令检测代码规范。

$ npm set-script lint "eslint ./src/"
$ npx husky add .husky/pre-commit "npm run lint" # 设置在 git commit 前执行 eslint 代码检查

现在执行 git commit 命令如果存在不规范的代码,本次 commit 就会被拦截。

image.png

如果本次我只修改了 src/index.tsx 这个文件,由于**历史原因最开始没有加 ESLint 规范,之前的一些未修改的文件也被检查了,当看到成百上千条 ESLint 报错提示,是不是会出现想收回使用 ESLint 工具的想法?**这种问题在团队开发中,如果一开始没有设定代码规范,后期加入代码规范时,这种问题还是会常见的,解决方案继续往下看。

lint-staged 仅检查修改的文件

每次只对修改的文件做检查,就要用到 lint-staged 这个工具了,解决老项目的历史包袱问题。

lint-staged 是对 git add 进入暂存区的文件进行检查,开发时依赖安装 npm i lint-staged -D,修改 package.json 文件。

注意,**npx eslint --fix**** 后不要指定路径,例如 **npx eslint --fix ./src/** 会出现只修改了 A 文件,但是其它有问题的 B、C 等文件也被检测了**。

// package.json
{
  "srcripts": {
  	"precommit": "lint-staged"
  },
  "lint-staged": {
    "*.{js,css,md,ts,tsx}": [
      "npx eslint --fix"
    ]
  }
}

修改 .husky 目录下的 pre-commit 文件。执行的流程是:

  • git commit 命令执行后 git hook 被触发
  • 进而执行 precommit、该命令脚本又执行了 lint-staged
  • lint-staged 则根据配置和只对 git add 到暂存区的文件进行扫描,执行 eslint 代码检测。
#!/bin/sh
. "$(dirname "$0")/_/husky.sh"

npm run precommit

commitlint 规范提交风格

关于 commit 如果没有进行规范,最终每个人写出来的风格也是不一样的,所以这个还得借助于工具,帮助我们规范提交的习惯。

分享两个 git commit message 的讨论:Stackoverflow - Git Commit Messages: 50/72 Formatting知乎 - 如何写好 Git commit log?

commitlint

安装 commitlint cli 和提交信息校验规则 @commitlint/config-conventional,详细的参考文档 commitlint.js.org

# 安装 commitlint cli and conventional config
$ npm install --save-dev @commitlint/{config-conventional,cli}

# 创建 commitlint.config.js 配置文件
# @commitlint/config-conventional 是推荐的 commit message 校验配置,
$ echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js

# husky 添加 commit-msg 文件
$ npx husky add .husky/commit-msg 'npx --no -- commitlint --edit "$1"'

使用格式 git commit -m '<type>[Optional scope]: <description>',介绍一下 type 属性的几种类型约束。

  • build:修改了构建系统,例如前端的 Webpack 构建工具
  • chore:
  • ci:持续集成工具的修改,例如 Jenkins
  • docs:文档
  • feat:新功能
  • fix:Bug 修复,这个用的就比较多了~
  • perf:做的一些性能提升
  • refactor:重构
  • revert:回滚之前某个 commit
  • style:代码格式的修改,例如通过 Prettier 格式化代码,但不会影响逻辑
  • test:测试

如果我这样 git commit -m '添加 commitlint 工具' 提交代码,就会收到以下错误提示。

image.png

正确的做法是加上类型信息 git commit -m 'feat: 添加 commitlint 工具'

commitzen

使用 commitlint 每次都需要输入类型,需要我们记住这些规范,于是就有了 [commitizen](https://github.com/commitizen/cz-cli)

全局安装 npm install -g commitizen,之后可以使用 git cz 替代 git commit 命令。还需要为 commitizen 指定一个 Adapter,例如 cz-conventional-changelog 基于 commit message 的轻量级约定,提供了一组简单的规则来创建明确的提交历史,参考 约定式提交

$ commitizen init cz-conventional-changelog --save-dev --save-exact

执行以上命令后,在 package.json 中会生成如下配置

{
	"config": {
    "commitizen": {
      "path": "./node_modules/cz-conventional-changelog"
    }
  }
}

对文件做一些任意修改,执行 git cz 命令后,运行效果如下所示:

image.png

当选择一个类型后,终端会出现一个交互式的操作,包括:影响范围简短的变更描述(必须)详细的变更描述是否存在不兼容的变更是否影响某些打开的 Issues,这个里面只有简短的变更描述是必填的。

image.png

commit message 成功后,执行 git log 看到的日志是这样子的。

image.png

如果想自定义 Adapter 而不是使用 cz-conventional-changelog,推荐看下 cz-customizable 这个开源项目,这里不再过多描述。

standard-version

结合 standard-version 实现自动生成 CHANGELOG。

$ npm i --save-dev standard-version

// package.json
{
  "scripts": {
    "release": "standard-version"
  }
}

以上介绍内容,实际应用可参考这个前后端项目 https://github.com/qufei1993/compressor,不同的是该项目是基于 TypeScript 编写,正好也可以学习下。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant