React入门 - 概述和演练教程

自从我第一次开始学习JavaScript以来,我一直听说过React,但我承认我只看了一眼就吓到了我。 我看到看起来像是一堆混有JavaScript和思想的HTML,这不是我们一直试图避免的吗? React有什么大不了的?
相反,我专注于学习vanilla JavaScript并在专业环境中使用jQuery。 经过几次沮丧,尝试开始使用React之后,我终于开始尝试了,我开始明白为什么我可能想要使用React而不是使用vanilla JS或jQuery。
我试着将我学到的所有内容浓缩成一个很好的介绍与你分享,所以在这里。

先决条件

在开始使用React之前,您应该事先知道一些事情。 例如,如果您以前从未使用过JavaScript或DOM,那么在尝试解决React问题之前,我会更熟悉它们。
以下是我认为是React的先决条件。

  • 基本熟悉HTML和CSS。
  • JavaScript和编程的基础知识。
  • 对DOM的基本了解。
  • 熟悉ES6语法和功能。
  • Node.js和npm全局安装。

目标

了解基本的React概念和相关术语,例如

  • Babel,Webpack,JSX,component,props,status和生命周期。
  • 构建一个非常简单的React应用程序,演示上述概念。
    这是最终结果的来源和在线演示。
    View Source on GitHub
    View Demo

什么是React?

  • React是一个JavaScript库 - 最受欢迎的库之一,在GitHub上有超过100,000颗星。
  • React不是一个框架(与Angular不同,它更加自以为是)。
  • React是一个由Facebook创建的开源项目。
  • React用于在前端构建用户界面(UI)。
  • React是MVC应用程序的视图层(模型视图控制器)

React最重要的一个方面是,您可以创建组件(类似于自定义,可重用的HTML元素),以快速有效地构建用户界面。 React还使用status和props简化了数据的存储和处理方式。

我们将在整篇文章中详细介绍所有这些内容,让我们开始吧。

设置和安装

有几种方法可以设置React,我会告诉你两个,这样你就可以很好地了解它是如何工作的。

静态HTML文件

第一种方法不是设置React的流行方式,也不是我们将如何完成本教程的其余部分,但是如果您曾经使用过像jQuery这样的库,它将会很熟悉且易于理解,而且它是 如果你不熟悉Webpack,Babel和Node.js,那么最简单的方法就是入门。

让我们从制作一个基本的index.html文件开始。 我们将在头部加载三个CDN - React,React DOM和Babel。 我们还将使用名为root的id创建一个div,最后我们将创建一个脚本标记,您的自定义代码将存在。

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  </head>

  <body>
    <div id="root"></div>

    <script type="text/babel">
      // React code will go here
    </script>
  </body>
</html>

在撰写本文时,我正在加载最新的稳定版本的库。

  • React - React顶层API
  • React DOM - 添加特定于DOM的方法
  • Babel - 一种JavaScript编译器,允许我们在旧浏览器中使用ES6 +

我们的应用程序的入口点将是根div元素,它按惯例命名。 您还会注意到文本/ babel脚本类型,这对于使用Babel是必需的。

现在,让我们编写我们的第一个React代码块。 我们将使用ES6类创建一个名为App的React组件。

class App extends React.Component {
  //...
}

现在我们将添加render()方法,这是类组件中唯一需要的方法,用于呈现DOM节点。

class App extends React.Component {
  render() {
      return (
          //...
      );
  }
}

在返回内部,我们将看起来像一个简单的HTML元素。 请注意,我们不会在此处返回字符串,因此请勿在元素周围使用引号。 这叫做JSX,我们很快就会对它有所了解。

class App extends React.Component {
  render() {
    return <h1>Hello world!</h1>
  }
}

最后,我们将使用React DOM render()方法将我们创建的App类渲染到HTML中的根div中。

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

