前言
- 它的本质是什么吗?运行中它会做如何的转换?
- jsx内部又是怎么生成了虚拟DOM?
- 虚拟DOM又是如何挂载到真实DOM上去的?
react中的jsx语法
- 简单的jsx语法, 就是在js中写html元素,前提是script标签必须加 type=text/babel, 否则jsx语法会报错
- jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖
const msg1 = <h2>哈哈哈</h2>
//在js中上面这段代码等价于下面这段react.createElement()的值
const msg2 = React.createElement("h2", null, "哈哈哈");
jsx语法在babel中会转成 React.createElement()的函数调用
注意:使用了React相关的方法一定先引入react.development.js 和react-dom.development.js这两个文件,否则会报错
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
babel转换
我们先用jsx语法, 开发页面。部分html代码省略,只写核心js代码
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
//babel.min文件 是将 jsx转成 React.createElement函数调用的
<script src="../react/babel.min.js"></script>
<script type='text/babel'>
// 这是jsx语法
const msg1 =
<div class="header">
<h2>头部</h2>
<div class="main">主题</div>
<footer>这里是尾部</footer>
</div>;
ReactDOM.render(msg1, document.getElementById("app"));
</script>
然后我们在把上面的jsx代码转成 React.createElement函数的形式
转换后的React.createElement函数调用代码到js中
<script src="../react/react.development.js"></script>
<script src="../react/react-dom.development.js"></script>
// type="text/babel" 可以去掉,babel.min.js 也可以不用了。
// 因为我们代码中没有了jsx语法了。下面的代码属于正常的js代码
<script>
const msg2 = React.createElement("div", {
class: "header"
}, React.createElement("h2", null, "\u5934\u90E8"), React.createElement("div", {
class: "main"
}, "\u4E3B\u9898"), React.createElement("footer", null, "\u8FD9\u91CC\u662F\u5C3E\u90E8"));
//React.reder 函数渲染到浏览器上
ReactDOM.render(msg2, document.getElementById("app"));
</script>
-
jsx语法和React.createElement函数的方法都可以得到相同的结果,渲染在页面的正是DOM也是一样的。
-
所以我们可以得出结论:jsx 仅仅只是 React.createElement(component, props, …children) 函数的语法糖
-
在真实开发中我们不会使用React.createElement函数的方式写代码,因为可读性太差了,代码量又多,难维护,我们更喜欢使用jsx语法来编写代码
-
React.render函数是怎样把createElement函数的返回值挂载到DOM上的呢?
虚拟DOM的创建过程
React.createElement 最终创建出来一个 ReactElement对象
在上面的React.createElement函数调用后,打印它的返回值
const msg2 = React.createElement("div", {
class: "header"
},React.createElement("h2", null, "\u5934\u90E8"), React.createElement("div", {
class: "main"
}, "\u4E3B\u9898"), React.createElement("footer", null, "\u8FD9\u91CC\u662F\u5C3E\u90E8"));
console.log(msg2);
ReactDOM.render(msg2, document.getElementById("app"));
-
msg2就是一个object对象,对象的第一层是html中的最外层class等于header的div。div里面的三个子元素,放在对象中的props对象中的children数组中一一对应。如果h2标签中还有子元素,那h2对象中的props对象中的children数组又会有objectd对象,… 这将一层一层的往下套
-
React利用ReactElement对象组成了一个JavaScript的对象树
-
msg2就是调用React.createElement函数时候,通过ReactElement函数转成一个JavaScript对象树的
-
在react源码下/packages/react/index.js文件找到了createElement函数,它是 ./src/React文件下导出的
-
找到react文件,发现它是 ./ReactElement文件下导出的
-
找到ReactElement文件,里面有一个createElement函数,在js中本质上就是在调用这个函数
-
它又调用了另一个ReactElement函数
-
这个ReactElement函数返回是一个object对象,这个对象就是我们在浏览器打印出来的那个msg2, 它就是javascript对象树
JavaScript的对象树就是大名鼎鼎的虚拟DOM
-
有了虚拟DOM,那怎么把虚拟DOM映射到真实的DOM上呢?
-
react是通过ReactDOM.render 函数把虚拟DOM挂载到真实DOM的
//把虚拟DOM挂载到 id为app的元素中
ReactDOM.render(msg2, document.getElementById("app"));
为什么要使用虚拟DOM
- 很难跟踪状态发生的改变:原有的开发模式,我们很难跟踪到状态发生的改变,不方便针对我们应用程序进行调试
- 操作真实DOM性能较低:传统的开发模式会进行频繁的DOM操作,而这一的做法性能非常的低