react入门基础教程(React入门初学者指南)

React 是一个了不起的 JavaScript 库,它席卷了开发社区。简而言之,它使开发人员可以更轻松地为 Web、移动和桌面平台构建交互式用户界面。今天,全球有数千家公司在使用 React,包括 Netflix 和 Airbnb 等知名公司。

在本指南中,我将向您介绍 React 及其几个基本概念。我们将使用 Create React App 工具快速启动并运行,然后我们将逐步完成构建简单 react 应用程序的过程。完成后,您将对基础知识有一个很好的概述,并准备好在您的 React 旅程中迈出下一步。

先决条件

在开始学习 React 之前,对HTML、CSS和JavaScript有一个基本的了解是有意义的。它还有助于对Node.js以及npm 包管理器有一个基本的了解。

要学习本教程,您需要在机器上安装 Node 和 npm。为此,请前往 Node.js下载页面并获取您需要的版本(npm 与 Node 捆绑在一起)。或者,您可以参考我们关于使用版本管理器安装 Node的教程。

什么是反应?

React 是一个用于构建 UI 组件的 JavaScript 库。与 Angular 或 Vue 等更完整的框架不同,React 只处理视图层,因此您需要额外的库来处理诸如路由、状态管理等事情。在本指南中,我们将重点介绍 React 开箱即用的功能。

React 应用程序是使用可以相互交互的可重用UI 组件构建的。React 组件可以是基于类的组件,也可以是所谓的函数组件。基于类的组件是使用ES6 类定义的,而函数组件是基本的 JavaScript 函数。这些往往是使用箭头函数定义的,但它们也可以使用function关键字。基于类的组件将实现一个render函数,该函数返回一些 JSX(React 对 Regular JavaScript 的扩展,用于创建 React 元素),而函数组件将直接返回 jsx。如果您从未听说过 JSX,请不要担心,因为我们稍后会仔细研究它。

React 组件可以进一步分为有状态无状态组件。无状态组件的工作只是显示它从其父 React 组件接收到的数据。如果它接收到任何事件或输入,它可以简单地将这些传递给它的父级来处理。

另一方面,有状态组件负责维护某种应用程序状态。这可能涉及从外部来源获取数据,或跟踪用户是否登录。有状态组件可以响应事件和输入来更新其状态。

根据经验,您应该尽可能编写无状态组件。这些更容易在您的应用程序和其他项目中重用。

了解虚拟 DOM

在开始编码之前,您需要了解 React 使用虚拟 DOM来处理页面渲染。如果您熟悉 jQuery,您就会知道它可以通过HTML DOM直接操作网页。在很多情况下,这种直接交互几乎不会造成任何问题。但是,对于某些情况,例如运行高度交互的实时 Web 应用程序,性能可能会受到很大影响。

为了解决这个问题,Virtual DOM(真实 DOM 的内存表示)的概念被发明出来,目前被包括 React 在内的许多现代 UI 框架应用。与 HTML DOM 不同,虚拟 DOM 更容易操作,并且能够在毫秒内处理大量操作而不会影响页面性能。React 会定期比较虚拟 DOM 和 HTML DOM。然后它计算一个差异,将其应用于 HTML DOM 以使其与虚拟 DOM 匹配。通过这种方式,React 确保您的应用程序以一致的每秒 60 帧的速度呈现,这意味着用户很少或没有延迟。

开始一个空白的 React 项目

根据先决条件,我假设您已经设置了 Node 环境,并带有最新版本的 npm(或可选的Yarn)。

接下来,我们将使用Create React App构建我们的第一个 React 应用程序,这是一个用于创建单页 React 应用程序的官方实用程序脚本。

现在让我们安装它:

npm i -g create-react-app

然后用它来创建一个新的 React 应用程序。

create-react-app message-app

根据您的 Internet 连接速度,如果这是您第一次运行该create-react-app命令,这可能需要一段时间才能完成。一路上安装了一堆包,这些包是建立一个方便的开发环境所必需的——包括一个 Web 服务器、编译器和测试工具。

如果你不想全局安装太多的包,你也可以npx,它允许你在不安装的情况下下载和运行一个包:

