2016 React Tutorial All In One
2016 React Tutorial All In One
Build Unique Search ExperiencesHosted Search API that delivers instant and relevant results from the first keystroke
https://classroom.udacity.com/nanodegrees/nd019-cn-preview/parts/27705889-df26-448f-afa2-4d316d589128/modules/4885698c-c850-41a0-86f7-92cbdb0b75f9/lessons/9a065aa0-91d4-44a3-ad96-8d9b44be4d11/concepts/a4123f05-9331-4144-8a25-36949c80f22a
函数组合的优势
因为函数组合是让 React 非常强大的重要原因之一,因此我们将深入了解这一概念。注意,函数组合就是将简单的函数组合到一起,并形成复杂的函数。有几个重要概念值得注意,包括:
- 简单的函数
- 组合到一起形成另一个函数
函数组合由简单的函数构成。我们看一个示例:
function getProfileLink (username) {
return 'https://github.com/' + username
}
这个函数极为简单,对吧?只有一行!类似地,getProfilePic
函数也只有一行:
function getProfilePic (username) {
return 'https://github.com/' + username + '.png?size=200'
}
这些函数绝对是简单的函数,要组合它们,我们将它们放进另一个函数中:
function getProfileData (username) {
return {
pic: getProfilePic(username),
link: getProfileLink(username)
}
}
我们可以不用组合形成 getProfileData
,而是直接向其提供数据:
function getProfileData (username) {
return {
pic: 'https://github.com/' + username + '.png?size=200',
link: 'https://github.com/' + username
}
}
理论上来说,根本没有问题;这完全是正确的 JavaScript 代码。但不是函数组合。这个不使用组合的版本还可能存在几个问题。如果要在其他地方需要用户的 GitHub 链接,那么可能需要重复的代码。好的函数应该遵守"DOT”规则:
只做一件事
这个函数做了好几件事(无论有多小);创建两个不同的 URL,将它们存储为对象上的某个属性,然后返回该属性。在组合版本中,每个函数只做一件事:
getProfileLink
– 只构建用户的 GitHub 个人资料链接字符串getProfilePic
– 只构建用户的 GitHub 个人资料照片字符串getProfileData
– 返回新的对象
React 与函数组合
React 频繁利用函数组合!React 使用组件构建 UI 的各个部分。我们看看一些伪代码示例。以下是三个不同的组件:
<Page />
<Article />
<Sidebar />
现在我们将这些简单的组件组合到一起,并创建更复杂的组件(又叫做 组合action!):
<Page>
<Article />
<Sidebar />
</Page>
现在,Page
组件里面具有 Article
和 Sidebar
组件。就像之前的示例那样,getProfileData
里具有 getProfileLink
和 getProfilePic
。
稍后我们将讲解组件,暂时先记住组合在构建 React 组件的过程中发挥了很大的作用。
函数组合总结
函数组合是指将简单的函数组合到一起并形成更复杂的函数。可以将每个函数看做一个只做一件事 (DOT)的构建模块。当你将这些简单的函数组合到一起并形成更复杂的函数时,这就叫做函数组合
https://www.linkedin.com/pulse/compose-me-function-composition-javascript-kevin-greene/
https://ramdajs.com/0.21.0/index.html
https://github.com/ramda/ramda
https://hackernoon.com/javascript-functional-composition-for-every-day-use-22421ef65a10
***
命令式代码
很多 JavaScript 都是命令式代码。如果你不知道这里的“命令式”是什么意思,那么可能会比较头疼。根据字典的定义,“命令式”是指:
表示一项命令;下达命令
如果将 JavaScript 代码写成命令式,则我们是在明确地告诉 JavaScript 做什么,如何执行。可以看做我们命令 JavaScript 明确执行哪些步骤。例如,下方的一个简单 for
循环:
const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
const excitedPeople = []
for (let i = 0; i < people.length; i++) {
excitedPeople[i] = people[i] + '!'
}
如果你以前接触过 JavaScript(无论多久),那么这段代码应该好理解。我们循环访问 people
数组中的每项,向他们的名称中添加一个感叹号,并将新字符串存储到 excitedPeople
数组中。很简单吧?
但这是命令式代码。我们命令 JavaScript 在每一步执行什么操作。我们需要给它下达命令:
- 为迭代器设定初始值 - (
let i = 0
) - 告诉
for
循环何时停止 - (i < people.length
) - 获得当前位置的用户并添加一个感叹号 - (
people[i] + '!'
) - 将
i
th 位置的数据存储到另一个数组中 - (excitedPeople[i]
) - 使
i
变量加一 - (i++
)
还记得使气温保持 22º 的示例吗?我会转动旋钮以吹出冷气。但是如果太冷的话,我就将旋钮拧的更高。最终,会变得太热,我需要再次将旋钮拧低些。我需要一点点地改变,自己管理温度。听起来像命令式情形吗?我需要手动执行多个步骤。不理想,因此改善下吧!
声明式代码
与命令式代码相反的是声明式代码。对于声明式代码,我们不编写所有步骤来获得最终结果。相反,我们声明要完成的操作,JavaScript 将帮助我们执行它。这种解释有点抽象,我们来看个示例。我们将刚刚查看的命令式 for
循环代码变得更加声明式。
对于命令式代码,我们执行所有步骤来获得最终结果。但是,我们实际上希望达到什么样的最终结果?起始点是名称数组:
const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
最终目标是名称保持不变,但是每个名称结尾处都有一个感叹号:
["Amanda!", "Geoff!", "Michael!", "Richard!", "Ryan!", "Tyler!"]
要从起点抵达终点,我们将使用 JavaScript 的 .map()
函数声明我们想要的结果。
const excitedPeople = people.map(name => name + '!')
就是这样!注意,对于这段代码,我们没有:
- 创建迭代器对象
- 告诉代码何时应该停止运行
- 使用迭代器访问
people
数组中的特定项 - 将每个新字符串存储在
excitedPeople
数组中
...所有这些步骤都由 JavaScript 的 .map()
数组方法来完成。
练习题
下面的代码是命令式还是声明式?
const people = ['Amanda', 'Geoff', 'Michael', 'Richard', 'Ryan', 'Tyler']
const longNames = people.filter(name => name.length > 6)
-
命令式
-
声明式
💡
.map()
和.filter()
💡已经快忘记 JavaScript 的
.map()
和.filter()
数组方法了?或者你就从未接触过这两个方法。无论何种情况,我们都将在 React “就是 JavaScript” 部分深入讲解这两个方法。继续学习吧!
React 是声明式
我们很快就会编写 React 代码,先再看一段示例,了解它为何是声明式。
<button onClick={activateTeleporter}>Activate Teleporter</button>
你可能觉得奇怪,但这段代码是有效的 React 代码,应该很容易理解。注意,按钮只有一个 onClick
属性... 我们没有使用 .addEventListener()
并利用所有涉及的步骤来设置事件处理过程。我们只是声明我们希望在按钮被点击时,运行 activateTeleporter
函数。
声明式代码总结
命令式代码告诉 JavaScript 如何执行每个步骤。对于声明式代码,我们告诉 JavaScript 我们希望实现什么结果,让 JavaScript 处理每个步骤。
React 是声明式代码,因为我们编写代码来声明我们想要什么,React 负责处理声明的代码,并执行所有的 JavaScript/DOM 步骤来实现我们期望的结果。
React 中的数据流总结
在 React 中,数据仅朝一个方向流动,即从父组件流向子组件。如果数据在兄弟子组件之间共享,那么数据应该存储在父组件中,并同时传递给需要数据的两个子组件。
就是 JavaScript
React 的优势之一是你要用到的很多功能都使用的是普通的 JavaScript。在过去几年内,函数式编程对 JavaScript 生态系统和社区产生了很大的影响。函数式编程是 JavaScript 中一个高级课题,涉及的内容用数百本书才能讲完。它太复杂了,以至于无法专研函数式编程的优势(我们还是要讲解 React 内容吧?)。React 基于的是大量的函数式编程的技巧…你将在这门课程中学习这些技巧。但是,有几个 JavaScript 函数对函数式编程来说非常重要,我们应该了解这些函数。这些函数包括 .map()
和 .filter()
方法。
我们详细了解下 .map()
和 .filter()
数组方法。
Array 的 .map()
方法
如果你不熟悉 JavaScript 的 Array .map()
方法 的话,其实它是在现有的数组上被调用,然后根据当做参数传入的函数返回的内容返回新的数组。我们看看:
const names = ['Michael', 'Ryan', 'Tyler'];
const nameLengths = names.map( name => name.length );
看看发生了什么情况。.map()
方法适用于数组,因此首先要有数组:
const names = ['Michael', 'Ryan', 'Tyler'];
我们针对 names
数组调用 .map()
并传入一个函数作为参数:
names.map( name => name.length );
names
数组中的每一项都会调用传递给 .map() 的箭头函数!箭头函数接收数组中的第一个名称,将其存储在变量 name
中,并返回其长度。然后对剩下的两个名称执行相同的操作。
.map()
返回新的数组,该新数组的每一项都是箭头函数所返回的值:
const nameLengths = names.map( name => name.length );
因此 nameLengths
将为新的数组 [7, 4, 5]
。一定要理解这一点;.map()
方法返回新的数组,它没有修改原始数组。
上面只是简单地介绍了 .map()
方法的运行原理。要深入了解该方法,请访问 MDN 上的 .map()
。
.map()
测验
使用提供的音乐数据数组和 .map()
方法创建一个新的数组,并包含以下格式的元素:
<album> by <name> sold <sales> copies
将新数组存储在 albumSalesStrings
数组中。因此 albumSalesStrings
数组中的第一项应该是 "25 by Adele sold 1731000 copies"
Map 方法测验解决方案代码
Array 的 .filter()
方法
JavaScript 的 Array .filter()
方法 和 .map()
方法相似:
- 在数组上被调用
- 传入函数作为参数
- 返回新的数组
区别在于传递给 .filter()
的函数用作检验条件,数组中只有通过检验的项目才会包含在新数组中。我们看一个示例:
const names = ['Michael', 'Ryan', 'Tyler'];
const shortNames = names.filter( name => name.length < 5 );
和之前一样,起始数组如下:
const names = ['Michael', 'Ryan', 'Tyler'];
我们对 names
数组调用 .filter()
并向其传递一个函数作为参数:
names.filter( name => name.length < 5 );
和 .map()
一样,names
数组中的每一项都会调用传递给 .filter() 的箭头函数。第一项(即 'Michael'
)存储在 name
变量中。然后进行检验,也就是进行实际的过滤操作。它会检查名称的长度,如果大于等于 5
,那么就跳过该名称(并且不包含在新数组中!)。但是,如果名称的长度小于 5
,那么 name.length < 5
返回 true
并且该名称包含在新数组中!
最后,和 .map()
一样,.filter()
方法返回新的数组,而不是修改原始数组:
const shortNames = names.filter( name => name.length < 5 );
因此 shortNames
将为新数组 ['Ryan']
。注意,现在它里面只有一个名称,因为 'Michael'
和 'Tyler'
都是 5
个字符或更长,被滤除了。
上面只是简单地介绍了 .filter()
方法的运行原理。要深入了解该方法,请访问 MDN 上的 .filter()
。
.filter()
测验
使用提供的音乐数据数组和 .filter()
方法创建一个新的数组,其中仅包含名称在 10
和 25
个字符之间的专辑。将新数组存储在变量 results
中。
Filter 测验解决方案代码
将 .map()
和 .filter()
组合到一起
.map()
和 .filter()
的如此强大之处在于它们可以组合到一起。因为两个方法都返回数组,因此我们可以将它们的方法调用链到一起,一个方法返回的数据可以是另一个方法的新数组。
const names = ['Michael', 'Ryan', 'Tyler'];
const shortNamesLengths = names.filter( name => name.length < 5 ).map( name => name.length );
详细讲解下,names
数组被过滤,并返回新的数组,然后对该新数组调用 .map()
,并再次返回新的数组!.map()
返回的新数组存储在 shortNamesLengths
中。
首先是 .filter()
!
提醒下,你需要按照一定的顺序组合二者(先是 .filter()
,然后是 .map()
)。因为 .map()
针对数组中的每项都调用一次函数,因此如果数组已经过滤过的话,运行速度会更快。
.filter()
和 .map()
测验
使用同一音乐数据和 .filter()
及 .map()
来过滤并映射列表,将结果存储在变量 popular
中。使用 .filter()
从列表中过滤出销量超过 1,000,000 张的专辑。然后对返回的数组调用 .map()
,并创建一个项目格式如下的新数组:
<artist> is a great performer
popular
数组中的第一项将为 'Adele is a great performer'
。
Filter & Map 测验解决方案代码
> React 使用 JavaScript 对象来创建 `React 元素`。我们将使用这些 React 元素来描述我们希望页面看起来如何,React 将负责`生成 DOM 节点`来达到效果。
还记得在上节课提到的命令式与声明式代码之间的区别吗?我们编写的 React 代码是声明式,因为我们没有告诉 React 执行什么操作;我们编写 React 元素来描述页面应该看起来怎么样,React 会执行所有的实现工作。
在 DOM 上渲染元素
在上个视频中,我们使用 ReactDOM 的 render()
方法将我们的元素渲染到页面的特定区域。具体而言,我们在叫做 root
的 DOM 节点上渲染了 element
。这个 root
来自哪里?
使用 React 构建的应用通常有一个 root
DOM 节点。例如,一个 HTML 文件可能包含以下 <div>
:
<div id='root'></div>
通过将该 DOM 节点传入 getElementById()
,React 将最终能够控制它的所有内容。另一种思考方法是这个 <div>
将充当我们 React 应用的“钩子”;React 将控制该区域并渲染我们的 UI!
> React 的 .createElement()
方法获得元素的说明并返回简单的 JavaScript 对象。
https://doc.react-china.org/docs/dom-elements.html#%E6%89%80%E6%9C%89%E5%8F%97%E6%94%AF%E6%8C%81%E7%9A%84html%E5%B1%9E%E6%80%A7
值得注意的一点是,你无法使用默认的 for
属性。就像你需要使用 className
而不是 class
,你需要使用 htmlFor
而不是 for
。这是因为 'for' 是 JavaScript 的保留词。
我刚刚使用 React 的 .createElement()
方法构建了一个 “React 元素”。.createElement()
方法具有以下方法:
React.createElement( /* type */, /* props */, /* content */ );
我们详细分析每一项参数可以为:
-
type
– 字符串或 React 组件可以是任何现有 HTML 元素字符串(例如
‘p’
、‘span’
或‘header’
),或者你可以传递 React 组件(稍后我们将使用 JSX 创建组件)。
-
props
– 为null
或一个对象这是 HTML 属性的对象以及关于该元素的自定义数据。
-
content
–null
、字符串、React 元素或 React 组件你在此处传递的任何内容都将为所渲染元素的内容。包括纯文本、JavaScript 代码、其他 React 元素等。
### unique key
.createElement()
返回一个根元素
React.createElement( /* type */, /* props */, /* content */ );
创建一个特定类型的 React 元素。我们通常会传入一个标签,例如 <div>
或 <span>
来表示该类型,但是内容参数可以是另一个 React 元素!
看看下面的示例:
const element = React.createElement('div', null,
React.createElement('strong', null, 'Hello world!')
);
这里,当此 React 元素渲染为 HTML 时,"Hello world!"将包裹在 <div>
内。虽然我们可以嵌套 React 元素,但是注意整个调用仅返回一个元素。
## JSX
无论是 JSX 还是普通的 JavaScript(使用 createElement()
),两个示例都生成了相同的 HTML:
JSX 也返回一个根元素
在编写 JSX 时,请记住,它只能返回一个元素。该元素可以有任何数量的子元素,但是只能有一个根元素封装整体 JSX (通常是一个 <div>
或 <span>
)。请看看下面的示例:
const message = (
<div>
<h1>All About JSX:</h1>
<ul>
<li>JSX</li>
<li>is</li>
<li>awesome!</li>
</ul>
</div>
);
注意到上述代码中只有一个 <div>
元素,其他所有 JSX 都嵌套在其中了吗?如果你想要多个元素,就应该这么编写。为了完全弄明白这一点,下面的示例不正确,将导致错误:
const message = (
<h1>All About JSX:</h1>
<ul>
<li>JSX</li>
<li>is</li>
<li>awesome!</li>
</ul>
);
在此示例中,我们有两个兄弟元素,它们都在根级别(即 <h1>
和 <ul>
)。这样是不可行的,会产生错误。
Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag
因为我们知道 JSX 只是 .createElement()
的语法扩展,因此是合理的;.createElement()
只获得一个标签名称(字符串)作为其第一个参数。
组件简介
到目前为止,我们已经看到 .createElement()
和 JSX 可以帮助我们生成一些 HTML。但是,通常我们将使用 React 的主要功能之一来构建 UI:组件。组件是指可以重复利用的代码段,最终负责返回要渲染到网页上的 HTML。很多时候,你将看到用 JSX 编写的 React 组件。
因为 React 的侧重点是简化应用的 UI 构建过程,因此在任何 React 组件类中,只有一个方法是必须的:render()
。
我们开始构建我们的首个组件类吧!
💡 在 React 中声明组件 💡
在上个视频中,我们按以下方式定义了
ContactList
组件:
class ContactList extends React.Component { // ... }
换句话说,我们定义了一个其实是 JavaScript 类并且继承自
React.Component
的组件。在实际使用中(以及这门课程中),你可能还会看到下面的声明:
class ContactList extends Component { // ... }
两种方法的功能是一样的,但是确保你的模块导入部分能匹配!即,如果你选择像第二个示例那样声明组件,则
React
导入应该如下所示:
import React, { Component } from 'react';
创建元素总结
最后,记住 React 仅关心应用的 View 层级。这是用户能看见和互动的层级。因此,我们可以使用 .createElement()
向文档中渲染 HTML。但是更多时候,你将使用语法扩展来描述 UI 的外观应该如何。这种语法扩展称之为 JSX,看起来和编写在 JavaScript 文件中的普通 HTML 很像。JSX 编译为调用 React 的 .createElement()
方法,并输出要在浏览器中渲染的 HTML。
在构建 React 应用时一个便利的思维方式是React理念|(翻译链接)。组件代表的是 React 的模块性和可重复利用性。可以将组件类看做生成组件实例的工厂。这些组件类应该遵守单一功能原则,只做一件事。如果管理太多不同的任务,建议将组件拆分为更小的子组件。
课外资料:
https://zh.wikipedia.org/wiki/%E5%8D%95%E4%B8%80%E5%8A%9F%E8%83%BD%E5%8E%9F%E5%88%99
搭建 React 应用框架
JSX 很棒,但是它需要反编译为普通的 JavaScript,然后才能提交给浏览器。我们通常使用 Babel 等转译器来为我们实现转译。我们可以通过构建工具运行 Babel,例如 Webpack,它可以帮助我们打包Web项目的所有资源(JavaScript 文件、CSS、图片等)。
为了简化这一初始配置过程,我们可以使用 Facebook 的 Create React App 包来为我们实现所有设置!该工具非常实用,可以帮助我们开始构建 React 应用,因为它为我们设置了一切,我们无需进行任何配置!请 (通过 npm命令行)安装 Create React App,然后我们将讲解它的强大之处。
npm install -g create-react-app
当你试图安装一个全局的 package 但是出现报错时,可以查看一下 这篇文章。注意,想要找出这些全局软件包安装在哪儿,可以在终端运行npm list -g
查看(更多信息这里)。
https://github.com/udacity/reactnd-contacts-complete/commit/86824dc9f726e68a516f8cf85243b013a3b5c8b7
https://github.com/udacity/reactnd-contacts-complete
Yarn Package Manager
在下面的视频和 create-react-app 输出中,我们都需要使用 yarn start
来启动开发服务器。如果你从未听说过 Yarn,它就是一个类似于 NPM 的软件包管理器。Yarn 完全由 Facebook 创建而成,旨在改善 NPM 比较缓慢或缺少的关键部分。
如果你不想安装 Yarn,也没问题!它的强大之处在于每个 yarn
使用情况都可以替换为 npm
,一切还是正常运行!因此,如果命令是 yarn start
,你可以使用 npm start
来运行同一命令。
观察力强的学员可能注意到了,我的
index.js
文件不包含你的项目中出现的registerServiceWorker();
这一行。在我录完此视频之后,Create React App 的新版本添加了对 Service Worker 的调用。因为我们在此项目中不使用 Service Workers,因此没有任何影响。但是如果你想删掉,也可以!
create-react-app
总结
Facebook 的 create-react-app
是一个可以帮助构建 React 应用的命令行工具。借助该工具,就无需配置 Webpack 等模块打包工具,或者 Babel 等转译器。它们使用 create-react-app
进行预配置(并且隐藏起来),使你能够立即构建应用!
请点击以下链接,详细了解 create-react-app
:
- GitHub 上的 create-react-app
- create-react-app 发行帖子 React 博客
- create-react-app 的更新 React 博客
demos
https://codepen.io/xgqfrms/full/XqvNrv/">https://codepen.io/xgqfrms/full/XqvNrv/
(🐞 反爬虫测试!打击盗版⚠️)如果你看到这个信息, 说明这是一篇剽窃的文章,请访问 https://www.cnblogs.com/xgqfrms/ 查看原创文章!
refs
©xgqfrms 2012-2021
www.cnblogs.com/xgqfrms 发布文章使用:只允许注册用户才可以访问!
原创文章,版权所有©️xgqfrms, 禁止转载 🈲️,侵权必究⚠️!
本文首发于博客园,作者:xgqfrms,原文链接:https://www.cnblogs.com/xgqfrms/p/5615534.html
未经授权禁止转载,违者必究!