前言

这次准备介绍关于react的两个新生开发过程中时不时会犯错或者不理解的两个小知识点:

1. React组件为什么需要用标签包裹?

2. React中return为什么加()?

React组件为什么需要用标签包裹?

看这样一段代码:

 1function App() {
 2  return (
 3    <h1>Hello CodeSandbox</h1>
 4    <h2>Start editing to see some magic happen!</h2>
 5  );
 6}
 7

结果报错了

错误:JSX元素必须被一个可闭合标签包裹

相信不少同学刚开始学习react的时候都编写过类似的代码,但大家有想过为什么会报这样的错吗?以及这样设计的目的是什么? 不了解也没有关系,接下来让我们一起逐步解密~

再此之前先来补充React.createElementJSX的关系

React.createElement与JSX

JSX是React.createElement的语法糖

一段常见的jsx代码

 1// 部分jsx
 2<header className="App-header">
 3  <h1>哈喽</h1>
 4</header>

jsx代码会被编译成React.createElement函数组成的js代码

在组件中,需要被渲染的内容是用React.createElement(component, props, …children) 声明的,而 JSX 正是React.createElement函数的语法糖,JSX 会被编译为 React.createElement()

但浏览器本身不支持 JSX,所以在应用发布上线前,JSX 源码需要工具编译成由若干createElement函数组成的 JS 代码,然后才能在浏览器中正常执行。JSX 需要被编译,而编译这个动作就是由 Babel 来完成的。

Babel编译成 JS 就会变成:

 1
 2React.createElement("header", {className: "App-header"},
 3  React.createElement("h1", null, "哈喽")
 4);

React.createElement的基本用法

React.createElement(component, props, …children) 接受至少三个参数:

  1. type: 字符串(代表 HTML 标签)或 React 组件(函数组件或类组件)。
  2. props: 对象,包含了该元素的属性。传递给元素的属性在组件中可通过 this.props 或 props 访问。
  3. …children: 其余参数都会作为子节点(children)处理。这可以是更多的元素,字符串,数字等。

单根节点思想

当React在处理渲染一个组件的时候,它需要每个组件返回一个单一的根节点。结合上述的代码,我们可以得到:

 1const element = (
 2<div>
 3  <h1>Hello CodeSandbox</h1>
 4  <h2 className='title'>Start editing to see some magic happen!</h2>
 5</div>
 6)
 7
 8
 9const element = React.createElement(
10'div',
11null,
12React.createElement('h1', null, 'Hello CodeSandbox'),
13React.createElement('h2', { className='title' }, 'Start editing to see some magic happen!')
14)
15

综上所述,前言中的报错是因为React.createElement设计时是以单节点为基础的,所以同时传入多个节点会发生报错。

为什么设计React.createElement时是以单节点为基础的?

接下来我想请大家跟我一起设想一个场景⬇️

首先,请大家抛弃react语法,用原生JS去思考这个问题。

现在有一个列表页,存在着3条数据,我点击了搜索,发现当前页面新增了4条数据,总共7条数据,这个时候你作为开发者,该怎么去更新dom呢?

这个时候你可能脱口而出,将这4条数据一条条塞进去,但大家应该都明白,每次操作dom的性能开销是很大的,需要进行dom插入(进行dom树的搜索) ,且会引起页面的回流重绘。如果进行多次操作,性能开销就会成倍增长。

现在你应该想到了,那我一次性把这4条数据的dom都塞进去,不就操作一次dom了吗?首先恭喜你回答正确,但可以再思考一下,我们怎么做到将4个dom一次性插入呢?原生js可没有类似的api支持你做到这一点,所以我们是不是需要将这多个dom合并成一个单一节点?这就是React的单节点思想

所以我们之所以用标签包裹react组件的重要原因。

React组件中return为什么加()?

react有这么一个规则:

例如:

 1function Component() {
 2  return 
 3    <div>{/*假设这行JSX语句很长*/}</div>;
 4}

放到编译器里会生成:

 1
 2function Component() {
 3  return;
 4  React.createElement("div", null);
 5}

发现了没有

这都还没走到 React.createElement就被return了

为了修正这个问题,我们需要为 JSX 加上括号:

 1function Component() {
 2  return (
 3    <div>{/*假设这行JSX语句很长,为了提升一些代码可读性,特地换行*/}</div>
 4  );
 5}

再次编译:

 1function Component() {
 2  return React.createElement("div", null);
 3}

这才是我们最终想要的效果了~

原因如下:

JavaScript会自动在每行的末尾添加分号,如果return语句后没有使用括号,就会在它的末尾自动插入一个分号(;),从而导致语法错误。通过使用括号,可以确保return语句作为一个整体被正确解析,避免因为自动插入分号而导致的bug。

个人笔记记录 2021 ~ 2025