npx i -g create-react-app

运行这些命令中的任何一个都应输出类似于以下内容的内容:

... Success! Created react-app at C:\Users\mike\projects\github\message-app Inside that directory, you can run several commands: yarn start Starts the development server. yarn build Bundles the app into static files for production. yarn test Starts the test runner. yarn eject Removes this tool and copies build dependencies, configuration files and scripts into the app directory. If you do this, you can’t go back! We suggest that you begin by typing: cd message-app yarn start Happy hacking!

项目设置过程完成后,执行以下命令来启动您的 React 应用程序:

cd message-app npm start

您应该看到以下输出:

.... Compiled successfully! You can now view react-app in the browser. Local: http://localhost:3000 On Your Network: http://192.168.56.1:3000 Note that the development build is not optimized. To create a production build, use yarn build.

您的默认浏览器应该会自动启动,您应该会看到如下屏幕:

react入门基础教程(React入门初学者指南)(1)

现在我们已经确认我们的启动 React 项目正在运行且没有错误,让我们来看看幕后发生了什么。message-app您可以使用您喜欢的代码编辑器打开该文件夹。让我们从package.json文件开始:

{ "name": "message-app", "version": "0.1.0", "private": true, "dependencies": { "@testing-library/jest-dom": "^4.2.4", "@testing-library/react": "^9.3.2", "@testing-library/user-event": "^7.1.2", "react": "^16.13.1", "react-dom": "^16.13.1", "react-scripts": "3.4.3" }, "scripts": { "start": "react-scripts start", "build": "react-scripts build", "test": "react-scripts test", "eject": "react-scripts eject" }, "eslintConfig": { "extends": "react-app" }, "browserslist": { "production": [ ">0.2%", "not dead", "not op_mini all" ], "development": [ "last 1 chrome version", "last 1 firefox version", "last 1 safari version" ] } }

如您所见,Create React App 已经为我们安装了几个依赖项。前三个与React 测试库有关(正如您可能猜到的),它使我们能够测试我们的 React 代码。然后我们有react和react-dom,任何 React 应用程序的核心包,最后react-scripts是 ,它设置了开发环境并启动了一个服务器(你刚刚看到)。

然后是四个 npm 脚本,用于自动执行重复性任务:

  • start启动开发服务器
  • build创建应用的生产就绪版本
  • test运行上述测试
  • eject将公开您的应用程序的开发环境

这个最后的命令值得细说。Create React App 工具在您的实际代码和开发环境之间提供了清晰的分离。如果你运行npm run eject, Create React App 将停止隐藏它在幕后所做的事情,并将所有内容转储到你的项目package.json文件中。虽然这可以让您对应用程序的依赖项进行更精细的控制,但我不建议您这样做,因为您必须管理用于构建和测试项目的所有复杂代码。如果涉及到它,您可以使用customize-cra来配置您的构建过程而无需弹出。

Create React App 还支持ESLint(从属性可以看出eslintConfig),并使用react-app ESLint 规则进行配置。

该文件的browserslist属性package.json允许您指定您的应用程序将支持的浏览器列表。此配置由PostCSS工具和转译器(例如Babel )使用。

你会喜欢 Create React App 的最酷的功能之一是它提供开箱即用的热重载。这意味着我们对代码所做的任何更改都会导致浏览器自动刷新。对 JavaScript 代码的更改将重新加载页面,而对 CSS 的更改将更新 DOM 而无需重新加载。

现在,让我们先按Ctrl 停止开发服务器C。服务器停止后,删除文件夹中除serviceWorker.js和setupTests.js文件之外的所有内容src。如果您有兴趣了解服务人员的工作,可以在此处了解有关他们的更多信息。

除此之外,我们将从头开始创建所有代码,以便您了解src文件夹中的所有内容。

介绍 JSX 语法

React 文档将其定义为“JavaScript 的语法扩展”,JSX 使编写 React 组件变得容易。使用 JSX,我们可以传递 HTML 结构或 React 元素,就好像它们是标准的 JavaScript 值一样。

这是一个简单的例子:

