- 原文地址:Learning React.js is easier than you think
- 原文做者:Samer Buna
- 译文出自:掘金翻译计划
- 本文永久连接:github.com/xitu/gold-m…
- 译者:Cherry
- 校对者:LeviDing、undead25
你有没有注意到在 React 的 logo 中隐藏着一个六角星?只是顺便提下...
去年我写了一本简短的关于学习 React.js 的书,有 100 页左右。今年,我要挑战本身 —— 将其总结成一篇文章,并向 Medium 投稿。javascript
这篇文章不是讲什么是 React 或者 你该怎样学习 React。这是在面向那些已经熟悉了 JavaScript 和 DOM API 的人的 React.js 基本原理介绍html
本文采用嵌入式 jsComplete 代码段,因此为了方便阅读,你须要一个合适的屏幕宽度。前端
下面全部的代码都仅供参考。它们也纯粹是为了表达概念而提供的例子。它们中的大多数有更好的实践方式。java
您能够编辑和执行下面的任何代码段。使用 Ctrl+Enter 执行代码。每一段的右下角有一个点击后能够在 jsComplete/repl 进行全屏模式编辑或运行代码的连接。node
React 是围绕可重用组件的概念设计的。你定义小组件并将它们组合在一块儿造成更大的组件。react
不管大小,全部组件都是可重用的,甚至在不一样的项目中也是如此。android
React 组件最简单的形式,就是一个普通的 JavaScript 函数:ios
function Button (props) {
// 这里返回一个 DOM 元素,例如:
return <button type="submit">{props.label}</button>;
}
// 将按钮组件呈现给浏览器
ReactDOM.render(<Button label="Save" />, mountNode)复制代码
例 1:编辑上面的代码并按 Ctrl+Enter 键执行(译者注:译文暂时没有这个功能,请访问原文使用此功能,下同)git
括号中的 button 标签将稍后解释。如今不要担忧它们。
ReactDOM
也将稍后解释,但若是你想测试这个例子和全部接下来的例子,上述render
函数是必须的。(React 将要接管和控制的是ReactDOM.render
的第 2 个参数即目标 DOM 元素)。在 jsComplete REPL 中,你可使用特殊的变量mountNode
。github
例 1 的注意事项:
Button
。必需要这样作是由于咱们将处理 HTML 元素和 React 元素的混合。小写名称是为 HTML 元素保留的。事实上,将 React 组件命名为 “button” 而后你就会发现 ReactDOM 会忽略这个函数,仅仅是将其做为一个普通的空 HTML 按钮来渲染。上面的例 1 能够用没有 JSX 的纯 React.js 编写,以下:
function Button (props) {
return React.createElement(
"button",
{ type: "submit" },
props.label
);
}
// 要使用 Button,你能够这么作
ReactDOM.render(
React.createElement(Button, { label: "Save" }),
mountNode
);复制代码
例 2:不使用 JSX 编写 React 组件
在 React 顶级 API 中,createElement
函数是主函数。这是你须要学习的 7 个 API 中的 1 个。React 的 API 就是这么小。
就像 DOM 自身有一个 document.createElement 函数来建立一个由标签名指定的元素同样,React 的 createElement
函数是一个高级函数,有和 document.createElement
一样的功能,但它也能够用于建立一个表示 React 组件的元素。当咱们使用上面例 2 中的按钮组件时,咱们使用的是后者。
不像 document.createElement
,React 的 createElement
在接收第二个参数后,接收一个动态参数,它表示所建立元素的子元素。因此 createElement
实际上建立了一个树。
这里就是这样的一个例子:
const InputForm = React.createElement(
"form",
{ target: "_blank", action: "https://google.com/search" },
React.createElement("div", null, "Enter input and click Search"),
React.createElement("input", { className: "big-input" }),
React.createElement(Button, { label: "Search" })
);
// InputForm 使用 Button 组件,因此咱们须要这样作:
function Button (props) {
return React.createElement(
"button",
{ type: "submit" },
props.label
);
}
// 而后咱们能够经过 .render 方法直接使用 InputForm
ReactDOM.render(InputForm, mountNode);复制代码
例 3:React 建立元素的 API
上面例子中的一些事情值得注意:
InputForm
不是一个 React 组件;它仅仅是一个 React 元素。这就是为何咱们能够在 ReactDOM.render
中直接使用它而且能够在调用时不使用 <InputForm />
的缘由。React.createElement
调用,由于它是 JavaScript。className
代替 class
的缘由。咱们都但愿若是 React 的 API 成为 DOM API 自己的一部分,由于,你知道,它要好得多。上述的代码是当你引入 React 库的时候浏览器是怎样理解的。浏览器不会处理任何 JSX 业务。然而,咱们更喜欢看到和使用 HTML,而不是那些 createElement
调用(想象一下只使用 document.createElement
构建一个网站!)。这就是 JSX 存在的缘由。取代上述调用 React.createElement
的方式,咱们可使用一个很是简单相似于 HTML 的语法:
const InputForm =
<form target="_blank" action="https://google.com/search">
<div>Enter input and click Search</div>
<input className="big-input" name="q" />
<Button label="Search" />
</form>;
// InputForm “仍然”使用 Button 组件,因此咱们也须要这样。
// JXS 或者普通的表单都会这样作
function Button (props) {
// 这里返回一个 DOM 元素。例如:
return <button type="submit">{props.label}</button>;
}
// 而后咱们能够直接经过 .render 使用 InputForm
ReactDOM.render(InputForm, mountNode);复制代码
例 4:为何在 React 中 JSX 受欢迎(和例 3 相比)
注意上面的几件事:
className
代替 class
。咱们在上面(例 4)中写的就是 JSX。然而,咱们要将编译后的版本(例 3)给浏览器。要作到这一点,咱们须要使用一个预处理器将 JSX 版本转换为 React.createElement
版本。
这就是 JSX。这是一种折中的方案,容许咱们用相似 HTML 的语法来编写咱们的 React 组件,这是一个很好的方法。
“Flux” 在头部做为韵脚来使用,但它也是一个很是受欢迎的 应用架构,由 Facebook 推广。最出名的是 Redux,Flux 和 React 很是合适。
JSX,能够单独使用,不只仅适用于 React。
在 JSX 中,你能够在一对花括号内使用任何 JavaScript 表达式。
const RandomValue = () =>
<div>
{ Math.floor(Math.random() * 100) }
</div>;
// 使用:
ReactDOM.render(<RandomValue />, mountNode);复制代码
例 5:在 JSX 中使用 JavaScript 表达式
任何 JavaScript 表达式能够直接放在花括号中。这至关于在 JavaScript 中插入 ${}
模板。
这是 JSX 内惟一的约束:只有表达式。例如,你不能使用 if
语句,但三元表达式是能够的。
JavaScript 变量也是表达式,因此当组件接受属性列表时(不包括 RandomValue
组件,props
是可选择的),你能够在花括号里使用这些属性。咱们在上述(例 1)的 Button
组件是这样使用的。
JavaScript 对象也是表达式。有些时候咱们在花括号中使用 JavaScript 对象,这看起来像是使用了两个花括号,可是在花括号中确实只有一个对象。其中一个用例就是将 CSS 样式对象传递给响应中的特殊样式属性:
const ErrorDisplay = ({message}) =>
<div style={ { color: 'red', backgroundColor: 'yellow' } }>
{message}
</div>;
// 使用
ReactDOM.render(
<ErrorDisplay
message="These aren't the droids you're looking for"
/>,
mountNode
);复制代码
例 6:一个对象传递特殊的 React 样式参数
注意我解构的只是在属性参数以外的信息。这只是 JavaScript。还要注意上面的样式属性是一个特殊的属性(一样,它不是 HTML,它更接近 DOM API)。咱们使用一个对象做为样式属性的值而且这个对象定义样式就像咱们使用 JavaScript 那样(咱们能够这样作)。
你能够在 JSX 中使用 React 元素。由于这也是一个表达式(记住,一个 React 元素只是一个函数调用):
const MaybeError = ({errorMessage}) =>
<div>
{errorMessage && <ErrorDisplay message={errorMessage} />}
</div>;
// MaybeError 组件使用 ErrorDisplay 组件
const ErrorDisplay = ({message}) =>
<div style={ { color: 'red', backgroundColor: 'yellow' } }>
{message}
</div>;
// 如今咱们使用 MaybeError 组件:
ReactDOM.render(
<MaybeError
errorMessage={Math.random() > 0.5 ? 'Not good' : ''}
/>,
mountNode
);复制代码
例 7:一个 React 元素是一个能够经过 {} 使用的表达式
上述 MaybeError
组件只会在有 errorMessage
传入或者另外有一个空的 div
才会显示 ErrorDisplay
组件。React 认为 {true}
、 {false}
{undefined}
和 {null}
是有效元素,不呈现任何内容。
咱们也能够在 JSX 中使用全部的 JavaScript 的集合方法(map
、reduce
、filter
、 concat
等)。由于他们返回的也是表达式:
const Doubler = ({value=[1, 2, 3]}) =>
<div>
{value.map(e => e * 2)}
</div>;
// 使用下面内容
ReactDOM.render(<Doubler />, mountNode);复制代码
例 8:在 {} 中使用数组
请注意我是如何给出上述 value
属性的默认值的,由于这所有都只是 JavaScript。注意我只是在 div 中输出一个数组表达式。React 是彻底能够的。它只会在文本节点中放置每个加倍的值。
简单的函数组件很是适合简单的需求,可是有的时候咱们须要的更多。React 也支持经过使用 JavaScript 类来建立组件。这里 Button
组件(在例 1 中)就是使用类的语法编写的。
class Button extends React.Component {
render() {
return <button>{this.props.label}</button>;
}
}
// 使用(相同的语法)
ReactDOM.render(<Button label="Save" />, mountNode);复制代码
例 9:使用 JavaScript 类建立组件
类的语法是很是简单的:定义一个扩展的 React.Component
类(另外一个你须要学习的 React 的顶级 API)。该类定义了一个单一的实例函数 —— render()
,并使函数返回虚拟 DOM 对象。每一次咱们使用基于类的 Button
组件(例如,经过 <Button ... />
),React 将从这个基于类的组件中实例化对象,并在 DOM 树中使用该对象。
这就是为何上面的例子中咱们能够在 JSX 中使用 this.props.label
渲染输出的缘由,由于每个组件都有一个特殊的称为 props
的 实例 属性,这让全部的值传递给该组件时被实例化。
因为咱们有一个与组件的单个使用相关联的实例,因此咱们能够按照本身的意愿定制该实例。例如,咱们能够经过使用常规 JavaScript 构造函数来构造它:
class Button extends React.Component {
constructor(props) {
super(props);
this.id = Date.now();
}
render() {
return <button id={this.id}>{this.props.label}</button>;
}
}
// 使用
ReactDOM.render(<Button label="Save" />, mountNode);复制代码
例 10:自定义组件实例
咱们也能够定义类的原型而且在任何咱们但愿的地方使用,包括在返回的 JSX 输出的内部:
class Button extends React.Component {
clickCounter = 0;
handleClick = () => {
console.log(`Clicked: ${++this.clickCounter}`);
};
render() {
return (
<button id={this.id} onClick={this.handleClick}> {this.props.label} </button>
);
}
}
// 使用
ReactDOM.render(<Button label="Save" />, mountNode);复制代码
例 11:使用类的属性(经过单击保存按钮进行测试)
注意上述例 11 中的几件事情
handleClick
函数使用 JavaScript 新提出的 class-field syntax 语法。这仍然是 stage-2,可是这是访问组件安装实例(感谢箭头函数)最好的选择(由于不少缘由)。然而,你须要使用相似 Babel 的编译器解码为 stage-2(或者仅仅是类字段语法)来让上述代码工做。 jsComplete REPL 有预编译功能。// 错误:
onClick={this.handleClick()}
// 正确:
onClick={this.handleClick}复制代码
当处理 React 元素中的事件时,咱们与 DOM API 的处理方式有两个很是重要的区别:
onClick
而不是 onclick
。onClick={handleClick}
而不是 onClick="handleClick"
。React 用本身的对象包装 DOM 对象事件以优化事件处理的性能,可是在事件处理程序内部,咱们仍然能够访问 DOM 对象上全部能够访问的方法。React 将通过包装的事件对象传递给每一个调用函数。例如,为了防止表单提交默认提交操做,你能够这样作:
class Form extends React.Component {
handleSubmit = (event) => {
event.preventDefault();
console.log('Form submitted');
};
render() {
return (
<form onSubmit={this.handleSubmit}> <button type="submit">Submit</button> </form>
);
}
}
// 使用
ReactDOM.render(<Form />, mountNode);复制代码
例 12:使用包装过的对象
如下仅适用于类组件(扩展 React.Component
)。函数组件有一个稍微不一样的故事。
render
内部调用其余的组件,或者直接使用 ReactDOM.render
。this.props
访问。这些属性都是咱们在第 2 步传入的。constructor
方法将会被调用(若是定义的话)。这是咱们称之为的第一个:组件生命周期方法。componentDidMount
。咱们能够这样使用这个方法,例如:在 DOM 上作一些咱们如今知道的在浏览器中存在的东西。在今生命周期方法以前,咱们使用的 DOM 都是虚拟的。componentWillUnmount
。如下只适用于类组件。我有没有提到有人叫表象而已的部件 dumb?
状态类是任何 React 类组件中的一个特殊字段。React 检测每个组件状态的变化,可是为了 React 更加有效,咱们必须经过 React 的另外一个 API 改变状态字段,这就是咱们要学习的另外一个 API —— this.setState
:
class CounterButton extends React.Component {
state = {
clickCounter: 0,
currentTimestamp: new Date(),
};
handleClick = () => {
this.setState((prevState) => {
return { clickCounter: prevState.clickCounter + 1 };
});
};
componentDidMount() {
setInterval(() => {
this.setState({ currentTimestamp: new Date() })
}, 1000);
}
render() {
return (
<div> <button onClick={this.handleClick}>Click</button> <p>Clicked: {this.state.clickCounter}</p> <p>Time: {this.state.currentTimestamp.toLocaleString()}</p> </div>
);
}
}
// 使用
ReactDOM.render(<CounterButton />, mountNode);复制代码
例 13:setState 的 API
这多是最重要的一个例子由于这将是你彻底理解 React 基础知识的方式。这个例子以后,还有一些小事情须要学习,但从那时起主要是你和你的 JavaScript 技能。
让咱们来看一下例 13,从类开始,总共有两个,一个是一个初始化的有初始值为 0
的 clickCounter
对象和一个从 new Date()
开始的 currentTimestamp
。
另外一个类是 handleClick
函数,在渲染方法中咱们给按钮元素传入 onClick
事件。经过使用 setState
的 handleClick
方法修改了组件的实例状态。要注意到这一点。
另外一个咱们修改状态的地方是在一个内部的定时器,开始在内部的 componentDidMount
生命周期方法。它每秒钟调用一次而且执行另外一个函数调用 this.setState
。
在渲染方法中,咱们使用具备正常读语法的状态上的两个属性(没有专门的 API)。
如今,注意咱们更新状态使用两种不一样的方式:
handleClick
函数内部这样作。这两种方式都是能够接受的,可是当你同时读写状态时,第一种方法是首选的(咱们这样作)。在区间回调中,咱们只向状态写入而不读它。当有问题时,老是使用第一个函数做为参数语法。伴随着竞争条件这更安全,由于 setstate
其实是一个异步方法。
咱们应该怎样更新状态呢?咱们返回一个有咱们想要更新的的值的对象。注意,在调用 setState
时,咱们所有都从状态中传入一个属性或者全都不。这彻底是能够的由于 setState
实际上 合并 了你经过它(返回值的函数参数)与现有的状态,因此,没有指定一个属性在调用 setState
时意味着咱们不但愿改变属性(但不删除它)。
React 的名字是从状态改变的反应中得来的(虽然没有反应,但也是在一个时间表中)。这里有一个笑话,React 应该被命名为Schedule!
然而,当任何组件的状态被更新时,咱们用肉眼观察到的是对该更新的反应,并自动反映了浏览器 DOM 中的更新(若是须要的话)。
将渲染函数的输入视为两种:
当渲染函数的输入改变时,输出可能也会改变。
React 保存了渲染的历史记录,当它看到一个渲染与前一个不一样时,它将计算它们之间的差别,并将其有效地转换为在 DOM 中执行的实际 DOM 操做。
您能够将 React 看做是咱们用来与浏览器通讯的代理。以上面的当前时间戳显示为例。取代每一秒咱们都须要手动去浏览器调用 DOM API 操做来查找和更新 p#timestamp
元素,咱们仅仅改变组件的状态属性,React 作的工做表明咱们与浏览器的通讯。我相信这就是为何 React 这么受欢迎的真正缘由;咱们只是不喜欢和浏览器先生谈话(以及它所说的 DOM 语言的不少方言),而且 React 自愿传递给咱们,免费的!
如今咱们知道了一个组件的状态,当该状态发生变化的时候,咱们来了解一下关于这个过程的最后几个概念。
componentWillReceiveProps
。shouldComponentUpdate
的缘由 。此方法是一个实际问题,所以,若是须要自行定制或优化渲染过程,则必须经过返回 true 或 false 来回答这个问题。shouldComponentUpdate
,React 的默认事件在大多数状况下都能处理的很好。componentWillUpdate
。而后,React 将计算新渲染过的输出,并将其与最后渲染的输出进行对比。componentDidUpdate
。生命周期方法是逃生舱口。若是你没有作什么特别的事情,你能够在没有它们的状况下建立完整的应用程序。它们很是方便地分析应用程序中正在发生的事情,并进一步优化 React 更新的性能。
信不信由你,经过上面所学的知识(或部分知识),你能够开始建立一些有趣的 React 应用程序。若是你渴望更多,看看个人 Pluralsight 的 React.js 入门课程。
感谢阅读。若是您以为这篇文章有帮助,请点击下面的 💚。请关注个人更多关于 React.js 和 JavaScript 的文章。
我 Pluralsight 和 Lynda 建立了在线课程。我最新的文章在Advanced React.js、 Advanced Node.js 和 Learning Full-stack JavaScript中。我也作小组的在线和现场培训,覆盖初级到高级的 JavaScript、 Node.js、 React.js、GraphQL。若是你须要一个导师,请来找我 。若是你对此篇文章或者我写的其余任何文章有疑问,经过这个联系我,而且在 #questions 中提问。
感谢不少检验和改进这篇文章的读者,Łukasz Szewczak、Tim Broyles、 Kyle Holden、 Robert Axelse、 Bruce Lane、Irvin Waldman 和 Amie Wilt.
特别要感谢“惊人的” Amie,经验是一个实际的 Unicorn。谢谢你全部的帮助,Anime,真的很是感谢你。
掘金翻译计划 是一个翻译优质互联网技术文章的社区,文章来源为 掘金 上的英文分享文章。内容覆盖 Android、iOS、React、前端、后端、产品、设计 等领域,想要查看更多优质译文请持续关注 掘金翻译计划、官方微博、知乎专栏。