这是index.html的完整代码。
index.html

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8" />

    <title>Hello React!</title>

    <script src="https://unpkg.com/react@16/umd/react.development.js"></script>
    <script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
    <script src="https://unpkg.com/babel-standalone@6.26.0/babel.js"></script>
  </head>

  <body>
    <div id="root"></div>

    <script type="text/babel">
      class App extends React.Component {
        render() {
          return <h1>Hello world!</h1>
        }
      }

      ReactDOM.render(<App />, document.getElementById('root'))
    </script>
  </body>
</html>

现在,如果您在浏览器中查看index.html,您将看到我们创建的h1标记呈现给DOM。
在这里插入图片描述
Cool! 既然你已经完成了这个,你可以看到React并不是那么疯狂的开始。 它只是一些我们可以加载到HTML中的JavaScript帮助程序库。

我们这样做是出于演示目的,但从现在开始我们将使用另一种方法:创建React App。

创建React应用程序

我刚刚用于将JavaScript库加载到静态HTML页面并动态渲染React和Babel的方法效率不高,而且难以维护。
幸运的是,Facebook创建了Create React App,这是一个预先配置了构建React应用程序所需的一切的环境。 它将创建一个实时开发服务器,使用Webpack自动编译React,JSX和ES6,自动加载CSS文件,并使用ESLint来测试和警告代码中的错误。

要设置create-react-app,请在终端中运行以下代码,该目录位于您希望项目所在的目录之上。 确保Node.js中包含5.2或更高版本。

npx create-react-app react-tutorial

完成安装后,移动到新创建的目录并启动项目。

cd react-tutorial
npm start

运行此命令后,将使用新的React应用程序在localhost:3000上弹出一个新窗口。
在这里插入图片描述
如果查看项目结构,您将看到/ public和/ src目录,以及常规node_modules,.gitignore,README.md和package.json。

在/ public中,我们的重要文件是index.html,它与我们之前制作的静态index.html文件非常相似 - 只是一个根div。 这次,没有加载任何库或脚本./ src目录将包含我们所有的React代码。

要查看环境如何自动编译和更新您的React代码,请在/src/App.js中找到如下所示的行:

To get started, edit `src/App.js` and save to reload.

并将其替换为任何其他文本。 保存文件后,您会注意到localhost:3000使用新数据进行编译和刷新。

继续并删除/ src目录中的所有文件,我们将创建自己的样板文件,没有任何膨胀。 我们将保留index.css和index.js。

对于index.css,我只是将Primitive CSS的内容复制并粘贴到文件中。 如果需要,可以使用Bootstrap或任何您想要的CSS框架,或者根本不使用。 我发现它更容易使用。

现在在index.js中,我们导入了React,ReactDOM和CSS文件。

import React from 'react'
import ReactDOM from 'react-dom'
import './index.css'

让我们再次创建我们的App组件。 在此之前,我们只有一个<h1>,但现在我正在添加一个带有类的div元素。 您会注意到我们使用className而不是class。 这是我们第一次暗示这里编写的代码是JavaScript,而不是HTML。

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

最后,我们将像以前一样将应用程序呈现给根目录。

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

这是我们的完整index.js。 这次,我们将Component作为React的属性加载,因此我们不再需要扩展React.Component。

import React, { Component } from 'react'
import ReactDOM from 'react-dom'
import './index.css'

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

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

React 开发者工具

有一个名为React Developer Tools的扩展程序,可以让您在使用React时更轻松。 下载适用于Chrome的React DevTools,或者您喜欢使用的任何浏览器。
安装后,当您打开DevTools时,您将看到React的选项卡。 单击它,您将能够在编写组件时对其进行检查。 您仍然可以转到Elements选项卡以查看实际的DOM输出。 现在看起来似乎不是很多,但随着应用程序变得越来越复杂,使用它将变得越来越必要。
在这里插入图片描述
现在我们拥有了实际开始使用React所需的所有工具和设置。

JSX: JavaScript + XML

