Skip to content

Latest commit

 

History

History
858 lines (636 loc) · 34 KB

File metadata and controls

858 lines (636 loc) · 34 KB

一、在 React 中创建电影列表应用

当你买这本书时,你可能听说过 React,甚至可能尝试过一些可以在网上找到的代码示例。这本书的结构使得每一章中的代码示例的复杂性逐渐增加,因此,即使您觉得 React 的经验有限,如果您已经阅读了前一章,那么每一章都应该是可以理解的。当您读完本书后,您将知道如何使用 React 及其稳定的功能,直到版本 16.11,并且您还将拥有使用React NativeReact 360的经验。

第一章首先学习如何构建一个简单的电影列表应用,并为您提供我们将从外部来源获取的流行电影的概述。React 入门的核心概念将应用于本项目,如果您在使用 React 构建应用方面有一定的经验,这应该是可以理解的。如果你以前没有使用过 React,那也没问题;本书描述了代码示例中使用的 React 特性。

在本章中,我们将介绍以下主题:

  • 使用 webpack 和 React 设置新项目
  • 构建 React 项目

让我们潜水吧!

项目概述

在本章中,我们将在 React 中创建一个电影列表应用,该应用从本地 JSON 文件检索数据,并在带有 webpack 和 Babel 的浏览器中运行。样式将使用引导完成。您将构建的应用将返回 2019 年票房最高的电影列表,以及更多细节和每部电影的海报。

构建时间为 1 小时。

开始

本章的应用将从头开始构建,并使用可在的 GitHub 上找到的资产 https://github.com/PacktPublishing/React-Projects/tree/ch1-assets 应将这些资产下载到您的计算机上,以便您在本章后面使用它们。本章的完整代码也可以在 GitHub 上找到:https://github.com/PacktPublishing/React-Projects/tree/ch1

对于本书中创建的应用,您的计算机上至少需要安装 Node.js v10.16.3,以便运行npm命令。如果您尚未在计算机上安装 Node.js,请转到https://nodejs.org/en/download/ ,您可以在这里找到 macOS、Windows 和 Linux 的下载说明。

安装 Node.js 后,在命令行中运行以下命令以检查已安装的版本:

  • 对于 Node.js(应为 v10.16.3 或更高版本):
node -v
  • 对于npm(应为 v6.9.0 或更高版本):
npm -v

此外,您应该已经安装了React Developer Tools插件(用于 Chrome 和 Firefox),并将其添加到浏览器中。此插件可以从Chrome 网络商店中安装 https://chrome.google.com/webstore 或 Firefox 插件(https://addons.mozilla.org

创建电影列表应用

在本节中,我们将从头开始创建一个新的 React 应用,首先使用 webpack 和 Babel 创建一个新项目。从头开始设置 React 项目将帮助您了解项目的基本需求,这对于您创建的任何项目都至关重要。

建立一个项目

每次创建新的 React 项目时,第一步是在本地计算机上创建一个新目录。由于您将在本章中构建电影列表应用,请将此目录命名为movieList

在此新目录中,从命令行执行以下操作:

npm init -y

运行此命令将创建一个package.json文件,其中包含npm需要的关于此项目的最低限度的信息。通过在命令中添加-y标志,我们可以自动跳过设置nameversiondescription等信息的步骤。运行此命令后,将创建以下package.json文件:

{
    "name": "movieList",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC"
}

如您所见,npm软件包没有依赖关系,因为我们还没有安装任何软件包。我们将要安装和配置的第一个软件包是 webpack,我们将在本节的下一部分中进行。

设置网页包

要运行 React 应用,我们需要安装 webpack 4(在编写本书时,webpack 的当前稳定版本是 version 4)和 webpack CLI 作为devdependences。让我们开始:

  1. 使用以下命令从npm安装这些软件包:
npm install --save-dev webpack webpack-cli
  1. 下一步是将这些包包含在package.json文件中,并在我们的启动和构建脚本中运行它们。为此,将startbuild脚本添加到我们的package.json文件中:
{
    "name": "movieList",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
_       "start": "webpack --mode development",
+       "build": "webpack --mode production",
        "test": "echo \"Error: no test specified\" && exit 1"
    },
    "keywords": [],
    "author": "",
    "license": "ISC"
}

