React 教程:如何开始以及如何比较

已发表: 2022-03-11

前端和 JavaScript 尤其是一个奇怪的世界。 每天推出的新事物的数量经常被不与他们合作的人所嘲笑,而且很多人都在嘲笑他们。 尽管如此,有时我们还是对新的信息、图书馆和讨论感到有点不知所措,我们想要一些稳定的东西,比如可以在船上停留更长时间的避风港。 最近,React 似乎是动态 JavaScript 进化海洋中的这个温顺的港湾。

考虑到这一点,我们决定制作这个由多部分组成的 React 教程,以展示它的功能并看看它与 Angular 和 VueJS 的比较。

React 作为灯塔的插图清楚地显示在 JavaScript 代码的海洋上

当然,React 不是我们可以使用的唯一港口,但目前,它是最流行、最稳定、最创新的解决方案之一,虽然它仍然有很多升级,但它们更像是一种改进的选择,而不是而不是功能的必需品。

2019 年的 React 状态

React 是一个视图库,我们可以追溯到 2011 年,当它的第一个原型 FaxJs 出现在其 Facebook 页面上时,React 本身是由 Jordan Walke(他也是上述原型的作者)在 JSConfUS 上介绍的2013 年 5 月 29 日,并于 2013 年 7 月 2 日在 GitHub 上公开发布。

React 在 2014 年继续流行,当时会议开始出现扩大社区并普及 React。 不过,在我看来,2015 年对于 React 来说是具有里程碑意义的一年——大公司(例如 Airbnb 和 Netflix)开始喜欢并采用 React 解决方案。 此外,React Native 也在当年出现。 React Native 背后的想法并不是什么新鲜事物,但观看起来很有趣,尤其是在它得到 Facebook 支持的情况下。

另一个重大变化是 Redux,一个 Flux 实现。 这使得状态管理方式更加平易近人和更容易,使其成为迄今为止最成功的实施方式。

从那时到现在,还有很多其他的东西可用,包括 React 工具、核心算法重写、Fiber、语义版本控制的更改等等。 快进到今天,我们在 16.6.3 上,可能在带有 Hooks 的新版本可用前几周(它应该是 16.7.0,但由于对 React.lazy 的一些修复,该版本已经发布)。 React 是众所周知且稳定的,并且获得了很好的意见。

但是什么反应?

好吧,如果您是前端开发人员并且您还没有听说过它,那么我需要说恭喜您,因为这是一项了不起的壮举。

撇开玩笑不谈,React 是一个基于声明式组件的视图库,可帮助您构建 UI。 它是一个库,而不是一个框架,尽管起初很多人将其描述为后者。

显然,如果我们要添加 Redux、React Router 等,它开始拥有制作常规单页应用程序所需的所有东西,这可能是它有时被错误描述为框架而不是库的原因. 如果有的话,可以说,将环境的所有组件放在一起,“框架”一词有点合适,但就其本身而言,React 只是一个库。

让我们停止命名,关注 React 中的不同之处,以及我们在它开始之前没有的东西。 首先,当您第一次想到 React 时,您会想到 JSX,因为它是您查看代码时首先映入眼帘的东西。 JSX 是一种 JavaScript 语法扩展,有点类似于 HTML/XML。 说到 React 和 JSX,我们与 HTML 有一些区别,例如,React 中的一个类是className ,没有 tabindex 而是tabIndex ,样式接受具有 camelCased 属性的 JavaScript 对象等等。

有一些细微的差异,但任何人都应该立即了解它们。 事件处理是通过例如onChangeonClick属性来实现的,这些属性可以用来附加一些函数来处理事件。 此外,以后的组件可以通过使用 props 自由重用和自定义,因此没有理由多次编写相同的代码。

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

然而,JSX 在 React 中实际上并不是绝对必要的。 您可以编写常规函数来创建元素,而无需使用 JSX。 上面相同的代码,可以像下面一样使用。

 import React, { Component } from 'react'; export default class App extends Component { render() { return React.createElement( 'div', null, 'Hello World, ', this.props.name ); } }

