目代碼格式化實(shí)戰(zhàn):Vetur、ESLint與Prettier高效協(xié)同配置指南)
1. 項(xiàng)目概述為什么Vue開(kāi)發(fā)者離不開(kāi)代碼格式化三件套如果你在用VsCode寫Vue大概率遇到過(guò)代碼格式混亂、保存時(shí)自動(dòng)變樣、或者控制臺(tái)時(shí)不時(shí)冒出一些語(yǔ)法警告。這背后往往就是Vetur、ESLint和Prettier這三個(gè)插件在“工作”——或者更準(zhǔn)確地說(shuō)是它們之間沒(méi)協(xié)調(diào)好。我剛接觸Vue那會(huì)兒也被它們折騰得不輕一會(huì)兒是標(biāo)簽縮進(jìn)不對(duì)一會(huì)兒是單引號(hào)雙引號(hào)打架保存一次文件格式能變好幾個(gè)樣。后來(lái)花了些時(shí)間才把這套工具鏈理順。簡(jiǎn)單來(lái)說(shuō)這三個(gè)插件各司其職但又需要緊密配合。Vetur是Vue項(xiàng)目的“語(yǔ)言服務(wù)器”它讓VsCode能理解.vue文件提供語(yǔ)法高亮、智能提示、錯(cuò)誤檢查。ESLint是“代碼質(zhì)量警察”它根據(jù)你設(shè)定的規(guī)則檢查JavaScript/TypeScript代碼中的潛在問(wèn)題和風(fēng)格不一致。Prettier是“代碼格式化美工”它不管代碼邏輯對(duì)不對(duì)只負(fù)責(zé)把代碼按照統(tǒng)一的風(fēng)格重新排版讓代碼看起來(lái)整潔美觀。它們仨組合起來(lái)目標(biāo)是在你寫代碼甚至保存文件的瞬間自動(dòng)幫你把代碼整理得既規(guī)范又漂亮把團(tuán)隊(duì)協(xié)作中的風(fēng)格爭(zhēng)論降到最低。但理想很豐滿現(xiàn)實(shí)是如果你不進(jìn)行正確的配置它們很容易互相沖突導(dǎo)致更混亂的局面。這篇文章我就結(jié)合自己踩過(guò)的坑和項(xiàng)目中的實(shí)際配置把這套工具鏈的選型、配置和避坑要點(diǎn)給你講透讓你能快速搭建一個(gè)高效、無(wú)痛的Vue開(kāi)發(fā)環(huán)境。2. 核心工具深度解析與選型考量2.1 VeturVue開(kāi)發(fā)的基石遠(yuǎn)不止語(yǔ)法高亮很多人把Vetur當(dāng)成一個(gè)單純的語(yǔ)法高亮插件這就小看它了。它的核心是一個(gè)**語(yǔ)言服務(wù)器協(xié)議LSP**的實(shí)現(xiàn)。安裝了Vetur后VsCode才能把.vue文件識(shí)別為一個(gè)完整的、包含template、script、style三個(gè)語(yǔ)言塊的特殊文檔并分別對(duì)它們提供語(yǔ)言服務(wù)。它的核心功能包括語(yǔ)法高亮與代碼片段為Vue特有的模板語(yǔ)法、指令如v-if,v-for提供高亮和智能提示。Emmet支持在template塊中你可以像寫HTML一樣使用div.className然后按Tab鍵快速生成代碼這極大提升了模板編寫效率。錯(cuò)誤檢查與格式化Vetur內(nèi)置了對(duì)template和style塊的基礎(chǔ)格式化能力。注意這里說(shuō)的是“基礎(chǔ)”因?yàn)樗詭У母袷交魍ǔJ莗rettier或prettyhtml功能相對(duì)簡(jiǎn)單且容易與全局的Prettier沖突。智能跳轉(zhuǎn)與定義查找你可以按住Ctrl或Cmd點(diǎn)擊組件名跳轉(zhuǎn)到該組件的定義文件對(duì)于props、methods等也同樣支持。選型與注意事項(xiàng)為什么是Vetur而不是Volar這是一個(gè)常見(jiàn)問(wèn)題。Volar是另一個(gè)更現(xiàn)代、性能更好的Vue語(yǔ)言工具。但對(duì)于Vue 2項(xiàng)目或者一些尚未遷移到Vue 3 script setup語(yǔ)法的大型遺留項(xiàng)目Vetur的兼容性和穩(wěn)定性目前仍是更好的選擇。Volar對(duì)Vue 3和TypeScript的支持更極致。我的建議是Vue 2項(xiàng)目或混合項(xiàng)目用Vetur全新的Vue 3 TypeScript script setup項(xiàng)目可以優(yōu)先嘗試Volar。本文主要圍繞Vetur生態(tài)展開(kāi)。Vetur的格式化是“可選的”Vetur的強(qiáng)項(xiàng)在于“理解”Vue文件而非“格式化”。在實(shí)際配置中我們通常會(huì)禁用或嚴(yán)格限定Vetur的格式化功能將格式化工作完全交給更專業(yè)的Prettier以避免沖突。2.2 ESLint可定制的代碼質(zhì)量守護(hù)者ESLint是一個(gè)靜態(tài)代碼分析工具它的工作是在你寫代碼的時(shí)候就實(shí)時(shí)檢查出潛在的錯(cuò)誤、不推薦的寫法以及不符合團(tuán)隊(duì)約定的代碼風(fēng)格。它的核心價(jià)值在于錯(cuò)誤預(yù)防能發(fā)現(xiàn)諸如“變量定義了但未使用”、“使用了已廢棄的API”、“可能的邏輯錯(cuò)誤”等問(wèn)題在代碼運(yùn)行前就將其扼殺。強(qiáng)制代碼風(fēng)格可以統(tǒng)一團(tuán)隊(duì)的代碼風(fēng)格比如強(qiáng)制使用分號(hào)、強(qiáng)制使用單引號(hào)、強(qiáng)制縮進(jìn)為2個(gè)空格等。這比口頭約定或代碼評(píng)審時(shí)再指出要有效得多。高度可配置通過(guò).eslintrc.js等配置文件你可以自由組合各種規(guī)則。社區(qū)有大量現(xiàn)成的規(guī)則集如eslint:recommendedESLint推薦、vue/eslint-config-standard等你可以直接擴(kuò)展它們。選型與配置邏輯在Vue項(xiàng)目中我們通常不會(huì)使用原生的ESLint規(guī)則而是使用Vue生態(tài)專用的規(guī)則包。eslint-plugin-vue這是核心。它為Vue文件提供了專屬的linting規(guī)則比如要求組件名使用多單詞、強(qiáng)制模板中屬性的順序、校驗(yàn)v-bind指令的格式等。規(guī)則集選擇對(duì)于新項(xiàng)目我推薦使用vue/eslint-config-prettier。這個(gè)包的核心作用就是關(guān)閉所有與Prettier沖突的ESLint規(guī)則。因?yàn)镻rettier管格式ESLint管質(zhì)量讓它們各司其職避免用ESLint的規(guī)則去檢查本該由Prettier處理的空格、縮進(jìn)、引號(hào)等問(wèn)題這是解決沖突的關(guān)鍵一步。2.3 Prettier專精格式化的“霸道總裁”Prettier自稱是一個(gè)“有主見(jiàn)的代碼格式化工具”。這個(gè)“有主見(jiàn)”很有意思它意味著Prettier提供的配置選項(xiàng)是有限的它只提供那些最可能引起爭(zhēng)議的選項(xiàng)如行寬、縮進(jìn)、引號(hào)而對(duì)于一些細(xì)節(jié)格式它直接幫你決定了。這種“霸道”反而成了它的優(yōu)點(diǎn)因?yàn)樗鼜氐捉K結(jié)了“代碼末尾要不要加分號(hào)”這類無(wú)休止的爭(zhēng)論。它的工作方式很簡(jiǎn)單你給它一段“丑”的代碼它根據(jù)你的配置文件.prettierrc.js或默認(rèn)規(guī)則輸出一段格式完全統(tǒng)一的“美”的代碼。它不關(guān)心代碼邏輯只關(guān)心代碼的“長(zhǎng)相”。為什么需要它雖然ESLint也能做部分格式化但它的規(guī)則是“檢查”并“報(bào)告”需要你手動(dòng)去修復(fù)。而Prettier是“直接重寫”整個(gè)文件。結(jié)合VsCode的“保存時(shí)自動(dòng)格式化”功能你每次按CtrlS代碼就自動(dòng)變整潔了體驗(yàn)非常流暢。3. 環(huán)境搭建與核心配置實(shí)戰(zhàn)3.1 插件安裝與基礎(chǔ)配置首先在VsCode的擴(kuò)展商店中搜索并安裝以下三個(gè)插件Vetur(作者Pine Wu)ESLint(作者M(jìn)icrosoft)Prettier - Code formatter(作者Prettier)安裝完成后需要對(duì)VsCode本身進(jìn)行一些設(shè)置讓它們協(xié)同工作。打開(kāi)VsCode的設(shè)置JSON格式添加或修改以下配置{ // 1. 指定Vue文件的默認(rèn)格式化工具為Prettier這是避免沖突的關(guān)鍵 [vue]: { editor.defaultFormatter: esbenp.prettier-vscode }, // 2. 同樣對(duì)于JavaScript/TypeScript/JSON等文件也使用Prettier [javascript]: { editor.defaultFormatter: esbenp.prettier-vscode }, [typescript]: { editor.defaultFormatter: esbenp.prettier-vscode }, [json]: { editor.defaultFormatter: esbenp.prettier-vscode }, // 3. 非常重要的設(shè)置保存時(shí)自動(dòng)格式化代碼 editor.formatOnSave: true, // 4. 啟用ESLint插件對(duì)Vue文件的支持 eslint.validate: [ javascript, javascriptreact, typescript, typescriptreact, vue ], // 5. 關(guān)閉Vetur對(duì)template和style的格式化交給Prettier統(tǒng)一處理 vetur.format.defaultFormatter.html: none, vetur.format.defaultFormatter.css: none, vetur.format.defaultFormatter.scss: none, vetur.format.defaultFormatter.less: none, // 6. 可選但推薦保存時(shí)自動(dòng)執(zhí)行ESLint修復(fù)fix editor.codeActionsOnSave: { source.fixAll.eslint: explicit } }配置解讀第1、2點(diǎn)確保了所有相關(guān)文件的格式化權(quán)都交給了Prettier形成統(tǒng)一出口。第5點(diǎn)至關(guān)重要它解除了Vetur的格式化武裝避免了Vetur和Prettier對(duì)同一塊代碼進(jìn)行兩次不同的格式化操作。第6點(diǎn)實(shí)現(xiàn)了“保存時(shí)先讓ESLint自動(dòng)修復(fù)它能修復(fù)的問(wèn)題如引號(hào)、分號(hào)再讓Prettier進(jìn)行整體格式化”的完美流水線。3.2 項(xiàng)目級(jí)配置文件詳解接下來(lái)在項(xiàng)目根目錄創(chuàng)建配置文件這是團(tuán)隊(duì)協(xié)作和CI/CD流程保持一致性的基礎(chǔ)。第一步安裝必要的NPM包。在項(xiàng)目目錄下執(zhí)行npm install --save-dev eslint prettier eslint-plugin-vue vue/eslint-config-prettiereslint和prettier是核心。eslint-plugin-vue用于Vue語(yǔ)法檢查。vue/eslint-config-prettier用于關(guān)閉與Prettier沖突的規(guī)則。第二步創(chuàng)建ESLint配置文件.eslintrc.js。module.exports { root: true, // 表明這是根配置文件ESLint不再向上層目錄查找 env: { node: true, // 啟用Node.js全局變量 browser: true, // 啟用瀏覽器全局變量如window, document es2021: true // 支持ES2021語(yǔ)法 }, // 擴(kuò)展規(guī)則集Vue3推薦規(guī)則 ESLint推薦規(guī)則 關(guān)閉與Prettier沖突的規(guī)則 extends: [ plugin:vue/vue3-recommended, // 對(duì)于Vue2項(xiàng)目使用 plugin:vue/recommended eslint:recommended, vue/eslint-config-prettier ], parserOptions: { ecmaVersion: latest, sourceType: module }, rules: { // 在這里可以覆蓋或添加自定義規(guī)則 // 例如關(guān)閉組件名必須多單詞的規(guī)則根據(jù)團(tuán)隊(duì)習(xí)慣 // vue/multi-word-component-names: off, // 強(qiáng)制使用單引號(hào)這個(gè)其實(shí)會(huì)被Prettier覆蓋但這里聲明意圖 quotes: [error, single], // 強(qiáng)制語(yǔ)句末尾不加分號(hào)同樣會(huì)被Prettier覆蓋 semi: [error, never] } }第三步創(chuàng)建Prettier配置文件.prettierrc.js。我更喜歡用.js文件因?yàn)樗梢詫懽⑨?。module.exports { // 單行代碼的最大寬度超過(guò)會(huì)自動(dòng)換行 printWidth: 100, // 使用2個(gè)空格進(jìn)行縮進(jìn) tabWidth: 2, // 使用單引號(hào)而不是雙引號(hào) singleQuote: true, // 在對(duì)象或數(shù)組的最后一個(gè)元素后加逗號(hào)有助于Git diff清晰 trailingComma: es5, // 語(yǔ)句末尾不加分號(hào) semi: false, // 使用制表符還是空格縮進(jìn)false代表用空格 useTabs: false, // 將多行HTMLHTML、JSX、Vue、Angular元素的放在最后一行的末尾而不是單獨(dú)放在下一行 bracketSameLine: false, // 在對(duì)象字面量的括號(hào)之間打印空格 bracketSpacing: true, // 箭頭函數(shù)參數(shù)只有一個(gè)時(shí)是否加括號(hào)avoid為不加 arrowParens: avoid, // Vue文件中script和style標(biāo)簽的代碼是否縮進(jìn) vueIndentScriptAndStyle: true, // 行結(jié)束符保持LFUnix風(fēng)格以確??缙脚_(tái)一致性 endOfLine: lf }第四步可選但推薦創(chuàng)建格式化忽略文件.prettierignore。像node_modules、dist、*.min.js這些文件不需要也不應(yīng)該被格式化。node_modules dist *.min.js *.md .DS_Store4. 高級(jí)配置與工作流集成4.1 解決Vetur與Prettier的模板格式化沖突即便按照上述配置你可能會(huì)發(fā)現(xiàn).vue文件中的template部分格式化依然有問(wèn)題比如標(biāo)簽屬性被擠在一行。這是因?yàn)镻rettier需要專門的插件來(lái)處理Vue文件。你需要安裝npm install --save-dev vue/compiler-sfcPrettier會(huì)自動(dòng)識(shí)別并使用它來(lái)解析Vue文件。確保你的package.json中devDependencies里有這個(gè)包。4.2 配置VsCode工作區(qū)與多項(xiàng)目隔離如果你同時(shí)開(kāi)發(fā)多個(gè)項(xiàng)目它們的代碼風(fēng)格要求可能不同比如A項(xiàng)目用單引號(hào)B項(xiàng)目用雙引號(hào)。將配置放在VsCode的“用戶設(shè)置”里會(huì)全局生效造成沖突。正確的做法是使用工作區(qū)設(shè)置。在項(xiàng)目根目錄創(chuàng)建.vscode文件夾。在.vscode文件夾內(nèi)創(chuàng)建settings.json文件。將前面提到的所有VsCode編輯器配置如editor.defaultFormatter、editor.formatOnSave等移入這個(gè)文件。這樣當(dāng)你打開(kāi)這個(gè)項(xiàng)目時(shí)VsCode會(huì)優(yōu)先應(yīng)用工作區(qū)設(shè)置從而為不同項(xiàng)目應(yīng)用不同的規(guī)則實(shí)現(xiàn)完美隔離。4.3 集成到Git Hooks與CI流程為了保證提交到倉(cāng)庫(kù)的代碼都是格式規(guī)范的可以集成lint-staged和husky工具。安裝依賴npm install --save-dev lint-staged husky初始化Huskynpx husky install # 將husky install命令添加到package.json的prepare腳本中便于新成員克隆項(xiàng)目后自動(dòng)安裝 npm pkg set scripts.preparehusky install在package.json中配置lint-staged{ lint-staged: { *.{js,ts,vue}: [ eslint --fix, // 對(duì)暫存區(qū)的JS/TS/Vue文件執(zhí)行ESLint修復(fù) prettier --write // 執(zhí)行Prettier格式化 ] } }添加Git Hook執(zhí)行以下命令會(huì)在.husky目錄下創(chuàng)建pre-commit鉤子文件。npx husky add .husky/pre-commit npx lint-staged完成以上步驟后每次你執(zhí)行g(shù)it commitlint-staged都會(huì)自動(dòng)對(duì)你本次提交的、符合條件的文件先運(yùn)行eslint --fix再運(yùn)行prettier --write確保提交的代碼是整潔的。這被稱為“門禁檢查”是保障團(tuán)隊(duì)代碼庫(kù)質(zhì)量的有效手段。5. 常見(jiàn)問(wèn)題排查與實(shí)戰(zhàn)技巧5.1 格式化失靈或沖突問(wèn)題排查表問(wèn)題現(xiàn)象可能原因解決方案保存.vue文件時(shí)只有script部分被格式化template沒(méi)變化。1. 未安裝vue/compiler-sfc。2. Vetur的模板格式化未禁用與Prettier沖突。1. 運(yùn)行npm install --save-dev vue/compiler-sfc。2. 確認(rèn)VsCode設(shè)置中vetur.format.defaultFormatter.html已設(shè)置為none。保存時(shí)代碼格式在兩種風(fēng)格間來(lái)回跳變?nèi)鐔我?hào)變雙引號(hào)又變回來(lái)。ESLint和Prettier的規(guī)則沖突如對(duì)引號(hào)、分號(hào)的規(guī)則不一致。1. 確保ESLint配置extends了vue/eslint-config-prettier。2. 檢查.prettierrc.js和ESLintrules中關(guān)于quotes、semi的配置確保Prettier配置是唯一來(lái)源ESLint中相關(guān)規(guī)則可刪除或保持默認(rèn)。ESLint錯(cuò)誤提示無(wú)法自動(dòng)修復(fù)保存時(shí)紅色波浪線不消失。1. 該錯(cuò)誤不屬于ESLint的“自動(dòng)可修復(fù)”類型如未使用的變量。2.editor.codeActionsOnSave配置未生效或ESLint插件未正確識(shí)別文件類型。1. 手動(dòng)修復(fù)這類邏輯錯(cuò)誤。2. 檢查VsCode設(shè)置中eslint.validate是否包含vue。重啟VsCode或ESLint服務(wù)器命令面板運(yùn)行ESLint: Restart ESLint Server。Prettier格式化后代碼不符合預(yù)期如屬性換行奇怪。Prettier配置.prettierrc.js中的參數(shù)如printWidth、bracketSameLine設(shè)置不當(dāng)。根據(jù)團(tuán)隊(duì)風(fēng)格調(diào)整.prettierrc.js中的參數(shù)??梢允褂胣px prettier --write .命令全局格式化一次觀察效果。在Vue單文件組件中style部分的格式化無(wú)效。同template可能是Vetur的樣式格式化未禁用。確認(rèn)VsCode設(shè)置中vetur.format.defaultFormatter.css、scss、less等已設(shè)置為none。5.2 個(gè)人實(shí)操心得與技巧配置優(yōu)先級(jí)牢記于心當(dāng)格式化出問(wèn)題時(shí)按這個(gè)順序檢查項(xiàng)目.prettierrc.js 項(xiàng)目.eslintrc.js VsCode工作區(qū)設(shè)置.vscode/settings.json VsCode用戶全局設(shè)置。高優(yōu)先級(jí)覆蓋低優(yōu)先級(jí)。“先Lint后Format”理解editor.codeActionsOnSaveESLint Fix和editor.formatOnSavePrettier的執(zhí)行順序很重要。理想的工作流是保存時(shí)先觸發(fā)ESLint修復(fù)那些可自動(dòng)修復(fù)的風(fēng)格問(wèn)題如引號(hào)然后Prettier再進(jìn)行整體的、無(wú)爭(zhēng)議的排版格式化。我們的配置正是這樣設(shè)置的。善用命令面板當(dāng)插件行為異常時(shí)多用CtrlShiftP打開(kāi)命令面板運(yùn)行諸如ESLint: Restart ESLint Server、Developer: Reload Window重啟VsCode等命令往往能解決很多疑難雜癥。團(tuán)隊(duì)統(tǒng)一配置是前提這套工具鏈最大的價(jià)值在于團(tuán)隊(duì)協(xié)作。務(wù)必通過(guò).prettierrc.js、.eslintrc.js、.vscode/settings.json可提交到倉(cāng)庫(kù)將配置固化在項(xiàng)目中新成員克隆項(xiàng)目后安裝依賴和推薦插件就能獲得完全一致的開(kāi)發(fā)體驗(yàn)無(wú)需再手動(dòng)調(diào)整任何設(shè)置。關(guān)于規(guī)則取舍不要過(guò)度糾結(jié)于每一條ESLint規(guī)則。初期可以直接采用plugin:vue/vue3-recommended和eslint:recommended這類成熟規(guī)則集。只有在團(tuán)隊(duì)對(duì)某條規(guī)則有強(qiáng)烈共識(shí)時(shí)再去rules里覆蓋它。保持配置的簡(jiǎn)潔和可維護(hù)性。折騰好這套配置初期可能會(huì)花點(diǎn)時(shí)間但一旦跑順?biāo)拖窨諝庖粯哟嬖谟谀愕拈_(kāi)發(fā)環(huán)境中你幾乎感覺(jué)不到它但它卻時(shí)時(shí)刻刻保障著你代碼的整潔與健康。它節(jié)省的是未來(lái)無(wú)數(shù)個(gè)小時(shí)的代碼評(píng)審爭(zhēng)吵、格式修復(fù)和Bug排查的時(shí)間。