正如您所见,我们在React代码中使用的是HTML,但它并不是HTML。 这是JSX,代表JavaScript XML。

使用JSX,我们可以编写看似HTML的内容,还可以创建和使用我们自己的类似XML的标记。 这是JSX看起来分配给变量的内容。

const heading = <h1 className="site-heading">Hello, React</h1>

编写React不是强制使用JSX。 在引擎盖下,它正在运行createElement,它接受标记,包含属性的对象和组件的子项,并呈现相同的信息。 以下代码将具有与上述JSX相同的输出。

const heading = React.createElement('h1', { className: 'site-heading' }, 'Hello, React!')

JSX实际上更接近JavaScript,而不是HTML,因此在编写时需要注意几个关键的区别。

  • 使用className而不是类来添加CSS类,因为class是JavaScript中的保留关键字。
  • JSX中的属性和方法是camelCase - onclick将成为onClick。
  • 自动关闭标签必须以斜线结尾 - 例如 <img />

JavaScript表达式也可以使用花括号嵌入JSX中,包括变量,函数和属性。

const name = 'Tania'
const heading = <h1>Hello, {name}</h1>

JSX比在vanilla JavaScript中创建和附加许多元素更容易编写和理解,这也是人们喜欢React的原因之一。

Components

到目前为止,我们已经创建了一个组件–App组件。 React中的几乎所有内容都由组件组成,组件可以是类组件或简单组件。

大多数React应用程序都有许多小组件,所有内容都加载到主App组件中。 组件也经常得到自己的文件,所以让我们改变我们的项目来做到这一点。

从index.js中删除App类,所以它看起来像这样。

import React from 'react'
import ReactDOM from 'react-dom'
import App from './App'
import './index.css'

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

我们将创建一个名为App.js的新文件,并将组件放在那里。

import React, { Component } from 'react'

class App extends Component {
  render() {
    return (
      <div className="App">
        <h1>Hello, React!</h1>
      </div>
    )
  }
}

export default App

我们将组件导出为App并将其加载到index.js中。 将组件分离到文件中并不是强制性的,但如果不这样做,应用程序将开始变得笨拙和失控。

Class Components

让我们创建另一个组件。 我们要创建一个表。 创建Table.js,并用以下数据填充它。

import React, { Component } from 'react'

class Table extends Component {
  render() {
    return (
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Job</th>
          </tr>
        </thead>
        <tbody>
          <tr>
            <td>Charlie</td>
            <td>Janitor</td>
          </tr>
          <tr>
            <td>Mac</td>
            <td>Bouncer</td>
          </tr>
          <tr>
            <td>Dee</td>
            <td>Aspiring actress</td>
          </tr>
          <tr>
            <td>Dennis</td>
            <td>Bartender</td>
          </tr>
        </tbody>
      </table>
    )
  }
}

export default Table

我们创建的这个组件是一个自定义类组件。 我们将自定义组件大写,以区别于常规HTML元素。 回到App.js,我们可以在表中加载,首先导入它:

import Table from './Table'

然后将它加载到App的render()中,之前我们有“Hello,React!”。 我还改变了外容器的类。

return (
  <div className="container">
    <Table />
  </div>
)

如果您回到实时环境,您将看到加载的表格。
在这里插入图片描述
现在我们已经看到了自定义类组件是什么。 我们可以反复使用这个组件。 但是,由于数据是硬编码的,因此目前不太有用。

Simple Components

React中的另一种组件是简单组件,它是一个函数。 此组件不使用class关键字。 让我们使用我们的表并为它制作两个简单的组件 - 表头和表体。

我们将使用ES6箭头函数来创建这些简单的组件。 首先是表头。

const TableHeader = () => {
  return (
    <thead>
      <tr>
        <th>Name</th>
        <th>Job</th>
      </tr>
    </thead>
  )
}

然后是Body。

