五. 代码规范
讲解如何使用 「EditorConfig + Prettier + ESLint」 组合来实现代码规范化。 这样做带来好处:
-
解决团队之间代码不规范导致的可读性差和可维护性差的问题。 -
解决团队成员不同编辑器导致的编码规范不统一问题。 -
提前发现代码风格问题,给出对应规范提示,及时修复。 -
减少代码审查过程中反反复复的修改过程,节约时间。 -
自动格式化,统一编码风格,从此和脏乱差的代码说再见。
集成 EditorConfig 配置
EditorConfig 有助于为不同 IDE 编辑器上处理同一项目的多个开发人员维护一致的编码风格。 在项目根目录下增加 .editorconfig 文件:
# Editor configuration, see http://editorconfig.org
# 表示是最顶层的 EditorConfig 配置文件
root = true
[*] # 表示所有文件适用
charset = utf-8 # 设置文件字符集为 utf-8
indent_style = space # 缩进风格(tab | space)
indent_size = 2 # 缩进大小
end_of_line = lf # 控制换行类型(lf | cr | crlf)
trim_trailing_whitespace = true # 去除行首的任意空白字符
insert_final_newline = true # 始终在文件末尾插入一个新行
[*.md] # 表示仅 md 文件适用以下规则
max_line_length = off
trim_trailing_whitespace = false
注意:
-
VSCode 使用 EditorConfig 需要去插件市场下载插件 「EditorConfig for VS Code」 。

集成Prettier
Prettier 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。
-
安装
# 安装 prettier
npm i prettier -D
或者
yarn add prettier --dev
-
配置
在本项目根目录下创建 .prettier.js 文件,并配置:
module.exports = {
tabWidth: 2,
jsxSingleQuote: true,
jsxBracketSameLine: true,
printWidth: 100,
singleQuote: true,
semi: false,
overrides: [
{
files: '*.json',
options: {
printWidth: 200,
},
},
],
arrowParens: 'always',
}
创建.prettierignore
# 忽略格式化文件 (根据项目需要自行添加)
node_modules
dist
-
使用
Prettier 安装且配置好之后,就能使用命令来格式化代码:
# 格式化所有文件(. 表示所有文件)
npx prettier --write .
注意:
-
VSCode 编辑器使用 Prettier 配置需要下载插件 「Prettier – Code formatter」 。

