yanyue404 / blog

Just blog and not just blog.
https://yanyue404.github.io/blog/
Other
87 stars 13 forks source link

代码规范方案落地 #242

Open yanyue404 opened 1 year ago

yanyue404 commented 1 year ago

代码格式规范 & Git 提交规范简介

对于代码格式规范,通过 ESLint + Prettier + VSCode 配置,达到了在保存代码时,自动规范化代码格式;

对于 Git 提交规范,通过 husky 监测 Git Hooks 钩子,通过约定式提交规范、commitizen、commitlint、husky(pre-commit、commit-msg 钩子)、lint-staged 几个插件的配置使用来约束代码的规范提交。

编辑器与 npm 的准备工作

推荐 VS Code 版本 1.69.2 或更新的版本

Eslint 和 Prettier 都有 npm 包和 vscode 插件,他们的区别是 vscode 插件提供编辑器格式化和代码检查的功能,如果设置保存自动格式化和自动代码检查后,你可以实时的在控制台看到结果;npm 包是提供命令行的能力,用来配合脚本进行整个项目代码的检查,也可以配合后面 husky 在 git hook 中进行检查或格式化。

settings.json

{
  // ! 大部分情况不需要自动格式化
  "editor.formatOnSave": true,
  "eslint.enable": true, // 本地 eslint 校验开关
  "editor.codeActionsOnSave": {
    "source.fixAll.eslint": true
  }

在众多 git 提交规范化工具中,commitizen 是其中一个比较好用的一个工具,它的仓库名称是 cz-cli,它提供了一个 git cz 的指令用于代替 git commit,就是说当使用 commitizen 进行代码提交的时候,会在提交时填写一些必须的提交字段才会允许进行代码的提交。

npm install -g commitizen

手动集成

可以直接尝试自动化集成(推荐)!!!

  1. npm scripts 命令
{
  // 用于自定义 git cz 提交代码
  "config": {
    "commitizen": {
      "path": "node_modules/cz-customizable"
    }
  },
  "scripts": {
    "prepare": "husky install",
    "lint": "eslint --ext .vue pages/ --ext .vue components/prd --ext .js store/",
    "prettier": "prettier pages/** store/* components/prd/*  --write",
    "lint:fix": "npm run lint -- --fix"
  },
  // 用于提交代码时校验暂存区文件
  "lint-staged": {
    "pages/*.vue": ["eslint --fix", "prettier --write", "git add"],
    "store/*.js": ["eslint --fix", "prettier --write", "git add"]
  }
}
  1. 配置文件添加
# editorconfig.org
root = true

[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

[*.md]
trim_trailing_whitespace = false
// ESLint 配置文件遵循 commonJS 的导出规则,所导出的对象就是 ESLint 的配置对象
// 文档:https://eslint.bootcss.com/docs/user-guide/configuring
module.exports = {
  // 表示当前目录为根目录,ESLint规则被限制到该目录下
  root: true,
  // env表示启用ESLint检测的环境
  env: {
    // 在node环境下启动ESLint检测
    node: true,
    browser: true,
  },
  // ESLint中基础配置需要继承的配置
  extends: ["@nuxtjs", "plugin:nuxt/recommended", "prettier"],
  // 解析器
  // parser: '@babel/eslint-parser',
  parserOptions: {
    parser: "@babel/eslint-parser",
    // 指定ES版本,默认为5,某些require引入js会报错,修改为8后正常
    ecmaVersion: 8,
    requireConfigFile: false,
  },
  // 需要修改的启用规则及其各自的错误级别
  /**
   * 错误级别分为三种:
   * "off" 或 0 - 关闭规则
   * "warn" 或 1 - 开启规则,使用警告级别的错误:warn (不会导致程序退出)
   * "error" 或 2 - 开启规则,使用错误级别的错误:error (当被触发的时候,程序会退出)
   */
  rules: {
    "space-before-function-paren": "off",
    "nuxt/no-cjs-in-config": "off",
    // webpack异步import时可以不在最顶层
    // 'import/first': 'off',
    // TODO 兼容老项目关闭部分规则,待修复
    // ===要改成==
    eqeqeq: "off",
    "no-prototype-builtins": "off",
    "vue/multi-word-component-names": "off",
    "vue/require-default-prop": "off",
    // 正则相关 减少非必要的转义字符(不太懂)
    "no-useless-escape": "off",
    "vue/no-reserved-component-names": "off",
    // 降级警告展示
    "no-console": "warn",
    camelcase: "warn",
    "no-async-promise-executor": "warn",
    "no-lonely-if": "warn",
    "object-shorthand": "off",
    "vue/attribute-hyphenation": "off",
  },
  globals: {
    // 全局变量定义 writable 可修改 readonly 只读
    urlParams: "writable",
    envParams: "writable",
    $nuxt: "readonly",
    traceRecord: "readonly",
    weui: "readonly",
    WeixinJSBridge: "readonly",
    wx: "readonly",
  },
};
// https://www.prettier.cn/docs/options.html
module.exports = {
  printWidth: 80,
  // 缩进级别的空格数
  tabWidth: 2,
  // 单引号
  singleQuote: true,
  // 尾随逗号
  trailingComma: "none",
  // 对象花括号与内容之间加空格
  bracketSpacing: true,
  // 句末分号
  semi: false,
  // 制表符缩进
  useTabs: false,
  // 多行 HTML 元素 > 放在最后一行的末尾
  bracketSameLine: false,
};
module.exports = {
  // 继承的规则
  extends: ["@commitlint/config-conventional"],
  // 定义规则类型
  rules: {
    // type 类型定义,表示 git 提交的 type 必须在以下类型范围内
    "type-enum": [
      // 0为disable,1为warning,2为error
      2,
      // 在什么情况下需要进行验证 never和always,never无视规则
      "always",
      // 泛型内容
      [
        "feat", // 新功能 feature
        "fix", // 修复 bug
        "docs", // 文档注释
        "style", // 代码格式(不影响代码运行的变动)
        "refactor", // 重构(既不增加新功能,也不是修复bug)
        "perf", // 性能优化
        "test", // 增加测试
        "chore", // 构建过程或辅助工具的变动
        "revert", // 回退
        "build", // 打包
      ],
    ],
    // subject 大小写不做校验
    "subject-case": [0],
  },
};
module.exports = {
  // 可选类型
  types: [
    { value: "feat", name: "feat:     新功能" },
    { value: "chore", name: "chore:    构建过程或辅助工具的变动" },
    { value: "test", name: "test:     增加测试" },
    { value: "fix", name: "fix:      修复" },
    { value: "build", name: "build:    打包" },
    { value: "style", name: "style:    代码格式(不影响代码运行的变动)" },
    { value: "docs", name: "docs:     文档变更" },
    { value: "perf", name: "perf:     性能优化" },
    {
      value: "refactor",
      name: "refactor: 重构(既不是增加feature, 也不是修复bug)",
    },
    { value: "revert", name: "revert:   回退" },
  ],
  scopes: [
    { name: "core" },
    { name: "admin" },
    { name: "exampleScope" },
    { name: "changeMe" },
  ],
  // 消息步骤
  messages: {
    type: "请选择提交类型:",
    scope: "请输入修改范围(可选):",
    // used if allowCustomScopes is true
    customScope: "Denote the SCOPE of this change:",
    subject: "请简要描述提交(必填):",
    body: "请输入详细描述(可选):",
    footer: "请输入要关闭的issue(可选):",
    confirmCommit: "确认使用以上信息提交?(y/n/e/h)",
  },
  // 展示空的 scope
  allowCustomScope: false,
  // 跳过问题
  skipQuestions: ["scope", "body", "footer"],
  // subject文字长度默认是100
  subjectLimit: 100,
};
  1. npm packages 安装
npm i @babel/eslint-parser@7.14.7 @nuxtjs/eslint-config@6.0.1 eslint@7.29.0 eslint-config-prettier@8.5.0 eslint-plugin-nuxt@2.0.0 eslint-plugin-vue@9.3.0 prettier@2.7.1 husky@7.0.1 @commitlint/cli@17.0.3 @commitlint/config-conventional@17.0.3 cz-customizable@6.9.1 lint-staged@9.5.0 --save-dev
  1. 初始化 husky
npm run prepare

自动化集成(推荐)

使用 shell 脚本自动初始化集成代码规范

sh ./project-name/common/project-init/build.sh

脚本做了哪些事情:

  1. 配置 package.json 命令
  2. 控制安装相关依赖
  3. 复制代码规则文件并初始化 husky

项目实践

1. 跳过 prettier 格式化

对于不同的文件,prettier 有不同的注释方式,详见 Prettier ignore

1px 问题,书写为 PX,添加 prettier 忽略注释

.test-px2rem {
  font-size: 20px;
  /* prettier-ignore */
  border: 1Px solid #000;
}

2. 跳过 eslint 校验规则

官方文档,通过注释来临时禁止规则出现警告, http://eslint.cn/docs/user-guide/configuring#disabling-rules-with-inline-comments

(1)、忽略下一行的校验

// eslint-disable-next-line

(2)、临时禁止一段代码

/* eslint-disable */
window.bonreeRUM &&
  window.bonreeRUM.config({
    id: 2524,
    key: "r6Y3498h",
    probability: 1000.0,
    userKey: "br-user",
    enable: true,
    beacon: { production: "bupload.bonree.com:80" },
    beaconHttps: { production: "bupload.bonree.com:443" },
  });
/* eslint-enable */

对指定的规则启用或禁用警告:

/* eslint-disable no-eval */
const obj = eval(this.cmsTabList.cms_key_product_tab_user_appraise) || [];
/* eslint-enable no-eval */

(3)整个文件忽略

<template></template>
<script>
  /* eslint-disable */
</script>

3. VS Code 编辑器 settings.json 配置 prettier

vscode 编辑器统一的代码格式化的配置信息(vscode 本地安装需要 prettier 插件)

配置优先级:项目本地的 .prettierrc.js > 项目本地的 .vscode/settings.json > 编辑器的 settings.json

{
  // 保存代码时自动格式化
  "editor.formatOnSave": true,
  // ? 格式化 prettier start
  "editor.defaultFormatter": "esbenp.prettier-vscode",
  "[javascript]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[vue]": {
    "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  /* prettier-ignore*/
  "prettier.printWidth": 80, // 超过最大值换行
  "prettier.tabWidth": 2, // 缩进级别的空格数
  "prettier.singleQuote": true, // 要单引号
  "prettier.trailingComma": "none", // 尾随逗号
  "prettier.bracketSpacing": true, // 对象花括号与内容之间加空格
  "prettier.semi": false, // 句尾添加分号
  "prettier.useTabs": false, // 制表符缩进
  "prettier.ignorePath": ".prettierignore" // 格式化忽略的文件控制
  // ? 格式化 prettier end
}

注意:格式化版本不一致

  1. 想要 使用VS Code 编辑器的 settings.json配置时注意以下错误,Detected local configuration (i.e. .prettierrc or .editorconfig), VS Code configuration will not be used(检测到本地配置(即 .prettierrc.editorconfig),VS Code 配置将不会被使用),这个时候需要保证项目根目录下删除括号中的两个文件。

未删除 .editorconfig 配置前的格式化日志 (摘自 VS Code terminal 面板 OUTPUT 标签面板):

["INFO" - 15:27:24] Formatting d:\TencentCloud\renewal-v3\src\pages\N20210025\config.js
["INFO" - 15:27:24] Using ignore file (if present) at d:\TencentCloud\renewal-v3\.prettierignore
["INFO" - 15:27:24] Using bundled version of prettier.
["INFO" - 15:27:24] File Info:
{
  "ignored": false,
  "inferredParser": "babel"
}
["INFO" - 15:27:24] Detected local configuration (i.e. .prettierrc or .editorconfig), VS Code configuration will not be used
["INFO" - 15:27:24] Prettier Options:
{
  "filepath": "d:\\TencentCloud\\renewal-v3\\src\\pages\\N20210025\\config.js",
  "parser": "babel",
  "useTabs": false,
  "tabWidth": 2,
  "endOfLine": "lf"
}
["INFO" - 15:27:24] Formatting completed in 28.6093ms.

删除 .editorconfig 配置后的格式化日志:

No local configuration (i.e. .prettierrc or .editorconfig) detected, falling back to VS Code configuration (没有检测到本地配置(即 . prettierrc 或 . editorconfig), 回落到 VS Code 配置)

["INFO" - 16:18:17] Formatting d:\TencentCloud\renewal-v3\src\pages\N20210025\config.js
["INFO" - 16:18:17] Using ignore file (if present) at d:\TencentCloud\renewal-v3\.prettierignore
["INFO" - 16:18:17] Using bundled version of prettier.
["INFO" - 16:18:17] File Info:
{
  "ignored": false,
  "inferredParser": "babel"
}
["INFO" - 16:18:17] No local configuration (i.e. .prettierrc or .editorconfig) detected, falling back to VS Code configuration
["INFO" - 16:18:17] Prettier Options:
{
  "arrowParens": "always",
  "bracketSpacing": true,
  "endOfLine": "lf",
  "htmlWhitespaceSensitivity": "css",
  "insertPragma": false,
  "jsxBracketSameLine": false,
  "jsxSingleQuote": false,
  "printWidth": 80,
  "proseWrap": "preserve",
  "quoteProps": "as-needed",
  "requirePragma": false,
  "semi": false,
  "singleQuote": true,
  "tabWidth": 2,
  "trailingComma": "none",
  "useTabs": false,
  "vueIndentScriptAndStyle": false,
  "filepath": "d:\\TencentCloud\\renewal-v3\\src\\pages\\N20210025\\config.js",
  "parser": "babel"
}
["INFO" - 16:18:17] Formatting completed in 33.1417ms.