如果一个项目的代码是一致的并且易于阅读,那不是很好吗?这种情况并不常见的原因是,强制执行这样一个级别的代码质量很麻烦。当手动完成某件事情时会成为负担时,您需要引入一个工具。
本章的重点是使用有助于确保 React 代码质量达到标准的工具。以下是您将在本章学到的内容:
- 安装和配置 ESLint
- 在 React 源代码上运行 ESLint
- 从 Airbnb 获取配置帮助
- Linting JSX 和 React 组件
- 将 ESLint 与代码编辑器集成
- 自定义 ESLint 错误和警告
- 使用 Prettier 自动格式化代码
自动化 React 源代码质量的第一步是安装和配置用于自动化 ESLint 的工具。安装 ESLint 后,它会在您的系统上安装一个eslint
命令。与其他安装命令的软件包一样,最好将它们作为项目的一部分本地安装,这样您就不必依赖系统上全局可用的命令。
要在项目中安装 ESLint,请运行以下npm
命令:
npm install eslint --save-dev
现在已经安装了 ESLint,您可以创建一个新的 npm 脚本来为您运行 ESLint。将以下内容添加到您的package.json
文件的scripts
部分:
"scripts": {
...
"lint": "eslint"
},
现在您有了一个eslint
命令,可以在项目中运行。试一试:
npm run lint
您应该在控制台中看到一条用法消息,而不是 lint 任何源文件:
eslint [options] file.js [file.js] [dir]
Basic configuration:
-c, --config path::String Use configuration from this file or shareable config
--no-eslintrc Disable use of configuration from .eslintrc
--env [String] Specify environments
--ext [String] Specify JavaScript file extensions - default: .js
--global [String] Define global variables
--parser String Specify the parser to be used
--parser-options Object Specify parser options
...
如您所见,您必须告诉eslint
命令您要删除哪些文件或目录。为了简单起见,我们假设所有代码都与package.json
位于同一目录中。您可以按如下方式修改您的package.json
文件,以便 ESLint 知道在何处查找文件:
"scripts": {
...
"lint": "eslint ."
},
您注意到在eslint
之后添加的点(.
了吗?这表示大多数系统上的当前目录。继续,再跑一次npm run lint
。这一次,您将看到一个不同的输出,因为 ESLint 实际上正在尝试查找要 lint 的源文件:
Oops! Something went wrong! :(ESLint: 4.15.0.
ESLint couldn't find a configuration file. To set up a configuration file for this project, please run:
eslint --init
好吧,让我们照它说的做。我们将运行npm run lint -- --init
来创建一个配置文件。执行此操作时,您将看到许多选项可供选择:
? How would you like to configure ESLint?
› Answer questions about your style
Use a popular style guide
Inspect your JavaScript file(s)
现在让我们来看第一个选项,并回答一些关于您计划编写的代码的基本问题。选择选项后,按回车进入第一个问题:
? Are you using ECMAScript 6 features? (y/N)
是的,你是。
? Are you using ES6 modules? (y/N)
是的,你是。
? Where will your code run? (Press <space> to select, <a> to toggle all, <i> to inverse selection)
›(*) Browser
( ) Node
选择Browser
。
? Do you use CommonJS? (y/N)
不。
? Do you use JSX? (y/N)
不。我们将在本章后面讨论 JSX。
? What style of indentation do you use? (Use arrow keys)
› Tabs
Spaces
你想用什么就用什么,因为我肯定会错的。
? What quotes do you use for strings? (Use arrow keys)
› Double
Single
仅有一个的你是什么动物?
? What line endings do you use? (Use arrow keys)
› Unix
Windows
Unix 在这里是一个安全的赌注。
? Do you require semicolons? (Y/n)
这是一个棘手的问题。分号在 JavaScript 源代码中不是必需的。有时它们可以提供帮助,而有时它们只是为 JavaScript 解释器已经理解的内容添加语法。如果不确定,请使用分号;您可以随时在以后更改 ESLint 配置:
? What format do you want your config file to be in? (Use arrow keys)
› JavaScript
YAML
JSON
使用你最喜欢的阅读和编辑工具。我将坚持使用 JavaScript 的默认选项:
Successfully created .eslintrc.js file
好极了让我们再次尝试运行此操作:
npm run lint
这次没有输出。这只是意味着 ESLint 没有发现任何错误。部分原因是项目中还没有代码,但现在已经有了一个已知的工作起点。让我们快速查看为您创建的 AutoT0x 文件:
module.exports = {
"env": {
"browser": true,
"es6": true
},
"extends": "eslint:recommended",
"parserOptions": {
"sourceType": "module"
},
"rules": {
"indent": [
"error",
4
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
};
由于您已经回答了创建此文件所需的问题,因此不需要更改任何内容。执行此操作时,这是要编辑的文件。当您刚刚学习 ESLint 时,键入这样的配置文件可能会让人不快。最终,当您决定需要调整代码质量标准时,ESLint 规则引用(https://eslint.org/docs/rules/ 是一个很好的资源。
作为为项目设置和配置 ESLint 的最后一步,让我们向 lint 介绍一些源代码。如果index.js
文件不存在,则创建一个index.js
文件,并添加以下功能:
export const myFunc = () => 'myFunc';
不要担心运行这个函数,linting 与测试或类型的用途不同。相反,从代码质量的角度来看,linting 为开发人员提供了一些容易遗漏的错误提示。正确性不同于代码质量。这意味着您可以使用 ESLint 使用大量可调整选项,告诉它如何评估代码。
现在,回到刚才添加的函数。您可以通过再次运行npm run lint
来验证此功能是否正常。果然,根据您在.eslintrc.js
中配置的规则,该功能很好。现在,尝试从函数中删除分号,使其如下所示:
export const myFunc = () => 'myFunc'
这一次,您从 ESLint 得到一个错误:
index.js
1:37 error Missing semicolon semi
Χ 1 problem (1 error, 0 warnings)
这正是您需要的输出类型。它提供源文件的名称、文件中错误/警告的位置,并描述发现的实际问题。
让我们再试一次。继续并恢复您删除的分号。现在,删除export
语句,使您的函数定义如下所示:
const myFunc = () => 'myFunc';
现在,当此代码被删除时,您会得到一个不同的错误:
index.js
1:7 error 'myFunc' is assigned a value but never used no-unused-vars Χ 1 problem (1 error, 0 warnings)
因为您删除了export
关键字,所以该模块只是分配给myFunc
的一个函数。它从未被使用过,埃斯林特可以告诉你。
拥有大型 JavaScript 代码库的组织在代码质量工具上投入了大量资金。这包括在配置 ESLint 等工具方面的投资。使用一组标准配置值来提高代码质量的最大好处在于,开发人员之间不会因为配置的细微差异而产生任何差异。
ESLint 允许您安装和使用 npm 包作为配置设置来使用和扩展。一个流行的选择是 Airbnb 标准。让我们再次使用 ESLintinit
工具开始使用 Airbnb JavaScript 代码质量标准。首先,再次运行init
工具:
npm run lint -- --init
第一个问题询问您希望如何配置 ESLint。您可以选择以下指南,而不是回答问题:
? How would you like to configure ESLint?
Answer questions about your style
› Use a popular style guide
Inspect your JavaScript file(s)
下一个问题让您选择要遵循的指南。您需要 Airbnb 指南:
? Which style guide do you want to follow?
Google
› Airbnb
Standard
现在,ESLint 将安装必要的 npm 软件包,以使用 Airbnb 的 ESLint 配置设置:
Checking peerDependencies of eslint-config-airbnb-base@latest
Installing eslint-config-airbnb-base@latest, eslint-plugin-import@^2.7.0
+ eslint-plugin-import@2.8.0
+ eslint-config-airbnb-base@12.1.0
让我们看看 ESLint 创建的.eslintrc.js
文件是什么样子的:
module.exports = {
"extends": "airbnb-base"
};
正如您所看到的,这个文件现在几乎没有什么内容,因为所有内容都由airbnb-base
npm 包处理。你的.eslintrc.js
只是在扩展它。让我们看看 Airbnb 的一些规则在起作用。将以下代码添加到index.js
:
const maybe = v => v ? v : 'default';
console.log(maybe('yes'));
// -> yes
console.log(maybe());
// -> default
maybe()
函数返回参数,如果它是真的;否则返回字符串default
。然后,maybe()
用字符串值调用,但根本没有值。注释表示这两个函数调用的输出。请随意运行此代码,以确保它按照广告的方式工作。
完成后,让我们看看 Airbnb 对代码的看法:
npm run lint
以下是输出:
index.js
1:15 error Arrow function used ambiguously with a conditional expression no-confusing-arrow
1:24 error Unnecessary use of conditional expression for default assignment no-unneeded-ternary
3:1 warning Unexpected console statement no-console
5:1 warning Unexpected console statement no-console
Χ 4 problems (2 errors, 2 warnings)
四个问题!哎哟让我们逐一浏览一下,看看能做些什么。第一个错误是no-confusing-arrow
,表示箭头函数与比较运算符的使用含糊不清。你可以去看看每个错误的细节(https://eslint.org/docs/rules/ 在这里您可以找到详细的解释和示例。
下一个错误no-unneeded-ternary
与第一个错误密切相关。它表明我们可以使用比三元表达式更简单的表达式,这将有助于提高代码的可读性。让我们试试看。maybe()
函数应该返回参数或一些默认值(如果参数为 falsy)。让我们尝试使用逻辑 OR(| |)来代替三元运算符:
const maybe = (v = 'default') => v;
这里的可读性略有提高,语法肯定更少。关于小改进本身,更重要的是,在这个代码库上工作的每个开发人员都将进行相同的小改进。现在让我们看看npm run lint
是怎么说的:
index.js
6:1 warning Unexpected console statement no-console
8:1 warning Unexpected console statement no-console
Χ 2 problems (0 errors, 2 warnings)
令人惊叹的你只有两个警告。但他们只是在抱怨你的电话。显然,Airbnb ESLint 规则不喜欢这样,但你喜欢。由于您只是通过扩展 Airbnb 规则设置作为起点,因此也可以将其禁用。在你的情况下,no-console
规则没有任何作用,因为你显然依赖它。为此,请将您的.eslintrc.js
文件编辑为如下所示:
module.exports = {
"extends": "airbnb-base",
"rules": {
"no-console": 0
}
};
在 ESLint 配置的extends
部分之后,您可以添加一个rules
部分,在这里您可以关闭airbnb-base
定义的特定规则。在本例中,将no-console
设置为0
会告诉 ESLint 它不应该报告这些警告。让我们再运行一次npm run lint
,看看这是否解决了所有问题。
果然,没有更多的错误要报告了!
假设您在尝试并喜欢了 Airbnb 的一套 ESLint 规则后,希望使用它。我们还假设您也希望 lint 您的 React 组件代码。在 ESLintinit
过程中,您已经回答了No
关于项目是否使用 React 的问题。这次,让我们说Yes
。因此,再次运行 ESLintinit
流程:
npm run lint -- --init
同样,您希望使用 Airbnb lint 规则:
? Which style guide do you want to follow?
Google
› Airbnb
Standard
当它询问您是否使用 React 时,说Yes
:
? Do you use React? (y/N) y
您会注意到安装了两个额外的软件包:
+ eslint-plugin-react@7.5.1
+ eslint-plugin-jsx-a11y@6.0.3
现在,让我们编写一些 React 代码,以便我们可以对其进行筛选。将以下组件添加到MyComponent.js
:
import React, { Component } from 'react';
class MyComponent extends Component {
render() {
return (
<section>
<h1>My Component</h1>
</section>
);
}
}
export default MyComponent;
下面是该组件的渲染方式:
import React from 'react';
import ReactDOM from 'react-dom';
import MyComponent from './MyComponent';
const root = document.getElementById('root');
ReactDOM.render(
<MyComponent />,
root
);
您无需担心在浏览器中运行此 React 应用;这只是为了确保 ESLint 能够解析 JSX 并对其进行 lint。现在让我们尝试运行 ESLint:
npm run lint
以下是此源代码在 lint 时生成的错误:
index.js
5:14 error 'document' is not defined no-undef
8:3 error JSX not allowed in files with extension '.js' react/jsx-filename-extension
9:7 error Missing trailing comma comma-dangle
MyComponent.js
3:1 error Component should be written as a pure function react/prefer-stateless-function
6:7 error JSX not allowed in files with extension '.js' react/jsx-filename-extension
要处理的两个源文件中都有错误。现在,让我们逐一检查这些错误。
index.js
的第一个错误是no-undef
,它指的是一个不存在的document
标识符。问题是,document
是一个全局存在于浏览器环境中的标识符。ESLint 不知道定义了这个全局标识符,所以我们必须告诉它.eslintrc.js
中的值:
module.exports = {
"extends": "airbnb",
"globals": {
"document": true
}
};
在 ESLint 配置的globals
部分,您可以列出 ESLint 应该识别的全局标识符的名称。如果标识符实际上对引用它的源代码是全局可用的,那么该值应该是true
。这样,ESLint 就知道不要抱怨在浏览器环境中被识别为全局标识符的东西。
为特定环境(如 web 浏览器)中存在的标识符添加全局变量的问题在于全局变量太多。您不需要维护这样的列表,以便 ESLint 传递您的源代码。谢天谢地,ESLint 有一个解决方案。您可以指定代码将在其中运行的环境,而不是指定globals
:
module.exports = {
"extends": "airbnb",
"env": {
"browser": true
}
};
在将browser
环境指定为true
的情况下,ESLint 知道所有浏览器全局变量,并且在代码中找到它们时不会抱怨。此外,您可以指定多个环境,因为在浏览器和 Node.js 中运行代码是很常见的。或者,即使您不在环境之间共享代码,您也可能希望 lint 一个同时包含客户端和服务器代码的项目。在这两种情况下,以下是多个 ESLint 环境的功能:
module.exports = {
"extends": "airbnb",
"env": {
"browser": true,
"node": true
}
};
下一个要修复的错误是react/jsx-filename-extension
。此规则来自初始化 ESLint 配置时安装的eslint-plugin-react
包。该规则要求您使用不同的扩展名命名包含 JSX 语法的文件。让我们假设您不想为此而烦恼(我不会责怪您,为几乎相同类型的文件内容维护两个文件扩展名需要花费大量精力)。让我们暂时禁用此规则。
以下是更新的 ESLint 配置:
module.exports = {
"extends": "airbnb",
"env": {
"browser": true,
"node": true
},
"rules": {
"react/jsx-filename-extension": 0
}
};
通过在配置的rules
部分将react/jsx-filename-extension
规则的值设置为0
来忽略该规则。继续,再跑一次npm run lint
。我们现在只有两个错误。
comma-dangle
规则肯定是固执己见的,但这是一个有趣的想法。让我们放大触发此错误的错误代码:
ReactDOM.render(
<MyComponent />,
root
);
ESLint 抱怨在root
参数后没有尾随逗号。其思想是在添加尾随逗号时:
- 以后添加项目更容易,因为逗号已经存在
- 当您提交代码时,它会导致更清晰的差异,因为添加或删除项只需要更改一行而不是两行
让我们假设这是有意义的,并且您决定保留此规则(我喜欢),下面是固定代码的样子:
ReactDOM.render(
<MyComponent />,
root,
);
现在我们再跑一次。还有一个错误!这是另一个特定于 React 的错误:react/prefer-stateless-function
。让我们再看看触发此错误的 React 组件:
import React, { Component } from 'react';
class MyComponent extends Component {
render() {
return (
<section>
<h1>My Component</h1>
</section>
);
}
}
export default MyComponent;
ESLint 在eslint-plugin-react
的帮助下告诉您,该组件应该作为函数而不是类来实现。它这样说是因为它能够检测到MyComponent
没有任何状态,也没有任何生命周期方法。因此,如果它作为一个函数实现,它:
- 不再依赖
Component
类 - 将是一个简单的函数,语法远远少于类
- 很明显,这种成分没有副作用
考虑到这些好处,让我们继续将MyComponent
重构为纯函数,正如 ESLint 错误所示:
import React, { Component } from 'react';
const MyComponent = () => (
<section>
<h1>My Component</h1>
</section>
);
export default MyComponent;
当你运行npm run lint
时,你会得到:
MyComponent.js
1:17 error 'Component' is defined but never used no-unused-vars
呜呜,你在修复另一个错误的过程中引入了一个新错误。没什么大不了的,这就是为什么你要过滤你的代码,去寻找那些容易漏掉的东西。在本例中,是因为我们忘记取出Component
导入,所以出现了no-unused-vars
错误。以下是固定版本:
import React from 'react';
const MyComponent = () => (
<section>
<h1>My Component</h1>
</section>
);
export default MyComponent;
你完成了,没有更多的错误!在eslint-config-airbnb
和eslint-plugin-react
的帮助下,您能够生成任何其他 React 开发人员都可以轻松阅读的代码,因为他们可能使用完全相同的代码质量标准。
到目前为止,您在本章中看到的一切,都是您自己设置和配置的。并不是说让 ESLint 启动并运行起来特别困难,而是create-react-app
将这一点完全抽象了出来。记住,create-react-app
的想法是尽快开始编写组件代码,而不必考虑配置 linter 之类的东西。
要了解这一点,让我们使用create-react-app
创建一个新的应用:
create-react-app my-new-app
然后,在应用创建后立即启动:
npm start
现在让埃斯林特来抱怨一些事情。在你的编辑器中打开App.js
应该是这样的:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
ESLint 认为这很好,所以让我们删除Component
导入,使App.js
现在看起来像这样:
import React from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">Welcome to React</h1>
</header>
<p className="App-intro">
To get started, edit <code>src/App.js</code> and save to reload.
</p>
</div>
);
}
}
export default App;
您的App
类现在正在尝试扩展Component
,但它并不存在。保存文件后,将调用 ESLint,因为它作为一个网页包插件与开发服务器集成。在 dev 服务器控制台中,您应该看到以下内容:
Failed to compile.
./src/App.js
Line 5: 'Component' is not defined no-undef
正如预期的那样,ESLint 会为您检测问题。将 ESLint 与开发服务器集成的好处在于,您不必记住调用npm run lint
命令。如果 ESLint 未通过,则整个构建将失败。
不仅会在 dev 服务器控制台中通知您生成失败,还会在浏览器中直接通知您:
这意味着,即使您忘记查看服务器控制台,也很难错过替换整个 UI 的控制台。如果您撤消了故意破坏 ESLint 的更改(将Component
导入添加回),则在保存App.js
后,您的 UI 将再次显示。
如果你想让你的create-react-app
代码更进一步,你可以。如果你在编写组件代码的中间,你想做的最后一件事就是切换到控制台或浏览器窗口,只是看看你写的东西是否足够好。对于一些人来说,更好的开发体验是在他们的编辑器中看到 lint 错误的发生。
让我们来看看如何用原子做这件事。首先,您需要安装linter-eslint
插件:
现在,当您在 Atom 中打开 JavaScript 源文件时,该插件将为您过滤它们,并内联显示错误和警告。唯一的挑战是create-react-app
实际上并没有为您创建.eslintrc.js
文件。这是因为create-react-app
的本质是默认情况下对您隐藏所有配置。
但是,ESLint 仍然由create-react-app
配置。这就是在启动开发服务器时如何删除源代码的方式。问题是您可能希望在编辑器 linter 中使用此配置。create-react-app
安装了一个名为eslint-config-react-app
的包,其中包含开发服务器使用的 ESLint 配置。您可以在自己的项目中使用此选项,以便编辑器 linter 的配置与在浏览器或控制台中输出的任何内容相同。这一点非常重要,您最不希望让编辑器告诉您有关代码的一件事,而您在浏览器中看不到任何问题。
如果在 Atom 中打开App.js
,则不会看到任何 lint 错误,因为:
- 没有了
linter-eslint
Atom 插件未运行,因为它未找到任何配置
以下是没有错误时文件的外观:
您所要做的就是添加扩展eslint-config-react-app
配置的 ESLint 配置。在项目的根目录中,创建以下.eslintrc.js
文件:
module.exports = {
"extends": "eslint-config-react-app"
};
现在 Atomlinter-eslint
插件将尝试在运行中删除您的开源文件。此外,它将使用与您的create-react-app
开发服务器完全相同的配置。让我们再次尝试删除Component
导入。现在,编辑器中的情况看起来有些不同:
如您所见,Component
标识符用红色下划线,因此代码的这一部分非常突出。在您的源代码下面,有一个窗格,其中显示了找到的每个 linter 错误的列表,以及关于每个错误的更多详细信息。如果您要运行npm start
,您将在 dev 服务器控制台和浏览器中看到完全相同的错误,因为 Atom 使用与create-react-app
相同的 ESLint 配置。
现在让我们消除这个错误。转到以下代码行:
import React from 'react';
将其更改回:
import React, { Component } from 'react';
编辑器中不应再出现可见的过梁错误。
ESLint 可用于改进代码的任何方面,包括其格式化方式。在这个作业中使用类似于 ESLint 的东西的问题在于,它只告诉您它发现的格式问题。你还得去修理它们。
这就是create-react-app
中的 ESLint 配置没有指定任何代码格式规则的原因。这就是像 Prettier 这样的工具的用武之地。它是一个针对 JavaScript 代码的自以为是的代码格式化程序。它对 JSX 的理解是开箱即用的,因此它非常适合格式化您的 React 组件。
《用户指南》create-react-app
中有一整节介绍如何设置 Git 提交钩子,在提交任何代码之前触发 Prettier 对其进行格式化:https://github.com/facebookincubator/create-react-app#user-指南。
我不会在这里重复这个指南,但基本思想是,在任何已提交的 JavaScript 源代码上调用 Prettier 的 Git 钩子就位将确保所有内容都是格式化的,很好,非常漂亮。仅依赖 Git 提交钩子的缺点是,作为开发人员,您在编写格式化代码时不一定会看到它。
除了在每次提交时设置 Prettier 来格式化 JavaScript 源代码外,添加一个代码编辑器插件可以极大地改善开发体验。同样,您可以安装适当的 Atom 软件包(或类似的软件包;Atom 很受欢迎,因此我在这里使用它作为示例编辑器):
一旦安装了prettier-atom
包,就可以使用 Atom 格式化 React 代码。默认情况下,此包使用键绑定Ctrl+Alt+F调用 Prettier 来格式化当前源文件。另一个选项是在保存时启用格式:
现在,每次保存 JavaScript 源代码时,Prettier 都会对其进行格式化。让我们测试一下。首先,打开App.js
并完全丢弃格式,使其看起来像这样:
总的让我们保存文件,看看会发生什么:
那好多了。想象一下,如果你不得不手动修复这个烂摊子。Prettier 让您的代码清晰明了,您几乎没有任何想法。
本章主要介绍如何使用工具强制执行 React 项目的代码质量级别。您了解的第一个工具是 ESLint。您学习了如何安装和配置它。您很少需要手动配置 ESLint。您学习了如何使用 ESLint 初始化工具,该工具将引导您完成可用于配置 ESLint 规则的各种选项。
接下来,您了解了可以在 React 应用中使用的不同标准 ESLint 配置。Airbnb 是一个流行的标准,您可以与 ESLint 一起使用,您可以根据团队的特定风格对其进行规则定制。您还可以告诉 ESLint 初始化工具您计划使用 React,并让它为您安装适当的软件包。
最后,您了解了create-react-app
如何使用 ESLint。当开发服务器运行时,它使用一个 Webpack 插件来过滤代码。您了解了create-react-app
如何为此配置 ESLint,以及如何将此配置用于代码编辑器。Prettier 是一个可以自动格式化代码的工具,这样您就不必花时间手动处理大量 ESLint 样式的警告。
在下一章中,您将学习如何使用 Storybook 在自己的环境中隔离 React 组件开发。