"+" symbol is used for the line which is added and "-" symbol is used for the line which is removed in the code.

前面的配置将使用 webpack 向我们的应用添加startbuild脚本。如您所见,npm start将在开发模式下运行 webpack,npm build将在生产模式下运行 webpack。最大的区别是,在生产模式下运行 webpack 将最小化我们的代码,从而减少项目包的大小。

  1. 在我们的项目中创建一个名为src的新目录,并在此目录中创建一个名为index.js的新文件。稍后,我们将配置 webpack,使此文件成为应用的起点。将以下代码行放置在此新创建的文件中:
console.log("movieList")

如果我们现在在命令行中运行npm startnpm build命令,webpack 将启动并创建一个名为dist的新目录。在这个目录中,将有一个名为main.js的文件,其中包含我们的项目代码。根据我们是在开发模式还是生产模式下运行 webpack,此文件中的代码将最小化。通过运行以下命令,可以检查代码是否正常工作:

node dist/main.js

此命令运行捆绑版本的应用,并应在命令行中返回movieList字符串作为输出。现在,我们可以从命令行运行 JavaScript 代码了。在本节的下一部分中,我们将学习如何配置 webpack,使其与 React 一起工作。

配置网页包以使用 React

现在我们已经用 webpack 为 JavaScript 应用设置了一个基本的开发环境,我们可以开始安装运行任何 React 应用所需的包。它们是reactreact-dom,前者是 React 的通用核心包,后者提供浏览器 DOM 和 React 的入口点。让我们开始:

  1. 通过在命令行中执行以下命令来安装这些软件包:
npm install react react-dom

仅安装 React 的依赖项不足以运行它,因为在默认情况下,并非每个浏览器都可以读取 JavaScript 代码所用的格式(如 ES2015+或 React)。因此,我们需要为每个浏览器将 JavaScript 代码编译成可读的格式。

  1. 为此,我们将使用 Babel 及其相关软件包,可以通过运行以下命令将其安装为devDependencies
npm install --save-dev @babel/core @babel/preset-env @babel/preset-react babel-loader

在 Babel 核心旁边,我们还将安装babel-loader,这是一个帮助程序,以便 Babel 可以使用 webpack 和两个预设软件包运行。这些预置包有助于确定哪些插件将用于将我们的 JavaScript 代码编译成浏览器可读的格式(@babel/preset-env),以及编译特定于 React 的代码(@babel/preset-react)。

在安装了 React 的软件包和正确的编译器之后,下一步是让它们与 webpack 一起工作,以便在运行应用时使用它们。

  1. 为此,在项目的根目录中创建一个名为webpack.config.js的文件。在此文件中,添加以下代码:
module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader:'"babel-loader',
                },
            },
        ],
    },
}

此文件中的配置告诉 webpack 对每个具有.js扩展名的文件使用babel-loader,并排除 Babel 编译器的node_modules目录中的.js文件。babel-loader的实际设置放在一个单独的文件中,称为.babelrc

  1. 我们还可以在项目的根目录中创建.babelrc文件,并在其中放置以下代码,这将babel-loader配置为在编译代码时使用@babel/preset-env@babel/preset-react预设:
{
    "presets": [
        [
            "@babel/preset-env", 
            {
                "targets": {
                    "node": "current"
                }
            }
        ],
        "@babel/react"
    ]
}

We can also declare the configuration for babel-loader directly inside the webpack.config.js file, but for better readability, we should place it in a separate .babelrc file. Also, the configuration for Babel can now be used by other tools that are unrelated to webpack.

@babel/preset-env预设中定义了选项,确保编译器使用最新版本的 Node.js,因此async/await等功能的 polyfills 仍然可用。现在我们已经设置了 webpack 和 Babel,我们可以运行 JavaScript 并从命令行做出反应。在本节的下一部分中,我们将创建第一个 React 代码并使其在浏览器中运行。

呈现 React 项目

现在我们已经设置了 React,以便它可以与 Babel 和 webpack 一起使用,我们需要创建一个可以编译和运行的实际 React 组件。创建一个新的 React 项目需要向项目中添加一些新文件,并对 webpack 的设置进行更改。让我们开始:

  1. 让我们编辑已经存在于我们的src目录中的index.js文件,以便我们可以使用reactreact-dom