const TableBody = () => {
  return (
    <tbody>
      <tr>
        <td>Charlie</td>
        <td>Janitor</td>
      </tr>
      <tr>
        <td>Mac</td>
        <td>Bouncer</td>
      </tr>
      <tr>
        <td>Dee</td>
        <td>Aspiring actress</td>
      </tr>
      <tr>
        <td>Dennis</td>
        <td>Bartender</td>
      </tr>
    </tbody>
  )
}

现在我们的Table类看起来像这样。

class Table extends Component {
  render() {
    return (
      <table>
        <TableHeader />
        <TableBody />
      </table>
    )
  }
}

一切都应该像以前一样出现。 如您所见,组件可以嵌套在其他组件中,并且可以混合使用简单组件和类组件。

类组件必须包含render(),并且返回只能返回一个父元素。

作为总结,让我们将一个简单的组件与一个类组件进行比较。

Simple Component

const SimpleComponent = () => {
  return <div>Example</div>
}

Class Component

class ClassComponent extends Component {
  render() {
    return <div>Example</div>
  }
}

请注意,如果返回包含在一行中,则不需要括号。

Props

现在,我们有一个很酷的Table组件,但数据是硬编码的。 关于React的一个重大问题是它如何处理数据,它使用属性(称为props)和state来实现。 首先,我们将专注于使用道具处理数据。

首先,让我们从TableBody组件中删除所有数据。

const TableBody = () => {
  return <tbody />
}

然后让我们将所有数据移动到一个对象数组,就好像我们引入了一个基于JSON的API一样。 我们必须在render()中创建这个数组。

class App extends Component {
  render() {
    const characters = [
      {
        name: 'Charlie',
        job: 'Janitor',
      },
      {
        name: 'Mac',
        job: 'Bouncer',
      },
      {
        name: 'Dee',
        job: 'Aspring actress',
      },
      {
        name: 'Dennis',
        job: 'Bartender',
      },
    ]

    return (
      <div className="container">
        <Table />
      </div>
    )
  }
}

现在,我们将使用属性将数据传递给子组件(Table),以及如何使用数据属性传递数据。 我们可以随意调用属性,只要它不是保留关键字,所以我将使用characterData。 我传递的数据是字符变量,我会在它周围加上花括号,因为它是一个JavaScript表达式。

return (
  <div className="container">
    <Table characterData={characters} />
  </div>
)

现在数据正在传递给Table,我们必须努力从另一端访问它。

class Table extends Component {
  render() {
    const { characterData } = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    )
  }
}

如果打开React DevTools并检查Table组件,您将在属性中看到数据数组。 这里存储的数据称为虚拟DOM,这是一种快速有效的方法,可以将数据与实际DOM同步。
在这里插入图片描述
但是,这些数据还没有在实际的DOM中。 在Table中,我们可以通过this.props访问所有属性。 我们只传递一个属性,通过characterData,所以我们将使用this.props.characterData来检索那些数据。

我将使用ES6属性简写来创建包含this.props.characterData的变量。

const { characterData } = this.props

由于我们的Table组件实际上由两个较小的简单组件组成,因此我将再次通过props将其传递给TableBody。

class Table extends Component {
  render() {
    const { characterData } = this.props

    return (
      <table>
        <TableHeader />
        <TableBody characterData={characterData} />
      </table>
    )
  }
}

现在,TableBody不带任何参数并返回单个标签。

const TableBody = () => {
  return <tbody />
}

我们将作为参数传递道具,并通过数组映射以返回数组中每个对象的表行。 此映射将包含在rows变量中,我们将其作为表达式返回。

const TableBody = props => {
  const rows = props.characterData.map((row, index) => {
    return (
      <tr key={index}>
        <td>{row.name}</td>
        <td>{row.job}</td>
      </tr>
    )
  })

  return <tbody>{rows}</tbody>
}

如果您查看应用程序的前端,则现在正在加载所有数据。

