前端要做代码规范

Why

团队开发中,每个人的编码习惯不同,代码格式不同。这就会导致代码难看,难以维护。统一代码风格可以:

  1. 增强代码的可读性,降低维护成本
  2. 有利于代码审查
  3. 养成规范代码的习惯,有利于自身成长

How

推荐使用 eslint + prettier 来进行代码格式化。

通过 git hook 调用来实现代码的自动格式化,git hooks 工具推荐 husky。

既然用到了 git hook,顺便把提交信息规范也做一下,这里推荐 commitlint。

用到的插件

  1. eslint: js/jsx 语法检查插件
    按照已有配置检查 js/jsx 语法,能抛出错误、警告,并且能修复一部分错误
  2. stylelint: css样式格式化工具
  3. prettier: 代码格式化插件
    按照已有配置进行代码格式化
  4. husky: git hooks 工具
    对 git 执行的一些命令,通过对应的 hooks 触发,执行自定义的脚本程序。
    比如,我们可以定义pre-commit钩子的脚本为npm run test。这样在代码提交前就会执行npm run test
  5. lint-staged: 在 git 暂存区运行 linters 的工具
    只检查暂存区内容,避免每次 lint 执行都针对所有代码
    相当于每次只对修改的内容执行 eslint + prettier 格式化
  6. commitlint: 提交信息检查工具
    检查提交信息是否符合规范

eslint 7.x

1. 安装

cnpm install eslint -D

2. 使用

  • 配置 eslint
    eslint --init添加 eslint 配置文件。然后修改配置,具体配置如下:
module.exports = {
    // 特定项目下,不再检索上级目录
    root: true,
    env: {
        browser: true,
        es6: true,
        node: true,
        amd: true
    },
    extends: [
        'eslint:recommended',
        'plugin:react/recommended',
        // eslint-config-prettier的缩写
        'prettier'
    ],
    plugins: ['react'],
    // 解析器选项
    parserOptions: {
        sourceType: 'module',
        ecmaFeatures: {
            jsx: true
        }
    },
    settings: {
        react: {
            version: 'detect'
        }
    },
    rules: {
        'no-unused-expressions': 'off',
        'no-unused-vars': 'warn',
        'no-debugger': 'error',
        'no-unreachable': 'warn',
        'react/prop-types': 'off'
    }
};

这里,我们用到了几个 eslint 的插件,需要安装:

cnpm install eslint-config-prettier eslint-plugin-react -D

eslint-config-prettier 的作用是使用 Prettier 默认推荐配置,并且关闭 eslint 自身的格式化功能,防止 Prettier 和 ESLint 的自动格式化的冲突

在 package.json 的 scripts 里添加 eslint 脚本命令,如下:

"scripts": {
  // ...
  "eslint": "eslint --ext js,jsx src --fix",
},

值得注意的是,这里我们指定了 src 目录,所以没必要再加.eslintignore文件了。
npm run eslint即可进行 eslint 检查和修复(只能修复部分格式的问题)。

stylelint14.x

1. 安装

cnpm install stylelint -D

2. 配置

创建.stylelintrc.js,增加以下配置:

module.exports = {
  extends: ["stylelint-config-standard-scss", "stylelint-config-prettier"],
  plugins: ["stylelint-order"],
  defaultSeverity: "warning",
  overrides: [],
  rules: {
    "color-no-invalid-hex": true,
    "annotation-no-unknown": true,
    "function-calc-no-unspaced-operator": true,
    "function-no-unknown": true,
    "block-no-empty": true,
    "unit-allowed-list": ["em", "rem", "s", "%", "px", "vw", "vh"],
    "no-duplicate-selectors": true,
    "selector-class-pattern": null,
    "order/properties-order": [
      "position",
      "top",
      "right",
      "bottom",
      "left",
      "z-index",
      "display",
      "justify-content",
      "align-items",
      "float",
      "clear",
      "overflow",
      "overflow-x",
      "overflow-y",
      "margin",
      "margin-top",
      "margin-right",
      "margin-bottom",
      "margin-left",
      "border",
      "border-style",
      "border-width",
      "border-color",
      "border-top",
      "border-top-style",
      "border-top-width",
      "border-top-color",
      "border-right",
      "border-right-style",
      "border-right-width",
      "border-right-color",
      "border-bottom",
      "border-bottom-style",
      "border-bottom-width",
      "border-bottom-color",
      "border-left",
      "border-left-style",
      "border-left-width",
      "border-left-color",
      "border-radius",
      "padding",
      "padding-top",
      "padding-right",
      "padding-bottom",
      "padding-left",
      "width",
      "min-width",
      "max-width",
      "height",
      "min-height",
      "max-height",
      "font-size",
      "font-family",
      "font-weight",
      "text-align",
      "text-justify",
      "text-indent",
      "text-overflow",
      "text-decoration",
      "white-space",
      "color",
      "background",
      "background-position",
      "background-repeat",
      "background-size",
      "background-color",
      "background-clip",
      "opacity",
      "filter",
      "list-style",
      "outline",
      "visibility",
      "box-shadow",
      "text-shadow",
      "resize",
      "transition"
    ]
  }
};

