一鍵格式化代碼帶來的快感 | 你還在爲每一個項目配置Stylelint和Eslint嗎

做者:JowayYoung
倉庫:GithubCodePen
博客:官網掘金思否知乎
公衆號:IQ前端
特別聲明:原創不易,未經受權不得轉載或抄襲,如需轉載可聯繫筆者受權css

前言

大部分前端項目都配置StylelintEslintTslintPrettier四大前端代碼校驗工具。代碼校驗工具如下簡稱Lint,爲了解決代碼不嚴謹,經過預設規則校驗代碼,檢測其是否存在錯誤/漏洞,並對錯誤/漏洞提示修復方案並儘量依據修復方案格式化出正確代碼。該功能稱爲格式化代碼,基本上全部編輯器都需配置該功能。html

Lint其實就是編輯器裏運行的一個腳本進程,將代碼解析成抽象語法樹,遍歷抽象語法樹並經過預設規則作一些判斷和修改,再將新的抽象語法樹轉換成正確代碼。整個校驗過程都跟抽象語法樹相關,若暫未接觸過抽象語法樹,可閱讀babel源碼eslint源碼瞭解其工做原理。前端

開發過程當中啓用Lint能帶來如下好處。vue

  • 可強制規範團隊編碼規範,讓新舊組員編碼習慣獲得一致提高
  • 可靈活定製團隊編碼風格,讓預設規則符合新舊組員心理預期
  • 增長項目代碼的可維護性可接入性,讓新組員能快速適應項目的架構與需求
  • 保障項目總體質量,可減小無用代碼重複代碼錯誤代碼漏洞代碼的產生概率

千萬不能自私node

有些同窗可能一時適應不了Lint帶來的強制性操做,會在本身編輯器裏關閉項目全部校驗功能,這種自私行爲會帶來很嚴重的後果。react

若上傳無任何校驗痕跡的代碼塊,當其餘組員將該代碼塊更新合併到原有代碼上時,因爲編輯器一直配置着團隊編碼規範,致使被拉下來的代碼塊立馬報錯甚至產生衝突。git

上述狀況會讓其餘組員花費更多時間解決由於你不遵照規矩而帶來的問題,還浪費團隊爲了研究如何讓總體編碼風格更適合組員的精力。github

這種自私行爲不可取,若團隊無任何編碼規範可隨意編碼,若已承認團隊編碼規範那就努力遵照,不給團隊帶來麻煩。web

背景

本文着重講解一鍵格式化代碼的部署,像Lint經常使用配置就不會講解,畢竟百度谷歌一搜一大堆。這個一鍵固然是ctrl+scmd+s保存文件啦。在保存文件時觸發Lint自動格式化代碼,這個操做固然不能100%保證將代碼格式化出最正確代碼,而是儘量依據修復方案格式化出正確代碼。言下之意就是可能存在部分代碼格式化失敗,但將鼠標移至紅色下劃線上會提示修復方案,此時可依據修復方案自行修正代碼。typescript

爲什麼寫下本文?筆者有着嚴謹的代碼邏輯和優雅的編碼風格,因此特別喜歡格式化代碼。然而又不想爲每一個項目配置Lint,這些重複無腦的複製粘貼讓筆者很反感,因此筆者只想一次配置全局運行Lint,這樣就無需爲每一個項目配置Lint。在大量百度谷歌都未能搜到一篇相關文章(搜到的所有文章都是單獨爲一個項目配置,害),筆者就花了半年多時間探討出本方案,真正作到一次配置全局運行。若使用本方案,相信能將全部項目的StylelintEslintTslintPrettier相關依賴和配置文件所有移除,使項目目錄變得超級簡潔,如同下圖。

目錄

筆者選用VSCode做爲前端開發的編輯器,其餘編輯器不是性能差就是配置麻煩,因此通通放棄,只認VSCode

在此強調兩個重要問題,這兩個問題影響到後面可否成功部署VSCode一鍵格式化代碼

  • Tslint官方已宣佈廢棄Tslint,改用Eslint代替其全部校驗功能
  • Eslint部分配置與Prettier部分配置存在衝突且互相影響,爲了保證格式化性能就放棄接入Prettier

因此部署VSCode一鍵格式化代碼只需安裝StylelintEslint兩個插件。爲了方便表述,統一如下名詞。

  • 如下說起的StylelintEslint均爲VSCode插件
  • 如下說起的stylelinteslint均爲NPM依賴

