Vue3+TS+Vite脚手架搭建全过程(二)

五. 代码规范

讲解如何使用 「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」
Vue3+TS+Vite脚手架搭建全过程(二)
image.png

集成Prettier

Prettier 是一款强大的代码格式化工具,支持 JavaScript、TypeScript、CSS、SCSS、Less、JSX、Angular、Vue、GraphQL、JSON、Markdown 等语言,基本上前端能用到的文件格式它都可以搞定,是当下最流行的代码格式化工具。

  1. 安装
# 安装 prettier

npm i prettier -D
或者
yarn add prettier --dev
  1. 配置

在本项目根目录下创建 .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
  1. 使用

Prettier 安装且配置好之后,就能使用命令来格式化代码:

# 格式化所有文件(. 表示所有文件)
npx prettier --write .

注意:

  • VSCode 编辑器使用 Prettier 配置需要下载插件 「Prettier – Code formatter」
Vue3+TS+Vite脚手架搭建全过程(二)
image.png

集成 ESLint 配置

  1. 安装
npm i eslint -D
  1. 配置

ESLint 安装成功后,执行 npx eslint –init,然后按照终端操作提示完成一系列设置来创建配置文件。

  • How would you like to use ESLint? (你想如何使用 ESLint?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「To check syntax, find problems, and enforce code style(检查语法、发现问题并强制执行代码风格)」

  • What type of modules does your project use?(你的项目使用哪种类型的模块?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 JavaScript modules (import/export)」

  • Which framework does your project use? (你的项目使用哪种框架?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「Vue.js」

  • Does your project use TypeScript?(你的项目是否使用 TypeScript?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「Yes」

  • Where does your code run?(你的代码在哪里运行?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「Browser 和 Node」(按空格键进行选择,选完按回车键确定)

  • How would you like to define a style for your project?(你想怎样为你的项目定义风格?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「Use a popular style guide(使用一种流行的风格指南)」

  • Which style guide do you want to follow?(你想遵循哪一种风格指南?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择了默认第一个

  • What format do you want your config file to be in?(您希望配置文件的格式是什么)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择了javascript

  • Would you like to install them now with npm?(你想现在就用 NPM 安装它们吗?)

Vue3+TS+Vite脚手架搭建全过程(二)我们这里选择 「Yes」,使用 NPM 下载安装这些依赖包。

「注意」:如果自动安装依赖失败,那么需要手动安装

npm i @typescript-eslint/eslint-plugin @typescript-eslint/parser eslint-config-airbnb-base eslint-plugin-import eslint-plugin-vue -D
  1. ESLint 配置文件 .eslintrc.cjs

「上一步」操作完成后,会在项目根目录下自动生成 .eslintrc.cjs 配置文件:

module.exports = {
  roottrue,
  env: {
    browsertrue,
    nodetrue,
    es2021true
  },
  parser'vue-eslint-parser',
  extends: [
    'eslint:recommended',
    'plugin:vue/vue3-recommended',
    'plugin:@typescript-eslint/recommended',
    'plugin:prettier/recommended',
    // eslint-config-prettier 的缩写
    'prettier'
  ],
  parserOptions: {
    ecmaVersion12,
    parser'@typescript-eslint/parser',
    sourceType'module',
    ecmaFeatures: {
      jsxtrue
    }
  },
  // 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', { max1 }],
    // 要求方法链中每个调用都有一个换行符
    'newline-per-chained-call': ['warn', { ignoreChainWithDepth3 }],
    // 禁止 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 规则,也在此文件中追加。

注意:

  • VSCode 使用 ESLint 配置文件需要去插件市场下载插件 「ESLint」Vue3+TS+Vite脚手架搭建全过程(二)

虽然,现在编辑器已经给出错误提示和修复方案,但需要我们一个一个去点击修复,还是挺麻烦的。很简单,我们只需设置编辑器保存文件时自动执行 eslint –fix 命令进行代码风格修复。 VSCode 在 settings.json 设置文件中,增加以下代码:

 "editor.codeActionsOnSave": {
    "source.fixAll.eslint"true
 }

解决 Prettier 和 ESLint 的冲突

在这里需要注意的是eslint和prettier同时配置后,可能会产生冲突。我们需要更新一下eslint的配置,解决冲突

npm i eslint-config-prettier --save-dev
  1. 在 .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 的配置规则来格式化代码,轻松解决二者冲突问题。

  1. 项目下新建 .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

这条命令做了四件事儿:

  1. 安装 husky 到开发依赖
  2. 在项目根目录下创建 .husky 目录
  3. 在 .husky 目录创建 pre-commit hook,并初始化 pre-commit 命令为 npm test
  4. 修改 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

Vue3+TS+Vite脚手架搭建全过程(二)上面这个 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 暂存区的文件),而不会影响到其他文件。

  1. 安装 lint-staged
npm i lint-staged -D
  1. 在 package.json里增加 lint-staged 配置项
Vue3+TS+Vite脚手架搭建全过程(二)
image.png
"lint-staged": {
  "*.{vue,js,ts}""eslint --fix"
},

这行命令表示:只对 git 暂存区的 .vue、.js、.ts 文件执行 eslint –fix。

  1. 修改 .husky/pre-commit hook 的触发命令为:npx lint-staged

Vue3+TS+Vite脚手架搭建全过程(二)至此,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

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!