前端代碼風格統一方案

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閱讀 229,001評論 6 537
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,786評論 3 423
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 176,986評論 0 381
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,204評論 1 315
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,964評論 6 410
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,354評論 1 324
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,410評論 3 444
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,554評論 0 289
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,106評論 1 335
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,918評論 3 356
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,093評論 1 371
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,648評論 5 362
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,342評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,755評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,009評論 1 289
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,839評論 3 395
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,107評論 2 375

推薦閱讀更多精彩內容