前端代码风格统一方案

Script

  1. 创建文件
touch uniformCodeStyle.sh
  1. Edit 文件
#!/usr/bin/env bash

yarn add eslint --dev
yarn run eslint --init
touch .eslintignore
echo .eslintrc.js > .eslintignore
yarn add eslint-plugin-bowen-lint eslint-plugin-jsx-a11y eslint-plugin-react-hooks@latest @typescript-eslint/parser -D

# first ] line number
line=$(grep -n ']' .eslintrc.js | sed -n "1"p | cut -d ":" -f 1)
commaLine=`expr $line - 1`

# MacOS sed command
sed -i '' "${commaLine} s/$/,/" .eslintrc.js
sed -i '' "${line}i\\
\        \"plugin:bowen-lint/reactLint\"
" .eslintrc.js
insert='\      quotes: ["error", "single", { allowTemplateLiterals: true }],\
\      indent: ["error", 2],\
\      "comma-dangle": ["error", "never"],\
\      "react/jsx-indent": ["error", 2],\
\      "no-console": ["error", { allow: ["warn", "error"] }],\
\      "react/jsx-tag-spacing": [\
\        "error",\
\        {\
\          closingSlash: "never",\
\          beforeSelfClosing: "allow",\
\          afterOpening: "never",\
\          beforeClosing: "allow"\
\        }\
\      ],\
\      "object-curly-spacing": ["warn", "never"],\
\      "react/jsx-indent-props": ["off", "tab"],\
\      "import/no-unresolved": "off",\
\      "no-unused-vars": "off",\
\      "@typescript-eslint/no-unused-vars": ["error"] \
'
sed -i '' "/rules/ a\\
$insert" .eslintrc.js
sed -i '' "/rules/ a\\ ${insert}" .eslintrc.js
yarn add --dev --exact prettier
touch .prettierrc.js
echo "module.exports = {
  singleQuote: true,
  printWidth: 100,
  tabWidth: 2,
  useTabs: false,
  semi: true,
  quoteProps: 'as-needed',
  jsxSingleQuote: false,
  trailingComma: 'none',
  bracketSpacing: true,
  jsxBracketSameLine: false,
  arrowParens: 'always',
  rangeStart: 0,
  rangeEnd: Infinity,
  requirePragma: false,
  insertPragma: false,
  proseWrap: 'preserve',
  htmlWhitespaceSensitivity: 'css',
  endOfLine: 'lf'
};" > .prettierrc.js
touch .prettierignore
echo "**/*.md
**/*.svg
**/*.ejs
**/*.html
package.json" > .prettierignore

yarn add lint-staged --dev
touch .lintstagedrc.js
echo "module.exports = {
  '**/*.{js,jsx,tsx,ts,scss,md,json}': ['prettier --write', 'git add'],
  '**/*.{js,jsx,ts,tsx}': 'yarn lint-staged:js',
}" > .lintstagedrc.js
yarn add @commitlint/{cli,config-conventional} -D
touch .commitlintrc.js
yarn add commitlint-config-bowen-lint -D
echo 'module.exports = {
    "extends": [
        "bowen-lint"
    ]
}' > .commitlintrc.js

yarn add husky@next --dev
yarn husky install

## MacOS sed command
sed -i '' "/scripts/ a\\
\    \"lint-staged:js\": \"eslint --ext .js,.jsx,.ts,.tsx\",
" package.json
npx husky add .husky/pre-commit "yarn lint-staged"
npx husky add .husky/commit-msg "yarn commitlint --edit"

  1. 执行
source uniformCodeStyle.sh

背景

随着前端应用的大型化和复杂化,越来越多的前端工程师和团队开始重视 JavaScript 代码规范。ESLint 是一款插件化的 JavaScript 代码静态检查工具, 其核心是通过对代码解析得到的 AST(Abstract Syntax Tree,抽象语法树)进行模式匹配,定位不符合约定规范的代码。可以根据个人/团队的代码风格进行配置,如果想降低配置成本,也可以直接使用开源配置方案,例如 eslint-config-airbnb。之后再搭配一些辅助工具(例如 husky 和 lint-staged),可以使得整个流程会更加顺畅。

Eslint