步驟

前方高能,兩大步驟就能爲VSCode部署一鍵格式化代碼,請認真閱讀喔!

安裝依賴

爲了搞清楚兩個插件集成哪些NPM依賴,如下區分安裝stylelinteslint及其相關依賴(看看便可,不要安裝,重點在後頭)。筆者有個習慣,就是喜歡將依賴更新到最新版本,在享受新功能的同時也順便填坑。

# Stylelint
npm i -D stylelint stylelint-config-standard stylelint-order
複製代碼
# Eslint
npm i -D eslint babel-eslint eslint-config-standard eslint-plugin-html eslint-plugin-import eslint-plugin-node eslint-plugin-promise eslint-plugin-react eslint-plugin-standard eslint-plugin-vue vue-eslint-parser
複製代碼
# TypeScript Eslint
npm i -D @typescript-eslint/eslint-plugin @typescript-eslint/parser typescript eslint-config-standard-with-typescript
複製代碼

安裝完成後需配置多份對應配置文件,CSS方面有css/scss/less/vue文件,JS方面有js/ts/jsx/tsx/vue文件。查看插件文檔,發現Stylelint只能在settings.json上配置,而Eslint可配置成多份對應配置文件,並在settings.json上經過特定字段指定Eslint配置文件路徑。

settings.json是VSCode的配置文件,用戶可經過插件暴露的字段自定義編輯器功能。
複製代碼

因爲配置文件太多很差管理,筆者開源了本身日常使用的配置文件集合,詳情可查看vscode-lint

配置文件裏的rule可根據本身編碼規範適當調整,在此不深刻講解,畢竟簡單得來誰都會。建議使用vscode-lint,若校驗規則不喜歡可自行調整。

如下會基於vscode-lint部署VSCode一鍵格式化代碼,找個目錄經過git克隆一份vscode-lint,並安裝其NPM依賴。若使用vscode-lint,上述依賴就不要安裝了🙅。

git clone https://github.com/JowayYoung/vscode-lint.git
cd vscode-lint
npm i
複製代碼