你会注意到我已经为每个表行添加了一个键索引。 在React中制作列表时应始终使用键,因为它们有助于识别每个列表项。 我们还将在我们想要操作列表项的那一刻看到这是如何必要的。

属性是将现有数据传递给React组件的有效方法,但是组件不能更改属性 - 它们是只读的。 在下一节中,我们将学习如何使用state来进一步控制React中的处理数据。

State

现在,我们将字符数据存储在变量中的数组中,并将其作为道具传递。 这很好开始,但想象一下我们是否希望能够从数组中删除一个项目。 使用props,我们有单向数据流,但有了状态,我们可以更新组件中的私有数据。

您可以将状态视为应保存和修改的任何数据,而无需将其添加到数据库中 - 例如,在确认购买之前在购物车中添加和删除项目。
首先,我们将创建一个状态对象。

class App extends Component {
  state = {}
}

该对象将包含您要在状态中存储的所有内容的属性。 对我们来说,这是角色。

class App extends Component {
  state = {
    characters: [],
  }
}

将我们之前创建的整个对象数组移动到state.characters中。

class App extends Component {
  state = {
    characters: [
      {
        name: 'Charlie',
        // the rest of the data
      },
    ],
  }
}

我们的数据正式包含在该状态。 由于我们希望能够从表中删除一个字符,因此我们将在父App类上创建一个removeCharacter方法。

要检索状态,我们将使用与以前相同的ES6方法获取this.state.characters。 要更新状态,我们将使用this.setState(),一种用于操作状态的内置方法。 我们将根据我们传递的索引过滤数组,并返回新数组。

您必须使用this.setState()来修改数组。 简单地将一个新值应用于this.state.property将不起作用。

removeCharacter = index => {
  const { characters } = this.state

  this.setState({
    characters: characters.filter((character, i) => {
      return i !== index
    }),
  })
}

filter不会变异,而是创建一个新数组,并且是在JavaScript中修改数组的首选方法。 这个特殊的方法是测试一个索引与数组中的所有索引,并返回除了传递的所有索引之外的所有索引。

现在我们必须将该函数传递给组件,并在每个可以调用该函数的字符旁边呈现一个按钮。 我们将removeCharacter函数作为prop传递给Table。

在这里插入图片描述
现在您应该了解状态如何初始化以及如何修改状态。

提交表格数据

现在我们将数据存储在状态中,我们可以从状态中删除任何项目。 但是,如果我们希望能够向州添加新数据呢? 在真实世界的应用程序中,您更可能从空状态开始并添加到其中,例如使用待办事项列表或购物车。
在其他任何事情之前,让我们从state.characters中删除所有硬编码数据,因为我们现在将通过表单更新。

class App extends Component {
  state = {
    characters: [],
  }
}

现在让我们继续在名为Form.js的新文件中创建一个Form组件。 我们将创建一个类组件,并且我们将使用构造函数(),这是我们迄今尚未完成的。 我们需要构造函数()来使用它,并接收父级的道具。

我们将把Form的初始状态设置为具有一些空属性的对象,并将该初始状态分配给this.state。

Form.js

import React, { Component } from 'react'

class Form extends Component {
  constructor(props) {
    super(props)

    this.initialState = {
      name: '',
      job: '',
    }

    this.state = this.initialState
  }
}

我们对此表单的目标是每次在表单中更改字段时更新表单的状态,并且当我们提交时,所有数据将传递到App状态,然后将更新表。

首先,我们将创建每次对输入进行更改时运行的函数。 事件将被传递,我们将Form的状态设置为具有输入的名称(键)和值。

handleChange = event => {
  const { name, value } = event.target

  this.setState({
    [name]: value,
  })
}

在我们继续提交表单之前,让我们开始工作。 在渲染中,让我们从状态中获取两个属性,并将它们指定为与正确的表单键对应的值。 我们将把handleChange()方法作为输入的onChange运行,最后我们将导出Form组件。