显然,我不建议您使用这种语法,尽管在某些情况下它可能会派上用场(例如,您想引入一个非常小的东西并且不想更改构建环境)。

实际上,我展示上面的代码片段还有另一个原因。 通常,开发人员不明白为什么我们需要执行以下操作:

 import React from 'react';

该片段应该是不言自明的。 即使我们正在提取Component ,我们仍然需要 React,因为 Babel 将 JSX 之上的转换为React.createElement 。 所以,如果我们不导入 React,它对我们来说只会失败。 我提到了 Babel,它是一个工具,它可以帮助我们引入 JavaScript 中(或者更确切地说是浏览器)中还没有的东西,或者以某种方式对其进行扩展(或者 Babel 7 支持的不同语言,如 TypeScript)。 感谢巴别塔:

  • JSX 会被做成浏览器可以理解的函数。
  • 我们可以使用浏览器中还没有的新特性(例如,类属性)。
  • 我们可以添加新浏览器中存在但旧浏览器中没有的功能,同时保持旧浏览器支持。

简而言之,JavaScript 的明天就是今天; 这可能需要它自己的文章。 值得一提的是,React 导入也可以通过其他一些技术绕过(比如通过 Webpack 引入 ProvidePlugin 等),但由于篇幅有限,我们将避免它,假设用户会使用 Create React App ( CRA)(有关此工具的更多信息将在后面提到)。

第二个重要的事情,比 JSX 本身更重要的是 React 基于虚拟 DOM。 简而言之,虚拟 DOM 是由开发人员编写的 JavaScript 表示的理想树的内存,稍后将其与真实 DOM 进行比较,并在称为协调的过程中与之同步。

React 与 Angular 和 Vue 相比如何?

我非常不喜欢比较库,尤其是当我们被迫将梨与苹果进行比较时(库与框架等)。

因此,我将尝试使用一系列与技术无关的简短问题和答案来比较 React 与 Angular 和 Vue,而不是说“X 比 Y 更好,因为它使用 JSX 而不是模板。 ” 诸如此类的点通常是个人喜好,一个人的主观选择。 此外,速度、内存分配等在 React 及其所有主要竞争对手(想到 Angular 和 Vue)中都非常相似。 关于这个问题有一个非常好的报告,但请记住这一点:绝大多数应用程序看起来不像在 10k 表中交换行的非常大的表。 因此,这些结果也是纯速度实验。 在现实世界中,你一开始就不会做这样的事情。

React 与 Angular 与 Vue.js 的图示

因此,让我们看一下与 React 有关的一些问题以及它与竞争对手的比较:

我想有很多工作机会。 React 有多受欢迎?

嗯,这很容易回答——选择 React。 实际上,我会说 React 的职位空缺大约是 Vue 的 6-10(相当大的传播,但有些门户网站是 1:50,有些是 1:6),比 Vue 多 2-4比角。 对 React 专家的需求很旺盛,那么为什么 Vue 在 GitHub 上如此受欢迎(事实上它比 React 拥有更多的星星)而职位空缺却更少? 我不知道。

我想要一个大社区,大量图书馆,为可能出现的问题提供快速解决方案。

做出反应。 不要再看了。

它是否易于使用,是否让开发变得愉快?

再一次,根据 2018 年和 2017 年的 JS 报告,React 和 Vue 都享有非常好的声誉,大多数开发人员表示他们会再次使用它们。 另一方面,Angular 有一种趋势,年复一年,让越来越多的人说他们不会使用它了。

我想创建一个新的单页应用程序,但我不想搜索库。

这可能是我认为 Angular 是更好选择的唯一地方。

没有大公司。 我想尽可能独立,我应该选择哪个?

Vue——​​它是我们大三人组中唯一独立的。 (Facebook 支持 React,而谷歌支持 Angular。)

最简单的开始和最快的学习曲线?

Vue/反应。 我在这里倾向于 Vue,但这只是我个人的看法。

为什么? 因为你甚至不需要知道 JSX(它是可选的),它基本上只是 HTML + CSS + JavaScript。

React 教程:开始你的第一个应用程序

React 教程:创建 React 应用的成功消息截图