配置插件

  • 打開VSCode
  • 選擇左邊工具欄插件,搜索並安裝StylelintEslint,安裝完成後重啓VSCode
  • 選擇文件 → 首選項 → 設置設置裏可選用戶工做區
    • 用戶:配置生效後會做用於全局項目(若大部分項目都是單一的React應用或Vue應用推薦使用全局配置)
    • 工做區:配置生效後只會做用於當前打開項目
  • 點擊設置右上角中間圖標打開設置(json),打開的對應文件是settings.json(上述有說起)
  • 插入如下配置:若在用戶選項下插入如下配置,遇到其餘項目需覆蓋配置時在工做區選項下插入eslint.options.configFile指定Eslint配置文件路徑
  • 重啓VSCode:爲了保障每次修改配置後都能正常格式化代碼,必須重啓VSCode
{
    "css.validate": false,
    "editor.codeActionsOnSave": {
        "source.fixAll.eslint": true,
        "source.fixAll.stylelint": true
    },
    "eslint.nodePath": "path/vscode-lint/node_modules",
    "eslint.options": {
        "configFile": "path/vscode-lint/eslintrc.js"
    },
    "less.validate": false,
    "scss.validate": false,
    "stylelint.configBasedir": "path/vscode-lint",
    "stylelint.configOverrides": {
        "extends": "stylelint-config-standard",
        "plugins": [
            "stylelint-order"
        ],
        "rules": {
            "at-rule-empty-line-before": "never",
            "at-rule-no-unknown": [
                true,
                {
                    "ignoreAtRules": [
                        "content",
                        "each",
                        "error",
                        "extend",
                        "for",
                        "function",
                        "if",
                        "include",
                        "mixin",
                        "return",
                        "while"
                    ]
                }
            ],
            "color-hex-case": "lower",
            "comment-empty-line-before": "never",
            "declaration-colon-newline-after": null,
            "declaration-empty-line-before": "never",
            "function-linear-gradient-no-nonstandard-direction": null,
            "indentation": "tab",
            "no-descending-specificity": null,
            "no-missing-end-of-source-newline": null,
            "no-empty-source": null,
            "number-leading-zero": "never",
            "rule-empty-line-before": "never",
            "order/order": [
                "custom-properties",
                "declarations"
            ],
            "order/properties-order": [
                // 佈局屬性
                "display",
                "visibility",
                "overflow",
                "overflow-x",
                "overflow-y",
                "overscroll-behavior",
                "scroll-behavior",
                "scroll-snap-type",
                "scroll-snap-align",
                // 佈局屬性:浮動
                "float",
                "clear",
                // 佈局屬性:定位
                "position",
                "left",
                "right",
                "top",
                "bottom",
                "z-index",
                // 佈局屬性:列表
                "list-style",
                "list-style-type",
                "list-style-position",
                "list-style-image",
                // 佈局屬性:表格
                "table-layout",
                "border-collapse",
                "border-spacing",
                "caption-side",
                "empty-cells",
                // 佈局屬性:彈性
                "flex-flow",
                "flex-direction",
                "flex-wrap",
                "justify-content",
                "align-content",
                "align-items",
                "align-self",
                "flex",
                "flex-grow",
                "flex-shrink",
                "flex-basis",
                "order",
                // 佈局屬性:多列
                "columns",
                "column-width",
                "column-count",
                "column-gap",
                "column-rule",
                "column-rule-width",
                "column-rule-style",
                "column-rule-color",
                "column-span",
                "column-fill",
                "column-break-before",
                "column-break-after",
                "column-break-inside",
                // 佈局屬性:格柵
                "grid-columns",
                "grid-rows",
                // 尺寸屬性
                "box-sizing",
                "margin",
                "margin-left",
                "margin-right",
                "margin-top",
                "margin-bottom",
                "padding",
                "padding-left",
                "padding-right",
                "padding-top",
                "padding-bottom",
                "border",
                "border-width",
                "border-style",
                "border-color",
                "border-colors",
                "border-left",
                "border-left-width",
                "border-left-style",
                "border-left-color",
                "border-left-colors",
                "border-right",
                "border-right-width",
                "border-right-style",
                "border-right-color",
                "border-right-colors",
                "border-top",
                "border-top-width",
                "border-top-style",
                "border-top-color",
                "border-top-colors",
                "border-bottom",
                "border-bottom-width",
                "border-bottom-style",
                "border-bottom-color",
                "border-bottom-colors",
                "border-radius",
                "border-top-left-radius",
                "border-top-right-radius",
                "border-bottom-left-radius",
                "border-bottom-right-radius",
                "border-image",
                "border-image-source",
                "border-image-slice",
                "border-image-width",
                "border-image-outset",
                "border-image-repeat",
                "width",
                "min-width",
                "max-width",
                "height",
                "min-height",
                "max-height",
                // 界面屬性
                "appearance",
                "outline",
                "outline-width",
                "outline-style",
                "outline-color",
                "outline-offset",
                "outline-radius",
                "outline-radius-topleft",
                "outline-radius-topright",
                "outline-radius-bottomleft",
                "outline-radius-bottomright",
                "background",
                "background-color",
                "background-image",
                "background-repeat",
                "background-repeat-x",
                "background-repeat-y",
                "background-position",
                "background-position-x",
                "background-position-y",
                "background-size",
                "background-origin",
                "background-clip",
                "background-attachment",
                "bakground-composite",
                "mask",
                "mask-mode",
                "mask-image",
                "mask-repeat",
                "mask-repeat-x",
                "mask-repeat-y",
                "mask-position",
                "mask-position-x",
                "mask-position-y",
                "mask-size",
                "mask-origin",
                "mask-clip",
                "mask-attachment",
                "mask-composite",
                "mask-box-image",
                "mask-box-image-source",
                "mask-box-image-width",
                "mask-box-image-outset",
                "mask-box-image-repeat",
                "mask-box-image-slice",
                "box-shadow",
                "box-reflect",
                "filter",
                "mix-blend-mode",
                "opacity",
                "object-fit",
                "clip",
                "clip-path",
                "resize",
                "zoom",
                "cursor",
                "pointer-events",
                "user-modify",
                "user-focus",
                "user-input",
                "user-select",
                "user-drag",
                // 文字屬性
                "line-height",
                "line-clamp",
                "vertical-align",
                "direction",
                "unicode-bidi",
                "writing-mode",
                "ime-mode",
                "text-overflow",
                "text-decoration",
                "text-decoration-line",
                "text-decoration-style",
                "text-decoration-color",
                "text-decoration-skip",
                "text-underline-position",
                "text-align",
                "text-align-last",
                "text-justify",
                "text-indent",
                "text-stroke",
                "text-stroke-width",
                "text-stroke-color",
                "text-shadow",
                "text-transform",
                "text-size-adjust",
                "src",
                "font",
                "font-family",
                "font-style",
                "font-stretch",
                "font-weight",
                "font-variant",
                "font-size",
                "font-size-adjust",
                "color",
                // 內容屬性
                "tab-size",
                "overflow-wrap",
                "word-wrap",
                "word-break",
                "word-spacing",
                "letter-spacing",
                "white-space",
                "caret-color",
                "quotes",
                "content",
                "content-visibility",
                "counter-reset",
                "counter-increment",
                "page",
                "page-break-before",
                "page-break-after",
                "page-break-inside",
                // 交互屬性
                "will-change",
                "perspective",
                "perspective-origin",
                "backface-visibility",
                "transform",
                "transform-origin",
                "transform-style",
                "transition",
                "transition-property",
                "transition-duration",
                "transition-timing-function",
                "transition-delay",
                "animation",
                "animation-name",
                "animation-duration",
                "animation-timing-function",
                "animation-delay",
                "animation-iteration-count",
                "animation-direction",
                "animation-play-state",
                "animation-fill-mode",
                // Webkit專有屬性
                "-webkit-overflow-scrolling",
                "-webkit-box-orient",
                "-webkit-line-clamp",
                "-webkit-text-fill-color",
                "-webkit-tap-highlight-color",
                "-webkit-touch-callout",
                "-webkit-font-smoothing",
                "-moz-osx-font-smoothing"
            ]
        }
    }
}
複製代碼

