前端工程化:有效地進行拼寫檢查

語言: CN / TW / HK

拼寫錯誤導致的問題

在專案開發過程中,即使我們再細心,也難免忙中出錯,犯下很多低階的錯誤。
比如這樣:
image.png
我們錯把 field 拼寫成 filed,這樣打印出來的是 undefined,而不是預期的 name。

ESLint 的基本介紹

但是幸運的是,有一些 Lint 工具會在這方面提供一些幫助。
比如在 TypeScript 中,會有一個錯誤提示。雖然這個提示提供的訊息並不是我們需要的。
image.png
當然我們可以選擇一些更專業的 Linter 來完成這項工作。
目前來說,最流行的 JavaScript Linter 是 ESLint。
如果配合 VSCode 這個編輯器使用的話,可以安裝 vscode-eslint 這個外掛。
然後在專案的根目錄下建立 .eslintrc.js 檔案,編寫一些配置。 javascript /** @type {import('eslint').Linter.Config} */ module.exports = { env: { browser: true, es2021: true, node: true, }, rules: { "no-undef": ["error"], }, }; env 屬性指定專案的執行環境。
rules 是具體的規則。
no-undef 這條規則的意思是不可以使用未定義的變數。
no-undef 屬性的值是一個數組,陣列的第一個元素是這條規則的級別。
ESLint 中的所有規則都有 3 個等級,off 表示關閉,wran 表示開啟和 error 表示錯誤。
你可以根據自己對程式碼要求的嚴格程度自定義規則等級。
當我們配置好了這條規則之後,編輯器的錯誤提示就會發生變化。
image.png
ESlint 會給我們提示 filed 這個變數沒有被定義,而被直接使用了。
這樣就可以幫助我們發現這個問題,從而更早的解決掉這個問題。
這是一種比較容易發現的問題。

宣告變數時的單詞拼寫錯誤

另一個更加隱祕晦澀的錯誤是,我們在定義變數時就把變數的單詞拼寫錯了。
比如下面這樣:
image.png
這種問題 ESLint 是沒有辦法檢測到的,而且真正執行起來也不會有什麼問題。
最大的問題在於維護。
假設這段程式碼的維護者換成了別人,他很難一眼看出這段程式碼究竟在表達什麼意思。
filed?是檔案的過去式,表達的意思是歸檔嗎?
他壓根就不可能會朝 field,也就是欄位的含義上去考慮。
這會讓接下來的整個邏輯流程閱讀起來變得非常費解。
這種問題屬於根源性的問題。

人工解決方案

解決辦法當然存在,建立業務術語表,然後經過人工程式碼審查(CodeReview)。
但是,人在真正意義上是不靠譜的。
即使再強大、再細緻的人,也會有焦急、疲倦、鬆懈的時候。所以即使是通過人工程式碼審查後的程式碼,也未必不會存在上面提到的這種問題。

機器解決方案

那麼能夠有一種更好的方式來避免這類問題呢?
比如能否依靠在真正意義上靠譜的機器來協助人類做這件事情?
當然是可以的。
ESLint 有一套外掛機制,可以通過外掛來擴充套件 ESLint 原本的功能。
其中有一個比較常用的外掛,eslint-plugin-spellcheck。
這個外掛的作用是幫助我們檢查單詞的拼寫錯誤。
安裝也非常簡單。 bash npm install eslint-plugin-spellcheck 之後需要更新 ESLint 的配置。 javascript /** @type {import('eslint').Linter.Config} */ module.exports = { env: { browser: true, es2021: true, node: true, }, plugins: [ "spellcheck", ], rules: { "no-undef": ["error"], "spellcheck/spell-checker": ['warn'] }, }; 這樣就可以對程式碼中單詞拼寫錯誤的。

拼寫錯誤與意圖表達錯誤

但是,講了這麼多,像上面提到的反例,spell-checker 仍然是無能為力的。
為什麼呢?因為上面提到的反例,表面上看只有一個錯誤,但無形之中還存在另一個錯誤。

  1. 拼寫錯誤,field 拼寫成了 filed。
  2. filed 仍然是個正確的單詞。如果將 filed 認為是一個正確的單詞,那麼第二個錯誤將是意圖表達錯誤。​

機器只能解決拼寫錯誤,但是意圖表達錯誤這類問題明顯超越了機器目前的能力邊界。
所以我們可以把反例稍微改一下。
image.png
filde 不是一個合法單詞,所以就得到 spell-checker 的警告提示。

spell-checker 的實現原理

spell-checker 的實現原理就是羅列出超過 4 萬個英文單詞進行匹配,如果不在這個範圍內的英文單詞就認為是拼寫錯誤。
當然我們也可以擴充套件這個單詞列表。
ESLinter 的 rules 中屬性值都是一個數組。第一個元素是規則級別,第二個元素就是一個物件,表示這個規則對應的配置項。
spell-checker 的配置項中有一個 skipWords,表示可以跳過一些單詞。

跳過特殊單詞

比如你在使用 ESModule 的方式來開發應用,其中用到了 Vue 這個庫。
我們需要在 .eslintrc.js 中新增一項配置,讓 ESLint 的直譯器以模組的方式解釋程式碼。 javascript module.exports = { // ... parserOptions: { sourceType: 'module' } } 但是 Vue 並不是一個合法的單詞。
image.png
我們就可以通過配置這個配置項來跳過對 Vue 的檢測。 javascript module.exports = { rules: { "spellcheck/spell-checker": [ "warn", { skipWords: ["Vue"], }, ], } 這樣程式碼就不會再出現警告了。
image.png
但是到目前為止,問題還是沒有完全解決。
因為一些庫中提供的 API 仍然不是合法單詞。比如 Teleport。
image.png
我們可以選擇手動新增 Teleport 這個單詞到 skipWords 中。
但問題是,Vue 中仍然提供了很多 API 不在合法單詞範圍內,比如 withCtx:
image.png
如果我們碰到哪個單詞就朝 skipWords 裡面新增哪個單詞,在使用的庫比較少的時候還可以接受。如果我們的專案中依賴了大量的庫,而這些庫中又存在了大量的不合法單詞 API,這會讓我們感到非常的繁瑣和痛苦。
另外一個問題就是,Node.js 很多內建模組的 API 同樣不是合法單詞。比如常用的 fs 和 readdir。image.png
那有沒有什麼方法可以解決上面的兩個問題呢?
答案是有的。

modules-words

modules-words 是一個獲取模組 API 單詞的庫,通過這個庫配合 spell-checker 可以很好的幫助我們跳過很多第三方模組或者 Node.js 內建模組的 API 單詞檢查。
首先安裝這個模組。 bash npm add modules-words --save-dev 之後修改 .eslintrc.js 配置檔案。 ```javascript const { getWords, getGlobalWords } = require("modules-words");

/* @type {import('eslint').Linter.Config} / module.exports = { // ... rules: { // ... "spellcheck/spell-checker": [ "warn", { skipWords: ["", ...getWords("vue"), ...getGlobalWords()], }, ], }, }; ``` 通過 modules-words 提供的 getWords 和 getGlobalWords 兩個函式,成功地將專案中可能使用到的單詞過濾出來,讓 spell check 能夠以更加符合我們預期的方式執行。