- JSX 的本质是什么,它和 JS 之间到底是什么关系?
- 为什么要用 JSX?不用会有什么后果?
- JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?
JSX 的本质
本质:
JSX 的本质是 **React.createElement** 这个 JavaScript 调用的语法糖。
React 官网给出的一段定义:
JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。
JSX会被编译为React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。“编译”这个动作,是由
Babel来完成的,其具备将JSX语法转换为JavaScript代码的能力。

可以看到,每一个 JSX 标签都被转化成了 React.createElement 调用,每个 JSX 元素都是语法糖。
我们写的 JSX 其实写的就是 React.createElement,它只是看起来有点像 HTML。
React 选用 JSX 语法的动机
为什么不直接使用 React.createElement 来创建元素呢?
JSX代码层次分明、嵌套关系清晰;- 提升了研发效率与研发体验和美感
React.createElement代码则给人一种非常混乱的“杂糅感”,- 代码读起来不友好,写起来费劲。

JSX 是如何映射为 DOM
createElement
源码
/**React的创建元素方法*/export function createElement(type, config, children) {// propName 变量用于储存后面需要用到的元素属性let propName;// props 变量用于储存元素属性的键值对集合const props = {};// key、ref、self、source 均为 React 元素的属性,不必深究let key = null;let ref = null;let self = null;let source = null;// config 对象中存储的是元素的属性if (config != null) {// 1.进来之后做的第一件事,是依次对 ref、key、self 和 source 属性赋值if (hasValidRef(config)) {ref = config.ref;}// 此处将 key 值字符串化if (hasValidKey(config)) {key = '' + config.key;}self = config.__self === undefined ? null : config.__self;source = config.__source === undefined ? null : config.__source;// 2.把 config 里面的属性都一个一个挪到 props 这个之前声明好的对象里面for (propName in config) {if (// 筛选出可以提进 props 对象里的属性hasOwnProperty.call(config, propName) &&!RESERVED_PROPS.hasOwnProperty(propName)) {props[propName] = config[propName];}}}// childrenLength 指的是当前元素的子元素的个数,// 减去的 2 是 type 和 config 两个参数占用的长度const childrenLength = arguments.length - 2;// 如果抛去type和config,就只剩下一个参数,一般意味着文本节点出现了if (childrenLength === 1) {// 直接把这个参数的值赋给props.childrenprops.children = children;// 处理嵌套多个子元素的情况} else if (childrenLength > 1) {// 声明一个子元素数组const childArray = Array(childrenLength);// 把子元素推进数组里for (let i = 0; i < childrenLength; i++) {childArray[i] = arguments[i + 2];}// 最后把这个数组赋值给props.childrenprops.children = childArray;}// 处理 defaultPropsif (type && type.defaultProps) {const defaultProps = type.defaultProps;for (propName in defaultProps) {if (props[propName] === undefined) {props[propName] = defaultProps[propName];}}}// 最后返回一个调用ReactElement执行方法,并传入刚才处理过的参数return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);}
入参分析
function createElement(type, config, children)
其有 3 个入参,这 3 个入参囊括了 React 创建一个元素所需要知道的全部信息。
type:用于标识节点的类型。- 可以是类似
h1div这样的标准HTML标签字符串, - 也可以是
React 组件类型或React fragment类型。
- 可以是类似
config:以对象形式传入,组件所有的属性都会以键值对的形式存储在 config 对象中。children:以对象形式传入,它记录的是组件标签之间嵌套的内容,也就是所谓的“子节点”“子元素”。
用户定义的组件必须以大写字母开头**
- 小写字母开头的元素代表一个
HTML 内置组件,比如<div>或者<span>会生成相应的字符串'div'或者'span'传递给React.createElement(作为参数)。 - 大写字母开头的元素则对应着在 JavaScript 引入或自定义的组件,如
<Foo />会编译React.createElement(Foo)。 ```jsx import React from ‘react’;
// 错误!组件应该以大写字母开头:
function hello(props) {
// 正确!这种
return
<a name="4Qm7g"></a>#### 函数执行过程`createElement` 中并没有太复杂的涉及算法或真实 DOM 的逻辑,其每一个步骤几乎都是在**格式化数据**。- 它像是**开发者**和 **ReactElement** 调用之间的一个“**转换器**”、一个**数据处理层**,一个**参数中**介。1. 它从`开发者`处**接受**相对简单的**参数**,1. 然后将这些参数按照 `ReactElement` 的预期做一层**格式化,**1. 最终调用 `ReactElement` 来实现**元素的创建**。<a name="4xl6X"></a>### <br /><a name="VnG5f"></a>### ReactElement<a name="8qHB1"></a>#### 源码```javascriptconst ReactElement = function(type, key, ref, self, source, owner, props) {const element = {// REACT_ELEMENT_TYPE是一个常量,用来标识该对象是一个ReactElement$$typeof: REACT_ELEMENT_TYPE,// 内置属性赋值type: type,key: key,ref: ref,props: props,// 记录创造该元素的组件_owner: owner,};//if (__DEV__) {// 这里是一些针对 __DEV__ 环境下的处理}return element;};
执行过程
ReactElement把传入的参数按照一定的规范,组装进了element对象里,- 并把它返回给了
React.createElement, - 最终 React.createElement 又把它交回到了
开发者手中。
Element 对象实例
ReactElement``对象实例,本质上是以 JavaScript 对象 形式存在的对 DOM 的描述,即“虚拟 DOM”
当打印这一个Element时,结果如下:
const AppJSX = (<div className="App"><h1 className="title">I am the title</h1><p className="content">I am the content</p></div>)console.log(AppJSX)

ReactDOM.render
虚拟DOM 和 渲染到页面上的真实DOM 之间还有一些距离,这个“距离”,由ReactDOM.render方法来填补。
在每一个React 项目 的入口文件中,都少不了对 React.render 函数的调用,其入参规则如下:
ReactDOM.render(// 需要渲染的元素(ReactElement)element,// 元素挂载的目标容器(一个真实DOM)container,// 回调函数,可选参数,可以用来处理渲染结束后的逻辑[callback])
其接收 3 个参数,其中第二个参数就是一个真实的 DOM 节点,
- 这个真实的DOM 节点充当“容器”的角色,React 元素最终会被渲染到这个“容器”里面去。如下:
```javascript
const rootElement = document.getElementById(“root”);
ReactDOM.render(
, rootElement);
// 对应
```