集成 ESLint 配置
-
安装
npm i eslint -D
-
配置
ESLint 安装成功后,执行 npx eslint –init,然后按照终端操作提示完成一系列设置来创建配置文件。
-
How would you like to use ESLint? (你想如何使用 ESLint?)
我们这里选择 「To check syntax, find problems, and enforce code style(检查语法、发现问题并强制执行代码风格)」
-
What type of modules does your project use?(你的项目使用哪种类型的模块?)
我们这里选择 「JavaScript modules (import/export)」
-
Which framework does your project use? (你的项目使用哪种框架?)
-
Does your project use TypeScript?(你的项目是否使用 TypeScript?)
-
Where does your code run?(你的代码在哪里运行?)
我们这里选择 「Browser 和 Node」(按空格键进行选择,选完按回车键确定)
-
How would you like to define a style for your project?(你想怎样为你的项目定义风格?)
我们这里选择 「Use a popular style guide(使用一种流行的风格指南)」
-
Which style guide do you want to follow?(你想遵循哪一种风格指南?)
-
What format do you want your config file to be in?(您希望配置文件的格式是什么)
-
Would you like to install them now with npm?(你想现在就用 NPM 安装它们吗?)
我们这里选择 「Yes」,使用 NPM 下载安装这些依赖包。
「注意」:如果自动安装依赖失败,那么需要手动安装
npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue -D
-
ESLint 配置文件 .eslintrc.cjs
在「上一步」操作完成后,会在项目根目录下自动生成 .eslintrc.cjs 配置文件:
module.exports = {
root: true,
env: {
browser: true,
node: true,
es2021: true
},
parser: 'vue-eslint-parser',
extends: [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
// eslint-config-prettier 的缩写
'prettier'
],
parserOptions: {
ecmaVersion: 12,
parser: '@typescript-eslint/parser',
sourceType: 'module',
ecmaFeatures: {
jsx: true
}
},
// eslint-plugin-vue @typescript-eslint/eslint-plugin eslint-plugin-prettier的缩写
plugins: ['vue', '@typescript-eslint', 'prettier'],
rules: {
'@typescript-eslint/ban-ts-ignore': 'off',
'@typescript-eslint/no-unused-vars': 'off',
'@typescript-eslint/explicit-function-return-type': 'off',
'@typescript-eslint/no-explicit-any': 'off',
'@typescript-eslint/no-var-requires': 'off',
'@typescript-eslint/no-empty-function': 'off',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/ban-ts-comment': 'off',
'@typescript-eslint/ban-types': 'off',
'@typescript-eslint/no-non-null-assertion': 'off',
'@typescript-eslint/explicit-module-boundary-types': 'off',
'no-var': 'error',
'prettier/prettier': 'error',
// 禁止出现console
'no-console': 'warn',
// 禁用debugger
'no-debugger': 'warn',
// 禁止出现重复的 case 标签
'no-duplicate-case': 'warn',
// 禁止出现空语句块
'no-empty': 'warn',
// 禁止不必要的括号
'no-extra-parens': 'off',
// 禁止对 function 声明重新赋值
'no-func-assign': 'warn',
// 禁止在 return、throw、continue 和 break 语句之后出现不可达代码
'no-unreachable': 'warn',
// 强制所有控制语句使用一致的括号风格
curly: 'warn',
// 要求 switch 语句中有 default 分支
'default-case': 'warn',
// 强制尽可能地使用点号
'dot-notation': 'warn',
// 要求使用 === 和 !==
eqeqeq: 'warn',
// 禁止 if 语句中 return 语句之后有 else 块
'no-else-return': 'warn',
// 禁止出现空函数
'no-empty-function': 'warn',
// 禁用不必要的嵌套块
'no-lone-blocks': 'warn',
// 禁止使用多个空格
'no-multi-spaces': 'warn',
// 禁止多次声明同一变量
'no-redeclare': 'warn',
// 禁止在 return 语句中使用赋值语句
'no-return-assign': 'warn',
// 禁用不必要的 return await
'no-return-await': 'warn',
// 禁止自我赋值
'no-self-assign': 'warn',
// 禁止自身比较
'no-self-compare': 'warn',
// 禁止不必要的 catch 子句
'no-useless-catch': 'warn',
// 禁止多余的 return 语句
'no-useless-return': 'warn',
// 禁止变量声明与外层作用域的变量同名
'no-shadow': 'off',
// 允许delete变量
'no-delete-var': 'off',
// 强制数组方括号中使用一致的空格
'array-bracket-spacing': 'warn',
// 强制在代码块中使用一致的大括号风格
'brace-style': 'warn',
// 强制使用骆驼拼写法命名约定
camelcase: 'warn',
// 强制使用一致的缩进
indent: 'off',
// 强制在 JSX 属性中一致地使用双引号或单引号
// 'jsx-quotes': 'warn',
// 强制可嵌套的块的最大深度4
'max-depth': 'warn',
// 强制最大行数 300
// "max-lines": ["warn", { "max": 1200 }],
// 强制函数最大代码行数 50
// 'max-lines-per-function': ['warn', { max: 70 }],
// 强制函数块最多允许的的语句数量20
'max-statements': ['warn', 100],
// 强制回调函数最大嵌套深度
'max-nested-callbacks': ['warn', 3],
// 强制函数定义中最多允许的参数数量
'max-params': ['warn', 3],
// 强制每一行中所允许的最大语句数量
'max-statements-per-line': ['warn', { max: 1 }],
// 要求方法链中每个调用都有一个换行符
'newline-per-chained-call': ['warn', { ignoreChainWithDepth: 3 }],
// 禁止 if 作为唯一的语句出现在 else 语句中
'no-lonely-if': 'warn',
// 禁止空格和 tab 的混合缩进
'no-mixed-spaces-and-tabs': 'warn',
// 禁止出现多行空行
'no-multiple-empty-lines': 'warn',
// 禁止出现;
semi: ['warn', 'never'],
// 强制在块之前使用一致的空格
'space-before-blocks': 'warn',
// 强制在 function的左括号之前使用一致的空格
// 'space-before-function-paren': ['warn', 'never'],
// 强制在圆括号内使用一致的空格
'space-in-parens': 'warn',
// 要求操作符周围有空格
'space-infix-ops': 'warn',
// 强制在一元操作符前后使用一致的空格
'space-unary-ops': 'warn',
// 强制在注释中 // 或 /* 使用一致的空格
// "spaced-comment": "warn",
// 强制在 switch 的冒号左右有空格
'switch-colon-spacing': 'warn',
// 强制箭头函数的箭头前后使用一致的空格
'arrow-spacing': 'warn',
'prefer-const': 'warn',
'prefer-rest-params': 'warn',
'no-useless-escape': 'warn',
'no-irregular-whitespace': 'warn',
'no-prototype-builtins': 'warn',
'no-fallthrough': 'warn',
'no-extra-boolean-cast': 'warn',
'no-case-declarations': 'warn',
'no-async-promise-executor': 'warn'
},
globals: {
defineProps: 'readonly',
defineEmits: 'readonly',
defineExpose: 'readonly',
withDefaults: 'readonly'
}
}
根据项目实际情况,如果我们有额外的 ESLint 规则,也在此文件中追加。
注意:
虽然,现在编辑器已经给出错误提示和修复方案,但需要我们一个一个去点击修复,还是挺麻烦的。很简单,我们只需设置编辑器保存文件时自动执行 eslint –fix 命令进行代码风格修复。 VSCode 在 settings.json 设置文件中,增加以下代码:
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
}
解决 Prettier 和 ESLint 的冲突
在这里需要注意的是eslint和prettier同时配置后,可能会产生冲突。我们需要更新一下eslint的配置,解决冲突
npm i eslint-config-prettier --save-dev
-
在 .eslintrc.cjs 添加 prettier 插件
module.exports = {
...
"extends": [
'eslint:recommended',
'plugin:vue/vue3-recommended',
'plugin:@typescript-eslint/recommended',
'plugin:prettier/recommended',
// eslint-config-prettier 的缩写
'prettier'
],
...
}
这样,我们在执行 eslint –fix 命令时,ESLint 就会按照 Prettier 的配置规则来格式化代码,轻松解决二者冲突问题。
-
项目下新建 .eslintignore
# eslint 忽略检查 (根据项目需要自行添加)
node_modules
dist
package.json 配置:
{
"script": {
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
"prettier": "prettier --write ."
}
}
上面配置完成后,可以运行以下命令测试下代码检查个格式化效果:
# eslint 检查
yarn lint
# prettier 自动格式化
yarn prettier
集成husky 和 lint-staged
我们用到 Git Hook,在本地执行 git commit 的时候,就对所提交的代码进行 ESLint 检测和修复(即执行 eslint –fix),如果这些代码没通过 ESLint 规则校验,则禁止提交。husky —— Git Hook 工具,可以设置在 git 各个阶段(pre-commit、commit-msg、pre-push 等)触发我们的命令。lint-staged —— 在 git 暂存的文件上运行 linters。
配置 husky
使用 husky-init 命令快速在项目初始化一个 husky 配置。
npx husky-init && npm install
这条命令做了四件事儿:
-
安装 husky 到开发依赖 -
在项目根目录下创建 .husky 目录 -
在 .husky 目录创建 pre-commit hook,并初始化 pre-commit 命令为 npm test -
修改 package.json 的 scripts,增加 “prepare”: “husky install”
husky 配置完毕,现在我们来使用它: husky 包含很多 hook(钩子),常用有:pre-commit、commit-msg、pre-push。这里,我们使用 pre-commit 来触发 ESLint 命令。 修改 .husky/pre-commit hook 文件的触发命令:
#!/usr/bin/env sh
. "$(dirname -- "$0")/_/husky.sh"
eslint --fix ./src --ext .vue,.js,.ts
上面这个 pre-commit hook 文件的作用是:当我们执行 git commit -m “xxx” 时,会先对 src 目录下所有的 .vue、.js、.ts 文件执行 eslint –fix 命令,如果 ESLint 通过,成功 commit,否则终止 commit。
存在问题:有时候只改动了一两个文件,却要对所有的文件执行 eslint –fix。假如这是一个历史项目,我们在中途配置了 ESLint 规则,那么在提交代码时,也会对其他未修改的“历史”文件都进行检查,可能会造成大量文件出现 ESLint 错误,显然不是我们想要的结果。
我们要做到只用 ESLint 修复自己此次写的代码,而不去影响其他的代码。所以我们还需借助一个神奇的工具 「lint-staged」 。
配置 lint-staged
lint-staged 这个工具一般结合 husky 来使用,它可以让 husky 的 hook 触发的命令只作用于 git add那些文件(即 git 暂存区的文件),而不会影响到其他文件。
-
安装 lint-staged
npm i lint-staged -D
-
在 package.json里增加 lint-staged 配置项

"lint-staged": {
"*.{vue,js,ts}": "eslint --fix"
},
这行命令表示:只对 git 暂存区的 .vue、.js、.ts 文件执行 eslint –fix。
-
修改 .husky/pre-commit hook 的触发命令为:npx lint-staged
至此,husky 和 lint-staged 组合配置完成。
六. 提交规范
git 规范在团队协作时,也是一个非常重要的点,我们通过 git 规范,在版本出现问题时可以清晰的定位
集成 cz-git 实现规范提交
安装 git-cz
npm install -D git-cz
配置package
安装完成后,在 package.json 配置提交命令
{
...
"dev": "vite",
"build": "vue-tsc && vite build",
"preview": "vite preview",
"lint": "eslint src --fix --ext .ts,.tsx,.vue,.js,.jsx",
"prettier": "prettier --write .",
"prepare": "husky install",
"commit": "git pull && git add -A && git-cz && git push"
...
}
随后在需要提交时,执行
yarn commit 或者 npm run commit
配置提交说明文件
在项目根目录下创建 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'],
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: ''
}
}
原文始发于微信公众号(前端大大大):Vue3+TS+Vite脚手架搭建全过程(二)
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/174125.html