在本书的第1部分中,我创建了一个SportsStore应用程序用来演示如何将不同的React功能与其他依赖进行包组
合创建一款真正的应用程序。在本书的这一部分,我将深入描述 React 的内置特性细节。在本章中,我们将学习React的项目结构和开发工具、编译封装代码和内容并发送到浏览器渲染的过程。我们首先了解React项目构建命令:create-react-app 项目名称
问题 | 答案 |
---|---|
create-react-app 是什么? | create-react-app包用于创建项目并设置 React 开发所需要的工具。 |
create-react-app 为什么有用? | 使用create-react-app模块创的建项目是为开发复杂应用程序而设计的,它提供了一整套用于开发、测试和部署的工具。 |
如何使用create-react-app | 使用npx create-react-app包创建项目,然后使用npm start命令启动开发工具。 |
create-react-app有什么陷阱或限制吗? | create-react-app模块提供了一种使用很少配置选项的特定工作方式。如果您习惯于使用其他不同的工作流程,这可能会令人沮丧。 |
还有其他选择吗? | create-react-app并不是创建项目的唯一方法。如本章后面所述,还有可以使用其他可用的模块创建项目。 |
下表是本章节主要内容:
问题 | 解决方案 |
---|---|
创建一个新的React 项目 | 使用create-react-app 创建项目并添项目以来模块 |
将HTML转换为JavaScript | 使用JSX语法来混合使用HTML和代码语句 |
添加静态内容 | 将文件添加到src文件夹,并使用import关键字将文件引入 到应用程序中 |
添加开发工具之外的静态内容 | 将文件添加到 public 文件夹并使用PUBLIC_URL属性定义引 用 |
禁用 linting 消息 | 向JavaScript文件添加注释 |
配置React开发工具 | 创建一个.env文件设置配置属性 |
调试React应用程序 | 使用React Devtools浏览器扩展或使用浏览器debug |
本章准备
要创建本章的示例项目,请打开一个新的命令提示符, 导航到创建项目的位置,运行下面的命令创建项目:
npx create-react-app projecttools
■ 注意 创建新项目时,可能会看到很多安全漏洞警告。react开发依赖大量的软件模块,每个模块都有自己的依赖项,安全问题不可避免地会出现。对于本书中的示例,使用指定的模块版本以确保获得预期的结果非常重要。
对于您自己的项目,您应该查看警告并将模块更新到漏洞已被修复的版本。进入项目文件夹, 运行下面的命令将 Bootstrap 包添加到项目中:cd projecttool<br />npm install bootstrap@4.1.2
要在应用程序中引入 Bootstrap,请将下面所示的语句添加到 index.js 文件中,可以在src文件夹中找到这个文件:
import React from 'react';
import ReactDOM from 'react-dom';
import './index.css';
import App from './App';
import * as serviceWorker from './serviceWorker';
import 'bootstrap/dist/css/bootstrap.css';
ReactDOM.render(<App />, document.getElementById('root'));
// If you want your app to work offline and load faster, you can change
// unregister() to register() below. Note this comes with some pitfalls.
// Learn more about service workers: http://bit.ly/CRA-PWA
serviceWorker.unregister();
使用命令提示符进入projecttools文件夹运行命令启动开发工具:npm start
■ 注意,这里使用 npm 命令启动开发工具而不是创建项目时使用的npx命令。
项目的初始化准备完成后,在浏览器中打开网址 http://localhost:3000,将显示以下截图所示内容。
了解React 项目结构
创建新项目时,我们首先要从基本的React应用程序文件、占位符内容和一整套开发工具开始学习。下面截图显示了projecttools文件夹的内容。
■ 注意 您不一定要使用 create-react-app 包来创建React项目,但这是最常用的方法,这种方法能够非常简单快速地地帮我们配置项目开发所需要的构建工具。如果你愿意,可以自己创建所有文件并配置工具,也可以使用其他可用于创建项目的技术(如中所述)h ttps:/ /reactjs.org / docs / c reate-a-new-react-app.html。
上面的截图显示了项目中的所有文件,接下来中我将进一步详细地讲解这些文件。
名称 | 说明 |
---|---|
node_modules | 该文件夹包含应用程序和开发工具所需的依赖模块,详见 “了解Packages 文件夹”部分。 |
public | 此文件夹用于存放静态内容,包括用于响应HTTP请求的index.html文件,详见“了解静态内容”部分。 |
src | 此文件夹包含应用程序代码和内容,详见“了解源码文件夹” 部分。 |
.gitignore | 此文件用于从Git修订控制包中排除文件和文件夹。 |
package.json | 此文件用于配置项目的所需的顶级模块,详见“了解 Packages 文件夹”部分。 |
package-lock.json | 此文件包含项目所需模块的完整列表,详见“了解 Packages 文件夹”部分。 |
README.md | 此文件包含有关项目工具的信息,可以在https://github.com/facebook/createreact-app中找到相同的内容。 |
了解源代码文件夹
src文件夹是文件中最重要的,因为我们的应用程序代码和内容就存放在这个文件夹中,同时也是存放我
们自定义项目文件的位置。create-react-app 将模块文件添加到快速启动开发中,如下表所示:
名称 | 说明 |
---|---|
index.js | 此文件负责配置和启动应用程序 |
index.css | 此文件包含应用程序的全局CSS样式。有关使用CSS文件的详细信息,请参阅“理解静态内容”部分。 |
App.js | 此文件是React顶级组件。接下来的两个章节我们将学习组件内容。 |
App.css |
此文件是新项目的占位符CSS样式。有关详细信息,请参阅“了解静态内容”部分。 |
App.test.js |
此文件包含顶级组件的单元测试。有关单元测试的详细信息,后续章节会进行讲解。 |
registerServiceWorker.js |
此文件由渐进式web应用程序使用,它可以脱机工作。本书教程并不涉及渐进式应用程序,但您可以在https://facebook.github.io/ create-react-app/docs/making-a-progressive-web-app中进一步了解。 |
logo.svg |
此图像文件包含 React logo,在创建项目时将被添加到项目中的占位 符组件中显示。请参阅“了解静态内容部分”。 |
了解模块文件夹
JavaScript应用程序开发依赖于一个丰富的模块生态系统,从将代码发送到浏览器进行渲染的模块,到在开发过
程中用于特定任务的小型模块。React项目需要许多模块:例如,本章开头创建的示例项目就需要900多个模块。在这些模块之间又存在复杂的依赖关系层次结构,很难手动管理,因此要使用模块管理器来进行管理。可以使用两个不同的模块管理器来创建React项目:npm,它是Node模块管理器,在第一章中这个模块管理器是Node自带。还一个是Yarn,这是npm最新的竞争对手,Yarn旨在改善模块管理。为了简单起见,我在本书中使用了npm。
■ 提示 你应该遵循教程中的例子使用npm来,如果你想在你自己的项目中使用Yarn,你可以在https://yarnpkg.com了解Yarn的使用方法。
当创建项目时,模块管理器会获取React开发所需的初始模块列表,每个模块都会被检查以获得项目所依赖的模块集。再次执行该过程以获取这些模块的依赖关系,并重复该过程,直到建立了一个完整的模块列表。模块管理器下载并安装所有模块,并将它们安装到node_modules文件夹中。
在package.json文件中使用dependencies和devDependencies属性定义初始模块集。dependencies部分用于列出应用程序运行所需的模块。devDependencies部分用于列出开发所需但不作为应用程序一部分部署的模块。
您可能会在您的项目中看到不同的详细信息,但这是模块中的 dependencies 部分。我的示例项目中的json文件:
...
"dependencies": {
"bootstrap": "^4.1.2",
"react": "^16.7.0",
"react-dom": "^16.7.0",
"react-scripts": "2.1.2"
},
...
React项目的模块依赖项部分只需要三个依赖包:React依赖包是React框架的主要功能,React-dom依赖包包含web应用程序所需的功能,react-scripts依赖包包含我在本章中描述的开发工具命令。第四个包是Bootstrap CSS框架,我们前面已将将bootstrap添加到项目中了。对于每个模块,package.json 件都包含了可使用版本的详细信息,下表是详细说明:
格式 | 说明 |
---|---|
16.7.0 | 直接表示版本号将只接受与版本号完全匹配的包,例如 16.7.0。 |
* | 使用星号表示接受任意版本的依赖包。 |
>16.7.0 >=16.7.0 | 在版本号前面加上>或>=可接受大于或大于给定版本的包的所有版本。 |
<16.7.0 <=16.7.0 | 在版本号前面加上<或<=接受包的任何版本,该版本号小于或等于给定版本。 |
~16.7.0 | 在版本号前面加上波浪号(~字符)接受要安装的版本,即使补丁版本(三个版本号中的最后一个)不匹配。例如,指定~16.7.0将接受16.7.1或16.7.2版(其中包含16.7.0版的补丁 版本),但不接受16.8.0版(这将是一个新的次要版本)。 |
^16.7.0 |
在版本号前面加上插入符号(^字符),即使次要版本号(三个版本号中的第二个版本号)或补丁 版本不匹配也接受。例如,指定^16.7.0将允许版本16.8.0和16.9.0,但不允许版本17.0.0。 |
依赖项部分中指定的版本号package.json文件将接受较小的更新和补丁程序。
了解全局和局部依赖包
模块管理器即可以安装用于特定单个项目的依赖包(称为局部安装),也可以适用于若有项目的依赖包(称为全局安装)。很少有依赖包需要全局安装,但create-reactapp 依赖包是个例外。作为本书准备工作的一部分,我在第一章中已经安装了这个包。create-react-app 依赖包需要全局安装,因为它用于创建新项目。项目所需的各个依赖包都被安装到项目中到 node_modules 文件夹中。
创建React项目时,开发所需的所有依赖包都会自动下载并安装到node_modules文件夹中,但表下表列出了一些在开发过程中可能会用到的NPM命令。所有这些命令都应该在项目文件夹中运行,该文件夹包含 package.json 文件。
命令 |
说明 |
---|---|
npx create-react-app |
将创建新的React项目。 |
npm install | 这用于在package.json 文件中安装项目所需要的依赖包 |
npm install package@version | 该命令用于在本地安装特定的依赖包并更新 package.json 文件将依赖包 添加到dependencies 部分 |
npm install —save-dev package@version |
此命令用于本在地安装在本地安装特定版本的依赖包,并更新 package.json 文件将依赖包添加到devDependencies部分,我们通过这 部分将开发所需但不是应用程序一部分依赖包的包添加到项目中。 |
npm install —global package@version |
此命令用于安装特定的全局依赖包 |
npm list | 此命令将列出所有本地包及其依赖项。 |
npm run | 此命令将执行包中定义的脚本之一package.json 文件,如下所述。 |
上表中最后一个命令很奇怪,但是依赖包管理器通常支持运行package.json文件 中 scripts 中定义的代码。在React项目中,这部分代码用于访问开发过程中使用的工具和部署应用程序的工具。这是package.json示例项目中的文件:
...
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
...
上面这些命令在下面中给出了说明,我将在后面的章节中演示它们的用法。
名称 | 说明 |
---|---|
start | 这个命令启动开发工具,详见“使用React开发工具”。 |
build | 此命令用于执行程序构建过程。 |
test | 这个命令运行单元测试,在测试章节进行学习。 |
ejec | 此命令将所有开发工具的配置文件复制到项目文件夹中。这是一个单向操作,仅当开发 工具的默认配置不适合项目时才应该使用。 |
上表中的命令通过使用npm run执行的,后面跟你需要的命令名,这个命令必须要在包含package.json 文件的项目文件夹中执行。所以如果您想在projecttools文件夹中执行 build 命令,你需要在命令提示符中打开projecttools 文件夹,然后执行 npm run build命 令。start命令是例外,直接使用npm start执行。
使用React开发工具
添加到项目中的开发工具会自动发现src文件夹中更改的文件,编译的应用程序以及打包可供浏览器使用的文件。这些任务都可以手动执行,但是自动更新可以带来更愉快的开发体验。如果它们还没有运行,在一个命令提示符中打开到projecttools文件夹,然后运行下面的命令来启动开发工具。npm start
开发工具使用的关键包是webpack,它是许多JavaScript开发工具和框架的核心。Webpack是一个模块绑定器,这意味着它将JavaScript模块打包到浏览器中使用,尽管对这个重要功能的描述可能平淡无奇,但是它是开发React应用程序用到的一个非常重要的工具。
当您运行清单9-5中的命令时,您将看到一系列关于 webpack 准备运行示例应用程序所需的依赖包信息。Webpack 以 index.js文件开始并加载包含import语句的所有模块,以创建一组依赖项。对index.js依赖的每个模块重复此过程,并且webpack在应用程序中一直工作,直到它对整个应用程序有一组完整的依赖项,然后将这些依赖项组合成一个文件,称为bundle。
捆绑过程可能需要一些时间,但只有在启动开发工具时才需要执行。完成初始准备后,您将看到这样一条消息,它告诉您应用程序已编译并绑定完成:
...
Compiled successfully!
You can now view projecttools in the browser.
Local: http://localhost:3000/
On Your Network: http://192.168.0.77:3000/
Note that the development build is not optimized.
To create a production build, use npm run build.
...
初始过程完成后,将为打开一个新的浏览器窗口http://localhost:3000/,显示图9-3中的占位符内容。
了解编译和转译过程
Webpack负责构建过程,其中一个关键步骤是Babel包执行的代码转译。Babel在React项目中有两个重要的任务:转换JSX内容和将使用的最新的JavaScript代码转换为旧浏览器可以执行的代码。
了解JSX转译
正如我在第3章“了解JSX转换”中所解释的,JSX格式是JavaScript的超集,它允许HTML与常规代码语句混合使用。JSX不完全支持标准的HTML,最明显的区别是HTML中的class等属性在JSX文件中表示为className。这些奇怪之处的原因是,在构建过程中,JSX文件的内容被Babel转换为调用的React API,因此每个HTML元素都被转换为调用React.createElement的方法。
在我们的App.js文件中有一个App组件(组件名被省略),这个组件有一个render方法返回简单的HTML元素,现在我用下面的代码替换render方法中的代码:
import React, { Component } from "react";
export default class extends Component {
render = () =>
<h4 className="bg-primary text-white text-center p-3">
This is an HTML element
</h4>
}
在转换过程中,h4元素被替换为React.createElement方法生成的JavaScript代码,并且不需要浏览器对JSX有特殊的理解。作为一个简单的演示,下面使用React.createElement方法也会直接获得同样的结果
import React, { Component } from "react";
export default class extends Component {
render = () => React.createElement("h4",
{ className: "bg-primary text-white text-center p-3" },
"This is an HTML element")
}
上面两段代码的渲染结果是一样的,当Babel处理App.js文件时,会生成第二段代码中的代码。当React在浏览器执行JavaScript代码时,它使用DOM API创建HTML元素。这看起来像是一种循环方法,但是JSX转换只在构建过程中执行,目的是使开发者更容易地编写React特性。
了解JavaScript语言转换
在经历了多年的停滞之后,JavaScript语言重新焕发了活力,并通过一些特性实现了现代化,这些特性简化了开发,并提供了其他编程语言中常见的特性,如第4章中描述的那些特性。并非所有浏览器都支持所有最新的语言特性,尤其是较旧的浏览器或那些用于公司环境的浏览器,在这些环境中,更新通常是缓慢推进的(如果有的话)。Babel将JavaScript的新特性转成老旧浏览器支持的代码,这些代码使用了更广泛浏览器都支持的特性,包括JavaScript复兴之前的浏览器。
在清单9-8中, 我在app.js文件中使用了HTML元素并使用最新的JavaScript特性来设置h4元素的内容。
import React, { Component } from "react";
let name = "Adam";
const city = "London";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () =>
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
}
这个组件依赖于几个最新的JavaScript特性:用于定义类的class和extenses关键字、用于定义变量和常量的let和const关键字,以及message方法中的胖箭头函数和模板字符串。当您保存更改时,React开发工具将自动编译和捆绑JavaScript代码并将其发送到浏览器,生成如下截图所示的内容。
要查看Babel是如何处理现代JavaScript特性的,请按F12键打开浏览器开发者调试器,选择Sources选项卡,然后在窗口左侧的项目栏中找到localhost:3000 > static/js项目,如图所示:
■ 技巧 Google Chrome发工具经常升级更新,您可能需要四处寻找Babel生成的代码。使用Ctrl+F并搜索London是找到您要查找的代码的好方法。另一种方法是将上面的代码粘贴到https://babeljs.io/repl,这将获得类似的结果。
如果您向下滚动或搜索London,如前所述,那么您将看到 Babel 生成的代码。旧版浏览器不支持的所有功能都将替换为向后兼容的代码,如下所示:
let name = "Adam";
const city = "London";
/* harmony default export */ __webpack_exports__["default"] = (class extends react__WEBPACK_IMPORTED_MODULE_0__["Component"] {
constructor(...args) {
super(...args);
this.message = () => `Hello ${name} from ${city}`;
this.render = () => /*#__PURE__*/Object(react_jsx_dev_runtime__WEBPACK_IMPORTED_MODULE_1__["jsxDEV"])("h4", {
className: "bg-primary text-white text-center p-3",
children: this.message()
}, void 0, false, {
fileName: _jsxFileName,
lineNumber: 7,
columnNumber: 5
}, this);
}
});
您不必详细了解这些代码是如何工作的,尤其是因为其中一些代码复杂且难以阅读。重要的是App.js文件会都会被处理, 这里还可以看到模板字符串被替换为字符串连接:
Hello ${name} from ${city}``
Babel会将一些特性(例如类)添加到bundle函数中进行处理,然后发送到浏览器中。JSX HTML片段被转换为调用React.createElement方法。
JavaScript的现代特性的转译是复杂的,但是JavaScript语言最近增加的特性主要是语法糖,目的是让开发人员更愉快地编写代码。
了解 Bable 的不足
Babel是一个优秀的工具,但是它只处理javascript语言特性。Babel无法将对最新的javascript API添加到不支持最新API的浏览器中。但是您仍然可以使用这些api,但是这样做会限制可以运行应用程序的浏览器的范围。
了解开发HTTP服务器
为了简化开发过程,该项目包含了webpack-dev-server包,这是一个与webpack集成的HTTP服务器。服务器配置在初始绑定过程完成后会立即开始侦听端口3000上的HTTP请求。当接收到HTTP请求时,开发HTTP服务器返回public/index.html 文件中的内容。当服务器处理 index. html 文件时。开发服务器会添加一些重要的的文件,您可以在浏览器窗口中右键单击,从弹出的菜单中选择“检查”,在Element选项中你能看到一下代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="/manifest.json">
<link rel="shortcut icon" href="/favicon.ico">
<title>React App</title>
</head>
<body>
<noscript>
You need to enable JavaScript to run this app.
</noscript>
<div id="root"></div>
<script src="/static/js/bundle.js"></script>
<script src="/static/js/0.chunk.js"></script>
<script src="/static/js/main.chunk.js"></script>
<script src="/main.a5f0dcc648ccc4241725.hot-update.js"></script>
</body>
</html>
开发服务器添加了脚本元素,这些元素告诉浏览器加载含有React框架、应用程序代码、静态内容(如CSS)的文件,以及一些支持开发工具的附加功能,并在检测项目文件更改时自动重新加载浏览器。
了解静态内容
有两种方法可以在React应用程序中添加静态内容,例如图像或CSS样式表。在大多数情况下,最好的方法是将所需的文件添加到src文件夹中,然后在代码文件中使用import语句声明对文件之间的依赖关系。
为了演示如何处理src文件夹中的静态内容,我将App.css文件中的CSS样式替换成下面的内容:
img {
background-color: lightcyan;
width: 50%;
}
我定义了img样式元素并设置背景颜色和宽度。下面的代码中,我将src文件夹中的两个静态文件依赖添加到App组件中,包括我在前列表中更新的CSS文件和创建项目时添加到项目中的占位符图像。
■ 技巧 index.css通过 index.js 文件引入到项目中,这个文件负责启动react应用程序的javascript文件。您可以在CSS文件中定义全局样式,它们将包含在发送到浏览器的内容中。
import React, { Component } from "react";
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () => (
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
<img src={reactLogo} alt="reactLogo" />
</div>
);
}
要导入不需要引用就能使用的内容,例如CSS样式表,import关键字后跟文件名,文件名必须包含文
件扩展名,如下所示:
...
import "./App.css";
...
要导入将在HTML元素中使用的内容,如图片,那么指定名称的被引入的内容必须要使用import语句形式,如这个语句:
...
import reactLogo from "./logo.svg";
...
此语句引入 logo.svg 文件并为其指定名称reactLogo,然后我可以在img元素的表达式中使用该名称,如下所示:
...
<img src={ reactLogo } alt="reactLogo" />
...
当您使用import关键字声明对静态内容的依赖性时,关于如何处理内容的决定将留给开发工具。对于小于10Kb的文件,内容将包含在 bundle.js 文件,以及将内容添加到HTML文档所需的JavaScript代码。这就是 App.css 在上面代码中导入的文件:CSS文件的内容将被包含在 bundle.js 文件中,以及创建样式元素所需的代码。
对于较大的文件和SVG文件,导入的文件将在单独的HTTP请求中请求。import语句指定的相对路径将自动替换为定位该文件的URL,并更改文件名以使其包含校验,从而确保浏览器不会缓存过时的数据。
你可以看到可以保存App.js文件后的结果。等待浏览器重新加载,然后使用F12打开开发者调试器检查“Element”选项卡,该选项卡将显示以下HTML内容(尽管为了简洁起见,我省略了大量 Bootstrap CSS样式):
<html lang="en">
<head>
<meta charset="utf-8">
<link rel="shortcut icon" href="/favicon.ico">
<meta name="viewport" content="width=device-width,initial-scale=1, shrink-to-fit=no">
<meta name="theme-color" content="#000000">
<link rel="manifest" href="/manifest.json">
<title>React App</title>
<style type="text/css">
img {
background-color: lightcyan;
width: 50%
}
</style>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root">
<div class="text-center">
<h4 class="bg-primary text-white text-center p-3">
Hello Adam from London
</h4>
<img src="/static/media/logo.5d5d9eef.svg" alt="reactLogo">
</div>
</div>
<script src="/static/js/bundle.js"></script>
<script src="/static/js/1.chunk.js"></script>
<script src="/static/js/main.chunk.js"></script>
<script src="/main.00ec8a0c115561c18137.hot-update.js"></script>
</body>
</html>
您可以看到CSS样式已经从JavaScript捆绑包中解包并通过一个style元素添加到HTML文档中,而图像文件是通过URL:/static/media/logo.5d5d9eef.svg引入的。在构建过程中,大文件会自动复制到应用程序代码中的url指定的位置,这意味着您不必担心它们是否可用。 对App.js 文件的更改将获得下面截图结果:
使用public文件夹中的静态内容
将静态内容放在src文件夹中有多个优点,但是您可能会发现这并不总是适合于每个项目,尤其是在构建时静态内容不可用且无法由React开发工具处理的情况下。在这些情况下,您可以将静态内容放入public文件夹中,尽管这意味着您必须要确保应用程序具有它所需的文件。为了演示Public文件夹的用法,我在其中添加了一个名为static.css的文件,内容如下:
img {
border: 8px solid black;
}
打开一个新的命令提示符,进入projecttools文件夹,然后运行下面所示的命令,将src文件夹中logo.svg 复制到 public 文件夹中。
npm cp src/logo.svg public/
如果以上命令提示错误,可以直接手动将logo.svg文件拷贝到public文件夹中。
在 下面的代码中,我在App组件中加入了HTML元素,用于渲染public文件夹中的图片和样式表。
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () => (
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
<img src={reactLogo} alt="reactLogo" />
<link rel="stylesheet" href={process.env.PUBLIC_URL + "/static.css"} />
<img src={process.env.PUBLIC_URL + "/logo.svg"} alt="reactLogo" />
</div>
);
}
若要指定静态文件的URL,则使用process.env.PUBLIC_URL属性加上表达式中的文件名组合。注意,我给样式表添加了一个link元素,因为我不能使用 bundle.js 文件来自动创建样式。将元素添加到组件后的结果:
了解显示错误
自动重新加载功能的即时性造成的问题是在开发过程中,您会经常观察控制台输出结果,因此您的注意力自然会被吸引到浏览器窗口中去。风险在于,当代码出错时,浏览器显示的内容仍然是静态的,因为编译过程无法生成一个通过HMR功能发送给浏览器的新模块。为了解决这个问题,webpack生成的捆绑包包含一个集成的错误显示会在浏览器窗口中显示问题的详细信息。为了演示处理错误的方式,我将下面的代码添加到App.js文件中。
import React, { Component } from "react";
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
not a valid statement
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () =>
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{ this.message() }
</h4>
<img src={ reactLogo } alt="reactLogo" />
<link rel="stylesheet"
href={ process.env.PUBLIC_URL + "/static.css"} />
<img src={ process.env.PUBLIC_URL + "/logo.svg" } alt="reactLogo" />
</div>
}
我添加了无效代码。保存文件后,我们代码就会被重新编译,并在命令提示符窗口中生成以下错误消息:
在浏览器窗口中也会显示相同的错误消息会,因此即使您没有注意到命令行消息,您也会意识到问题的存在。如果您单击堆栈跟踪,则浏览器将向开发服务器发送一个HTTP请求,开发服务器将尝试在您正在使用的代码编辑器中找出并突出显示存在问题的代码,如下图所示
■ 技巧 您可能需要配置react开发工具来指定编辑器,如“ 配置开发工具”部分所述,并且并非所有编辑器都支持。图下显示了Visual Studio Code是受支持的编辑器之一。
了解 Linter
React开发工具中有一个linter工具,这个工具负责检查项目中的代码和内容是否符合规则集。当您使用create-react-app包创建项目时,ESLint包将作为代码检查器使用,其中包含一组旨在帮助程序员避免常见错误的规则。作为演示,我在App.s文件中添加了一个变量,如清单9-15所示。(此更改还具有删除上一节中导致编译器错误的语句的效果)。
import React, { Component } from "react";
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
let error = "not a valid statement";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () => (
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
<img src={reactLogo} alt="reactLogo" />
<link rel="stylesheet" href={process.env.PUBLIC_URL + "/static.css"} />
<img src={process.env.PUBLIC_URL + "/logo.svg"} alt="reactLogo" />
</div>
);
}
保存文件后,命令行和浏览器控制台中将显示以下警告:
...
Compiled with warnings.
./src/App.js
Line 8: 'error' is assigned a value but never used no-unused-vars
...
无法禁用或重新配置代码检查器,这意味着您将收到一组固定规则的代码检查器警告,包括nounused-vars规则该规则在清单9-15中出现。您可以在https://github.com/facebook/create-reactapp/tree/master/packages/eslint-config-react-app中查看应用于React项目的规则集。当您收到警告时,搜索规则名称将为您提供问题的描述。在本例中,搜索no-unused-vars将连接https://eslint.org/docs/rules/no-unuse -vars,这说明了变量不能定义也不能使用。
给单个语句和文件代码检查器
尽管无法禁用代码检查器,但可以向文件添加注释以防止出现警告。在清单9-16中,我通过添加注释来禁用单个语句的no-unused-var规则:
import React, { Component } from "react";
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
// eslint-disable-next-line no-unused-vars
let error = "not a valid statement";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () => (
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
<img src={reactLogo} alt="reactLogo" />
<link rel="stylesheet" href={process.env.PUBLIC_URL + "/static.css"} />
<img src={process.env.PUBLIC_URL + "/logo.svg"} alt="reactLogo" />
</div>
);
}
如果您想为后面的语句禁用Linte规则,那么可以省略规则名:
...
// eslint-disable-next-line
let error = "not a valid statement";
...
如果您想给整个文件禁用代码检查器规则,那么可以在文件顶部添加一个注释:
/* eslint-disable no-unused-vars */
import React, { Component } from "react";
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
let error = "not a valid statement";
export default class extends Component {
message = () => `Hello ${name} from ${city}`;
render = () => (
<div className="text-center">
<h4 className="bg-primary text-white text-center p-3">
{this.message()}
</h4>
<img src={reactLogo} alt="reactLogo" />
<link rel="stylesheet" href={process.env.PUBLIC_URL + "/static.css"} />
<img src={process.env.PUBLIC_URL + "/logo.svg"} alt="reactLogo" />
</div>
);
}
如果您想对单个文件的所有规则禁用代码检查器,那么可以从注释中省略规则名:
...
/* eslint-disable */
import React, { Component } from 'react';
import "./App.css";
import reactLogo from "./logo.svg";
let name = "Adam";
const city = "London";
...
代码检查器将忽略 App.js但仍将检查项目中其他文件的内容。
使用TypeScript或Flow
代码检查器不是检测常见错误的唯一方法,一种很好的补充技术是static typechecking(静态类型检查),在这种方法中,您可以将变量和函数结果的数据类型的详细信息添加到代码中,以创建由编译器强制执行的策略。例如,可以指定函数始终返回字符串或其第一个参数只能是数字。编译应用程序时,将检查使用该函数的代码,以确保它只将数字值作为参数传递,并将结果仅作为字符串处理。向react项目添加静态类型检查有两种常见方法。第一种方法是使用typescript,它是微软公司创建的javascript的超集。typescript使使用javascript更像C或java,并支持静态类型检查。如果要使用typescript,则在创建项目时使用—scripts version参数,如下所示:
...
npx create-react-app projecttools --scripts-version=react-scripts-ts
...
react scripts ts值生成一个使用typescript工具和特性设置的项目。你可以在https://www.typescriptlang.org进一步了解和学习TypeScript。另一种选择是一个名为Flow的包,它只关注类型检查,没有typescript更广泛的特性。您
可以在https://flow.org 进一步学习。
配置开发工具
React开发工具提供了少量的配置选项,尽管在大多数项目中并不需要这些选项,
名称 | 说明 |
---|---|
BROWSER | 此选项用于指定开发工具完成初始生成过程时打开的浏览器。可以通过指 定浏览器的路径来指定浏览器,也可以使用“none”禁用此功能。 |
HOST | 此选项用于指定开发HTTP服务器绑定到的主机名,默认为localhost。 |
PORT | 此选项用于指定开发HTTP服务器使用的端口,默认值为3000。 |
HTTPS | 当设置为true时,此选项将为开发HTTP服务器启用SSL,后者将生成自签名证书。默认值为false。 |
PUBLIC_URL | 此选项用于更改用于从public文件夹请求内容的URL,如了解静态内容部分所述。 |
CI | 当设置为true时,此选项将在生成过程中将所有警告视为错误。默认值为false。 |
REACT_EDITOR | 此选项用于指定在浏览器中单击堆栈跟踪时打开代码文件的功能的编辑 器,如了解错误显示部分中所述。 |
CHOKIDAR_USEPOLLING | 当开发工具无法检测到src文件夹的更改时,此选项应设置为true,如果您在虚拟机或容器中工作,则可能会发生这种情况 |
GENERATE_SOURCEMAP | 将此选项设置为false将禁用源映射的生成,在调试期间,浏览器使用源 映射将捆绑的JavaScript代码与项目中的源文件关联起来。默认值为true。 |
NODE_PATH | 此设置用于搜索本地指定位置的Node.js模块的。 |
这些选项可以通过设置环境变量或创建.env文件来指定,这是我认为最可靠的方法。为了演示配置过程,我在projecttools文件夹中添加了一个名为.env的文件,并添加了如下所示的配置语句。PORT=3500<br />HTTPS=true
我使用PORT选项指定端口3500以接收请求,使用HTTPS选项在开发服务器中启用SSL。要查看更改的效果,请停止运行开发工具并运行清下面的命令再次启动开发工具:
npm start
初始构建过程完成后,打开的浏览器窗口将导航到https://localhost:3500。大多数浏览器都会显示签名证书安全警告,然后单击“高级”链接(或其等效链接)然后点击“继续访问”后浏览器就会打开web应用程序,如下图所示。
调试React应用程序
并不是所有的问题都能被编译器或代码检查器检测到,编译好的代码可能会以意想不到的方式运行。有 两种方法可以理解应用程序的行为,如下节所述。为了帮助演示调试功能,我添加了一个名为Display.js 并添加下面的代码创建Display组件:
import React, { Component } from "react";
export class Display extends Component {
constructor(props) {
super(props);
this.state = {
counter: 1
}
}
incrementCounter = () => {
this.setState({ counter: this.state.counter + 1 });
}
render() {
return (
<div>
<h2 className="bg-primary text-white text-center p-2">
<div>Props Value: {this.props.value}</div>
<div>Local Value: {this.state.counter} </div>
</h2>
<div className="text-center">
<button className="btn btn-primary m-2"
onClick={this.props.callback}>
Parent
</button>
<button className="btn btn-primary m-2"
onClick={this.incrementCounter}>
Local
</button>
</div>
</div>
)
}
}
组件显示自己的state(状态)属性以及其父组件接收的props值。组件显示两个button元素,其中一 个按钮用于更改state属性,另一个按钮用于调用props传递的回调函数。我将App组件内容替换成下面的代码,以便为调试部分做准备:
import React, { Component } from "react";
import { Display } from "./Display";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
city: "London"
}
}
changeCity = () => {
this.setState({ city: this.state.city === "London" ? "New York" : "London" })
}
render() {
return (
<Display value={this.state.city} callback={this.changeCity} />
);
}
}
当您保存更改后的文件时,应用程序将会被重新编译,您将看到下面截图所示的内容:
■ 注意 您可能会发现,当.env文件中的H TTPS选项设置为t rue时,浏览器不会自动更新。您
可以手动重新加载浏览器以查看更改后的内容,也可以禁用此选项并重新启动开发工具。
探索应用程序状态
React Devtools浏览器插件是调试React应用程序状态的最佳工具。google chrome和Mozilla Firefox都有可
用的版本,有关该插件的详细信息包括对其他平台的支持以及独立版本的详细信息,请访问https://
github.com/facebook/react-devtools。安装插件后,您将在浏览器的“开发者调试器”窗口中看到一个新增
的选项卡,可通过按F12按钮访问该选项卡(这就是为什么这些工具也称为F12工具)。
F12工具窗口中的React选项卡允许您查看和更改应用程序的结构和状态。您可以看到提供应用程序
功能的组件集,以及组件的状态数据和属性。
对于示例应用程序,如果您打开React选项卡并在左边的窗口中展开应用程序的结构,您就会看到
窗中的应用程序和显示的组件,以及应用程序呈渲染的HTML元素的视图。当您在左侧页面中选择一
个组件时,其属性和状态数据将显示在右侧窗口中,如下图所示:
如果单击浏览器窗口中的按钮,您将看到React Devtools显示的值发生了更改,这反映了应用程序的
活动状态。您还可以单击状态数据值并通过React Devtools更改其值,该工具允许直接操作应用程序的状
态。
■ 技巧 还有Redux数据存储包的调试器,我将在第19章讲解,并且Redux通常用于管理复杂 项目的数据。
使用浏览器Debugger
现代浏览器都带有复杂的调试器,可用于控制应用程序的执行并检查其状态。React开发工具包括对创建
源映射的支持,它允许浏览器将正在执行的压缩的和绑定的代码与高效调试所需的对开发人员更友好的源
代码相关联。
有些浏览器允许您使用这些源映射浏览应用程序的源代码,并创建断点,当到达断点时,断点将停止
应用程序的执行,并将控制权传递给调试器。在我写这篇文章的时候,创建断点的可靠性不高,在
Chrome上不起作用,在其他浏览器中可靠性参差不齐。因此,使用调试器调试应用程序的最可靠方法是
使用JavaScript的 debugger关键字,如下面的代码所示, 在 src 文件夹中的App.js 文件中触发Debugger
:
import React, { Component } from "react";
import { Display } from "./Display";
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
city: "London"
}
}
changeCity = () => {
debugger
this.setState({ city: this.state.city === "London" ? "New York" : "London" })
}
render() {
return (
<Display value={this.state.city} callback={this.changeCity} />
);
}
}
要有效地使用Debugger, 禁用projecttools文件夹中的.env文件中的安全连接
。如果不禁用此选项,则 只能看到由Babel生成的代码,而不是程序源代码:
HTTPS=false
在命令提示符中进入projecttools文件夹中运行下面的命令,重新启动开发工具:npm start
应用程序将正常执行,但当单击父按钮并调用changeCity方法后,浏览器将遇到debugger关键字时
就会停止执行应用程序。然后,您可以使用F12工具窗口中的控件检查变量及其在停止执行时的值,并
手动控制执行,如图9-12所示。浏览器正在执行由开发工具创建的压缩和绑定的代码,但显示的是源映
射中的相应代码。
总结
在本章中,我描述了如何使用create-react-app命令创建React项目,并解释了React开发中使用的文件和
文件夹的用途。我还解释了如何使用React开发工具,如何将应用程序绑定在浏览器中进行渲染以及错误显示
和代码检测器如何帮助我们避免常见问题,还讲解了在没有获得预期结果时如何调试应用程序。在下一章
中,我将介绍组件,组件是React应用程序的关键构建块。
若有疑问请加微信:jinnianhuixiayuma,标注:React高级教程