以上配置的pathvscode-lint所在的根目錄,若剛纔的vscode-lint克隆到E:/Github,那麼path就是E:/Github

示例

上述步驟完成後就可愉快敲代碼了。每次保存文件就會自動格式化CSS代碼JS代碼,這個格式化代碼不只會將代碼按照規範整理排序,甚至儘量依據修復方案格式化出正確代碼。

這樣就無需爲每一個項目配置Lint,將全部項目的StylelintEslintTslintPrettier相關依賴和配置文件所有移除,使項目目錄變得超級簡潔。

css/scss/less/vue文件

Stylelint

js/ts/jsx/tsx/vue文件

Eslint

疑問

更新eslint到v6+就會失效

不少同窗反映eslint v6+VSCode上失效,最高版本只能控制在v5.16.0。其實這自己就是配置問題,跟版本無關。vscode-linteslint使用v7照樣能使用Eslint,只要配置正確就能正常使用。

上述安裝行爲使用了NPM,那麼settings.jsoneslint.packageManager必須配置爲npm(小寫),但最新版本Eslint已默認此項,因此無需配置。若上述安裝行爲變成yarn install,那麼必須在settings.json裏添加如下配置。

{
    "eslint.packageManager": "yarn"
}
複製代碼

這個配置就是解決該問題的關鍵了。

首次安裝Eslint並執行上述配置就會失效

首次安裝Eslint可能會在js/ts/jsx/tsx/vue文件裏看到如下警告。

Eslint is disabled since its execution has not been approved or denied yet. Use the light bulb menu to open the approval dialog.
複製代碼

說明Eslint被禁用了,雖然配置裏無明確的禁用字段,但仍是被禁用了。此時移步到VSCode右下角的工具欄,會看到禁用圖標+ESLINT的標紅按鈕,單擊它會彈出一個彈框,選擇Allow Everywhere就能啓用Eslint全部校驗功能。

總結

總體過程看似簡單,其實筆者這半年填了不少坑纔有了vscode-lint,中間已省略了不少未記錄的問題,這些疑問不重要卻影響到不少地方。相信本文能讓不少同窗體驗VSCode一鍵格式化代碼所帶來的快感,最關鍵的部分仍是無需爲每一個項目配置Lint,這省下多少時間和精力呀!以爲牛逼給vscode-lint點個Star吧!

回看筆者往期高贊文章,也許能收穫更多喔!

結語

❤️關注+點贊+收藏+評論+轉發❤️,原創不易,鼓勵筆者創做更多高質量文章

關注公衆號IQ前端,一個專一於CSS/JS開發技巧的前端公衆號,更多前端小乾貨等着你喔

  • 關注後回覆資料免費領取學習資料
  • 關注後回覆進羣拉你進技術交流羣
  • 歡迎關注IQ前端,更多CSS/JS開發技巧只在公衆號推送
相關文章
相關標籤/搜索