import React from 'react';
import ReactDOM from 'react-dom';

const App = () => {
    return <h1>movieList</h1>;
};

ReactDOM.render(<App />, document.getElementById('root'));

如您所见,该文件导入了reactreact-dom包,定义了一个简单组件,该组件返回一个包含应用名称的h1元素,并使用react-dom呈现该组件。最后一行代码将App组件装入文档中具有rootID 的元素,该元素是应用的入口点。

  1. 我们可以通过在src目录中添加一个名为index.html的新文件来创建此文件,其中包含以下代码:
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta http-equiv="X-UA-Compatible" content="ie=edge">
    <title>movieList</title>
</head>
<body>
    <section id="root"></section>
</body>
</html>

这将添加一个 HTML 标题和正文。在head标记中是我们的应用的标题,body标记中是一个具有id属性root的部分。这与我们在src/index.js文件中装入App组件的元素相匹配。

  1. 呈现 React 组件的最后一步是扩展 webpack,以便在运行时将缩小的捆绑代码添加到body标记中,如scripts。因此,我们应该安装html-webpack-plugin软件包作为一种依赖:
npm install --save-dev html-webpack-plugin

将此新包添加到webpack.config.js文件中的网页包配置中:

const HtmlWebPackPlugin = require('html-webpack-plugin');

const htmlPlugin = new HtmlWebPackPlugin({
 template: './src/index.html',
 filename: './index.html',
});

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: 'babel-loader',
                },
            },
        ],
    },
    plugins: [htmlPlugin],
};

html-webpack-plugin的配置中,我们将应用的入口点设置为index.html。文件这样,webpack 就知道在哪里将包添加到body标记。

我们还可以通过替换导出配置中的htmlPlugin常量,将插件的配置直接添加到 webpack 的导出配置中。随着我们的应用越来越大,这可能会降低网页包配置的可读性,具体取决于我们的首选项。

现在,如果我们再次运行npm start,webpack 将以开发模式启动,并将index.html文件添加到dist目录中。在这个文件中,我们将看到,在您的body标记中,插入了一个新的scripts标记,将我们指向我们的应用包,即dist/main.js文件。如果我们在浏览器中打开此文件或从命令行运行open dist/index.html,它将直接在浏览器中返回movieList结果。在生产模式下运行npm build命令启动 Webpack 时也可以这样做;唯一的区别是我们的代码将被缩小。

通过使用 webpack 设置开发服务器,可以加快此过程。我们将在本节的最后一部分中完成此操作。

创建开发服务器

在开发模式下工作时,每次更改应用中的文件时,都需要重新运行npm start命令。由于这有点乏味,我们将安装另一个名为webpack-dev-server的软件包。该软件包添加了一个选项,即每当我们更改项目文件并管理内存中的应用文件时,都会强制 webpack 重新启动,而不是通过构建dist目录。webpack-dev-server套装还可以安装npm

npm install --save-dev webpack-dev-server

另外,我们需要编辑package.json文件中的start脚本,以便在运行start脚本时直接使用webpack-dev-server而不是 webpack:

{
    "name": "movieList",
    "version": "1.0.0",
    "description": "",
    "main": "index.js",
    "scripts": {
-       "start": "webpack --mode development",
+       "start": "webpack-dev-server --mode development --open",        
        "build": "webpack --mode production"
    },
    "keywords": [],
    "author": "",
    "license": "ISC"

    ...
}

前面的配置将启动脚本中的 webpack 替换为webpack-dev-server,在开发模式下运行 webpack。这将创建一个本地服务器,运行带有--open标志的应用,确保每次更新任何项目文件时重新启动 webpack。

To enable hot reloading, replace the --open flag with the --hot flag. This will only reload files that have been changed instead of the entire project. 

现在,我们已经为 React 应用创建了基本的开发环境,您将在本章的下一节中进一步开发和构建它。

构建项目

随着开发环境的建立,是时候开始创建电影列表应用了。首先让我们看看项目的当前结构,其中项目根目录中的两个目录很重要:

  • 第一个目录名为dist,在这里可以找到 webpack 捆绑版应用的输出

  • 第二个名为src,包含我们应用的源代码:

movieList
|-- dist
    |-- index.html
    |-- main.js