这里, 我们用到了几个插件:
stylelint-config-standard-scss: stylelint默认规则只能格式化css,这里我们使用该插件的规则来格式化scss。
stylelint-config-prettier: 避免stylelint与prettier冲突的插件。
stylelint-order: 给属性排序的插件。属性会按照rules里 order/properties-order 所定义的顺序排序。
此外,我们还要安装stylelint-scss,因为stylelint默认是没有格式化scss的能力的。
安装:

cnpm install stylelint-scss stylelint-config-standard-scss stylelint-config-prettier stylelint-order -D

在 package.json 的 scripts 里添加 stylelint 脚本命令,如下:

"scripts": {
  // ...
  "stylelint": "stylelint src/**/*.{less,scss,css} --fix",
},

使用npm run stylelint即可对src下的样式文件进行格式化。

prettier

1. 安装

cnpm install prettier -D

2. 使用

  • 配置 prettier
    创建 prettierrc.js 文件:
echo module.exports = {}>.prettierrc.js

添加配置,这里可以根据自己需要调整风格。比如:

module.exports = {
  printWidth: 120,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  singleQuote: false,
  jsxSingleQuote: true,
  jsxBracketSameLine: true,
  trailingComma: "none",
  bracketSpacing: true
};

最好再加上.prettierignore文件,避免把不必要的文件也进行格式化。

#ignore
node_modules
.DS_Store
yarn*
*-lock*
dist*
public/

prettier --write即可进行 prettier 格式化。

lint-stated 13.x

1. 安装

cnpm install lint-staged -D

2. 使用

  • 在 package.json 里添加 lint-staged 选项
"lint-staged": {
  "**/*.{js,jsx,ts,tsx}": [
    "eslint --fix"
  ],
  "**/*.{js,jsx,ts,tsx,cjs,json,less,scss,css,md}": [
    "prettier --write"
  ],
  "**/*.{less,scss,css}": [
    "stylelint --fix"
  ]
},
  • 在 package.json 的 scripts 里添加 lint-staged 脚本命令
"scripts": {
  // ...
  "lint-staged": "lint-staged"
},

这样,当我们使用npm run lint-staged的时候,就会自动调用 eslint+prettier 格式化。

commitlint

1. 安装

cnpm install --save-dev @commitlint/config-conventional @commitlint/cli

2. 使用

  • 初始化 commitlint 配置文件
echo "module.exports = {extends: ['@commitlint/config-conventional']}" > commitlint.config.js
  • 配置 commitlint
    1. 提交信息结构
      通常 commitlint 认为我们提交信息格式如下:
type(scope?): subject
body?
footer?

其中, scope/body/footer 这三个可有可无。

  1. 校验规则
    一般的校验规则格式如下:
    [规则名称]: [level, when, value]
    level: 有三个参数。0 代表禁用, 1 代表警告, 2 代表错误
    when: 有两个参数。always 代表总是, never 代表从不
    value: 参数值
    比如:
"subject-empty": [2, "never"],
"body-empty": [2, "always"],
"type-enum": [2, "always", ['feat', 'fix', 'docs', 'style', 'refactor', 'test', 'revert']]

就是强制 body 必须为空,subject 不可为空,type 必须是上面数组里的其中一个。不然就报错。

git commit -m "feat: 增加了新功能"
git commit -m "fea: 增加了新功能" // 报错,type 必须为'feat', 'fix', 'docs', 'style', 'refactor', 'test', 'revert'中的一个

更多规则参考官网
https://commitlint.js.org/#/reference-rules

  1. 自定义校验规则
    如果已有的规则满足不了需求,我们还可以自定义校验规则。自定义校验规则写在 plugins 属性中。
