草庐IT

前端要做代码规范

麦西的西 2023-03-28 原文

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 校验工具)
前端架构师神技,三招统一代码风格(一文讲透)

有关前端要做代码规范的更多相关文章

  1. ruby - 如何在 buildr 项目中使用 Ruby 代码? - 2

    如何在buildr项目中使用Ruby?我在很多不同的项目中使用过Ruby、JRuby、Java和Clojure。我目前正在使用我的标准Ruby开发一个模拟应用程序,我想尝试使用Clojure后端(我确实喜欢功能代码)以及JRubygui和测试套件。我还可以看到在未来的不同项目中使用Scala作为后端。我想我要为我的项目尝试一下buildr(http://buildr.apache.org/),但我注意到buildr似乎没有设置为在项目中使用JRuby代码本身!这看起来有点傻,因为该工具旨在统一通用的JVM语言并且是在ruby中构建的。除了将输出的jar包含在一个独特的、仅限ruby​​

  2. ruby-on-rails - Rails 源代码 : initialize hash in a weird way? - 2

    在rails源中:https://github.com/rails/rails/blob/master/activesupport/lib/active_support/lazy_load_hooks.rb可以看到以下内容@load_hooks=Hash.new{|h,k|h[k]=[]}在IRB中,它只是初始化一个空哈希。和做有什么区别@load_hooks=Hash.new 最佳答案 查看rubydocumentationforHashnew→new_hashclicktotogglesourcenew(obj)→new_has

  3. ruby-on-rails - 浏览 Ruby 源代码 - 2

    我的主要目标是能够完全理解我正在使用的库/gem。我尝试在Github上从头到尾阅读源代码,但这真的很难。我认为更有趣、更温和的踏脚石就是在使用时阅读每个库/gem方法的源代码。例如,我想知道RubyonRails中的redirect_to方法是如何工作的:如何查找redirect_to方法的源代码?我知道在pry中我可以执行类似show-methodmethod的操作,但我如何才能对Rails框架中的方法执行此操作?您对我如何更好地理解Gem及其API有什么建议吗?仅仅阅读源代码似乎真的很难,尤其是对于框架。谢谢! 最佳答案 Ru

  4. ruby - 模块嵌套代码风格偏好 - 2

    我的假设是moduleAmoduleBendend和moduleA::Bend是一样的。我能够从thisblog找到解决方案,thisSOthread和andthisSOthread.为什么以及什么时候应该更喜欢紧凑语法A::B而不是另一个,因为它显然有一个缺点?我有一种直觉,它可能与性能有关,因为在更多命名空间中查找常量需要更多计算。但是我无法通过对普通类进行基准测试来验证这一点。 最佳答案 这两种写作方法经常被混淆。首先要说的是,据我所知,没有可衡量的性能差异。(在下面的书面示例中不断查找)最明显的区别,可能也是最著名的,是你的

  5. ruby - 寻找通过阅读代码确定编程语言的ruby gem? - 2

    几个月前,我读了一篇关于ruby​​gem的博客文章,它可以通过阅读代码本身来确定编程语言。对于我的生活,我不记得博客或gem的名称。谷歌搜索“ruby编程语言猜测”及其变体也无济于事。有人碰巧知道相关gem的名称吗? 最佳答案 是这个吗:http://github.com/chrislo/sourceclassifier/tree/master 关于ruby-寻找通过阅读代码确定编程语言的rubygem?,我们在StackOverflow上找到一个类似的问题:

  6. ruby - Net::HTTP 获取源代码和状态 - 2

    我目前正在使用以下方法获取页面的源代码:Net::HTTP.get(URI.parse(page.url))我还想获取HTTP状态,而无需发出第二个请求。有没有办法用另一种方法做到这一点?我一直在查看文档,但似乎找不到我要找的东西。 最佳答案 在我看来,除非您需要一些真正的低级访问或控制,否则最好使用Ruby的内置Open::URI模块:require'open-uri'io=open('http://www.example.org/')#=>#body=io.read[0,50]#=>"["200","OK"]io.base_ur

  7. 程序员如何提高代码能力? - 2

    前言作为一名程序员,自己的本质工作就是做程序开发,那么程序开发的时候最直接的体现就是代码,检验一个程序员技术水平的一个核心环节就是开发时候的代码能力。众所周知,程序开发的水平提升是一个循序渐进的过程,每一位程序员都是从“菜鸟”变成“大神”的,所以程序员在程序开发过程中的代码能力也是根据平时开发中的业务实践来积累和提升的。提高代码能力核心要素程序员要想提高自身代码能力,尤其是新晋程序员的代码能力有很大的提升空间的时候,需要针对性的去提高自己的代码能力。提高代码能力其实有几个比较关键的点,只要把握住这些方面,就能很好的、快速的提高自己的一部分代码能力。1、多去阅读开源项目,如有机会可以亲自参与开源

  8. 7个大一C语言必学的程序 / C语言经典代码大全 - 2

    嗨~大家好,这里是可莉!今天给大家带来的是7个C语言的经典基础代码~那一起往下看下去把【程序一】打印100到200之间的素数#includeintmain(){ inti; for(i=100;i 【程序二】输出乘法口诀表#includeintmain(){inti;for(i=1;i 【程序三】判断1000年---2000年之间的闰年#includeintmain(){intyear;for(year=1000;year 【程序四】给定两个整形变量的值,将两个值的内容进行交换。这里提供两种方法来进行交换,第一种为创建临时变量来进行交换,第二种是不创建临时变量而直接进行交换。1.创建临时变量来

  9. git使用常见问题(提交代码,合并冲突) - 2

    文章目录git常用命令(简介,详细参数往下看)Git提交代码步骤gitpullgitstatusgitaddgitcommitgitpushgit代码冲突合并问题方法一:放弃本地代码方法二:合并代码常用命令以及详细参数gitadd将文件添加到仓库:gitdiff比较文件异同gitlog查看历史记录gitreset代码回滚版本库相关操作远程仓库相关操作分支相关操作创建分支查看分支:gitbranch合并分支:gitmerge删除分支:gitbranch-ddev查看分支合并图:gitlog–graph–pretty=oneline–abbrev-commit撤消某次提交git用户名密码相关配置g

  10. ruby - 这两段代码有什么区别? - 2

    打印1:defsum(i)i=i+[2]end$x=[1]sum($x)print$x打印12:defsum(i)i.push(2)end$x=[1]sum($x)print$x后者是修改全局变量$x。为什么它在第二个例子中被修改而不是在第一个例子中?类Array的任何方法(不仅是push)都会发生这种情况吗? 最佳答案 变量范围在这里无关紧要。在第一段代码中,您仅使用赋值运算符=为变量i赋值,而在第二段代码中,您正在修改$x(也称为i)使用破坏性方法push。赋值从不修改任何对象。它只是提供一个名称来引用一个对象。方法要么是破坏性

随机推荐