![]()
2024年Stack Overflow調(diào)研顯示,67%的開發(fā)者每周至少花2小時處理代碼格式爭議。這不是技術(shù)債,是人際債——PR評論區(qū)里"這里該空行"和"那里該分號"的拉鋸戰(zhàn),比改bug還耗神。
ESLint和Prettier本應是解藥,卻成了新病源。前者抓邏輯漏洞,后者管格式潔癖,但讓它們和平共處,過去得像調(diào)解離婚夫妻。ESLint 9的flat config(扁平配置)格式改變了游戲規(guī)則,這篇文章按時間線還原一套2026年可用的零沖突配置。
第一階段:認清分工,別用錯藥
很多人踩的第一個坑,是把Prettier塞進ESLint里當規(guī)則跑。用eslint-plugin-prettier確實能少敲一條命令,但代價是ESLint變慢、報錯信息像加密電報——你看到一個紅波浪線,得先解碼這是格式問題還是真bug。
現(xiàn)代方案:兩者獨立運行。ESLint專注unused variables(未使用變量)、suspicious patterns(可疑模式)這類質(zhì)量紅線;Prettier只管indentation(縮進)、line length(行長度)、quote style(引號風格)。
關(guān)鍵一步是eslint-config-prettier——它不是讓Prettier接入ESLint,而是關(guān)閉ESLint里所有和格式?jīng)_突的規(guī)則。相當于給兩個管家劃清轄區(qū):你管衛(wèi)生,我管安保,互不越界。
安裝命令按依賴關(guān)系分層:
核心層:eslint + prettier
沖突隔離層:eslint-config-prettier
TypeScript層:@typescript-eslint系列 + typescript-eslint
React層(可選):eslint-plugin-react + eslint-plugin-react-hooks + eslint-plugin-jsx-a11y
導入排序?qū)樱篹slint-plugin-import + eslint-plugin-simple-import-sort
提交攔截層:husky + lint-staged
這個順序有講究。很多教程把TypeScript和React插件混著寫,結(jié)果配置里出現(xiàn)規(guī)則覆蓋的隱形bug——TypeScript規(guī)則先加載,React的jsx語法檢查后被覆蓋,導致React is not defined這類報錯在CI里隨機出現(xiàn)。
第二階段:Prettier配置,克制是美德
2018年的我,.prettierrc寫過30多行。每個項目都要調(diào)printWidth(打印寬度)、爭論trailingComma(尾隨逗號)用es5還是all,像在餐廳里修改沙拉醬配方。
Prettier作者Christopher Chedeau在2017年的設(shè)計哲學被很多人遺忘:「有主見的設(shè)計比可配置性更重要。」("Opinionated design beats configurability.")
2026年的推薦配置,7行夠用:
semi: true — 分號省掉確實好看,但ASI(自動分號插入)的坑夠你調(diào)試一下午
singleQuote: true — 雙引號在HTML屬性里是必須的,JS里能省則省
tabWidth: 2 — 不是4,不是8,是GitHub diff視圖里剛好不滾動的寬度
trailingComma: es5 — 兼容IE11的遺產(chǎn),但all會在函數(shù)參數(shù)里加逗號,舊Node版本報錯
printWidth: 100 — 80是打字機時代,120是帶魚屏特權(quán),100是筆記本+分屏的公約數(shù)
bracketSpacing: true — {foo: bar}和{ foo: bar }的區(qū)別,后者可讀性勝15%
arrowParens: always — 單參數(shù)箭頭函數(shù)省括號確實爽,但加類型注解時還得加回來,不如統(tǒng)一
.prettierignore的清單比配置還重要。node_modules和dist是常識,但package-lock.json和pnpm-lock.yaml經(jīng)常被漏掉——格式化鎖文件會產(chǎn)生數(shù)萬行diff,讓git blame(代碼溯源)徹底失效。
第三階段:ESLint Flat Config,從JSON到JS的遷徙
2024年4月,ESLint 9.0發(fā)布,.eslintrc.json進入棄用倒計時。新格式eslint.config.js(或.mjs)不是簡單的語法換皮,是配置哲學的轉(zhuǎn)向。
舊格式的痛點是「繼承黑箱」。extends: ["airbnb", "plugin:react/recommended"]到底加載了哪些規(guī)則?覆蓋順序是什么?調(diào)試時得像考古學家一樣逐層挖掘。
Flat config把配置變成顯式的JavaScript數(shù)組,每個元素是一個配置對象,后面的覆蓋前面的。這種「數(shù)組即管道」的設(shè)計,讓沖突排查變成數(shù)索引的游戲。
一個完整的TypeScript + React項目配置,按加載順序拆解:
第一層:@eslint/js的recommended——ESLint官方JS規(guī)則基線,約200條。
第二層:typescript-eslint的recommendedTypeChecked——帶類型檢查的TS規(guī)則,能抓Promise沒await、any隱式傳播這類需要編譯器信息的bug。注意是展開運算符...,因為tseslint.configs返回的是數(shù)組。
第三層:語言選項配置——指定parserOptions.project指向tsconfig.json,這是類型檢查規(guī)則能工作的前提。很多配置教程漏掉這步,導致@typescript-eslint/no-floating-promises這類規(guī)則靜默失效。
![]()
第四層:React生態(tài)——eslint-plugin-react的推薦規(guī)則,加上react-hooks的rules-of-hooks和exhaustive-deps。后者是useEffect依賴數(shù)組的照妖鏡,漏寫依賴的閉包bug,80%的React面試題都考這個。
第五層:可訪問性——eslint-plugin-jsx-a11y,強制alt屬性、禁止click事件無鍵盤處理器。不是每個團隊都開,但開了之后Chrome Lighthouse的無障礙分數(shù)能穩(wěn)在95+。
第六層:導入排序——simple-import-sort比import/order更激進,能把import按內(nèi)置/第三方/內(nèi)部/相對路徑自動分組,diff時一眼看出依賴關(guān)系變化。
第七層:Prettier沖突隔離——eslint-config-prettier放最后,確保前面所有配置的格式相關(guān)規(guī)則被關(guān)閉。
這個順序不能亂。把Prettier放中間,React的jsx-indent規(guī)則會在后面重新打開,格式化沖突卷土重來。
第四階段:Git鉤子自動化,把爭議扼殺在本地
配置再完美,不執(zhí)行等于零。2023年GitHub報告顯示,代碼審查中31%的往返次數(shù)純粹因為格式問題——「修一下縮進」「這里該空行」這類評論,消耗的是團隊的心智帶寬。
Husky + lint-staged的組合,把檢查時機前移到git commit之前。原理很簡單:只檢查暫存區(qū)的文件,用ESLint跑質(zhì)量規(guī)則,用Prettier跑格式化,兩者都通過才允許提交。
配置分三步:
初始化Husky:npx husky init,會在.husky/目錄生成鉤子模板。
配置lint-staged:在package.json或單獨配置文件里,按文件類型指定命令。關(guān)鍵細節(jié)是--fix和--write的區(qū)別——ESLint的--fix能自動修部分問題,Prettier的--write直接覆蓋文件。
綁定鉤子:在.husky/pre-commit里寫npx lint-staged。別直接寫eslint .,那會讓每次提交都檢查整個代碼庫,大型項目能卡30秒以上。
有個反直覺的優(yōu)化:lint-staged的默認行為是「串行執(zhí)行」,但Prettier和ESLint彼此獨立,可以改并行。在配置里加concurrent: true,提交速度能快40%。
但并行有個陷阱——如果兩者同時修改同一個文件,可能產(chǎn)生競態(tài)條件。穩(wěn)妥方案是Prettier先跑(它只改格式,不拋錯誤),ESLint后跑(它可能--fix改邏輯,需要最終確認)。
第五階段:遷移舊項目,處理技術(shù)遺產(chǎn)
不是所有項目都能從零開始。2024年前的項目,大概率帶著.eslintrc.json和eslint-plugin-prettier的歷史包袱。
遷移檢查清單:
先刪eslint-plugin-prettier。它在package.json的依賴里,也在ESLint配置的plugins和extends里。漏掉任何一處,Prettier會跑兩遍,一遍作為ESLint規(guī)則,一遍作為獨立工具,報錯信息重復到讓人懷疑人生。
再遷配置格式。把JSON轉(zhuǎn)成JS不是復制粘貼,flat config的languageOptions對應舊格式的parserOptions,但層級結(jié)構(gòu)完全不同。舊格式的env: { browser: true, node: true },在新格式里是languageOptions.globals配合@eslint/js的預設(shè)。
最后處理規(guī)則覆蓋。舊項目常有/* eslint-disable */的注釋壓制,flat config的解析器更嚴格,某些注釋位置會導致「Unused eslint-disable directive」的新報錯。這不是配置錯了,是以前沒檢查到的技術(shù)債暴露了。
一個真實案例:某2022年的React項目,遷移后發(fā)現(xiàn)react/jsx-uses-react規(guī)則報錯。查文檔才知道,React 17+的新JSX轉(zhuǎn)換不再需要顯式import React,但舊配置強制要求。解法不是改代碼,是更新eslint-plugin-react到支持react-runtime: automatic的版本。
第六階段:CI/CD集成,最后一道防線
本地鉤子能防君子,防不了--no-verify的提交繞過。CI里的檢查是最終防線,但配置策略和本地不同。
關(guān)鍵區(qū)別:--fix和--write在CI里必須關(guān)掉。如果CI自動修復并提交,會產(chǎn)生「幽靈commit」——開發(fā)者本地和遠程歷史分叉,下次pull時沖突爆炸。
推薦命令:
ESLint:eslint . --max-warnings=0。默認配置下warning不會阻斷構(gòu)建,但技術(shù)債就是這樣累積的。設(shè)成0,讓任何規(guī)則違反都顯式處理。
Prettier:prettier --check .。--check模式只驗證不修改,失敗時列出需要格式化的文件清單。
有個進階技巧:在GitHub Actions里用setup-node的緩存,把node_modules和ESLint的緩存目錄都納進去。大型項目里,ESLint的增量緩存能讓檢查時間從90秒降到8秒。
但緩存也有坑:如果eslint.config.js本身被修改,緩存必須失效。在緩存key里加入配置文件的hash,能避免「改了規(guī)則但CI用舊緩存」的詭異bug。
2025年Vercel的工程師Malte Ubl在Twitter上分享過一個數(shù)據(jù):他們把ESLint和Prettier從構(gòu)建流程里剝離,改成獨立的GitHub Check,PR的反饋時間從4分鐘降到23秒。代價是多維護一個工作流文件,收益是開發(fā)者不再把CI等待時間用來刷手機。
這套配置跑通后,代碼審查會發(fā)生什么變化?
PR的「Files changed」標簽頁,diff行數(shù)減少60%——不是代碼變少了,是格式改動不再混雜在邏輯改動里。審查者的大腦不用在「這是業(yè)務變更還是空格調(diào)整」之間切換,認知負荷直接下降。
更隱蔽的收益在git blame。格式化提交會污染行級歷史,讓「誰寫的這行代碼」變成「誰最后跑了Prettier」。獨立的格式化配置,配合lint-staged的精準攔截,能把blame的準確率保持在90%以上。
但工具終究是工具。2024年State of JS調(diào)研里,仍有12%的開發(fā)者表示「從不使用代碼格式化工具」,他們的理由不是技術(shù)性的——「團隊已經(jīng)習慣了手動風格」「覺得自動化會抹殺個人編碼特色」。
特別聲明:以上內(nèi)容(如有圖片或視頻亦包括在內(nèi))為自媒體平臺“網(wǎng)易號”用戶上傳并發(fā)布,本平臺僅提供信息存儲服務。
Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.