module.exports = {
  extends: ["@commitlint/config-conventional"],
  rules: {
    "type-empty": [2, "always"],
    "scope-empty": [2, "always"],
    "subject-empty": [2, "always"],
    "header-max-length": [2, "always", 100],
    "body-empty": [2, "always"],
    "footer-empty": [2, "always"],
    "action-enum": [
      2,
      "always",
      ["Fixed", "Feature",  "Add", "Modify", "Update", "Delete"]
    ],
    "issue-rule": [2, "always", ["TMP", "TTT"]] // 根据自己需要输入即可
  },
  plugins: [
    {
      rules: {
        "action-enum": ({ raw }, when = "always", value = []) => {
          return [
            value.includes(raw.split(" ")[0]),
            `提交信息不合规范! {Action}错误!
    必须以 "{Action}{空格}#{标号}{空格}" 开头。
    {Action}可选:${value.join("|")}
    比如: Fixed #TMP-111 修复接口传参错误的问题
    ${when === "never" ? "另外: action-enum规则第二个参数必须是always, 建议修改" : ""}...`
          ];
        },
        "issue-rule": ({ raw }, when, value = []) => {
          const issueStr = `^([A-Z][a-z]*\\s#(${value.join("|")})\\-[1-9][0-9]*)`;
          const issueReg = new RegExp(issueStr, "g");
          return [
            issueReg.test(raw),
            `提交信息不合规范! {标号}错误!
    必须以 "{Action}{空格}#{标号}{空格}" 开头。
    {标号}可选: ${value.join("|")}
    比如: Fixed #TMP-111 修复接口传参错误的问题
    ${when === "never" ? "另外: action-enum规则第二个参数必须是always, 建议修改" : ""}...`
          ];
        }
      }
    }
  ]
};

这里,我把 type, scope, subject, body, footer 都强制为空,然后自定义了两个规则action-enumissue-rule
提交代码的时候,如果不符合'必须以 "{Action}{空格}#{标号}{空格}" 开头'的规则,就会报错,提交失败。例如:

git commit -m "Fixed #TMO-222 修复了传参错误的bug"

由于我们定义的标号前缀里面没有TMO,因此会报错:


husky 8.x

1. 安装

cnpm install husky -D

2. 使用

npm set-script prepare "husky install"
npm run prepare

这里我们在 scripts 加了一个 prepare 命令,这个命令会在执行 npm install 时自动执行。

npx husky add .husky/pre-commit "npm run lint-staged"
npx husky add .husky/commit-msg 'npx --no-install commitlint --edit "$1"'

这里我们添加了两个钩子,pre-commit 与 commit-msg。

pre-commit 会在提交前执行npm run lint-staged命令

commit-msg 会在提交时执行npx --no-install commitlint --edit "$1"

至此,我们的配置完成。代码提交的时候会自动对修改的代码进行格式化,同时会按照 commitlint 里的设置来进行提交信息校验。
如果有问题,则会报错,且代码会提交失败。

vscode 插件

1. eslint 和 prettier 插件

推荐使用 vscode 插件 eslint 和 prettier,可以在 settings.json 中设置:

"editor.formatOnSave": true,
"eslint.run": "onSave"

当我们保存的时候,会自动进行格式化, 会自动把 eslint 的错误语法用波浪线标出来。

2. 把 eslint 和 prettier 插件配置加到项目目录

vscode 的配置分两类,工作区和用户区。工作区的优先级高于用户区。
在项目根目录加上.vscode 文件夹,里面是 settings.json 文件。
那么我们的项目就是一个工作区了。
修改 settings.json 配置如下:

{
  "[javascriptreact]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[javascript]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "[json]": {
      "editor.defaultFormatter": "esbenp.prettier-vscode"
  },
  "editor.formatOnSave": true,
  "eslint.run": "onSave"
}

这样就完成了 vscode 配置的共享。


参考资料:
husky官方文档
lint-staged官方文档
commitlint官方网站
commitlint 从0到1 (git commit 校验工具)
前端架构师神技,三招统一代码风格(一文讲透)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,240评论 6 498
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,328评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,182评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,121评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,135评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,093评论 1 295
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,013评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,854评论 0 273
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,295评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,513评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,678评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,398评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,989评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,636评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,801评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,657评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,558评论 2 352

推荐阅读更多精彩内容