前端工程化:有效地进行拼写检查

发布 2021-11-26 阅读 16分钟
# **拼写错误导致的问题** 在项目开发过程中,即使我们再细心,也难免忙中出错,犯下很多低级的错误。 比如这样: ![image](https://qiniu.3yya.com/dd18beada15005666804274a56a3f1fd/dd18beada15005666804274a56a3f1fd.png) 我们错把 field 拼写成 filed,这样打印出来的是 undefined,而不是预期的 name。 # **ESLint 的基本介绍** 但是幸运的是,有一些 Lint 工具会在这方面提供一些帮助。 比如在 TypeScript 中,会有一个错误提示。虽然这个提示提供的消息并不是我们需要的。 ![image](https://qiniu.3yya.com/724680e2fbed7957107e46a6e122f479/724680e2fbed7957107e46a6e122f479.png) 当然我们可以选择一些更专业的 Linter 来完成这项工作。 目前来说,最流行的 JavaScript Linter 是 ESLint。 如果配合 VSCode 这个编辑器使用的话,可以安装 [vscode-eslint](https://marketplace.visualstudio.com/items?itemName=dbaeumer.vscode-eslint&ssr=false) 这个插件。 然后在项目的根目录下创建 .eslintrc.js 文件,编写一些配置。 ```jsx /** @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](https://qiniu.3yya.com/b294d1f11c17e35e011c3b93c27ef14d/b294d1f11c17e35e011c3b93c27ef14d.png) ESlint 会给我们提示 filed 这个变量没有被定义,而被直接使用了。 这样就可以帮助我们发现这个问题,从而更早的解决掉这个问题。 这是一种比较容易发现的问题。 # **声明变量时的单词拼写错误** 另一个更加隐秘晦涩的错误是,我们在定义变量时就把变量的单词拼写错了。 比如下面这样: ![image](https://qiniu.3yya.com/7e3583c683c59ecf8648e208aa7307be/7e3583c683c59ecf8648e208aa7307be.png) 这种问题 ESLint 是没有办法检测到的,而且真正运行起来也不会有什么问题。 最大的问题在于维护。 假设这段代码的维护者换成了别人,他很难一眼看出这段代码究竟在表达什么意思。 filed?是文件的过去式,表达的意思是归档吗? 他压根就不可能会朝 field,也就是字段的含义上去考虑。 这会让接下来的整个逻辑流程阅读起来变得非常费解。 这种问题属于根源性的问题。 # **人工解决方案** 解决办法当然存在,建立业务术语表,然后经过人工代码审查(CodeReview)。 但是,人在真正意义上是不靠谱的。 即使再强大、再细致的人,也会有焦急、疲倦、松懈的时候。所以即使是通过人工代码审查后的代码,也未必不会存在上面提到的这种问题。 # **机器解决方案** 那么能够有一种更好的方式来避免这类问题呢? 比如能否依靠在真正意义上靠谱的机器来协助人类做这件事情? 当然是可以的。 ESLint 有一套插件机制,可以通过插件来扩展 ESLint 原本的功能。 其中有一个比较常用的插件,eslint-plugin-spellcheck。 这个插件的作用是帮助我们检查单词的拼写错误。 安装也非常简单。 ```bash npm install eslint-plugin-spellcheck ``` 之后需要更新 ESLint 的配置。 ```jsx /** @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](https://qiniu.3yya.com/0d8faa03b1e8390f776ceca0ef81f3ee/0d8faa03b1e8390f776ceca0ef81f3ee.png) filde 不是一个合法单词,所以就得到 spell-checker 的警告提示。 # **spell-checker 的实现原理** spell-checker 的实现原理就是罗列出超过 4 万个英文单词进行匹配,如果不在这个范围内的英文单词就认为是拼写错误。 当然我们也可以扩展这个单词列表。 ESLinter 的 rules 中属性值都是一个数组。第一个元素是规则级别,第二个元素就是一个对象,表示这个规则对应的配置项。 spell-checker 的配置项中有一个 skipWords,表示可以跳过一些单词。 # **跳过特殊单词** 比如你在使用 ESModule 的方式来开发应用,其中用到了 Vue 这个库。 我们需要在 .eslintrc.js 中添加一项配置,让 ESLint 的解释器以模块的方式解释代码。 ```jsx module.exports = { // ... parserOptions: { sourceType: 'module' } } ``` 但是 Vue 并不是一个合法的单词。 ![image](https://qiniu.3yya.com/b7baa56893ffa9bd6f483efe653ecd9f/b7baa56893ffa9bd6f483efe653ecd9f.png) 我们就可以通过配置这个配置项来跳过对 Vue 的检测。 ```jsx module.exports = { rules: { "spellcheck/spell-checker": [ "warn", { skipWords: ["Vue"], }, ], } ``` 这样代码就不会再出现警告了。 ![image](https://qiniu.3yya.com/502f198c52d96bf306b6b6ac2c228911/502f198c52d96bf306b6b6ac2c228911.png) 但是到目前为止,问题还是没有完全解决。 因为一些库中提供的 API 仍然不是合法单词。比如 Teleport。 ![image](https://qiniu.3yya.com/c54edcaa5adf66afa938fe8a20476c95/c54edcaa5adf66afa938fe8a20476c95.png) 我们可以选择手动添加 Teleport 这个单词到 skipWords 中。 但问题是,Vue 中仍然提供了很多 API 不在合法单词范围内,比如 withCtx: ![image](https://qiniu.3yya.com/619d202f96dc83bb7441d25cba3cd602/619d202f96dc83bb7441d25cba3cd602.png) 如果我们碰到哪个单词就朝 skipWords 里面添加哪个单词,在使用的库比较少的时候还可以接受。如果我们的项目中依赖了大量的库,而这些库中又存在了大量的不合法单词 API,这会让我们感到非常的繁琐和痛苦。 另外一个问题就是,Node.js 很多内置模块的 API 同样不是合法单词。比如常用的 fs 和 readdir。 ![image](https://qiniu.3yya.com/d9d009db8fc0f1e787e4adcbd1da7231/d9d009db8fc0f1e787e4adcbd1da7231.png) 那有没有什么方法可以解决上面的两个问题呢? 答案是有的。 # **modules-words** modules-words 是一个获取模块 API 单词的库,通过这个库配合 spell-checker 可以很好的帮助我们跳过很多第三方模块或者 Node.js 内置模块的 API 单词检查。 首先安装这个模块。 ```bash npm add modules-words --save-dev ``` 之后修改 .eslintrc.js 配置文件。 ```jsx 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 能够以更加符合我们预期的方式运行。