注意事项
本文默认开发环境是 VSCode,团队内尽可能统一开发环境,避免编辑器差异、插件不兼容的问题。
以下提到的工具核心都是配置文件,并且这些配置文件通常都能通过多种格式配置,例如 .js
、.yaml
、.json
或者直接在 package.json
中配置。后面不再赘述相关设定,具体配置形式可以在官方文档查询,本文仅解析部分配置项目。
代码风格
代码风格一致是项目协作的基石,使用 ESLint 和 Prettier 可以避免由于代码格式不一致带来的代码合并冲突,也可以提高代码可读性和可维护性。虽然在认识 ESLint 和 Prettier提到过,但是下面想要作为升级版讲得更完整一点。
ESlint
本文介绍的第一个工具是 ESlint,其功能是:
- 提供代码规范,例如:建议你使用
===
而不是==
,建议你未修改过的变量使用const
而不是let
等这样的规则 - 其次是格式化代码,控制缩进换行之类的问题
安装
npm install --save-dev eslint
VSCode 插件
![ESlint VSCode 插件](https://img.ssshooter.com/img/front-end-code-quality/eslint-vscode.png)
在项目中安装了 ESlint 确实提供了文件校验和整理的接口,但实际上在写代码过程中格式化总不能每次都自己调一下 api 处理当前文件,这个时候就需要使用 ESlint 插件。安装插件后可以通过快捷键调用 eslint api 处理当前文件,甚至在保存时自动格式化当前文件,在编码窗口也会使用黄线和红线标注警告和错误代码。
安装完 ESlint 本体和 VSCode 插件你可以使用一些基本功能,但要与 React、Vue、TS 等文件配合使用需要安装相应插件(注意这里说的是 ESlint 的 plugins,跟前面提到的 VSCode 插件没有关系),后面会详细介绍插件(plugins)相关问题。
配置
先看一眼 ESlint 的配置文件,它用于配置和扩充 ESLint 规则,这是一个例子:
{ "root": true, "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], "parser": "@typescript-eslint/parser", "parserOptions": { "project": ["./tsconfig.json"] }, "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/strict-boolean-expressions": [ 2, { "allowString": false, "allowNumber": false } ] }, "ignorePatterns": ["src/**/*.test.ts", "src/frontend/generated/*"]}
接着详细解析一下 extends、plugins、rules 三个配置。
Extends
上面提到的规则有一两百条,要每条手动配置不现实,我们可以使用 Extends。
Extends 是配置的集合,添加了 Extends 等于添加了一组配置。配置 extends 的值时可以忽略包名的 eslint-config-
前缀。
很多大厂都有自己的一套规范,例如前端代码规范领域著名的 airbnb,他们的配置文件是 eslint-config-airbnb,安装之后使用时只需要这么写:
{ "extends": "airbnb"}
不过……其实个人建议用 eslint:recommended
或者 standard
而不是 airbnb
,因为 airbnb 实属管太多,例如 no-plusplus
和 no-underscore-dangle
正常使用并没有什么问题,他也给开了,跟原来的编码习惯差距比较大所以选择不用了 😂。
当然,配置集合还有其他选择,例如 eslint-config-alloy;你还可以发布自己的配置集合,其实这也是官方推荐的做法。
Plugins
Plugins 比 Extends 更强劲,不止可以补充配置,更能新增 ESLint 自定义规则。配置 plugins 的值时可以忽略包名的 eslint-plugin-
前缀;因为 plugin 中也可以包含配置集合,使用 plugin 中的配置集合时可以使用plugin:包名/配置名
的格式,如 plugin:vue/essential。
在安装 eslint-plugin-vue 之后可以这样添加插件,就能在 ESLint 中新增一大堆 Vue 的规则(注意只是新增规则,并未配置规则是否使用):
{ // ... "extends": ["plugin:vue/essential"], "plugins": ["vue"] // ...}
各种语言详细的配置这里就不一一赘述了,eslint-plugin-xxx
的文档一般会提供比较完善的帮助。
Rules
在 extends 添加完规则集合之后,很可能还要根据自己习惯微调一些规则,这时候就可以在 rules
配置一些单条规则。
规则的等级有三种:
- “off” or 0 - turn the rule off
- “warn” or 1 - turn the rule on as a warning (doesn’t affect exit code)
- “error” or 2 - turn the rule on as an error (exit code is 1 when triggered)
配置方式大概长这样(一些特殊规则会有其他配置项,可以在规则对应页面获取相关信息):
{ "plugins": ["plugin1"], "rules": { "eqeqeq": "off", "curly": "error", "quotes": ["error", "double"], "plugin1/rule1": "error" }}
其他问题
配置未生效:
在更新完 .eslintrc
却发现新配置在 VSCode 没有生效时,可以通过 ctrl shift P 然后选 Reload Window
快速重启 ESlint 插件。
VSCode 保存时自动格式化配置,修改 settings.json
:
{ "editor.codeActionsOnSave": { "source.fixAll.eslint": true } // "editor.formatOnSave": true,}
Prettier
Prettier 是一个现代化的代码格式化工具。相比 ESLint,它专注于代码格式化,可以处理多种语言,包括 JavaScript、CSS、SCSS、markdown、yaml 等。Prettier 补全了 ESLint 只处理 js 系文件的问题,但对于 js 这个两者皆可处理的交集,仍然需要一些额外的兼容操作。
安装
npm install --save-dev --save-exact prettier
注意一定要加 --save-exact
,因为不同版本的 prettier 处理某些格式时会有差异,为了保证团队全员格式相同,必须统一 prettier 版本。
VSCode 插件
![Prettier VSCode 插件](https://img.ssshooter.com/img/front-end-code-quality/prettier-vscode.png)
原理跟 ESLint 一样,npm 安装只提供 api,安装 VSCode 插件才能在编辑器方便格式化。
配置
By far the biggest reason for adopting Prettier is to stop all the ongoing debates over styles.
Prettier 为了让大家少在格式上争吵,只提供了少数配置项,这样大家只要在这几个项目中争吵就可以了(误)。下面这些是 Prettier 几乎全部配置,文件名为 .prettierrc
:
{ "arrowParens": "always", "bracketSameLine": true, "bracketSpacing": true, "embeddedLanguageFormatting": "auto", "htmlWhitespaceSensitivity": "css", "insertPragma": false, "jsxSingleQuote": false, "printWidth": 80, "proseWrap": "preserve", "quoteProps": "as-needed", "requirePragma": false, "semi": true, "singleAttributePerLine": false, "singleQuote": true, "tabWidth": 2, "trailingComma": "es5", "useTabs": false, "vueIndentScriptAndStyle": false}
ESlint 兼容
之前有在用 eslint 和 prettier 让跨 IDE 协作更舒服提到可以手动让 ESlint 和 Prettier 兼容,不过那只是因为使用不同编辑器方便同步的一种方法,这里再介绍一下用 ESlint 插件兼容的方法。
要完整使用 ESLint 接替 Prettier 的工作需要 eslint-plugin-prettier。它的原理是使用 eslint-config-prettier 仅关闭所有 prettier 相关规则,然后通过插件把 Prettier 的规则转到 ESlint 一起校验。要做到上述全部操作只需要下面一行配置:
{ "extends": ["plugin:prettier/recommended"]}
因为配置 "extends": ["plugin:prettier/recommended"]
后相当于填充了以下一组配置:
{ "extends": ["prettier"], "plugins": ["prettier"], "rules": { "prettier/prettier": "error", "arrow-body-style": "off", "prefer-arrow-callback": "off" }}
其他问题
特殊规则:
注意 prettier 覆盖了的一些特殊规则,例如会默认关掉 vue/html-self-closing
等规则,请再另外根据自己需求再 rules
配置。
提交门禁
以上是统一代码格式相关的工具,但没有保证提交到代码库的代码经过格式化,使用 husky 可以在提交前对代码检查,保存提交的代码符合团队规范。
Husky
Git hooks made easy 🐶 woof!
husky 为 npm 项目提供介入 Git hook 的能力,它支持所有 Git hook,但我们一般只会用到 pre-commit
和 commit-msg
。
pre-commit
用于提交前的检查,commit-msg
用于提交信息检测。
# 安装 huskynpm install husky --save-dev# 启用 Git hooknpx husky install# 配置 npm 的 prepare 命令,用于依赖安装后自动运行特定脚本# 注意,使用 pkg 需要 npm 7 以上版本npm pkg set scripts.prepare="husky install"
无法使用 pkg
可以直接在 package.json
配置:
{ "scripts": { "prepare": "husky install" }}
之后使用 husky add <file> [cmd]
的格式添加 hook 触发的命令即可,接着介绍一下在 pre-commit
和 commit-msg
分别要使用的 lint-staged
和 commitlint
。
lint-staged
Run linters against staged git files and don’t let 💩 slip into your code base!
对于新接入 ESLint 的代码库,每次提交都检测所有文件,不通过就不允许提交的话,是不是有点过分了?使用 lint-staged 可以只校验当前提交的文件,让你的项目渐进式更新代码风格。
安装
npm install --save-dev lint-staged
配置
配置文件名 .lintstagedrc
,因为 lint-staged 的配置比较简短,可以直接写在 package.json
的 lint-staged
对象里。
配置范例:
{ "src/**/*.{ts,js}": ["eslint --cache --fix"], "src/**/*.{json,less}": ["prettier --write"]}
husky 触发
# 在已安装 husky 的前提下运行npx husky add .husky/pre-commit "npx lint-staged"
commitlint
commitlint 可以检测提交信息是否符合规范。
简单来说就是这样的格式:type(scope?): subject
,实际例子可能是这样的:feat(blog): add comment section
。详细规范可以查看理解语义化 Commit。
安装
npm install --save-dev @commitlint/config-conventional @commitlint/cli
配置
配置文件名 .commitlintrc
,配置范例:
{ extends: ['@commitlint/config-conventional'], rules: { 'type-enum': [ 2, 'always', [ 'feat', // 新功能(feature) 'fix', // 修补bug 'docs', // 文档(documentation) 'style', // 格式(不影响代码运行的变动) 'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动) 'test', // 增加测试 'revert', // 回滚 'chore', // 构建过程或辅助工具的变动 'perf', // 性能优化 'types' // typescript类型定义文件更改 ] ], 'type-case': [0], 'type-empty': [0], 'scope-empty': [0], 'scope-case': [0], 'subject-full-stop': [0, 'never'], 'subject-case': [0, 'never'], 'header-max-length': [0, 'always', 72] }}
可以在 Github 查看更完整的填写示例。
husky 触发
# 在已安装 husky 的前提下运行npx husky add .husky/commit-msg 'npx --no -- commitlint --edit ${1}'
流畅协作
有统一的代码风格,并且确保这些规定能执行之后,下面还有一些可以让协作更流畅的工具。
Typescript
在正经地使用 TS 后,团队成员可以清楚看到每个变量的类型,包括后端接口返回的数据。不过为什么我要说“正经”使用呢,因为很多人把 TS 玩成“Anyscript”。
顺带的好处是,因为已经有类型提示,你就总会被提醒某些值有可能是 undefined
,有了这些提醒,你基本可以避免前端噩梦 Cannot read properties of undefined
。
确认,写 TS 会在最初开发的时候花费不少时间写类型,但是维护代码(甚至是别人写的代码)时得到的好处,谁用谁懂。
Storybook
storybook 是一个可以轻松展示项目内公共组件的工具,对一个组件写 story 就能脱离项目预览这个组件,并且可以自动生成 props 文档。团队可以要求成员在添加公共组件后为组件写 story,这样所有成员都可以对组件有直观的认识,减少重复造轮子的可能性。
对于大多数前端项目,直接安装并运行 storybook 就可以了。
npx storybook@latest init
npm run storybook
就我最近的 Vite Vue3 项目来说十分流畅,装上基本能用,配置文件作了一点小改动,把 Vite 的 proxy 加到 viteFinal
里,以及添加 remarkGfm
插件。
import type { StorybookConfig } from '@storybook/vue3-vite'import remarkGfm from 'remark-gfm'
const config: StorybookConfig = { stories: ['../src/**/*.mdx', '../src/**/*.stories.@(js|jsx|ts|tsx)'], addons: [ '@storybook/addon-links', '@storybook/addon-essentials', '@storybook/addon-interactions', { name: '@storybook/addon-docs', options: { mdxPluginOptions: { mdxCompileOptions: { remarkPlugins: [remarkGfm], }, }, }, }, ], framework: { name: '@storybook/vue3-vite', options: {}, }, docs: { autodocs: 'tag', }, viteFinal(config, { configType }) { if (configType === 'DEVELOPMENT') { if (config.server) config.server.proxy = { '/api': { target: 'http://xxxx/api/v1/', changeOrigin: true, rewrite: (path) => path.replace(/^\/api/, ''), }, } config.mode = 'dev' } if (configType === 'PRODUCTION') { config.mode = 'test' } return config },}export default config
包管理器
使用固定包管理器,一般选择有以下几种:
想快速了解几种管理器的差异可以查看《速通 npm、yarn、pnpm》。同时强烈推荐《JavaScript package managers compared: npm, Yarn, or pnpm?》,讲得全面且清楚,下面贴一张来自此文的性能对比图:
![npm,yarn,pnpm performance](https://img.ssshooter.com/img/npm-yarn-pnpm.png)
可以按照喜好和兼容性自行选择,但是无论选择哪个管理器,非必要时,不要删除 lock 文件!!! 在安装依赖时,管理器需要计算依赖版本,很耗时,有 lock 可以直接按 lock 列表安装。
但是有 lock 的时候,依赖的下载地址是固定的,所以配仓库会不生效,持续集成时需要注意。
P.S. 不同管理器在配 husky 的时候可能有些差异
引擎版本
如果保留了依赖的 lock 文件,那么记录当前应用适配的 node 版本也是十分必要的,不同 node 版本会造成下载的依赖不一样、甚至依赖根本不适配当前 node 版本;另外,还有一些老项目,必须用旧的 node 版本才能运行,但是接手的时候根本不知道用的那个版本,就十分让人无奈。
为解决这个问题,我们可以在 package.json
指定 node 和 npm 版本:
{ "engines": { "node": ">=0.10.3 <15", "npm": "~1.0.20" }}
团队人多、项目多时,很可能出现大家版本不一样的情况,在必要时,可以使用 node 版本管理器,这里推荐三款:
- nvm unix, macOS nvm-windows Windows
- n macOS, Linux
- fnm macOS, Windows, Linux
基本上可以做到一行命令安装版本、一行命令切换版本,十分方便。
.npmrc
相信文件名带着 rc
已经很熟悉了,.npmrc
作用就是添加项目级别的 npm 配置,例如:
- 改仓库,有的公司有自己的镜像,可以在
.npmrc
配置,不用每次都在安装的时候带一串参数,也不会影响全局配置 - 还可以添加一些二进制包的下载地址,例如那个烦人的 sass
registry=https://mirrors.huaweicloud.com/repository/npm/sass_binary_site=https://npm.taobao.org/mirrors/node-sass/
分支策略
选择一种分支策略,可以选择以下三种策略,也可以以这些策略为基础调整出一个最适合自己团队的策略:
总结
- 使用 ESlint 和 Prettier 控制代码规范和代码风格
- 添加 VSCode 插件极速格式化当前文件
- 添加提交门禁,保证上传到版本控制的代码符合规范
- 使用 husky 添加提交钩子,控制提交是否成功
- lint-staged 用于只检查本次提交文件是否符合要求,利于项目渐进式控制代码规范
- commitlint 保证提交信息可读性
- 使用 Typescript,开发时发力,维护时轻松
- 使用 Storybook 直观了解项目的复用组件
- 团队内使用统一的包管理器,保留依赖 lock 文件
- 固定引擎版本,防止依赖变化,也能避免老项目不知道使用什么版本运行的窘境
- 使用
.npmrc
固定仓库等配置 - 选择一种分支策略,使代码历史更有序
各位看完觉得有遗漏或者有什么不懂我再补充!