|-- node_modules
|-- src
    |-- index.js
    |-- index.html
.babelrc
package.json
webpack.config.js

Another directory that can be found in the root directory of our project is called node_modules. This is where the source files for every package that we install using npm are placed. It is recommended you don't make any manual changes to files inside this directory.

在下面的小节中,我们将学习如何构建 React 项目。这一结构也将在本书其余章节中使用。

创建新组件

React 的官方文档没有说明关于如何构建 React 项目的任何首选方法。尽管有两种常见的方法在社区中很流行:按功能或路径构造文件,或按文件类型构造文件。

电影列表应用将使用一种混合方法,其中文件首先按文件类型进行结构化,然后按功能进行结构化。实际上,这意味着将有两种类型的组件:称为容器的顶级组件和与这些顶级组件相关的低级组件。创建这些组件需要添加以下文件和代码更改:

  1. 实现此结构的第一步是创建一个名为containerssrc新子目录。在这个目录中,创建一个名为List.js的文件。这将是包含电影的列表的容器,包含以下内容:
import React, { Component } from 'react';

class List extends Component {
    render() {
        return <h1>movieList</h1>;
    }
};

export default List;
  1. 这个容器应该包含在我们的应用的入口点中,以便它可见。因此,我们需要将其包含在index.js文件的src目录中,并引用它:
import React from 'react';
import ReactDOM from 'react-dom';
+ import List from './containers/List';

const App = () => {
-   return <h1>movieList</h1>;
+   return <List />;
};

ReactDOM.render(<App />, document.getElementById('root'));
  1. 如果开发服务器仍在运行(如果没有,请再次执行npm start命令),我们将看到应用仍然返回相同的结果。我们的应用应具有以下文件结构:
movieList
|-- dist
    |-- index.html
    |-- main.js
|-- src
 |-- containers
 |-- List.js
    |-- index.js
    |-- index.html
.babelrc
package.json
webpack.config.js
  1. 下一步是将组件添加到List容器中,稍后我们将使用该容器来显示有关电影的信息。该组件将被称为Card,并应位于名为components的新src子目录中,该子目录将被放置在与该组件同名的目录中。我们需要在src目录中创建一个名为components的新目录,在这里我们将创建另一个名为Card的新目录。在此目录中,创建一个名为Card.js的文件,并将以下代码块添加到空的Card组件中:
import React from 'react';

const Card = () => {
    return <h2>movie #1</h2>;
};

export default Card;
  1. 现在,将此Card组件导入List的容器中,并将return功能替换为以下代码,返回此组件而不是h1元素:
import React, { Component } from 'react';
+ import Card from '../components/Card/Card';

class List extends Component {
    render() {
-       return <h1>movieList</h1>;
+       return <Card />;
    }
};

export default List;

现在我们已经添加了这些目录和Card.js文件,我们的应用文件的结构如下所示:

movieList
|-- dist
    |-- index.html
    |-- main.js
|-- src
 |-- components
 |-- Card
 |-- Card.js
    |-- containers
        |-- List.js
    |-- index.js
    |-- index.html
.babelrc
package.json
webpack.config.js

如果我们再次在浏览器中访问我们的应用,将不会有可见的更改,因为我们的应用仍然返回相同的结果。但如果我们在浏览器中打开 React Developer Tools 插件,我们会注意到应用当前由多个堆叠组件组成:

<App>
    <List>
        <Card>
            <h1>movieList</h1>
        </Card>
    </List>
</App>

在本节的下一部分中,您将使用构建 React 项目的知识并创建新组件来获取有关我们希望在此应用中显示的电影的数据

检索数据

随着开发服务器和项目结构的建立,是时候向其中添加一些数据了。如果您还没有从入门部分下载 GitHub 存储库中的资产,那么现在就应该下载了。这些资产是此应用所需的,并包含一个 JSON 文件,其中包含五部票房最高的电影及其相关图像文件的数据。

data.json文件由一个数组组成,数组中的对象包含有关电影的信息。此对象具有titledistributoryearamountimgranking字段,其中img字段是具有srcalt字段的对象。src字段是指也包含的图像文件。

我们需要将下载的文件添加到另一个子目录中的本项目根目录中,data.json文件应放置在名为assets的子目录中,图像文件应放置在名为media的子目录中。添加这些新目录和文件后,我们的应用结构如下所示:

movieList
|-- dist
    |-- index.html
    |-- main.js
|-- src
 |-- assets
 |-- data.json
    |-- components
        |-- Card
            |-- Card.js
    |-- containers
        |-- List.js
 |-- media
 |-- avatar.jpg
 |-- avengers_infinity_war.jpg
 |-- jurassic_world.jpg
 |-- star_wars_the_force_awakens.jpg
 |-- titanic.jpg
    |-- index.js
    |-- index.html
.babelrc
package.json
webpack.config.js

此数据将仅在顶级组件中检索,这意味着我们应该在List容器中添加一个fetch函数,更新此容器的状态,并将其作为道具传递给低级组件。state对象可以存储变量;每次这些变量发生变化时,我们的组件都会重新启动。让我们开始:

  1. 在检索电影数据之前,Card组件需要准备好接收此信息。要显示有关电影的信息,我们需要用以下代码替换Card组件的内容:
import React from 'react';

const Card = ({ movie }) => {
     return (
        <div>
            <h2>{`#${movie.ranking} - ${movie.title} (${movie.year})`}</h2>
            <img src={movie.img.src} alt={movie.img.alt} width='200' />
            <p>{`Distributor: ${movie.distributor}`}</p>
            <p>{`Amount: ${movie.amount}`}</p>
        </div>
    );
};

export default Card;
  1. 现在,可以通过向List组件添加constructor函数来实现检索数据的逻辑,该函数将包含一个空数组作为电影的占位符,以及一个指示数据是否仍在加载的变量:
...

class List extends Component {+
+   constructor() {
+       super()
+       this.state = {
+           data: [],
+           loading: true,
+       };
+   }

    return (
      ...
  1. 在设置了constructor函数之后,我们应该立即设置一个componentDidMount函数,在List组件安装完成后,我们将在这里获取数据。在这里,我们应该使用一个async/await函数,因为fetchAPI 返回一个承诺。获取数据后,更新state,用电影信息替换数据的空数组,loading变量设置为false
...

class List extends Component {

    ...

 +    async componentDidMount() {
 +        const movies = await fetch('../../img/data.json');
 +        const moviesJSON = await movies.json();

 +        if (moviesJSON) {
 +            this.setState({
 +                data: moviesJSON,
 +                loading: false,
 +            });
 +        }
 +    }

    return (
      ...

The previous method that we use to retrieve information from JSON files using fetch doesn't take into account that the request to this file may fail. If the request fails, the loading state will remain true, meaning that the user will keep seeing the loading indicator. If you want to display an error message when the request doesn't succeed, you'll need to wrap the fetch method inside a try...catch block, which will be shown later on in this book.

  1. 将此状态传递给Card组件,最终可以在我们在第一步中更改的Card组件中显示。该组件还将获得一个key道具,这是迭代中渲染的每个组件所必需的。由于该值需要唯一,所以使用电影的id,如下所示:
class List extends Component {

    ...

    render() {
 _     return <Card />
 +     const { data, loading } = this.state;

+      if (loading) {
+         return <div>Loading...</div>
+      }

+      return data.map(movie => <Card key={ movie.id } movie={ movie } />);
    }
}

export default List;

如果我们再次在浏览器中访问我们的应用,我们将看到它现在显示电影列表,包括一些基本信息和图像。此时,我们的应用将类似于以下屏幕截图:

如您所见,应用应用了有限的样式,它只呈现从 JSON 文件获取的信息。在本节的下一部分中,将使用名为Bootstrap的包添加样式。

添加样式

仅仅显示电影信息是不够的。我们还需要对项目应用一些基本样式。向项目添加样式是通过引导包完成的,引导包根据类名向组件添加样式。Bootstrap 可以从npm安装,需要使用以下更改:

  1. 要使用 Bootstrap,我们需要从npm开始安装它,并将其放置在此项目中:
npm install --save-dev bootstrap
  1. 另外,将此文件导入 React 应用的入口点,称为index.js,以便我们可以在整个应用中使用样式:
import React, { Component } from 'react';
import ReactDOM from 'react-dom';
import List from './containers/List';
+ import 'bootstrap/dist/css/bootstrap.min.css';

const App = () => {
    return <List />;
}

ReactDOM.render(<App />, document.getElementById('root'));

如果我们再次尝试运行开发服务器,我们将收到一个错误,显示为"You may need an appropriate loader to handle this file type."。因为 Webpack 无法编译 CSS 文件,我们需要添加适当的加载程序来实现这一点。我们可以通过运行以下命令来安装这些组件:

npm install --save-dev css-loader style-loader
  1. 我们需要将这些包作为规则添加到网页包配置中:
const HtmlWebPackPlugin = require('html-webpack-plugin');

const htmlPlugin = new HtmlWebPackPlugin({
    template: './src/index.html',
    filename: './index.html',
});

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
                use: {
                    loader: "babel-loader"
                }
            },
+           {
+               test: /\.css$/,
+               use: ['style-loader', 'css-loader']
+           }
        ]
    },
    plugins: [htmlPlugin]
};

