当谈到 React 16 时,您脑海中会浮现什么?Context?Error Boundary?这些都是非常重要的。React 16 带着这些好东西以及更多功能而来,但在这篇文章中,我们将探讨它引入的渲染能力——即使用 Fragment 和数组组件渲染子元素的能力。
这些是 React 16 发布后出现的新概念,非常令人兴奋,让我们仔细看看它们,并更好地了解它们。
Fragment
过去,React 组件只能返回一个元素。如果您尝试返回多个元素,您将遇到此错误:Syntax error: Adjacent JSX elements must be wrapped in an enclosing tag
。解决方法是使用一个包装 div 或 span 元素作为封闭标签。
因此,与其这样做
class Countries extends React.Component {
render() {
return (
<li>Canada</li>
<li>Australia</li>
<li>Norway</li>
<li>Mexico</li>
)
}
}
…您必须在这些列表项上添加一个ol
或ul
标签作为包装器
class Countries extends React.Component {
render() {
return (
<ul>
<li>Canada</li>
<li>Australia</li>
<li>Norway</li>
<li>Mexico</li>
</ul>
)
}
}
大多数情况下,这可能不是您对应用程序的初始设计,但您别无选择,只能做出妥协。
React 16 通过Fragments解决了这个问题。这个新功能允许您包装一组子元素,而无需添加额外的节点。因此,与其像上一个示例中那样添加一个额外的元素作为包装器,我们可以使用<React.Fragment>
来完成这项工作
class Countries extends React.Component {
render() {
return (
<React.Fragment>
<li>Canada</li>
<li>Australia</li>
<li>Norway</li>
<li>Mexico</li>
</React.Fragment>
)
}
}
您可能会认为这没什么区别。但是,想象一下,您有一个组件,它列出了不同的物品,例如水果和其他东西。这些物品都是组件,如果您使用的是旧版本的 React,这些单个组件中的物品将必须用一个封闭标签包装起来。但是现在,您可以使用片段,并摆脱这些不必要的标记。
以下是一个示例,说明我的意思
class Items extends React.Component {
render() {
return (
<React.Fragment>
<Fruit />
<Beverages />
<Drinks />
</React.Fragment>
)
}
}
我们在片段中有三个子组件,现在可以为包装它的容器创建一个组件。这更符合将所有东西都创建为组件,并能够以更少的代码编译代码的理念。
Fragment 简写
使用 Fragments 时,有一个简写语法可以使用。为了保持其片段的本质,语法就像一个片段本身,只留下空括号。
回到我们上一个示例
class Fruit extends React.Component {
render() {
return (
<>
<li>Apple</li>
<li>Orange</li>
<li>Blueberry</li>
<li>Cherry</li>
</>
)
}
}
问题:Fragment 比容器 div 更好吗?
您可能正在寻找一个很好的理由来使用 Fragments,而不是您一直在使用的容器 div。Dan Abramov 在 StackOverflow 上回答了这个问题。总结来说
- 它速度略快,内存使用量更少(无需创建额外的 DOM 节点)。这只有在非常庞大或非常深的树上才真正有用,但应用程序性能通常会因“死于一千刀”而受到影响。这是一个减少的“刀口”。
- 一些 CSS 机制(如 flexbox 和 grid)具有特殊的父子关系,在中间添加 div 会使在提取逻辑组件时更难维护设计。
- DOM 检查器更整洁。
Fragment 中的键
当映射一个项目列表时,您仍然需要像以前一样使用键。例如,假设我们想要从父组件传递一个项目列表作为 props 到子组件。在子组件中,我们想遍历这个项目列表,并将每个项目输出为一个单独的实体。以下是实现方式
const preload = {
"data" : [
{
"name": "Reactjs",
"url": "https://reactjs.ac.cn",
"description": "A JavaScript library for building user interfaces",
},
{
"name": "Vuejs",
"url": "https://vuejs.ac.cn",
"description": "The Progressive JavaScript Framework",
},
{
"name": "Emberjs",
"url": "https://www.emberjs.com",
"description": "Ember.js is an open-source JavaScript web framework, based on the Model–view–viewmodel pattern"
}
]
}
const Frameworks = (props) => {
return (
<React.Fragment>
{props.items.data.map(item => (
<React.Fragment key={item.id}>
<h2>{item.name}</h2>
<p>{item.url}</p>
<p>{item.description}</p>
</React.Fragment>
))}
</React.Fragment>
)
}
const App = () => {
return (
<Frameworks items={preload} />
)
}
查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 创建的 React Fragment 笔记。
您可以看到,在这种情况下,我们没有在 Frameworks 组件中使用任何 div。这就是关键区别!
使用组件数组渲染子元素
React 16 推出的第二个关键功能是能够使用组件数组渲染多个子元素。这是一个明显的省时功能,因为它允许我们一次性将多个子元素塞入 render,而无需一个一个地进行操作。
以下是一个示例
class Frameworks extends React.Component {
render () {
return (
[
<p>JavaScript:</p>
<li>React</li>,
<li>Vuejs</li>,
<li>Angular</li>
]
)
}
}
您也可以对无状态函数组件执行相同的操作
const Frontend = () => {
return [
<h3>Front-End:</h3>,
<li>Reactjs</li>,
<li>Vuejs</li>
]
}
const Backend = () => {
return [
<h3>Back-End:</h3>,
<li>Express</li>,
<li>Restify</li>
]
}
const App = () => {
return [
<h2>JavaScript Tools</h2>,
<Frontend />,
<Backend />
]
}
查看 CodePen 上 Kingsley Silas Chijioke (@kinsomicrote) 创建的 React Fragment 2 笔记。
结论
与 React 16 中引入的 Context API 和 Error Boundary 功能一样,使用 Fragment 渲染子组件以及使用数组组件渲染多个子组件是您在构建应用程序时可以开始使用的两个很棒的功能。
您是否已在项目中开始使用这些功能?请在评论中告诉我您的使用经验,以便我们互相交流。🙂
使用子元素数组时,键 prop 怎么办?
警告:数组或迭代器中的每个子元素都应具有唯一的“key”prop。有关更多信息,请查看 https://fb.me/react-warning-keys。
在 App 中
我不确定我是否理解你的意思,但我认为以上示例中不会出现键问题,除非我们可能遍历一个数组列表。
感谢您的解释。您将如何将标签放入一个
<
ul>标签中?