render() {
  const { name, job } = this.state;

  return (
    <form>
      <label>Name</label>
      <input
        type="text"
        name="name"
        value={name}
        onChange={this.handleChange} />
      <label>Job</label>
      <input
        type="text"
        name="job"
        value={job}
        onChange={this.handleChange} />
    </form>
  );
}

export default Form;

在App.js中,我们可以渲染表格下方的表格。

return (
  <div className="container">
    <Table characterData={characters} removeCharacter={this.removeCharacter} />
    <Form />
  </div>
)

现在,如果我们转到应用程序的前端,我们将看到一个尚未提交的表单。 更新一些字段,您将看到Form的本地状态正在更新。
在这里插入图片描述

最后一步是允许我们实际提交该数据并更新父状态。 我们将在App上创建一个名为handleSubmit()的函数,它将通过使用现有的this.state.characters并使用ES6扩展运算符添加新的字符参数来更新状态。

handleSubmit = character => {
  this.setState({ characters: [...this.state.characters, character] })
}

让我们确保将它作为Form上的参数传递给我们。

<Form handleSubmit={this.handleSubmit} />

现在在Form中,我们将创建一个名为submitForm()的方法,该方法将调用该函数,并将Form状态作为我们之前定义的字符参数传递。 它还会将状态重置为初始状态,以便在提交后清除表单。

submitForm = () => {
  this.props.handleSubmit(this.state)
  this.setState(this.initialState)
}

最后,我们将添加一个提交按钮来提交表单。 我们使用onClick而不是onSubmit,因为我们没有使用标准提交功能。 点击将调用我们刚刚创建的submitForm。

<input type="button" value="Submit" onClick={this.submitForm} />

就是这样! 该应用程序已完成。 我们可以在表格中创建,添加和删除用户。 由于Table和TableBody已经从状态中拉出,它将正确显示。

在这里插入图片描述

引入API数据

React的一个非常常见的用法是从API中提取数据。 如果您不熟悉API是什么或如何连接到一个API,我建议您阅读如何使用JavaScript连接到API,它将引导您了解API以及如何将它们与vanilla JavaScript一起使用。
作为一个小测试,我们可以创建一个Api.js文件,并在那里创建一个新的应用程序。 我们可以测试的公共API是Wikipedia API,我在这里有一个URL端点用于随机*搜索。 您可以转到该链接查看API - 并确保在浏览器上安装了JSONView。
我们将使用JavaScript的内置Fetch从该URL端点收集数据并显示它。 您可以通过更改index.js中的URL来切换我们创建的应用程序和此测试文件 - 从’./Api’导入应用程序。
我不打算逐行解释这个代码,因为我们已经学会了通过状态数组创建组件,渲染和映射。 此代码的新方面是componentDidMount(),一个React生命周期方法。 生命周期是React中调用方法的顺序。 安装是指插入DOM的项目。
当我们引入API数据时,我们希望使用componentDidMount,因为我们希望在引入数据之前确保组件已呈现给DOM。 在下面的代码片段中,您将看到我们如何从Wikipedia API引入数据,并将其显示在页面上

import React, { Component } from 'react'

class App extends Component {
  state = {
    data: [],
  }

  // Code is invoked after the component is mounted/inserted into the DOM tree.
  componentDidMount() {
    const url =
      'https://en.wikipedia.org/w/api.php?action=opensearch&search=Seona+Dancing&format=json&origin=*'

    fetch(url)
      .then(result => result.json())
      .then(result => {
        this.setState({
          data: result,
        })
      })
  }

  render() {
    const { data } = this.state

    const result = data.map((entry, index) => {
      return <li key={index}>{entry}</li>
    })

    return <ul>{result}</ul>
  }
}

export default App

在本地服务器中保存并运行此文件后,您将看到DOM中显示的Wikipedia API数据。
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值