前言
最近在学习React过程中,对JSX的理解不是很清楚,只知道JSX是一种可以通过Bable转译成一个名为 React.createElement() 函数调用的JavaScript 语法扩展。还有为什么通过把一个 React 元素,和根 DOM 节点传入 ReactDOM.render(),就可以把这个React 元素渲染到根 DOM 节点中。小小的脑袋有大大的疑惑。
今天就来聊一下有关React中JSX写的东西如何从最开始浏览器不懂的语法,变身成为页面渲染的种子选手,从而在页面显示。
希望看完这篇文章的你也有所收获吧
✍️✍️✍️✍️✍️✍️✍️
首先来看看我们整篇文章的脉络:
1、JSX定义和使用
JSX 在react官方文档是这么定义的:
它被称为 JSX,是一个 JavaScript 的语法扩展。
好吧,这么直接说,有点子抽象。让我们先来看一段的代码:
<body>
<div id="app"></div>
//引入CDN 下面的代码都需要引入我就不一一注释啦
<script type="text/babel">
ReactDOM.render(
<h2>hello</h2>,
document.getElementById("app")
)
</script>
</body>
我们通过ReactDOM.render()
函数将<h2>hello</h2>
元素渲染到根节点中,当然我们也可以这样:
<script type="text/babel">
const message = <h2>hello</h2>
ReactDOM.render(
message,
document.getElementById("app")
)
</script>
我们把<h2>hello</h2>
提到函数外面去,使用定义的message
代替,页面可以正常显示。
其中的这句代码就是我们说的JSX代码
const message = <h2>hello</h2>
这个有趣的标签语法既不是字符串也不是 HTML。
注意这里有个小细节:就是我们这里的代码的script标签里都有一个type="text/babel"
,(当然在上面我们也引入了babel.min.js)如果没有这个属性,将会编译失败
这是因为我们的浏览器是不能直接识别JSX语法的,它必须通过Babel进行编译。
Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用。
下面让我们来介绍一下这个 React.createElement() 函数
2、JSX 是 React.createElement()的语法糖
这两句代码的x显示效果是一样的,只不过一种是使用JSX语法,但是它通过Bable编译也会变成下面的这个样子。
const message = <h2>hello</h2>
const message = React.createElement("h2",null,"hello");
那既然两种方式都可以实现,为什么还要使用JSX语法,这样不是多了一步Babel编译吗?
事实是这样,连react官网都说React 不强制要求使用 JSX
。但是,你看上面的代码是只有一行,所以两种方法没什么很大区别,当代码多了,一个标签内嵌套标签甚至多个标签,这里还不要说加组件了。很明显代码是非常多的,因为我们需要不断的调用React.createElement()方法。
让我们尝试写一个更复杂的代码,去理解:
render() {
return (
<div>
<div className="head1">
<h1>我是标题1</h1>
</div>
<div className="head1">
<h2>我是标题2</h2>
</div>
<div className="head3">
<h3>我是标题1</h3>
</div>
</div>
)
}
我们可以来到Babel官网将上面的代码编译一下
下面JSX代码通过Babel进行编译后的:
"use strict";
/*#__PURE__*/
React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "head1"
}, /*#__PURE__*/React.createElement("h1", null, "\u6211\u662F\u6807\u98981")), /*#__PURE__*/React.createElement("div", {
className: "head1"
}, /*#__PURE__*/React.createElement("h2", null, "\u6211\u662F\u6807\u98982")), /*#__PURE__*/React.createElement("div", {
className: "head3"
}, /*#__PURE__*/React.createElement("h3", null, "\u6211\u662F\u6807\u98981")));
可以看到,Babel 会把 JSX 转译成一个名为 React.createElement() 函数调用,(划重点,可以看到它的复杂性)
在我们刚才调用React.createElement中,这个函数接收三个参数
- type
当前reactElement的类型
如果是标签元素,就使用"div"
如果是组件元素,就直接用组件的名称 - config
所有jsx中的属性都在config中以对象的属性和值的形式存储 - children
存放在标签中的内容,以children数组的方式进行存储
如果是多个元素
而实际上这个函数调用完后会返回一个对象:
这里截取了源码生成ReactElement对象的一小部分代码作为参考:
让我们打印看一下我们上面举的这个例子,也就是我们的ReactElement
对象,长什么样:
到目前为止我们已经知道了,JSX语法的代码会通过Babel工具编译成一个名为 React.createElement()
函数调用,并且返回一个ReactElement
对象,那么这个对象有什么用呢,它又是如何渲染到我们的页面?
3、如何从虚拟DOM树到渲染页面
你可能会奇怪,前面刚说到返回一个ReactElement
对象,怎么就虚拟DOM树了。
在前端,用对象就可以表示一棵树了。不用怀疑,因为我们的html中DOM树就是这么来的:
上面我们返回的对象就是我们说的虚拟DOM树,想要渲染到根 DOM 节点中,只需传入 ReactDOM.render();
那么ReactDOM.render()又是如何将虚拟DOM树转换成真实DOM树呢?
这也是我不懂的地方,个人觉得还不是很懂,还是要先去摸索摸索了。
总结
最后,我们来总结一下整篇文章
实际上JSX 是React.createElement(component,props,...children)
函数的语法糖,所有JSX最终都会转换成React.createElement
的函数调用,而React.createElement
函数返回的就是一个ReactElement对象
,这个对象就是我们所说的虚拟DOM,而这个虚拟DOM会通过ReactDOM.render()
方法,挂载到我们同时传进去的根 DOM 节点中。
前端新人码字路过,肯定有不足或者写的不好的地方。欢迎大佬评论建议。万分感谢。