现在开始使用 React 最简单的方法是使用 CRA,这是一个 CLI 工具,可以为您创建项目并帮助您避免 Webpack/Babel 等的所有必要设置。 相反,您依赖于它的默认配置方式以及随着时间的推移其中包含的内容。 多亏了这一点,您无需关心某些关键库的重大更新。

当然,稍后,您可以通过运行npm run eject自己“弹出”并处理每个方面。 这种方法有它自己的优点,因为您可以使用原本不可用的东西(例如,装饰器)来增强您的应用程序,但它也可能是一个令人头疼的问题,因为它需要许多额外的文件和更多的时间。

所以,首先要做的是:

 npx create-react-app {app-name}

然后npm run start可以开始了。

类与函数组件

我们应该首先解释这些组件的不同之处。 基本上,每个组件都可以是一个函数。 它们之间的主要区别在于,第一类具有一些功能组件中不可用的特性:它们可以有状态并使用 refs、生命周期等。这是当前的播放状态,从 16.7 版本开始(或者无论如何它会由于已经提到的更改而被调用),我们也将有钩子,因此状态和引用将可以使用钩子。

类组件有两种类型: ComponentPureComponent 。 两者之间的唯一区别是PureComponent对 props 和 state 进行了浅层比较——在你不想进行“浪费”渲染的情况下,它有自己的好处,组件及其子组件处于完全相同的状态渲染后。 不过,这只是一个肤浅的比较。 如果您想实现自己的比较(例如,因为您正在传递复杂的道具),只需使用Component并覆盖shouldComponentUpdate (默认情况下返回 true)。 从 16.6+ 开始,函数组件也可以使用类似的东西——这要归功于React.memo ,它是一个高阶组件,默认行为类似于PureComponent (浅比较),但它需要第二个参数,您可以在其中传递您自己的自定义 props 比较.

作为一般的经验法则,如果你可以使用函数组件(你不需要类特性),那么就使用它。 很快,从 16.7.0 开始,由于生命周期方法,将只需要使用类组件。 我倾向于认为函数组件更透明,更容易推理和理解。

React 生命周期方法

安装、更新和卸载组件的图示

构造函数(道具)

  • 可选的,尤其是在 CRA 如此流行的情况下,其中默认包含 JavaScript 的类字段声明。 如果您在类主体中通过箭头函数绑定您的方法,那么声明是没有意义的。 类似的状态也可以初始化为类属性。
  • 应该只用于初始化 ES6 类中对象和绑定方法的本地状态。

组件DidMount()

  • 在此处进行 Ajax 调用。
  • 如果您需要事件侦听器、订阅等,请在此处添加它们。
  • 您可以在此处使用setState (但它会使组件重新渲染)。

组件WillUnmount()

  • 清除所有仍在进行中的东西——例如,应该中断 Ajax、取消订阅、清除计时器等等。
  • 不要调用setState ,因为它没有意义,因为组件将被卸载(并且您会收到警告)。

componentDidUpdate(prevProps, prevState, 快照)

  • 在组件刚刚完成更新时发生(在初始渲染时不会发生)。
  • 具有三个可选使用的参数(先前的道具、先前的状态和仅在您的组件实现getSnapshotBeforeUpdate时才会出现的快照)。
  • 仅当shouldComponentUpdate返回 true 时才会发生。
  • 如果你在这里使用setState ,你应该保护它,否则你将陷入无限循环。

shouldComponentUpdate(nextProps, nextState)

  • 仅用于性能优化。
  • 如果它返回 false,则不会调用渲染。
  • 如果覆盖的 SCO 只是浅的 props/state 比较,则可以使用PureComponent

getSnapshotBeforeUpdate()

  • 可用于保存有关当前 DOM 的一些信息,例如,当前滚动位置,稍后可以在componentDidUpdate中重用该信息以恢复滚动位置。

componentDidCatch(错误,信息)

  • 应该发生日志记录错误的地方。
  • 可以调用setState ,但在未来的版本中,它将被删除以支持静态方法getDerivedStateFromError(error) ,该方法将通过返回一个值来更新状态以更新状态。

