在 React 中使用 Fragment 或数组组件渲染子元素

Avatar of Kingsley Silas
Kingsley Silas

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 价值 200 美元的免费积分!

当谈到 React 16 时,您脑海中会浮现什么?ContextError 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>
    )
  }
}

…您必须在这些列表项上添加一个olul标签作为包装器

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 AbramovStackOverflow 上回答了这个问题。总结来说

  1. 它速度略快,内存使用量更少(无需创建额外的 DOM 节点)。这只有在非常庞大或非常深的树上才真正有用,但应用程序性能通常会因“死于一千刀”而受到影响。这是一个减少的“刀口”。
  2. 一些 CSS 机制(如 flexbox 和 grid)具有特殊的父子关系,在中间添加 div 会使在提取逻辑组件时更难维护设计。
  3. 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 APIError Boundary 功能一样,使用 Fragment 渲染子组件以及使用数组组件渲染多个子组件是您在构建应用程序时可以开始使用的两个很棒的功能。

您是否已在项目中开始使用这些功能?请在评论中告诉我您的使用经验,以便我们互相交流。🙂