The order in which loaders are added is important since css-loader handles the compilation of the CSS file and style-loader adds the compiled CSS files to the React DOM. Webpack reads these settings from right to left and the CSS needs to be compiled before it's attached to the DOM.

  1. 应用现在应该在浏览器中正确运行,并且应该从默认引导样式表中获得一些小的样式更改。让我们首先对index.js文件进行一些更改,并将其样式设置为整个应用的容器。我们需要更改呈现给 DOM 的App组件,并用div容器包装List组件:
...

const App = () => {
    return (
+        <div className='container-fluid'>
            <List />
 </div>
    );
};

ReactDOM.render(<App />, document.getElementById('root'));
  1. List组件内部,我们需要设置网格来显示Card组件,显示电影信息。用以下代码包装map功能和Card组件:
...

class List extends Component {

    ...

    render() {
        const { data, loading } = this.state;

        if (loading) {
            return <div>Loading...</div>;
        }

         return (
 +         <div class='row'>
                {data.map(movie =>
 +                 <div class='col-sm-2'>
                        <Card key={ movie.id } movie={ movie } />
 +                 </div>
                )}
 +          </div>
        );
    }
}

export default List;
  1. Card部件的代码如下所示。这将使用引导为Card组件添加样式:
import React from 'react';

const Card = ({ movie }) => {
    return (
        <div className='card'>
            <img src={movie.img.src} className='card-img-top' alt={movie.img.alt} />
            <div className='card-body'>
                <h2 className='card-title'>{`#${movie.ranking} - ${movie.title} (${movie.year})` }</h2>
            </div>
            <ul className='list-group list-group-flush'>
                <li className='list-group-item'>{`Distributor: ${movie.distributor}`}</li>
                <li className='list-group-item'>{`Amount: ${movie.amount}`}</li>
            </ul>
        </div>
    );
};

export default Card;
  1. 要添加收尾工作,请打开index.js文件并插入以下代码以添加标题,该标题将放置在应用中电影列表的上方:
...

const App = () => {
    return (
        <div className='container-fluid'>
_            <h1>movieList</h1>
+            <nav className='navbar sticky-top navbar-light bg-dark'>
+               <h1 className='navbar-brand text-light'>movieList</h1>
+           </nav>

            <List />
        </div>
    );
};

ReactDOM.render(<App />, document.getElementById('root'));

如果我们再次访问浏览器,我们将看到应用已通过引导应用了样式,这将使其看起来如下所示:

Bootstrap 中的样式规则已经应用到我们的应用中,使它看起来比以前更加完整。在本节的最后一部分中,我们将向项目中添加 ESLint 包,这将通过在整个项目中同步模式来简化代码维护。

添加 ESLint

最后,我们将向项目中添加 ESLint,以确保我们的代码符合某些标准,例如,我们的代码遵循正确的 JavaScript 模式。添加 ESLint 需要进行以下更改:

  1. 通过运行以下命令从npm安装 ESLint:
npm install --save-dev eslint eslint-loader eslint-plugin-react

第一个名为eslint的包是核心包,它帮助我们识别 JavaScript 代码中可能存在问题的模式。eslint-loader是 Webpack 每次更新代码时用来运行 ESLint 的包。最后,eslint-plugin-react为 React 应用向 ESLint 添加了特定规则。

  1. 要配置 ESLint,我们需要在项目的根目录中创建一个名为.eslintrc.js的文件,并向其中添加以下代码:
module.exports = {
    "env": {
        "browser": true,
        "es6": true
    },
    "parserOptions": {
        "ecmaFeatures": {
            "jsx": true
        },
        "ecmaVersion": 2018,
        "sourceType": "module"
    },
    "plugins": [
        "react"
    ],
    "extends": ["eslint:recommended", "plugin:react/recommended"]
};       

env字段设置代码运行的实际环境,并在其中使用es6函数,而parserOptions字段为使用jsx和现代 JavaScript 添加了额外的配置。然而,有趣的是,plugins字段,在这里我们指定代码使用react作为框架。extends字段用于eslintrecommended设置,以及 React 的框架特定设置。

We can run the eslint --init command to create custom settings, but using the preceding settings is recommended, so that we ensure the stability of our React code.

  1. 如果我们查看命令行或浏览器,就不会看到任何错误。但是,我们必须将eslint-loader包添加到 Web 包配置中。在webpack.config.js文件中,在babel-loader旁边添加eslint-loader
...

module.exports = {
    module: {
        rules: [
            {
                test: /\.js$/,
                exclude: /node_modules/,
+               use: ['babel-loader', 'eslint-loader'] 
            },
            {
                test: /\.css$/,
                use: ['style-loader', 'css-loader']
            }
        ]
    },
    plugins: [htmlPlugin]
};

通过重新启动开发服务器,webpack 现在将使用 ESLint 来检查我们的 JavaScript 代码是否符合 ESLint 的配置。在我们的命令行(或浏览器中的控制台选项卡)中,应该可以看到以下错误:

movieList/src/components/Card/Card.js
 3:17  error 'movie' is missing in props validation  react/prop-types

使用 React 时,建议我们验证发送给组件的任何道具,因为 JavaScript 的动态类型系统可能导致变量未定义或类型不正确的情况。我们的代码可以在不验证道具的情况下运行,但要修复此错误,我们必须安装prop-types包,该包曾是 React 的一个功能,但后来被弃用。让我们开始:

  1. 我们用于检查道具类型的软件包可以从npm安装:
npm install --save prop-types
  1. 现在,我们可以通过将包导入Card组件并将验证添加到此文件底部来验证我们组件中的propTypes
import React from 'react';
+ import PropTypes from 'prop-types';

const Card = ({ movie }) => {
    ...
};

+ Card.propTypes = {
+    movie: PropTypes.shape({}),
+ };

export default Card;
  1. 如果我们再次查看命令行,我们将看到丢失的propTypes验证错误已经消失。然而,我们的道具的验证仍然不是很具体。我们还可以通过指定movie属性的所有字段的propTypes来使这一点更加具体:
...

Card.propTypes = {
_   movie: PropTypes.shape({}),
+    movie: PropTypes.shape({
+    title: PropTypes.string,
+    distributor: PropTypes.string,
+     year: PropTypes.number,
+     amount: PropTypes.string,
+     img: PropTypes.shape({
+       src: PropTypes.string,
+       alt: PropTypes.string
+     }),
+     ranking: PropTypes.number
+   }).isRequired  
};  

我们还可以通过在propTypes验证中添加isRequired来指示 React 呈现组件所需的道具。

祝贺您已经使用 React、ReactDom、webpack、Babel 和 ESLint 从头创建了一个基本的 React 应用。

总结

在本章中,您已经为 React from scratch 创建了一个电影列表应用,并了解了 React 的核心概念。本章从您使用 webpack 和 Babel 创建一个新项目开始。这些库可以帮助您在浏览器中以最少的设置编译和运行 JavaScript 和 React 代码。然后,我们描述了如何构造 React 应用。这一结构将贯穿本书。应用的原则为您提供了基础知识,从中可以从无到有地创建 React 应用,并以可扩展的方式对其进行结构构建。

如果您以前使用过 React,那么这些概念可能并不难理解。如果你还没有,那么如果你觉得有些概念很奇怪,也不要担心。接下来的章节将以您在本章中使用的功能为基础,让您有足够的时间充分理解这些功能。

您将在下一章中构建的项目将侧重于创建具有更高级样式的可重用 React 组件。这将脱机使用,因为它将被设置为一个渐进式 Web 应用PWA)。

进一步阅读