有两个额外的方法都是静态的并且在其他解释中提到过

静态 getDerivedStateFromError(错误)

  • 此处提供了错误信息。
  • 应该返回一个对象值,该值将更新可用于处理错误的状态(通过显示某些内容)。
  • 由于它是静态的,因此它无法访问组件实例本身。

静态 getSnapshotBeforeUpdate(道具,状态)

  • 应该在 props 随时间变化的情况下使用——例如,根据 React 文档,它可能对转换组件有用。
  • 由于它是静态的,因此它无法访问组件实例本身。

请注意,目前可用的方法很少,但它们应该在 React 17.0 中被删除,所以这里没有提到它们。

状态与道具

让我们从Props开始,因为它们更容易和更快地解释。 道具是传递给组件的属性,稍后可以在其中重用以显示信息/业务逻辑等。

 import React, { Component } from 'react'; export default class App extends Component { render() { return ( <div> <HelloWorld name="Someone :)"/> </div> ); } } const HelloWorld = (props) => <div>Hello {props.name}</div>

在上面的例子中, name是一个道具。 props 是只读元素,不能在子组件中直接更改。 另外,人们经常做的一种不好的做法是,将 props 复制到 state 并在之后对 state 进行操作。 当然,在某些情况下,您想要执行诸如“提交后将更新父组件的初始状态”之类的操作,但这比较少见——在这种情况下,初始状态馈送可能是有意义的。 此外,不仅可以将字符串等属性传递给子组件,还可以将数字、对象、函数等传递给子组件。

Props 还有一个更有用的东西,称为defaultProps ,它是一个静态字段,可以告诉您组件的默认 props 是什么(例如,当它们没有传递给组件时)。

在“提升状态”的情况下,其中一个组件(父组件)的状态稍后会被其子组件重用(例如,一个子组件显示它而另一个允许编辑),那么我们需要将函数传递给子组件parent,它允许我们更新父级的本地状态。

另一方面, State是可以修改的本地状态,但可以通过使用this.setState间接修改。 如果有人直接改变状态,组件将不会意识到更改,并且不会重新渲染以反映提到的状态更改。

SetState是一种更改本地状态对象的方法(通过进行浅合并),之后,组件通过重新渲染自身来响应它。 请注意,在使用setState后, this.state属性不会立即反映函数中提到的更改(它具有异步性质),因为由于优化,可能会将一些setState实例批处理在一起。 它有几种调用方式,其中一种可能性允许我们在更新状态后立即对组件执行某些操作:

  • setState({value: '5'})
  • setState((state, props) => ({value: state.name + “'s”}))
  • setState([object / function like above], () => {}) - 这种形式允许我们附加callback ,当状态将反映我们想要拥有的数据时(在第一个参数中)将调用它。
 import React, { Component } from 'react'; export default class App extends Component { state = { name: 'Someone :)' } onClick = () => this.setState({ name: 'You' }) render() { return ( <div> <HelloWorld name={this.state.name} onClick={this.onClick}/> </div> ); } } const HelloWorld = (props) => <div onClick={props.onClick}>Hello {props.name}</div>

反应上下文

React 最近稳定了 Context API(它在 React 中已经存在了很长一段时间,但尽管被 Redux 等一些最流行的库广泛使用,但它还是一项实验性功能),它帮助我们解决了一个问题:道具钻取。 简而言之,道具钻探是一种在结构中深入传递道具的方法——例如,它可以是组件的某种主题、特定语言的本地化、用户信息等。 在 Context 之前(或者更确切地说,在它成为非实验性之前),它是通过从父级到子级以递归方式传递到最后一片叶子来深入研究的(显然 Redux 也可以解决这个问题)。 请注意,此功能仅解决道具钻孔问题,不能替代 Redux 或 Mobx 之类的东西。 显然,如果您只是为此使用状态管理库,那么您可以自由替换它。

包起来

我们的 React 教程的第一部分到此结束。 在接下来的文章中,我们希望解决更高级的主题,从样式和检查类型,到生产部署和性能优化。

相关:维护控制:Webpack 和 React 指南,Pt。 1