Find and fix problems in your JavaScript code.

  1. Install

    yarn add eslint --dev
    
  2. Set up a configuration file

    yarn run eslint --init 
    

    创建 .eslintrc.js 文件,并使用 eslint-plugin-react, @typescript-eslint/eslint-plugin, eslint-config-airbnb, eslint, eslint-plugin-import, eslint-plugin-jsx-a11y, eslint-plugin-react-hooks, @typescript-eslint/parser 等 package

    'off' or 0 - turn the rule off

    'warn' or 1 - turn the rule on as a warning (doesn't affect exit code)

    'error' or 2 - turn the rule on as an error (exit code will be 1)

  3. View .eslintrc.js

    module.exports = {
        "parser": {},  //定义ESLint的解析器
        "extends": [], // 定义文件继承的子规范
        "plugins": [], // 定义了该eslint文件所依赖的插件
        "env": {},
        "rules": {}
    }
    
  4. Individual Rules

    rules: {
        indent: ['error', 4],
        quotes: ['error', 'single', { allowTemplateLiterals: true }],
        'comma-dangle': ['error', 'never'],
        'react/jsx-indent': ['error', 4],
        'no-console': ['error', { allow: ['warn', 'error'] }],
        'react/jsx-tag-spacing': [
            'error',
            {
                closingSlash: 'never',
                beforeSelfClosing: 'allow',
                afterOpening: 'never',
                beforeClosing: 'allow',
            },
        ],
        'object-curly-spacing': ['warn', 'never'],
        'react/jsx-filename-extension': [
            'warn',
            { extensions: ['.js', '.jsx'] },
        ],
        'react/jsx-indent-props': ['off', 'tab'],
        'no-use-before-define': 'off',
    }
    
  5. All Rules

  6. Ignore

    touch .eslintignore
    
    // .eslintignore
    .eslintrc.js
    

Prettier

Prettier is an opinionated code formatter.

  1. Install

    yarn add --dev --exact prettier
    
  2. Config file

    touch .prettierrc.js
    
    // .prettierrc.js
    module.exports = {
      ...fabric.prettier,
      "singleQuote": true,
      "trailingComma": "all",
      "printWidth": 80,
    };
    
  3. Ignore file

    touch .prettierignore
    
    // .prettierignore
    # Ignore artifacts:
    build
    **/*.md
    **/*.svg
    **/*.ejs
    **/*.html
    package.json
    

Eslint VS Prettier

这里说下 Eslint 与 Prettier 之间的区别,二者的侧重点不同,前者是代码规范检查,如是否可以使用 var,尾逗号,函数括号前面是否留空格,便于团队保 持比较统一的代码风格,而 Prettier 则是代码格式化插件,可以根据一定的规则对我们的 js、css、less、jsx、vue 等文件进行格式化,保证团队输出的代 码是统一的,所以二者除了小部分规则有交集之外,二者是可以在我们的开发中相辅相成的。

Use Prettier for formatting and linters for catching bugs!

Husky

Husky improves your commits and more 🐶 woof!

You can use it to lint your commit messages, run tests, lint code, etc... when you commit or push. Husky supports
all Git hooks.

Husky 是一个 git 钩子插件,支持所有的 Git Hooks 钩子,我们可以在这些钩子触发的时候执行某些命令或者操作

  1. Install

    yarn add husky@next --dev
    
  2. Enable Git hooks

    yarn husky install
    
  3. Edit package.json

    {
      "private": true,
      "scripts": {
        "husky-test": "echo 'Hello world!'",
        "postinstall": "husky install"
      }
    }
    
  4. Add a Hook

    npx husky add .husky/pre-commit "yarn husky-test"
    

    Husky 只是提供了提交时的钩子,然而有时候我们处理的项目并不是新项目,这个时候,可能只想对本次提交的代码,做代码检查,而不是对现有目录内所有的文件做检查,所以我们需要引入 lint-staged 这个插件

lint-staged

Run linters against staged git files and don't let 💩 slip into your code base!

  1. Install

    yarn add lint-staged --dev
    
  2. Configuration

    touch .lintstagedrc.js
    
  3. Edit script

    // package.json
    script: {
        ...
        "lint-staged": "lint-staged",
        "lint": "npm run lint:js && npm run lint:prettier",
        "lint:js": "eslint --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
        "lint:prettier": "check-prettier lint",
        "lint-staged:js": "eslint --ext .js,.jsx,.ts,.tsx",
        "lint:fix": "eslint --fix --cache --ext .js,.jsx,.ts,.tsx --format=pretty ./src",
        "prettier": "prettier -c --write \"**/*\""
    }
    
  4. Filtering files

    module.exports = {
      '**/*.{js,jsx,tsx,ts,scss,md,json}': ['prettier --write', 'git add'],
      '**/*.{js,jsx,ts,tsx}': 'yarn lint-staged:js',
    }
    
  5. Edit .husky/_/pre-commit

    yarn lint-staged
    

Commitlint

Helps your team adhering to a commit convention.

  1. Install

    yarn add @commitlint/{cli,config-conventional} -D 
    
  2. Configuration

    touch .commitlintrc.js
    
    // .commitlintrc.js
    module.exports = {
      extends: ['@commitlint/config-conventional'],
      rules: {
        'type-enum': [
          2,
          'always',
          [
            'feat', // 新功能(feature)
            'fix', // 修补bug
            'docs', // 文档(documentation)
            'style', //  格式(不影响代码运行的变动)
            'refactor', // 重构(即不是新增功能,也不是修改bug的代码变动)
            'test', // 增加测试
            'chore', // 构建过程或辅助工具的变动
          ],
        ],
        'scope-empty': [1, 'always'],
        'body-leading-blank': [2, 'always'],
        'footer-leading-blank': [2, 'always'],
      },
    };
    
  3. Husky

    npx husky add .husky/commit-msg "yarn commitlint --edit"
    

进阶

如果想要让整个公司规模化地应用统一的 JavaScript 代码规范,问题就会变得较为复杂

问题分析

技术层面

  • 技术选型分散 - 团队内工程技术选型往往并不统一,如 React/Vue、JavaScript/TypeScript 等。
  • 技术场景更加广泛 - 对于大型团队,其开发场景一般不会局限在传统 Web 领域内,往往还会涉及 Node.js、React Native、小程序、桌面应用(例如 Electron)等更广泛的技术场景。
  • 工程数量的增加和工程方案离散化导致 ESLint 方案的复杂度提升 - 这样会进一步增加工程接入成本、升级成本和方案维护成本。

需要解决的问题

如何制定统一的代码规范和对应的 ESLint 配置?

  • 场景支撑 - 如何实现对场景差异的支持?如何保证不同场景间一致部分(例如 JavaScript 基础语法)的规范一致性?
  • 技术选型支撑 - 如何在支撑不同技术选型的前提下,保证基础规则(例如缩进)的一致性?
  • 可维护性 - 具体到规则配置上,能否提升可复用性?在方案升级迭代时成本是否可控?

解决方案

代码风格统一方案
  • 基础层 - 制定统一的基础语法和格式规范,提供通用的代码风格和语法规则配置,例如缩进、尾逗号等等。
  • 框架支撑层(可选) - 提供对通用的一些技术场景、框架的支持,包括 React、Vue 等;这一层借助开源社区的各种插件进行配置,并对各种框架的规则都进行了一定的调整。
  • TypeScript 层(可选) - 这一层借助 typescript-eslint,提供对 TypeScript 的支持。
  • 适配层(可选) - 提供对特殊场景的定制化支持,例如配合 prettier 使用、或者某些团队的特殊规则诉求。

具体的实际项目中,可以灵活的选择各层级、各类型的搭配,获得和项目匹配的 ESLint 规则集。最终,形成了如下所示的 ESLint 配置集:

Eslint 配置集

这种通过分层、分类的结构设计,还有利于后期的维护:

  • 对基础层的修改,只需修改一处即会全局生效。
  • 对非基础层某一部分的调整不会产生关联性的影响。
  • 如需扩展对某一类型的支持,只需关注这一类型的特殊规则配置。

Individual Plugin

eslint-plugin-bowen-lint

Installation

You'll first need to install ESLint:

$ npm i eslint --save-dev

// yarn
$ yarn add eslint -D

Next, install eslint-plugin-bowen-lint:

$ npm install eslint-plugin-bowen-lint --save-dev

// yarn
$ yarn add eslint-plugin-bowen-lint -D

Usage

Add bowen-lint to the extends section of your .eslintrc configuration file. You can omit the eslint-plugin- prefix:

{
    "extends": [
        "plugin:bowen-lint/reactLint"
    ]
}

Necessary Config

  • eslint-config-airbnb

Necessary Plugin

  • eslint-plugin-import

Supported Config

  • plugin:bowen-lint/reactLint
  • plugin:bowen-lint/vueLint
  • plugin:bowen-lint/tsLint
  • plugin:bowen-lint/prettierLint

Why Plugin not Config?

  • Plugins - which third-party plugins define additional rules, environments, configs, etc. for ESLint to use.
  • Rules - which rules are enabled and at what error level.
  • Config -> extends
    -> Extending Configuration File
    -> A configuration file, once extended, can inherit all the traits of another configuration file (including rules,
    plugins, and language options) and modify all the options.

How to create eslint plugin

generator-eslint

  1. Install

    npm i -g yo
    npm i -g generator-eslint
    
  2. Create empty file

    mkdir eslint-plugin-demo
    cd eslint-plugin-demo
    
  3. Eslint:plugin

    yo eslint:plugin
    
  4. Eslint:rule

    yo eslint:rule
    
  5. 注意:如果是 extends 的参数值,需要放置在 package.jsdependencies 属性中

代码集成检查

在代码 Commit 时,通过 GitHook 触发 ESLint 检查。其优点在于能实时响应开发者的动作,给出反馈,快速定位和修复问题;缺陷在于开发者可以主动跳过检查。使用 Husky + Lint-staged 结合,在代码 Commit 时,通过 GitHook 触发对 git 暂存区文件的检查。

Individual Config

commitlint-config-bowen-lint

Installation

$ npm install commitlint-config-bowen-lint --save-dev

// yarn
$ yarn add commitlint-config-bowen-lint -D

Usage

Add bowen-lint to the extends section of your .commitlintrc.js configuration file. You can omit
the commitlint-config- prefix:

{
    "extends": [
        "bowen-lint"
    ]
}

Configuration

extends: ['@commitlint/config-conventional'],
rules: {
    'type-enum': [
        2,
        'always',
        [
            'feat', 
            'fix', 
            'docs',
            'style', 
            'refactor', 
            'test', 
            'chore', 
        ],
    ],
    'scope-empty': [1, 'always'],
    'body-leading-blank': [2, 'always'],
    'footer-leading-blank': [2, 'always'],
}

相关材料

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

推荐阅读更多精彩内容