import React from 'react'; export default function App() { const message = <h1>I'm a heading</h1>; //JSX FTW! return ( message ); }

注意线const message = <h1>I'm a heading</h1>;。这就是 JSX。如果你试图在网络浏览器中运行它,它会给你一个错误。但是,在 React 应用程序中,JSX 由转译器(例如 Babel)解释,并呈现为 React 可以理解的 JavaScript 代码。

注意:您可以在我们的教程“ JSX 简介”中了解更多关于 JSX 的信息。

过去,React JSX 文件通常带有.jsx文件扩展名。如今,Create React App 工具会生成带有.js文件扩展名的 React 文件。虽然.jsx仍然支持文件扩展名,但 React 的维护者建议使用.js. 然而,有一个反对的 React 开发者群体,包括我自己,他们更喜欢使用这个.jsx扩展,原因如下:

  • 在 VS Code 中,Emmet可以开箱即用地处理.jsx文件。但是,您可以将 VS Code 配置为将所有.js文件视为JavaScriptReact使 Emmet 在这些文件上工作。
  • 标准 JavaScript 和 React JavaScript 代码有不同的 linting 规则。

但是,对于本教程,我将遵守 Create React App 为我们提供的内容并坚持使用.js文件结尾。

你好世界!在反应

让我们开始编写一些代码。在src新创建的文件夹中message-app,创建一个index.js文件并添加以下代码:

import React from 'react'; import ReactDOM from 'react-dom'; ReactDOM.render(<h1>Hello World</h1>, document.getElementById('root'));

npm start使用或再次启动开发服务器yarn start。您的浏览器应显示以下内容:

react入门基础教程(React入门初学者指南)(2)

这是最基本的“Hello World”React 示例。该index.js文件是您的项目的根目录,将在其中呈现 React 组件。让我解释一下代码是如何工作的:

  • 第 1 行:导入 React 包以处理 JSX 处理。
  • 第 2 行:导入 ReactDOM 包以渲染根 React 组件。
  • 第 3 行:调用渲染函数,传入:<h1>Hello World</h1>: 一个 JSX 元素document.getElementById('root'): 一个 HTML 容器(JSX 元素将在此处呈现)。

HTML 容器位于public/index.html文件中。在第 31 行,您应该看到<div id="root"></div>. 这被称为根 DOM 节点,因为其中的所有内容都将由React 虚拟 DOM管理。

虽然 JSX 看起来确实很像 HTML,但也有一些关键的区别。例如,您不能使用class属性,因为它是 JavaScript 关键字。相反,className在它的位置使用。此外,诸如在 JSXonclick中拼写的事件。onClick现在让我们修改我们的 Hello World 代码:

const element = <div>Hello World</div>; ReactDOM.render(element, document.getElementById('root'));

我已将 JSX 代码移出到一个名为element. 我也用h1标签替换了div标签。要让 JSX 工作,您需要将元素包装在单个父标记中。

看看下面的例子:

const element = <span>Hello,</span> <span>Jane</span>;

上面的代码不起作用。您将收到一个语法错误,指示您必须将相邻的 JSX 元素括在封闭标记中。像这样的东西:

const element = <div> <span>Hello, </span> <span>Jane</span> </div>;

在 JSX 中评估 JavaScript 表达式怎么样?简单的。只需像这样使用花括号:

const name = "Jane"; const element = <p>Hello, {name}</p>

……或者像这样:

const user = { firstName: 'Jane', lastName: 'Doe' } const element = <p>Hello, {user.firstName} {user.lastName}</p>

更新您的代码并确认浏览器显示“Hello, Jane Doe”。尝试其他示例,例如{ 5 2 }. 现在您已经掌握了使用 JSX 的基础知识,让我们继续创建一个 React 组件。

声明 React 组件

上面的示例是向您展示如何ReactDOM.render()工作的简单方式。通常,我们将所有项目逻辑封装在 React 组件中,然后将其传递给ReactDOM.render函数。

在src文件夹中,创建一个名为App.js并键入以下代码的文件:

import React, { Component } from 'react'; class App extends Component { render(){ return ( <div> Hello World Again! </div> ) } } export default App;

在这里,我们通过定义一个 JavaScript 类来创建一个 React 组件,该类是React.Component. 我们还定义了一个返回 JSX 元素的渲染函数。您可以在标签中放置额外的 JSX 代码<div>。接下来,src/index.js使用以下代码进行更新,以查看浏览器中反映的更改:

import React from 'react'; import ReactDOM from 'react-dom'; import App from './App'; ReactDOM.render(<App/>, document.getElementById('root'));

首先我们导入App组件。然后我们App使用 JSX 格式进行渲染,如下所示:<App/>. 这是必需的,以便 JSX 可以将其编译为可以推送到React DOM. 保存更改后,查看浏览器以确保它呈现正确的消息。

接下来,我们将看看如何应用样式。

样式化 JSX 元素

有多种方法可以设置 React 组件的样式。我们将在本教程中看到的两个是:

  1. JSX 内联样式
  2. 外部样式表

下面是我们如何实现 JSX 内联样式的示例:

// src/App.js render() { const headerStyle = { color: '#ff0000', textDecoration: 'underline' } return ( <div> <h1 style={headerStyle}>Hello World Again!</h1> </div> ) }

React 样式看起来很像普通的 CSS,但是有一些关键的区别。例如,headerStyle是一个对象字面量。我们不能像平常那样使用分号。此外,为了使它们与 JavaScript 语法兼容,还更改了一些 CSS 声明。例如text-decoration,我们使用代替textDecoration。基本上,对所有 CSS 键使用驼峰式大小写,但供应商前缀(例如WebkitTransition,必须以大写字母开头)除外。

我们也可以这样实现样式:

// src/App.js return ( <div> <h1 style={{color:'#ff0000', textDecoration: 'underline'}}>Hello World Again!</h1> </div> )

第二种方法是使用外部样式表。默认情况下,已经支持外部 CSS 样式表。如果您想使用 Sass 等预处理器,请查阅文档以了解如何配置它。

在src文件夹中,创建一个名为App.css并键入以下代码的文件:

h1 { font-size: 4rem; }

src/App.js在文件顶部添加以下导入语句:

import './App.css';

保存后,您应该会在浏览器上看到文本内容的大小发生了巨大变化。你也可以像这样使用 CSS 类:

.header-red { font-size: 4rem; color: #ff0000; text-decoration: underline; }

更新src/App.js如下:

<h1 className="header-red">Hello World Again!</h1>

我们不能使用 HTML 的class属性,因为它是一个保留的 JavaScript 关键字。相反,我们使用className. 下面应该是您的预期输出。

react入门基础教程(React入门初学者指南)(3)

现在您已经了解了如何为您的 React 项目添加样式,让我们继续学习无状态和有状态的 React 组件。

无状态与有状态组件

无状态组件,也称为哑组件,只是一个显示信息的组件。它不包含任何操作数据的逻辑。它可以接收来自用户的事件,然后将其传递给父容器进行处理。

创建文件message-view.js并将以下示例代码复制到其中。这是一个哑组件的完美示例(尽管从技术上讲它更像是一个静态组件):

import React from 'react'; class MessageView extends React.Component { render() { return( <div className="message"> <div className="field"> <span className="label">From: </span> <span className="value">John Doe</span> </div> <div className="field"> <span className="label">Status: </span> <span className="value"> Unread</span> </div> <div className="field content"> <span className="label">Message: </span> <span className="value">Have a great day!</span> </div> </div> ) } } export default MessageView;

src/App.css接下来,使用以下代码添加一些基本样式:

body { background-color: #EDF2F7; color: #2D3748; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; } h1 { font-size: 2rem; } .container { width: 800px; margin: 0 auto; } .message { background-color: #F7FAFC; width: 400px; margin-top: 20px; border-top: solid 2px #fff; border-radius: 8px; padding: 12px; box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05); } .field{ display: flex; justify-content: flex-start; margin-top: 2px; } .label { font-weight: bold; font-size: 1rem; width: 6rem; } .value { color: #4A5568; } .content .value { font-style: italic; }

最后,修改src/App.js,使整个文件看起来像这样:

import React, { Component } from 'react'; import './App.css'; import MessageView from './message-view'; class App extends Component { render(){ return ( <MessageView /> ) } } export default App;

到目前为止,代码应该很容易解释,因为到目前为止我已经解释了所涉及的概念。现在看看你的浏览器,你应该有以下结果:

react入门基础教程(React入门初学者指南)(4)

我们之前提到过,React 提供了基于类的组件和函数组件。我们可以MessageView使用这样的函数式语法重写:

import React from 'react'; export default function MessageView() { return ( <div className="message"> <div className="field"> <span className="label">From: </span> <span className="value">John Doe</span> </div> <div className="field"> <span className="label">Status: </span> <span className="value"> Unread</span> </div> <div className="field content"> <span className="label">Message: </span> <span className="value">Have a great day!</span> </div> </div> ); }

请注意,我已经删除了Component导入,因为函数语法中不需要。这种风格一开始可能会让人困惑,但你很快就会发现用这种方式编写 React 组件会更快。

此外,随着React hooks的出现,这种编写 React 组件的风格变得越来越流行。

通过道具传递数据

你已经成功创建了一个无状态的 React 组件。但是,它并不完整,因为需要做更多的工作才能将其与有状态的组件或容器正确集成。目前,MessageView正在显示静态数据。我们需要对其进行修改,使其能够接受输入参数。我们使用称为props的东西来做到这一点——我们将从父组件传递下来的数据。

从改变MessageView组件开始,如下所示:

import React from 'react'; class MessageView extends React.Component { render() { const message = this.props.message; return( <div className="message"> <div className="field"> <span className="label">From: </span> <span className="value">{message.from}</span> </div> <div className="field"> <span className="label">Status: </span> <span className="value">{message.status}</span> </div> <div className="field content"> <span className="label">Message: </span> <span className="value">{message.content}</span> </div> </div> ) } } export default MessageView;

这里要注意的主要事情是我们如何定义message变量。我们为它分配一个值this.props.message,我们将从有状态的父组件传递下来。在我们的 JSX 中,我们可以引用我们的message变量并将其输出到页面。

现在让我们为我们的MessageView. 创建一个新文件message-list.js并添加以下代码:

import React, { Component } from 'react'; import MessageView from './message-view'; class MessageList extends Component { state = { message: { from: 'Martha', content: 'I will be traveling soon', status: 'read' } } render() { return( <div className="container"> <h1>List of Messages</h1> <MessageView message={this.state.message} /> </div> ) } } export default MessageList;

在这里,我们使用状态来存储包含我们消息的对象。React 的部分魔力在于,当状态对象发生变化时,组件将重新渲染(从而更新 UI)。

然后,在我们的 JSX 中,我们将对象的message属性传递给组件。stateMessageView

最后一步是更新我们的App组件以呈现我们的新有状态MessageList组件,而不是无状态MessageView组件:

import React, { Component } from 'react'; import MessageList from './message-list'; import './App.css'; class App extends Component { render(){ return ( <MessageList /> ) } } export default App;

保存更改后,检查您的浏览器以查看结果。

react入门基础教程(React入门初学者指南)(5)

花点时间确保您了解正在发生的事情。我们在(有状态的)组件中声明了一个state对象。MessageList该对象的message属性包含我们的消息。在我们的render函数中,我们可以使用称为 props 的东西将该消息传递给我们的(无状态)子组件。

在(无状态)MessageView组件中,我们可以使用this.props.message. 然后我们可以将此值传递给我们的 JSX 以呈现到页面。

呸!

道具检查

随着您的应用程序增长并且数据作为道具来回传递,验证组件是否正在接收它们期望的数据类型将很有用。

幸运的是,我们可以使用prop-types 包来做到这一点。要查看此操作的快速示例,请MessageView按如下方式更改我们的组件:

import React from 'react'; import PropTypes from 'prop-types'; class MessageView extends Component { // This stays the same ] MessageView.propTypes = { message: PropTypes.object.isRequired } export default MessageView;

message如果prop 丢失,这将导致你的 React 应用程序抱怨。如果组件接收到的不是对象,它也会导致它抱怨。

您可以通过更改父组件的状态来尝试一下,如下所示:

state = { message: 'Not an object!' }

返回浏览器并打开控制台。您应该看到以下记录到控制台:

Warning: Failed prop type: Invalid prop `message` of type `string` supplied to `MessageView`, expected `object`. in MessageView (at message-list.js:13) in MessageList (at App.js:9) in App (at src/index.js:6)

组件重用

现在让我们看看如何使用MessageView实例显示多条消息。这就是 React 开始大放异彩的地方,因为它使代码重用变得非常容易(如您所见)。

首先,我们将更state.message改为一个数组并将其重命名为messages. 然后,我们将使用 JavaScript 的map 函数生成MessageView组件的多个实例,每个实例对应于state.messages数组中的一条消息。

我们还需要使用唯一值填充一个名为key的特殊属性,例如id. React 需要它来跟踪列表中的哪些项目已更改、添加或删除。

更新MessageList代码如下:

class MessageList extends Component { state = { messages: [ { _id: 'd2504a54', from: 'John', content: 'The event will start next week', status: 'unread' }, { _id: 'fc7cad74', from: 'Martha', content: 'I will be traveling soon', status: 'read' }, { _id: '876ae642', from: 'Jacob', content: 'Talk later. Have a great day!', status: 'read' } ] } render() { const messageViews = this.state.messages.map( message => <MessageView key={message._id} message={message} /> ) return( <div className="container"> <h1>List of Messages</h1> {messageViews} </div> ) } }

检查您的浏览器以查看结果:

react入门基础教程(React入门初学者指南)(6)

如您所见,使用 React 很容易定义构建块来创建强大而复杂的 UI 界面。

重构以使用 React Hooks

Hooks是 React 的最新版本,但它们正在席卷 React 世界。简而言之,它们使获取 React 函数组件并为其添加状态(和其他功能)成为可能。

我将通过重构我们的MessageView组件使其成为一个函数组件来完成本教程,该组件使用 React 钩子管理其状态。请注意,这仅在使用 React v16.8 及更高版本时才有可能。

import React, { useState } from 'react'; import MessageView from './message-view'; export default function MessageList () { const initialValues = [ { _id: 'd2504a54', from: 'John', content: 'The event will start next week', status: 'unread' }, { _id: 'fc7cad74', from: 'Martha', content: 'I will be traveling soon', status: 'read' }, { _id: '876ae642', from: 'Jacob', content: 'Talk later. Have a great day!', status: 'read' } ]; const [messages] = useState(initialValues); const messageViews = messages.map( message => <MessageView key={message._id} message={message} /> ); return ( <div className="container"> <h1>List of Messages</h1> {messageViews} </div> ); }

在上面的例子中,我用useState React hookstate替换了对象。顾名思义,这允许您管理组件的状态。

在处理大型项目时,使用挂钩将帮助您避免所谓的道具钻孔。道具钻探看到您通过多个组件(最终不需要该数据)传递道具只是为了到达一个深度嵌套的组件。

我们还可以将我们的MessageView组件转换为函数组件:

import React from 'react'; import PropTypes from 'prop-types'; const MessageView = ({ message }) => { const { from, status, content } = message; return( <div className="message"> <div className="field"> <span className="label">From: </span> <span className="value">{from}</span> </div> <div className="field"> <span className="label">Status: </span> <span className="value">{status}</span> </div> <div className="field content"> <span className="label">Message: </span> <span className="value">{content}</span> </div> </div> ); }; MessageView.propTypes = { message: PropTypes.object.isRequired } export default MessageView;

请注意我们现在如何在组件中接收消息道具:

const MessageView = ({ message }) => { ... }

这利用了一种称为对象解构的技术,它允许您从数组或对象中提取单个项目,并使用速记语法将它们放入变量中。

我们在这里使用相同的技术,从message对象中获取我们需要的值,并避免在所有内容前加上message:

const { from, status, content } = message;

这就是很多!

,

免责声明:本文仅代表文章作者的个人观点,与本站无关。其原创性、真实性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容文字的真实性、完整性和原创性本站不作任何保证或承诺,请读者仅作参考,并自行核实相关内容。文章投诉邮箱:anhduc.ph@yahoo.com

    分享
    投诉
    首页