React v16.2.0: Improved Support for Fragments
React 16.2 现在已经发布了!最大的增加是提高了对子组件的渲染方法返回多个子元素的支持。我们称这一特性称为 fragments:
Fragment 看起来像是空的 JSX 标签。他们能够让 let you group a list of children 而无需添加额外的节点到 DOM 树上:
render() {
return (
<>
<ChildA />
<ChildB />
<ChildC />
</>
);
}
这一令人激动的新特性能够通过 React 和 JSX 添加来实现。
什么是 Fragments? {#what-are-fragments}
对于一个组件来说,普遍模式是返回一个列表的子组件。用 HTML 展示这个例子的如下:
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
React 16 之前的版本,React 实现这一功能的唯一方式是通过用一个额外的元素来包装子节点,通常使用一个 div
或 span
:
render() {
return (
// Extraneous div element :(
<div>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</div>
);
}
为了处理这一限制,React 16.0 增加了对组件的 render
方法返回一个包含元素的数组 的支持。你可以将其他们放进数组里,而不用将子元素包装在一个 DOM 元素内:
render() {
return [
"Some text.",
<h2 key="heading-1">A heading</h2>,
"More text.",
<h2 key="heading-2">Another heading</h2>,
"Even more text."
];
}
然而,这和普通 JSX 有一些令人混淆的区别:
- 数组中的子元素必须用逗号分隔,
- 数组中的子元素必须有一个唯一的 key 以防止 React 的 key 警告。
- 字符串必须包括在引号内。
为了对 fragment 提供更好的一致性开发体验,React 现在提供了第一等的 Fragment
组件以可以用来替换数组。
render() {
return (
<Fragment>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</Fragment>
);
}
你可以像使用其他任何元素一样使用 <Fragment />
,而不用改变你写 JSX 的方式。没有逗号,没有 key,没有引号。
Fragment 能够通过 React 对象进行使用:
const Fragment = React.Fragment;
<Fragment>
<ChildA />
<ChildB />
<ChildC />
</Fragment>
// This also works
<React.Fragment>
<ChildA />
<ChildB />
<ChildC />
</React.Fragment>
JSX Fragment 语法 {#jsx-fragment-syntax}
在 Facebook, 片段(Fragments)在我们的代码库中是一个通用模式。我们期望也能被其他团队广泛采用。为了保证开发体验尽可能地便利,我们为 JSX 增加了 fragment 的语法支持:
render() {
return (
<>
Some text.
<h2>A heading</h2>
More text.
<h2>Another heading</h2>
Even more text.
</>
);
}
React 中,类似于之前一节展示的例子,是 <React.Fragment />
的语法糖。(使用 JSX 的非 React 框架可能在编译时有些差异。)
JSX 里的 Fragments 语法受到了来自之前技术如 E4X 中的 XMLList() <></>
构造函数的激励。使用一对空标签其意味不用添加了一个实际的元素到 DOM 结构中。
带有 key 属性的 Fragments {#keyed-fragments}
注意 <></>
语法不接受属性,包括 key。
若你需要一个带有 key 属性的 fragment,你可以直接使用 <Fragment />
。一个例子是将一个集合映射到一个片段的数组 — 例如,创建一个列表:
function Glossary(props) {
return (
<dl>
{props.items.map(item => (
// Without the `key`, React will fire a key warning
<Fragment key={item.id}>
<dt>{item.term}</dt>
<dd>{item.description}</dd>
</Fragment>
)}
</dl>
);
}
key
是唯一一个能够传递给 Fragment
的属性。未来,我们可能还会增加额外属性,如事件处理。
在线演示 {#live-demo}
你可以在 CodePen 体验 JSX fragment。
Fragment 语法支持 {#support-for-fragment-syntax}
在 JSX 支持 fragment 语法将取决你用于构建应用的工具。请对 JSX 社区致力于采用新语法保持些耐心。我们也在和大多数流行项目的维护者紧密的合作:
Create React App {#create-react-app}
试验性支持的 fragment 语法将会在接下来几天中加入到 Create React App。稳定版的发布可能需要更长的时间,由于我们需要等到上游项目的采纳。
Babel {#babel}
对于 JSX fragment 已经在 Babel v7.0.0-beta.31 及以上版本中得到支持!若你已经在使用 Babel 7,很容易将其升级到最新版本的 Babel 和 transform 插件(plugin transform):
# for yarn users
yarn upgrade @babel/core @babel/plugin-transform-react-jsx
# for npm users
npm update @babel/core @babel/plugin-transform-react-jsx
或若你正在使用 react preset:
# for yarn users
yarn upgrade @babel/core @babel/preset-react
# for npm users
npm update @babel/core @babel/preset-react
注意 Babel 7 目前技术上仍在 beta 阶段,但稳定版本将很快发布。
不幸的是,目前仍然还没有对 Babel 6.x 进行支持,同时目前也没有计划反向移植到旧版中。
Babel 以及 Webpack (babel-loader){#babel-with-webpack-babel-loader}
如果你在使用 Babel 和 Webpack,则不需要额外的步骤,因为 babel-loader 将会使用你的同等安装的 Babel 版本。
Babel 及其他框架 {#babel-with-other-frameworks}
如果你在使用 JSX 及非 React 框架如 Inferno 或 Preact,在 babel-plugin-transform-react-jsx 中有一个编译选项 能够配置 Babel 编译器使其能够将 <></>
这一语法糖编译为一个自定义标签。
TypeScript {#typescript}
TypeScript 已经完全支持 fragment 语法!请升级到 2.6.2 版本。(注意这非常重要,即使你已经升级到了 2.6.1 版本,由于这一支持是作为补丁发布在 2.6.2 上的。)
使用如下命令升级到最新版本的 TypeScript:
# for yarn users
yarn upgrade typescript
# for npm users
npm update typescript
Flow {#flow}
Flow 对 JSX fragment 的语法支持从 0.59 版本 开始!简单运行
# for yarn users
yarn upgrade flow-bin
# for npm users
npm update flow-bin
以升级到最新版本的 Flow。
Prettier {#prettier}
Prettier 将在他们即将发布的 1.9 版本中支持 fragment 语法。
ESLint {#eslint}
当其和 babel-eslint 一同使用时,ESLint 3.x 支持 JSX fragment 语法:
# for yarn users
yarn add eslint@3.x babel-eslint@7
# for npm users
npm install eslint@3.x babel-eslint@7
若你已经在使用,则将其升级:
# for yarn users
yarn upgrade eslint@3.x babel-eslint@7
# for npm users
npm update eslint@3.x babel-eslint@7
确保将下面这行代码添加进你的 .eslintrc.js
文件:
"parser": "babel-eslint"
仅此而已!
注意 babel-eslint
并非由 ESLint 官方支持。我们将在未来几周寻求将对 fragment 的支持添加到 ESLint 4.x 上。
编辑器支持 {#editor-support}
其可能会花费些时间来让你的文本编辑器支持 fragment 语法。请对社区采纳这一最新的变更保持些耐心。同时,如果你的编辑器仍未支持 fragment 语法,你可能会看到些错误或异常的高亮语法。通常来讲,这些错误可以被安全地忽略。
TypeScript 编辑器支持 {#typescript-editor-support}
如果你是一位 TypeScript 用户 — 好消息!Visual Studio 2015,Visual Studio 2017 以及 Sublime Text via Package Control 已经支持了 JSX fragment。Visual Studio Code 也将很快进行更新,但需要配置其 TypeScript 版本为 2.6.2 及之后。
其他工具 {#other-tools}
对于其他工具,请检查对应的文档以查看其是否支持。然而,如果你被你的工具所阻碍,你可以先采用 <Fragment>
组件并在工具能够正确支持之后用 codemod 将其进行替换为缩写语法。
安装 {#installation}
React v16.2.0 已经发布到 npm 仓库上。
通过 Yarn 安装 React 16,运行:
yarn add react@^16.2.0 react-dom@^16.2.0
通过 npm 安装 React 16,运行:
npm install --save react@^16.2.0 react-dom@^16.2.0
我们也通过 CDN 提供了一个 UMD 构建的 React 版本:
<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>
查阅文档了解安装细节说明。
变更日志 {#changelog}
React {#react}
- 为 React 添加名为
Fragment
的导出(export)对象。(@clemmy 在 #10783 提的 PR) - 在
React.Children
的工具集中增加试验性的 Call/Return 类型。(@MatteoVH 在 #11422 提的 PR)
React DOM {#react-dom}
- 修复当使用多列单选按钮,单选按钮无法被选中。(@landvibe 在 #11227 提的 PR)
- 修复单选按钮在某些情况下无法接受到
onChange
事件。(@jquense 在 #11028 提的 PR)
React Test Renderer {#react-test-renderer}
- 修复当在
componentWillMount
调用setState()
时,其回调函数过早调用的问题。(@accordeiro 在 #11507 提的 PR)
React Reconciler {#react-reconciler}
内部变更 {#internal-changes}
- 许多公开的 API 测试用例被重写。非常感谢每一位的贡献!
感谢 {#acknowledgments}
本次发布由我们的开源贡献者完成。非常感谢提交问题的人,对语法讨论的贡献和审阅 PR,在第三方库中增加 JSX fragment 支持等等!
尤为感谢 TypeScript 和 Flow 团队,以及 Babel 的维护者们,帮助为新语法在工具上提供了无缝支持。
感谢 Gajus Kuizinas 和其他贡献者在开源社区提出了 Fragment
的原型。