五、React 外围是组件
在 React 中,咱们应用组件(有状态、可组合、可重用)来形容 UI。
在任何编程语言中,你都能够将组件视为简略的函数。
React 组件也一样,它的输出是 props,输入是对于 UI 的形容。咱们能够在多个 UI 中重用单个组件,组件也能够蕴含其余组件。React 组件的实质上就是一个一般的 JavaScript 函数。
只管一些 React 组件是纯组件,但也能够在组件中引入副作用。例如,组件在浏览器中渲染时可能会更改网页的题目,或者可能会将浏览器视图滚动到某个地位。
最重要的是,React 组件能够领有一个公有状态来保留在组件生命周期内可能发生变化的数据。这个公有状态驱动组件输入到原生 DOM 中!
为什么将 React 称为响应式设计?
当 React 组件的状态(它是其输出的一部分)产生更改时,它所代表的 UI(其输入)也会产生更改。UI 形容中的这种变动必须反映在咱们正在应用的设施中。在浏览器中,咱们须要更新 DOM 树。在 React 应用程序中,咱们不会手动执行此操作。
state
更新时,React 主动响应,并在须要时主动(并无效)更新到 DOM 上。
六、函数组件
React 组件,最简略的模式就是 JavaScript 函数:
function Button (props) {
// 在这里返回一个 DOM / React 元素。例如:return <button type="submit">{props.label}</button>;
}
// 在浏览器中渲染一个 Button 元素
ReactDOM.render(<Button label="Save" />, mountNode);
咱们在 ReactDOM.render
中渲染 Button
组件,应用了相似 HTML 的款式,但它既不是 HTML,也不是 JS,甚至不是 React。这就是 JSX,它是 JavaScript 的扩大,容许咱们以相似于 HTML 的函数语法编写函数调用。
你能够尝试在 Button
函数内返回其余 HTML 元素,看看它们是如何被反对的(例如,返回 input
元素或 textarea
元素)。
1. JSX 不是 HTML
每个 JSX 元素只是调用 React.createElement(component, props, ...children)
的语法糖。因而,应用 JSX 能够实现的任何事件都能够通过纯 JS 实现。
上例就能够编写为不应用 JSX 的代码:
ReactDOM.render(
React.createElement(
Hello,
{name: 'Button'},
React.createElement(
'div',
null,
`Hello ${this.props.name}`,
)
),
document.getElementById('root'),
); // 在浏览器中渲染一个简略的 div 元素,显示 Hello Bottle
React.render
与 React.createElement
是 React 最外围的 API 办法,每一个 React 我的项目都必须要引入这两个 API。
浏览器不辨认 JSX。咱们在浏览器中运行 JSX,会报错:
所以,在我的项目中使用 JSX,咱们须要应用像 Babel 或 TypeScript 这样的转换器。例如,当 咱们应用 create-react-app 创立我的项目时,就会在外部应用 Babel 来转换我的项目中的 JSX。
JSX 基本上是一种折中,使咱们可能应用与 HTML 十分类似的语法,应用编译器将其转换为 React.createElement
调用,而不是间接应用 React.createElement
语法创立 React 组件。
React 组件是一个返回 React 元素的 JS 函数。当应用 JSX 时,<tag></tag>
语法会被转化为 React.createElement("tag")
。在创立 React 组件时应该牢记这一点。咱们不是在写 HTML,而切实应用 JS 扩大来创立 React 元素(实际上是 JS 对象)的函数调用。
因而,JSX 容许咱们类 HTML 的语法来示意 React 树,浏览器和 React 均不须要辨认它,只有编译器才有。咱们发送给浏览器的是无 JSX 代码。
相干 React 实战视频解说:进入学习
2. 命名必须以大写字母结尾
请留神咱们在下面例子中将组件命名为 Button
。第一个字母是大写字母,这是一个规定,因为咱们在解决混合的 HTML 元素和 React 元素时,JSX 编译器(如 Babel)会将所有以小写字母结尾的名称视为 HTML 元素。
- HTML 元素作为字符串传递给
React.createElement
调用 - React 元素须要作为变量传递
<button></button> // React.createElement("button", null)
<Button></Button> // React.createElement(Button, null)
再看一个例子:
function button () {return <div>My Fancy Button</div>;};
// 将会渲染一个 HTML button 组件
// 疏忽 React 组件 button
ReactDOM.render(<button />, mountNode);
3. 第一个参数是 props
的对象
就像能够为 HTML 元素传递 id
或 title
等属性一样,React 元素在渲染时也能够接管属性列表。例如,下面的 Button
元素就承受了 一个 label
属性。在 React 中,React 元素接管的属性列表称为 props
。
应用函数组件时,你不用将蕴含属性列表的对象命名为 props
,但这是规范做法。但当咱们应用类组件时,属性列表始终命名为 props
。
请留神,props
是可选的。有些组件能够没有 props
。然而,组件必须有返回值。React 组件不能返回 undefined
(显式或隐式)。它必须返回一个值。它能够返回 null
以使渲染器疏忽其输入。
每当我应用 props
(或 state
)时,我喜爱应用对象解构。例如,Button
组件函数能够应用 props
解构写法:
const Button = ({label}) => (<button type="submit">{label}</button>
);
这种办法有许多益处,但最重要的是看上去不便,并确保组件不会收到任何其余不须要的额定 props
。
留神我这里应用的是 箭头函数 而不是惯例 函数 。这只是我集体的一种格调 偏好 。有些人喜爱惯例函数,这没有任何问题。我认为重要的是要与你抉择的格调 保持一致。
4. JSX 中的表达式
你能够在 JSX 中的任何地位应用一对大括号来蕴含 JavaScript 表达式:
const RandomValue = () => (
<div>
{Math.floor(Math.random() * 100) } </div>
);
ReactDOM.render(<RandomValue />, mountNode);
请留神,只有表达式 能够蕴含在 {}
内。
例如,你不能蕴含惯例 if
语句,但三元表达式是能够的。任何有 返回值的 都是能够。
你能够在函数中放入任何代码,使它返回一些值,并在大括号内调用该函数。然而,尽量不要在 {}
内进行简单的逻辑操作。
JavaScript 变量也是表达式,因而当组件收到 props
时,你能够在 {}
应用 props
。这就是咱们为什么能在 Button
函数组件中应用 {label}
的起因。
JavaScript 对象也是表达式。咱们应用大括号内的 JavaScript 对象, 这使得它看起来像双大括号:{{a:42}}
。但这并不是一个不同的语法,它仅仅示意在惯例 JSX 括号内,应用对象而已。
例如,在这些 {}
中应用对象的一个用例是将 CSS 款式对象传递给 style
:
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
);
style
下面的属性是一个非凡的属性。React 将这些款式对象转换为内联 CSS 款式属性。当然,这不是设置 React 组件款式的最佳办法,但在条件款式中,应用它十分不便。例如,随机将输入的文本设为绿色或红色:
class ConditionalStyle extends React.Component {render() {
return (<div style={{ color: Math.random() < 0.5 ? 'green': 'red' }}>
How do you like this?
</div>
);
}
}
ReactDOM.render(
<ConditionalStyle />,
mountNode,
);
这比有条件地应用类名更容易应用。
5. JSX 不是模板语言
一些解决 HTML 的库为它提供了模板语言。应用具备循环和条件的 ” 加强 ”HTML 语法编写动静视图。而后,这些库应用 JavaScript 将模板转换为 DOM 操作。能够在浏览器中应用 DOM 操作来显示加强的 HTML 形容的 DOM 树。
React 勾销了那一步。咱们不会应用 React 应用程序向浏览器发送模板。咱们向它发送了一个用 React API 形容的对象树。React 应用这些对象生成显示所需 DOM 树的操作。
应用 HTML 模板时,库会将你的应用程序解析为字符串,React 应用程序被解析为对象树。
尽管 JSX 可能看起来像模板语言,但实际上并非如此。它只是一个 JavaScript 扩大,它容许咱们用一个看起来像 HTML 模板的语法来示意 React 的对象树。浏览器基本不须要解决 JSX,React 也不用解决它!只有编译器才有。咱们发送给浏览器的是无模板和无 JSX 代码。
例如,对于 todos
咱们下面看到的数组,如果咱们要应用模板语言在 UI 中显示该数组,咱们须要执行以下操作:
<ul>
<% FOR each todo in the list of todos %>
<li><%= todo.body %></li>
<% END FOR %>
</ul>
这 <% %>
是示意动静加强局部的一种语法。你可能还会看到 {{}}
语法。某些模板语言应用非凡属性来加强逻辑。一些模板语言应用空格缩进(off-side rule)。
当 todos
数组产生更改时(咱们须要应用模板语言更新 DOM 中出现的内容),咱们必须从新出现该模板或计算 DOM 树中咱们须要反映 todos
数组中更改的地位。
在 React 应用程序中,基本没有模板语言。相同,咱们应用 JSX:
<ul>
{todos.map(todo =>
<li>{todo.body}</li>
)}
</ul>
在浏览器中应用之前,它被转换为:
React.createElement(
"ul",
null,
todos.map(todo =>
React.createElement("li", null, todo.body)
),
);
React 获取这个对象树并将其转换为 DOM 元素树。从咱们的角度来看,咱们曾经实现了这棵树。咱们不治理任何口头。咱们只治理 todos
数组自身的操作。
七、class 组件
React 也反对通过 JavaScript class
语法创立组件。这是应用 class
编写的雷同 Button
组件示例:
class Button extends React.Component {render() {
return (<button>{this.props.label}</button>
);
}
}
// 雷同的语法应用它
ReactDOM.render(<Button label="Save" />, mountNode);
在此语法中,你定义了 Button
继承自 React.Component
,它是 React 顶级 API 中的次要类之一。基于类的 React 组件必须至多定义一个名为的实例办法 render
。此 render
办法返回示意从组件实例化的对象的输入的元素。每次咱们应用 Button
组件(通过渲染 <Button … />
)时,React 将从这个基于类的组件中 实例化 一个对象,并应用该对象来创立一个 DOM 元素。它还会将 DOM 出现的元素与它从类创立的实例相关联。
留神咱们在渲染的 JSX 中应用 this.props.label
的形式,每个组件有 props
属性,在组件实例化时,它蕴含传递给该组件元素的参数。与函数组件不同的是,class
组件中的 render
函数不接管任何参数。
八、函数与类
在 React 中应用函数组件是受限的。因为函数组件没有 state
状态。但在 React v16.8 引入 Hooks 之后就变得不同了,它能让组件在不应用 class
的状况下应用 state
以及其余的 React 个性,
我置信新的 API 会缓缓取代旧的 API,但这并不是我想激励你应用它的惟一起因。
我在大型应用程序中应用了这两个 API,我能够通知你,新 API 比旧 API 更优越的方面有很多,其中我认为这些是最重要的:
- 你不用应用
class
及其state
。你仅须要应用在每个渲染上刷新的简略函数。state
被明确申明,没有任何暗藏。所有这些基本上意味着你将在代码中遇到更少的惊喜。 - 你能够将相干的
state
逻辑分组,并将其分为独立的可组合和可共享单元。这使得咱们更容易将简单组件合成为更小的部件。它还使测试组件更容易。 - 你能够以申明形式应用任何有状态逻辑,而无需在组件树中应用任何分层“嵌套”。
尽管在可预感的将来,基于 class
的组件将持续成为 React 的一部分,但作为 React 开发人员,我认为开始应用函数(和 Hook),并专一于学习新 API 是有意义的。
1. 组件与元素
你可能会在 React 指南和教程中找到 component
和 element
这两个词。我认为 React 学习者须要了解重要的区别。
- React Component 是一个模板,蓝图,寰球定义。能够是函数或类(应用 render 办法)。
- React Element 是从组件返回的元素。它是与实在 DOM 绝对应的虚构节点。对于函数组件,此元素是函数返回的对象,对于类组件,元素是组件的
render
办法返回的对象。React 元素不是你在浏览器中看到的,它们只是内存中的对象,你无奈扭转它们。
React 在外部创立、更新和销毁对象,以找出须要渲染在浏览器的 DOM 元素树。应用类组件时,通常将其浏览器渲染的 DOM 元素称为组件实例。你能够渲染同一组件的许多实例。你不须要手动在类中创立实例,你只须要记住它就在 React 的内存中。对于函数组件,React 只应用函数的调用来确定要渲染的 DOM 实例。
九、组件的长处
术语 “ 组件 ” 被许多框架和库应用。咱们能够应用 HTML5 性能 (如自定义元素和 HTML 导入) 编写原生 Web 组件。
组件,无论咱们是在原生调用还是通过像 React 这样的库调用,都有许多长处。
首先,组件使你的代码更 易读,更易于应用。思考这个 UI:
<a href=”http://facebook.com”>
<img src=”facebook.png”/>
</a>
这个 UI 代表什么?如果你说 HTML,你能够在这里疾速解析并说“这是一个可点击的图像”。如果咱们要将这个 UI 转换成一个组件,咱们能够命名它 ClickableImage
!
<ClickableImage />
当事件变得更简单时,HTML 变得更加艰难,此时,组件容许咱们应用咱们相熟的语言疾速了解 UI 所代表的内容。这是一个更大的例子:
<TweetBox>
<TextAreaWithLimit limit="280" />
<RemainingCharacters />
<TweetButton />
</TweetBox>
在不查看理论 HTML 代码的状况下,咱们确切地晓得此 UI 示意的内容。此外,如果咱们须要批改残余字符局部的输入,咱们必须晓得确切要去哪里批改。
React 组件也能够在同一个应用程序中和多个应用程序中 重用。例如,以下是 ClickableImage
组件:
const ClickableImage = ({href, src}) => {
return (<a href={href}>
<img src={src} />
</a>
);
};
领有 href
和 src
属性的变量是使该组件可重用的起因。例如,要应用此组件,咱们能够应用一组 props
渲染它:
<ClickableImage href="http://google.com" src="google.png" />
咱们能够通过应用不同的 props
重用它:
<ClickableImage href="http://bing.com" src="bing.png" />
在函数式编程中,咱们有纯函数的概念。如果咱们给纯函数雷同的输出,咱们将始终取得雷同的输入。
如果 React 组件不依赖于其定义之外的任何内容,咱们也能够将该组件标记为纯组件。纯组件在没有任何问题的状况下更有可能被重用。
咱们能够将 HTML 元素视为浏览器中的内置组件。咱们也能够应用本人的自定义组件来 组成 更大的组件。例如,让咱们编写一个显示搜索引擎列表的组件。
const SearchEngines = () => {
return (
<div className="search-engines">
<ClickableImage href="http://google.com" src="google.png" />
<ClickableImage href="http://bing.com" src="bing.png" />
</div>
);
};
留神我是如何应用 ClickableImage
组件来组成 SearchEngines
组件的!
咱们还能够 SearchEngines
通过将数据提取到变量中并将其设计为应用该变量来使组件可重用。
例如,咱们能够采纳以下格局引入数据数组:
const data = [{ href: "http://google.com", src: "google.png"},
{href: "http://bing.com", src: "bing.png"},
{href: "http://yahoo.com", src: "yahoo.png"}
];
而后,为了 <SearchEngines data={data} />
能胜利渲染,咱们须要将 data
数组从对象列表映射到 ClickableImage
组件列表:
const SearchEngines = ({engines}) => {
return (
<List>
{engines.map(engine => <ClickableImage {...engine} />)} </List>
);
};
ReactDOM.render(<SearchEngines engines={data} />,
document.getElementById("mountNode")
);
SearchEngines
实用于咱们提供给它的任何搜索引擎列表。