一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第4天,点击查看活动详情。
前言
大家好呀, 我是L同学。在上篇文章react基础(三)中,我们学习了如何在事件中传递参数,进行条件渲染,列表渲染等内容。学习了基本使用,接下来在这篇文章中,我们将学习jsx的本质,虚拟DOM等内容。
jsx的本质
我们先看一下jsx的基本使用。
<body>
<div id="app"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<script src="../react/babel.min.js"></script>
<script type="text/babel">
const message = <h2>hello react</h2>
ReactDOM.render(message, document.getElementById('app'))
</script>
</body>
所有的jsx都会借助babel最终转换成React.createElement的函数调用。所以说,jsx是React.createElement(component, props, ...children)函数的语法糖。
其中,React.createElement需要传递三个参数。
第一个参数: type。也就是当前ReactElement的类型。如果是标签元素,那么就是用字符串表示。在上面例子中,用'h2'这个字符串表示。如果是组件,那么就直接使用组件的名字。
第二个参数: config。在html元素中可以添加属性。所有在jsx中的属性都在config中以对象的属性和值的形式进行存储。
第三个参数: children。存放在标签中的内容,以children数组的方式进行存储。
上面说过,jsx最终会被babel转换为React.createElement的函数调用。我们可以在babel官网测试下。
我们可以在图中看到我们写得jsx代码最终做了转换。
那么我们接下来再做测试。我们不写jsx代码,直接写经过babel转换后的代码,会有问题吗?
此时我们就不需要babel了,把babel引入和type="text/babel"删掉。
<body>
<div id="app"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<!-- <script src="../react/babel.min.js"></script> -->
<script>
// const message = <h2>hello react</h2>
const message = React.createElement("h2", null, "hello react")
ReactDOM.render(message, document.getElementById('app'))
</script>
</body>
我们可以看到内容可以正常显示到页面上。
接下来我们来测试下组件的情况。
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
}
}
render() {
return (
<div>
<div className='header'>
<h1 title='标题'>我是标题</h1>
</div>
<div className='content'>
<h2>我是页面的内容</h2>
<button>按钮</button>
<button>+1</button>
</div>
<div className='footer'>
<p>我是尾部内容</p>
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('app'))
</script>
页面内容可以正常显示。
然后我们将babel转换后的代码放到render函数中。
<body>
<div id="app"></div>
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
<script src="../react/babel.min.js"></script>
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
}
}
render() {
return /*#__PURE__*/ React.createElement("div", null, /*#__PURE__*/React.createElement("div", {
className: "header"
}, /*#__PURE__*/React.createElement("h1", {
title: "\u6807\u9898"
}, "\u6211\u662F\u6807\u9898")), /*#__PURE__*/React.createElement("div", {
className: "content"
}, /*#__PURE__*/React.createElement("h2", null, "\u6211\u662F\u9875\u9762\u7684\u5185\u5BB9"), /*#__PURE__*/React.createElement("button", null, "\u6309\u94AE"), /*#__PURE__*/React.createElement("button", null, "+1")), /*#__PURE__*/React.createElement("div", {
className: "footer"
}, /*#__PURE__*/React.createElement("p", null, "\u6211\u662F\u5C3E\u90E8\u5185\u5BB9")));
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
</script>
</body>
我们可以看到页面还是可以正常的显示。
总结一下,所有的jsx都会借助babel最终转换成React.createElement的函数调用。所以说,jsx是React.createElement(component, props, ...children)函数的语法糖。
虚拟DOM的创建过程
我们知道,jsx最终会被转换为React.createElement函数。然后React.createElment会创建出来ReactElement对象。react会利用ReactElement对象组建一个javascript对象树,也就是虚拟DOM。
我们可以把jsx返回的结果打印出来。
<script type="text/babel">
class App extends React.Component {
constructor() {
super()
this.state = {
}
}
render() {
var elementObj = (
<div>
<div className='header'>
<h1 title='标题'>我是标题</h1>
</div>
<div className='content'>
<h2>我是页面的内容</h2>
<button>按钮</button>
<button>+1</button>
</div>
<div className='footer'>
<p>我是尾部内容</p>
</div>
</div>
)
console.log(elementObj);
return elementObj
}
}
ReactDOM.render(<App/>, document.getElementById('app'))
</script>
那么有了虚拟DOM,那么如何将虚拟DOM转换为真实DOM最终渲染到页面上呢?
实际上,ReactDOM.render函数会将虚拟DOM转换为真实DOM。
我们来总结下整个流程。
jsx -> React.createElement函数 -> ReactElement(对象树) -> ReactDOM.render -> 真实DOM
使用虚拟DOM的原因
我们为什么要使用虚拟DOM,而不是直接修改真实DOM呢?
这是因为真实DOM,一方面很难跟踪状态发生改变,另一方面,操作真实DOM性能较低。