React开发小点:组件为什么需要用标签包裹?return为什么加()?

1,190 阅读4分钟

React开发小点:组件为什么需要用标签包裹?return为什么加()?

前言

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

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

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

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

看这样一段代码:

function App() {
  return (
    <h1>Hello CodeSandbox</h1>
    <h2>Start editing to see some magic happen!</h2>
  );
}

结果报错了

image.png

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

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

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

React.createElement与JSX

JSX是React.createElement的语法糖

一段常见的jsx代码

// 部分jsx
<header className="App-header">
  <h1>哈喽</h1>
</header>

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

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

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

Babel编译成 JS 就会变成:


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

React.createElement的基本用法

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

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

单根节点思想

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

const element = (
<div>
  <h1>Hello CodeSandbox</h1>
  <h2 className='title'>Start editing to see some magic happen!</h2>
</div>
)

// 上述JSX最终会转化为⬇️
const element = React.createElement(
'div',
null,
React.createElement('h1', null, 'Hello CodeSandbox'),
React.createElement('h2', { className='title' }, 'Start editing to see some magic happen!')
)

综上所述,前言中的报错是因为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有这么一个规则:

例如:

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

放到编译器里会生成:


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

发现了没有

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

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

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

再次编译:

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

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

原因如下:

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