挖坑指南:ESLint + VS Code自動格式化程式碼
前言
最近在整理公司的之前的專案,對整個產品線進行梳理重構。由於專案的編碼人員換了好幾撥,每個人編碼又各有各的風格。現在在重新翻看,可讀性很差。考慮到後期專案的擴充套件,以及對現有專案的優化,決定對程式碼進行整理,並統一使用ESLint進行規範約束。
開始
“編碼一時爽,重構火葬場。”
剛開始接觸vue學習的時候,也都是自然而然地關閉了ESLint。以至於沒有從頭開始養成良好的習慣,現在也是從0開始,將ESLint正式引入到工作中。
如果你才剛剛起步,我建議你先了解下ESLint以及它在前端工作流和團隊協作中的必要性。從開始上手時,就規範自己的 編碼行為,好過走到半路又回過來重新開始。
實踐
如何上手?首先還是官方文件。我覺得不論是學什麼,你得先去了解它是什麼,以及它為什麼出現,然後才是衡量是否要在工作或生活中使用它。
官方文件:https://eslint.org/
官方的描述
ESLint它很靈活,所有的檢查都是基於規則的。
ESLint規則:https://cn.eslint.org/docs/rules/
使用ESLint三部曲
- 安裝
npm install -g eslint - 初始化
npm init - 編寫配置檔案(由於ESLint配置檔案支援多種檔案擴充套件,此處以.eslintrc.js為例)
.eslintignore:可以配置指定的目錄或檔案不進行ESLint檢查。module.exports = { /** * 預設情況下,ESLint會在所有父級目錄裡尋找配置檔案,一直到根目錄。 * 為了將ESLint限制在一個特定的專案,設定root: true; * ESLint一旦發現配置檔案中有 root: true,就會停止在父級目錄中尋找。 */ root: true, // 指定解析器 // babel-ESLint: 一個對Babel解析器的包裝,使其能夠與ESLint相容。 // parser: 'babel-eslint', // 設定解析器能幫助ESLint確定什麼是解析錯誤。 parserOptions: { parser: 'babel-eslint', // 指定js版本。語法上的支援 ecmaVersion: 6 }, // 指令碼在執行期間訪問的額外的全域性變數 // globals: {}, // env: 指定指令碼的執行環境 env: { // 一個環境定義了一組預定義的全域性變數。 browser: true, // 會自動開啟es6語法支援。 es6: true, node: true }, // 使用第三方外掛。全域性安裝的 ESLint 例項只能使用全域性安裝的ESLint外掛。本地同理,不支援混用。 plugins: [ 'html', 'vue' ], // 配置檔案從基礎配置中繼承已啟用的規則。 /** * eslint:recommended 啟用核心規則,在規則頁面中被標記為 √ 的。 */ extends: [ // plugin:(此處不能有空格)包名/配置名稱。解析時plugin是解析成 eslint-plugin-vue。如果有空格會解析失敗,eslint-plugin- vue。 // plugin可以省略包名的字首 eslint-plugin- // 'plugin:vue/essential', // '@vue/standard', 'eslint:recommended' // 也可以指定另一個基本配置檔案的絕對路徑或相對路徑 ], /** * 每個規則有【3】個錯誤級別。 * off或0: 關閉該規則; * warn或1: 開啟規則,使用警告級別的錯誤,不會導致程式退出; * error或2: 開啟規則,使用錯誤級別的錯誤,當被觸發的時候,程式會退出。 */ rules: { /** * 【================================================ Possible Errors ================================================】 * 這些規則與JavaScript程式碼中可能的錯誤或邏輯錯誤有關。 */ // 強制"for"迴圈中更新子句的計算器朝著正確的方向移動 'for-direction': 2, // 禁止function定義中出現重名引數 'no-dupe-args': 2, // 禁止物件字面量中出現重複的key 'no-dupe-keys': 2, // 禁止出現重複的case標籤 'no-duplicate-case': 2, // 禁止對catch子句的引數重新賦值 'no-ex-assign': 2, // 禁止對關係運算符的左運算元使用否定操作符 'no-unsafe-negation': 2, // 禁止出現令人困惑的多行表示式 'no-unexpected-multiline': 2, // 禁止在return、throw、continue、break語句之後出現不可達程式碼 'no-unreachable': 2, // 禁止在finally語句塊中出現控制流語句 'no-unsafe-finally': 2, // 要求使用isNaN()檢查NaN 'use-isnan': 2, // 強制typeof表示式與有效的字串進行比較 'valid-typeof': 2, // 還可以寫表示式,厲害了~ 'no-debugger': process.env.NODE_ENV === 'production' ? 'error': 'off', 'no-console': process.env.NODE_ENV === 'production' ? 'error': 'off', /** * 【================================================ Best Practices ================================================】 * 這些規則是關於最佳實踐的,幫助你避免一些問題。 */ // 強制 getter 和 setter在物件中成對出現 'accessor-pairs': 2, // 強制所有控制語句使用一致的括號風格 'curly': [2, 'multi-line'], // 強制在點號之前和之後一致的換行 'dot-location': [2, 'property'], // 要求使用 ===和 !== 'eqeqeq': [2, 'allow-null'], // 禁用arguments.caller 或 arguments.callee 'no-caller': 2, // 禁止使用空解構模式 'no-empty-pattern': 2, // 禁止eval() 'no-eval': 2, // 禁止使用類似eval()的方法 'no-implied-eval': 2, // 禁止擴充套件原生型別 'no-extend-native': 2, // 禁止不必要的.bind()呼叫 'no-extra-bind': 2, // 禁止case語句落空 'no-fallthrough': 2, // 禁止數字字面量中使用前導和末尾小數點 'no-floating-decimal': 2, // 禁用__iterator__屬性 'no-iterator': 2, // 禁用標籤語句 'no-labels': [2, { 'allowLoop' : false, 'allowSwitch': false }], // 禁用不必要巢狀塊 'no-lone-blocks': 2, // 禁止使用多個空格 'no-multi-spaces': 2, // 禁止使用多行字串 'no-multi-str': 2, // 禁止對String,Number 和 Boolean 使用new操作符 'no-new-wrappers': 2, // 禁用八進位制字面量 'no-octal': 2, // 禁止在字串中使用八進位制轉義序列 'no-octal-escape': 2, // 禁止使用__proto__屬性 'no-proto': 2, // 禁止多次宣告同一變數 'no-redeclare': 2, // 禁止在return語句中使用賦值語句 'no-return-assign': [2, 'except-parens'], // 禁止自我賦值 'no-self-assign': 2, // 禁止自我比較 'no-self-compare': 2, // 禁用逗號操作符 'no-sequences': 2, // 禁止丟擲異常字面量 'no-throw-literal': 2, // 禁止一成不變的迴圈條件 'no-unmodified-loop-condition': 2, // 禁止不必要的.call()和.apply() 'no-useless-call': 2, // 禁止不必要的轉義字元 'no-useless-escape': 2, // 禁用with語句 'no-with': 2, // 要求IIFE使用括號括起來 'wrap-iife': 2, // 要求或禁止Yoda條件。 if("red" === color) { //字面量在前,變數在後 } 'yoda': [2, 'never'], // 比較絕不能是Yoda條件(需要變數在前,字面量在後) /** * 【================================================ ECMAScript 6 ================================================】 * 這些規則只與ES6有關,即通常所說的ES2015。 */ // 強制箭頭函式前後使用一致的空格 'arrow-spacing': [2, { 'before': true, 'after' : true }], // 要求在建構函式中有super()呼叫 'constructor-super': 2, // 強制generator函式中*號周圍使用一致的空格 'generator-star-spacing': [2, { 'before': true, 'after' : true }], // 禁止修改類宣告的變數 'no-class-assign': 2, // 禁止修改const宣告的變數 'no-const-assign': 2, // 禁止類成員中出現重複的名稱 'no-dupe-class-members': 2, // 禁止 Symbolnew 操作符和 new 一起使用 'no-new-symbol': 2, // 禁止在建構函式中,在呼叫super()之前使用 this 或 super 'no-this-before-super': 2, // 禁止在物件中使用不必要的計算屬性 'no-useless-computed-key': 2, // 禁止不必要的建構函式 'no-useless-constructor': 2, // 禁止模板字串中嵌入表示式周圍空格的使用 'template-curly-spacing': [2, 'never'], // 強制yield*表示式中的*周圍使用空格 'yield-star-spacing': [2, 'both'], // 要求使用const宣告那些聲明後不再被修改的變數 'prefer-const': 2, /** * 【================================================ Stylistic Issues ================================================】 * 這些規則是關於程式碼風格的。 */ // 獲取當前執行環境的上下文時,強制使用一致的命名(此處強制使用 'that')。 'consistent-this': [2, 'that'], // 禁止或強制在程式碼塊中開括號前和閉括號後有空格 { return 11 } 'block-spacing': [2, 'always'], // 強制在程式碼塊中使用一致的大括號風格 'brace-style': [2, '1tbs', { 'allowSingleLine': true }], // 強制使用駝峰拼寫法命名規定 'camelcase': [0, { 'properties': 'always' }], // 要求或禁止末尾逗號 'comma-dangle': [2, 'never'], // 強制在逗號前後使用一致的空格 'comma-spacing': [2, { 'before': false, 'after' : true }], // 強制在逗號前後使用一致的空格 'comma-style': [2, 'last'], // 要求或禁止檔案末尾存在空行 'eol-last': 2, // 強制使用一致的縮排 'indent': [2, 2, { 'SwitchCase': 1 }], // 強制在JSX屬性中一致地使用雙引號或單引號 'jsx-quotes': [2, 'prefer-single'], // 要求建構函式首字母大寫 'new-cap': [2, { 'newIsCap': true, 'capIsNew': false }], // 要求構造無參建構函式時有圓括號 'new-parens': 2, // 禁用Array建構函式 'no-array-constructor': 2, // 禁止空格和tab的混合縮排 'no-mixed-spaces-and-tabs': 2, // 禁止出現多行空行 'no-multiple-empty-lines': [2, { // 最大連續空行數 max: 2 }], // 禁止在函式識別符號和其呼叫之間有空格 'func-call-spacing': 2, // 禁止行尾空格 'no-trailing-spaces': 2, // 禁止可以在有更簡單的可替代的表示式時使用三元操作符 'no-unneeded-ternary': [2, { // 允許表示式作為預設的賦值模式 'defaultAssignment': true }], // 禁止屬性前有空白 'no-whitespace-before-property': 2, // 強制函式中的變數要麼一起宣告要麼分開宣告 'one-var': [2, { 'initialized': 'never' }], // 強制操作符使用一致的換行符 'operator-linebreak': [2, 'after', { 'overrides': { '?': 'before', ':': 'before' } }], // 要求或禁止塊內填充 'padded-blocks': [2, 'never'], // 強烈使用一致的反勾號``、雙引號""或單引號'' 'quotes': [2, 'single', { // 允許字串使用單引號或者雙引號,只要字串中包含了一個其他引號,否則需要轉義 'avoidEscape': true, // 允許字串使用反勾號 'allowTemplateLiterals': true }], // 禁止使用分號代替ASI(自動分號插入) 'semi': [2, 'never'], // 強制分號之前和之後使用一致的空格 'semi-spacing': [2, { 'before': false, 'after' : true }], // 強制在塊之前使用一致的空格 'space-before-blocks': [2, 'always'], // 強制在圓括號內使用一致的空格 'space-in-parens': [2, 'never'], // 要求操作符周圍有空格 'space-infix-ops': 2, // 強制在一元操作符前後使用一致的空格 'space-unary-ops': [2, { 'words' : true, 'nonwords': false }], // 強制在註釋// 或/*使用一致的空格 'spaced-comment': [1, 'always', { 'markers': ['global', 'globals', 'eslint', 'eslint-disable', '*package', '!', ','] }], // 強制在大括號中使用一致的空格 'object-curly-spacing': [2, 'always', { 'objectsInObjects': false }], // 禁止或強制在括號內使用空格 'array-bracket-spacing': [2, 'never'], // 強制要求在物件字面量的屬性中鍵和值之間使用一致的間距 'key-spacing': [2, { 'beforeColon': false, 'afterColon' : true }], /** * 【================================================ Node.js and CommonJS ================================================】 * 這些規則是關於Node.js 或 在瀏覽器中使用CommonJS的。 */ // 要求回撥函式中有容錯處理 'handle-callback-err': [2, '^(err|error)$'], // 禁止呼叫 require 時使用new操作符 'no-new-require': 2, // 禁止對__dirname和__filename進行字串連線 'no-path-concat': 1, // 強制在function的左括號之前使用一致的空格 'space-before-function-paren': [2, 'never'], /** * 【================================================ Possible Errors ================================================】 * 這些規則與JavaScript程式碼中可能的錯誤或邏輯錯誤有關。 */ // 禁止條件表示式中出現賦值操作符 'no-cond-assign': 2, // 禁止在正則表示式中使用控制字元 'no-control-regex': 0, // 禁止在正則表示式中使用空字符集 'no-empty-character-class': 2, // 禁止不必要的布林轉換 'no-extra-boolean-cast': 2, // 禁止不必要的括號 'no-extra-parens': [2, 'functions'], // 禁止對function宣告重新賦值 'no-func-assign': 2, // 禁止在巢狀塊中出現變數宣告或function宣告 'no-inner-declarations': [2, 'functions'], // 禁止RegExp建構函式中存在無效的正則表示式字串 'no-invalid-regexp': 2, // 禁止在字串和註釋之外不規則的空白 'no-irregular-whitespace': 2, // 禁止把全域性物件作為函式呼叫 'no-obj-calls': 2, // 禁止正則表示式字面量中出現多個空格 'no-regex-spaces': 2, // 禁用稀疏陣列 'no-sparse-arrays': 2, /** * 【================================================ Variables ================================================】 * 這些規則與變數宣告有關。 */ // 禁止刪除變數 'no-delete-var': 2, // 不允許標籤與變數同名 'no-label-var': 2, // 禁止將識別符號定義為受限的名字 'no-shadow-restricted-names': 2, // 禁止未宣告的變數,除非它們在/*global */註釋中被提到 'no-undef': 2, // 禁止將變數初始化為undefined 'no-undef-init': 2, // 禁止出現未使用的變數 'no-unused-vars': [2, { 'var' : 'all', 'args': 'none' }], /** * 【================================================ 配置定義在外掛中的規則 ================================================】 * 格式: 外掛名/規則ID */ // 'vue/no-parsing-error': [2, { 'x-invalid-end-tag': false }] } }
這個專案框架是iview-admin2.x.x的,專案本身基於vue 3.x以及webpack4.x,專案自身做了基本配置。
還要安裝我們的外掛,
npm install eslint-plugin-html --save-dev
npm install eslint-plugin-vue --save-dev
在VS Code中自動按照ESLint規則格式化程式碼
- 在VS Code中安裝外掛(ESLint、Vurter、Prettier)
- 檔案 => 首選項 => 設定開啟settings.json檔案,個人配置如下
-
{ // 是否允許自定義的snippet片段提示 "editor.snippetSuggestions" : "top", "editor.fontSize" : 20, "editor.fontWeight" : "400", "editor.formatOnType" : true, "workbench.iconTheme" : "material-icon-theme", "workbench.colorTheme" : "One Dark Pro", "guides.enabled": false, "editor.tabSize": 2, "git.confirmSync": false, "window.zoomLevel": 0, "editor.renderWhitespace": "boundary", "editor.cursorBlinking": "smooth", "editor.minimap.enabled": true, "editor.minimap.renderCharacters": false, "window.title": "${dirty}${activeEditorMedium}${separator}${rootName}", "editor.codeLens": true, // 配置檔案關聯,以便啟用對應的提示 "files.associations": { "*.vue": "vue", "*.wxss": "css" }, // 配置emmet是否啟用tab展開縮寫 "emmet.triggerExpansionOnTab": true, // 配置emmet對檔案型別的支援 "emmet.syntaxProfiles": { "javascript": "jsx", "vue": "html", "vue-html": "html" }, // 是否開啟eslint檢測 "eslint.enable": true, // 檔案儲存時是否根據eslint進行格式化 "eslint.autoFixOnSave": true, // eslint配置檔案 "eslint.options": { "extensions": [ ".js", ".vue" ] }, // eslint能夠識別的檔案字尾型別 "eslint.validate": [ "javascript",{ "language": "vue", "autoFix": true },"html", "vue" ], "search.exclude": { "**/node_modules": true, "**/bower_components": true, "**/dist": true }, // 格式化快捷鍵(預設):Shift+Alt+F // prettier進行格式化時,開啟eslint支援 // "prettier.eslintIntegration": true, // 是否使用單引號 "prettier.singleQuote": true, }
總結
遇到的問題:
- 未安裝外掛(要注意觀看官網的使用規則)eslint-plugin-html和eslint-plugin-vue報錯;
-
parser的配置問題:https://github.com/vuejs/eslint-plugin-vue/issues/30
其實遇到的坑還是蠻多的,捋清楚了,寫文章時倒是沒什麼可寫了。還是說一下,
- 關於外掛和依賴,可以自己多實踐一下,不安裝會有什麼影響,它的作用是什麼;
- 儘可能地多看官網的介紹,再結合自己和團隊的編碼風格,制定統一的規範;
- ESLint+VS Code(此前使用sublime較多)可以加快我們的工作效率,提高程式碼質量;
- VS Code的配置,只是幫我們程式碼風格(如自動新增空格),一些JS的變數、語法上(如==和===)的東西,還是需要我們自己手動去修改的。
最後,你有什麼想法和思考可以在下方留言評論,大家一起談論技術,一起成長,感謝~或者掃描下方二維碼,與我取得聯絡~ (記得備註:CSND喔~)
後記
好的文章看過很多,對於自己的表述不是很滿意,可能常常會讓人越看越疑惑。還是繼續堅持吧,記錄下來,總會成長的...