为什么React 16中的Fragments比容器div更好?

在React 16.2中,增加了对Fragments的改进支持。更多信息可以在React的博客文章在这里。中找到

下面的代码大家都很熟悉:

render() {
return (
// Extraneous div element :(
<div>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</div>
);
}

是的,我们需要一个容器div,但这不是一个大的交易。

在React 16.2中,我们可以这样做来避免周围的容器div:

render() {
return (
<Fragment>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</Fragment>
);
}

无论哪种情况,我们仍然需要一个容器元素来包围内部元素。

我的问题是,为什么使用Fragment更可取?它对性能有帮助吗?如果有,为什么?想要一些见解。

61592 次浏览
  1. 它的速度稍微快一点,内存使用量也更少(不需要创建额外的DOM节点)。这只有在非常大和/或很深的树上才有真正的好处,但应用程序的性能往往会受到千刀万剐。这就少了一刀。
  2. 一些CSS机制,如Flexbox和CSS Grid具有特殊的父子关系,在中间添加divs使得在提取逻辑组件时很难保持所需的布局。
  3. DOM检查器不那么杂乱。: -)

你可以在这个React问题中找到一些其他用例的描述:添加片段API以允许从渲染中返回多个组件

  • 增加了以前JSX不可能的特性
  • 更好的语义jsx标记。包装器元素是在需要的时候使用的,而不是因为它们是被迫使用的。
  • 更少的整体dom标记(增加的渲染性能和更少的内存开销)

它很简单,当您不需要包装器元素时,您不必强制使用它。拥有更少的元素很好,但我认为最大的好处是能够在jsx中呈现以前不可能呈现的元素,并为包装器元素添加更好的语义,因为它们现在是可选的。

这在以前是不可能的:

 <select>
{this.renderOptions()}
</select>

在React 15中,你无法判断是否需要wrapper元素:

<span>
<h1>Hello</h1>
{this.getContent()}
</span>

除了以上所有答案之外,还有一个优点:代码的可读性Fragment组件支持语法糖形式<>。因此,您的问题中的代码可以更容易地编写为:

render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
);
}

根据文档

在React中,此糖化为<React.Fragment/>元素,如前一节的示例中所示。(使用JSX的非react框架可能会编译成不同的东西。)

整洁,对吧?

注意,如果你需要为片段提供key,你仍然需要使用<Fragment>语法。

根据reactjs.org 文档<Fragment> </Fragment>代替div最重要的需求是避免破坏HTML语义。当我们使用div而不是<Fragment> </Fragment>时,我们破坏了HTML语义。

了解更多html语义。请点击 也有情况下,如果你使用div而不是片段,它将是无效的html,例如,看看这段代码:

class Columns extends React.Component {
render() {
return (
<div>
<td>Hello</td>
<td>World</td>
</div>
);
}
}


<table>
<tr>
<div>
<td>Hello</td>
<td>World</td>
</div>
</tr>
</table>

Fragments解决了这个问题。

  1. 使用<React.Fragment>...</React.Fragment>,我们可以向JSX元素添加父标记,而无需向DOM添加额外的节点。
  2. 你可以用React替换额外的div标签。片段
  3. 写作的反应。每次碎片对你来说都太长了。反应。Fragment有一个可以使用的简写语法。它是<>...</>.

在使用React时,有时需要呈现多个元素或返回一组相关项。下面是一个例子:

function App() {
return (
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
);
}

如果你试图用上面的代码运行你的应用程序,你会遇到一个错误,说明相邻的JSX元素必须包装在一个封闭的标记中。这意味着您需要在父div中包装这两个元素。

function App() {
return (
<div>
<h1>Hello React!</h1>
<h1>Hello React Again!</h1>
</div>
);
}

这样做可以修复错误,但会带来一定程度的风险。您正在向DOM添加一个额外的节点,这是不必要的。在这种情况下,上面的子组件将被包含在父组件中,这就成了一个问题。

function Table() {
return (
<table>
<td>This is a Table Component</td>
<Columns />
</table>
);
}


function Columns() {
return (
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
);
}

Table组件的结果HTML将无效,因为添加了额外的div。

function Table() {
return (
<table>
<td>This is a Table Component</td>
<div>
<td>Hello React!</td>
<td>Hello React Again!</td>
</div>
</table>
);
}

让我们来看看使用React Fragment来解决这个问题的更好方法,它不会向DOM添加任何额外的节点。语法如下所示:

function Columns() {
return (
<React.Fragment>
<td>Hello React!</td>
<td>Hello React Again!</td>
</React.Fragment>
);
}

你也可以使用简短的语法<></>声明一个片段。

function Columns() {
return (
<>
<td>Hello React!</td>
<td>Hello React Again!</td>
</>
);
}

当您希望对组件进行分组,但不想在生成的HTML中使用div标签时,您可以使用fragment。为<> <p> Hello </p> </>生成的HTML就是:<p> Hello </p>

如果我们使用div容器,<div>…</div>也会被生成!