文章目录

  • 1. JSX的本质: React.createElement这个 JavaScript 调用的语法糖
  • 2. 为什么要用 JSX: 在降低学习成本的同时,也提升了研发效率与研发体验
  • 3. JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?
    • 3.1 入参解读:创造一个元素需要知道哪些信息
    • 3.2 出参解读:初识虚拟 DOM
  • 4. 总结

本篇文章主要讲解 JSX的来龙去脉,不讲解 JSX的语法(不了解的可参考: JSX 简介)。如果你对以下三个问题能够清晰解答,本篇文章就可以不用看了。

  1. JSX 的本质是什么,它和 JS 之间到底是什么关系?
  2. 为什么要用 JSX?不用会有什么后果?
  3. JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?

如果不能清晰地回答,那希望看完本篇文章你能有所收获。

先举个小例子让你回忆一下JSX,以下代码中函数组件App中返回的部分即为JSX(<div className="App">Hello World!</div>):

import React from "react";
import ReactDOM from "react-dom";function App() {return <div className="App">Hello World!</div>;
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

1. JSX的本质: React.createElement这个 JavaScript 调用的语法糖

JSX的本质是JavaScript 的语法扩展。React官方对JSX的描述:

JSX is a syntax extension to JavaScript. It is similar to a template language, but it has full power of JavaScript.
JSX 是一个 JavaScript 语法扩展。它类似于模板语言,但它具有 JavaScript 的全部能力。

这个描述的意思是使用JSX语法可以扩展JavaScript 的功能:在JavaScript 中可以像写HTML一样来构建UI(原生JS是不具备这种能力的),但编译后最终其实还是纯JS代码。JSX 的定位是 JavaScript 的“扩展”,而非 JavaScript 的“某个版本”,所以浏览器并不会像天然支持 JavaScript 一样地支持 JSX。要使JSX在JavaScript中生效,我们需要借助Babel(Babel 是一个工具链,主要用于将 ECMAScript 2015+ 版本的代码转换为向后兼容的 JavaScript 语法,以便能够运行在当前和旧版本的浏览器或其他环境中。类似的,Babel 也具备将 JSX 语法转换为 JavaScript 代码的能力。)进行编译:JSX 会被编译为 React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。

如下一个Babel编译的例子:

可以看到,所有的 JSX 标签都被转化成了 React.createElement 调用,这也就意味着,我们写的 JSX 其实写的就是 React.createElement。

这里我们可以得出结论:JSX 的本质其实是React.createElement这个 JavaScript 调用的语法糖

2. 为什么要用 JSX: 在降低学习成本的同时,也提升了研发效率与研发体验

从上文Babel编译的例子中我们已经初步体会到使用JSX相对于直接使用JS编写代码的的简洁性差别,看起来好像还可以接受,那让我们看看稍微复杂点的例子:

可以很明显地看出,JSX 代码层次分明、嵌套关系清晰;而 React.createElement 代码则给人一种非常混乱的“杂糅感”,这样的代码不仅读起来不友好,写起来也费劲。实际项目中只会比例子更加复杂,相信没有人愿意用React.createElement的方式来编写吧?

这里我们可以得出结论:JSX 语法糖允许前端开发者使用我们最为熟悉的类 HTML 标签语法来创建虚拟 DOM,在降低学习成本的同时,也提升了研发效率与研发体验。

3. JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?

从上文我们已知所有的 JSX 标签最终都被转化成了 React.createElement 调用,那React.createElement这个函数到底做了些什么呢?让我们进入React的源码(版本17.0.1)一探究竟:

/*** 源码位置:packages\react\src\ReactElement.js* 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) { // 进来之后做的第一件事,是依次对 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;// 接着就是要把 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];}}}if (__DEV__) {// 这里是一些针对 __DEV__ 环境下的处理,暂时可以不用管}// 最后返回一个调用ReactElement执行方法,并传入刚才处理过的参数return ReactElement(type,key,ref,self,source,ReactCurrentOwner.current,props,);
}

上面源码不复杂,其实该函数并没有做很多复杂的事情,基本上是在进行格式化数据的操作,执行到最后会 return 一个针对 ReactElement 的调用。让我们通过一个流程图来更清晰地理解createElement的逻辑处理过程:

createElement 就像是开发者和 ReactElement 调用之间的一个“转换器”、一个数据处理层。它可以从开发者处接受相对简单的参数,然后将这些参数按照 ReactElement 的预期做一层格式化,最终通过调用 ReactElement 来实现元素的创建。整个过程如下图所示:

3.1 入参解读:创造一个元素需要知道哪些信息

export function createElement(type, config, children)

createElement函数包含3入参,分别如下:

  • type:用于标识节点的类型。它可以是类似h1div这样的标准 HTML 标签字符串,也可以是 React 组件类型或 React fragment 类型。
  • config:以对象形式传入,组件所有的属性都会以键值对的形式存储在 config 对象中。
  • children:以对象形式传入,它记录的是组件标签之间嵌套的内容,也就是所谓的“子节点”“子元素”。

3.2 出参解读:初识虚拟 DOM

从源码中我们可以看到出参是一个针对 ReactElement 的调用,让我们继续看看ReactElement 源码

const 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 其实只做了一件事情,那就是创建,说得更精确一点,是组装:ReactElement 把传入的参数按照一定的规范,“组装”进了 element 对象里,并把它返回给了 React.createElement,最终 React.createElement 又把它交回到了开发者手中。整个过程如下图所示:

我们可以将JSX打印出来看看,来验证一下:

import React from "react";
import ReactDOM from "react-dom";function App() {return <div className="App">Hello World!</div>;
}const myJSX = <div className="App">Hello World!</div>
console.log(myJSX)const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

结果如下:

这个 ReactElement 对象实例,本质上是以JavaScript 对象形式存在的对 DOM 的描述,也就是老生常谈的“虚拟 DOM”(准确地说,是虚拟 DOM 中的一个节点)。

既然是“虚拟 DOM”,那就意味着和渲染到页面上的真实 DOM 之间还有一些距离,这个“距离”,就是由大家喜闻乐见的ReactDOM.render方法来填补的。

在每一个 React 项目的入口文件中,都少不了对 ReactDOM.render 函数的调用。下面简单介绍下 ReactDOM.render 方法的入参规则:

ReactDOM.render(// 需要渲染的元素(ReactElement)element, // 元素挂载的目标容器(一个真实DOM)container,// 回调函数,可选参数,可以用来处理渲染结束后的逻辑[callback]
)

ReactDOM.render 方法可以接收 3 个参数,其中第二个参数就是一个真实的 DOM 节点,这个真实的 DOM 节点充当“容器”的角色,React 元素最终会被渲染到这个“容器”里面去。这个真实 DOM 一定是确实存在的。比如,在 App 组件对应的 index.html 中,已经提前预置 了 id 为 root 的根节点:

<body><div id="root"></div>
</body>

这里我们可以得出结论:JSX 背后的功能模块是什么React.createElement,该函数并没有做很多复杂的事情,基本上是在进行格式化数据的操作,执行到最后会 return 一个针对 ReactElement 的调用。

4. 总结

问题 回答
JSX 的本质是什么,它和 JS 之间到底是什么关系? JSX 的本质其实是React.createElement这个 JavaScript 调用的语法糖
为什么要用 JSX?不用会有什么后果? JSX 语法糖允许前端开发者使用我们最为熟悉的类 HTML 标签语法来创建虚拟 DOM,在降低学习成本的同时,也提升了研发效率与研发体验。
JSX 背后的功能模块是什么,这个功能模块都做了哪些事情? JSX 背后的功能模块是什么React.createElement,该函数并没有做很多复杂的事情,基本上是在进行格式化数据的操作,执行到最后会 return 一个针对 ReactElement 的调用

若对您有帮助,支持一下哦~

参考内容:
JSX
JSX 代码是如何“摇身一变”成为 DOM 的?

React中JSX背后的故事相关推荐

  1. React中JSX的理解

    React中JSX的理解 JSX是快速生成react元素的一种语法,实际是React.createElement(component, props, ...children)的语法糖,同时JSX也是J ...

  2. react中jsx行内样式(style)的国定写法、jsx双花括号{{}}写法的解释

    共index.js.index.html. TodoList.js这三个文件,主要看TodoList.js中的Input标签的style样式双花括号{{}}的写法,会在下方做全面的解释 运行效果: i ...

  3. 点评315:分众和中移动背后的故事

    点评315:分众和中移动背后的故事 今天315晚会,继去年大揭分众无线垃圾短信,今年直指垃圾短信最终责任人中国移动.在315晚会上已经报道过的事情我不在累述了,我说说当年分众无线背后的事情. 分众无线 ...

  4. React中JSX的用法和理解

    文章目录 React的特点 相关js库 虚拟DOM JSX(JavaScript XML) 使用jsx创建虚拟DOM 使用js创建虚拟DOM 区分js语句(代码)与js表达式 类 事件处理 高阶函数和 ...

  5. 歌谣学前端之React中jsx

    前言 我是歌谣 我有个兄弟 巅峰的时候排名c站总榜19 叫前端小歌谣 曾经我花了三年的时间创作了他 现在我要用五年的时间超越他 今天又是接近兄弟的一天人生难免坎坷 大不了从头再来 歌谣的意志是永恒的 ...

  6. react中 JSX介绍-基本使用

    JSX是什么 JSX:是 JavaScript XML的缩写. 在 JS 代码中书写 XML 结构 注意:JSX 不是标准的 JS 语法,是 JS 的语法扩展.脚手架中内置了 @babel/plugi ...

  7. React中jsx的规则

    jsx语法规则:1.定义虚拟DOM时,不要写引号.2.标签中混入JS表达式时要用{}.3.样式的类名指定不要用class,要用className.4.内联样式,要用style={{key:value} ...

  8. 歌谣学前端之React中jsx注意事项

    前言 我是歌谣 我有个兄弟 巅峰的时候排名c站总榜19 叫前端小歌谣 曾经我花了三年的时间创作了他 现在我要用五年的时间超越他 今天又是接近兄弟的一天人生难免坎坷 大不了从头再来 歌谣的意志是永恒的 ...

  9. react 中 JSX 语法的转化过程

    JSX 语法的转化过程 JSX 仅仅是 createElement() 方法的语法糖(简化语法) JSX 语法被 @babel/preset-react插件 编译为 createElement() 方 ...

最新文章

  1. 在CentOS 6.3 64bit上安装libunwind库
  2. 长知识啦!字符也可以作为下标!_只愿与一人十指紧扣_新浪博客
  3. 【最新合集】编译原理习题(含答案)_11-14中间代码生成_MOOC慕课 哈工大陈鄞
  4. 计算机视觉知识基础_我见你:计算机视觉基础知识
  5. 代码收藏——js+asp 的屏幕滚动脚本
  6. java 1.5.0 gcj_CentOS安装JAVA后JAVA版本不对的问题
  7. Linux查看系统各类信息
  8. C# String.Format格式说明
  9. 解决: Incorrect username or password, or no permission ( Docker 方式运行 Nexus3 登陆密码不为 admin123 、重置登陆密码)
  10. 系统架构师学习笔记-分布式系统
  11. 【学习随笔】iquery初涉
  12. paip.提升安全性----我们需要多长的密码
  13. java实现代码在线编译器-从零开发(一)简单本地编译+运行测试
  14. Oracle RMAN备份与还原
  15. Jsp和Servlet的关系(通俗易懂)
  16. spss分析qpcr数据_手把手教你使用 SPSS 分析实时荧光定量数据
  17. 前端机器人流程设计的最佳实践:输入输出文件结构和逻辑框架
  18. 天玑9200和骁龙8+哪个好 天玑9200和骁龙8+gen1对比
  19. android 通知栏设置,安卓手机通知栏介绍:安卓手机通知栏设置方法
  20. 七彩虹将星 X15 AT 2023 参数配置 七彩虹将星 X15 AT 评测

热门文章

  1. QT串口动态实时显示大量数据波形曲线(一)========“串口设置与ui界面添加(灯与按钮)”
  2. 李宏毅深度学习自用笔记(未完)
  3. 怎么下载没水印的谷歌卫星地图_谷歌卫星地图有水印怎么办
  4. JS文件位置对浏览器的影响
  5. burp Fiddler抓包软件
  6. 苹果市值蒸发超千亿美元;戴威称 ofo 不会倒闭;人人网被卖,多牛接盘 | 雷锋早报...
  7. Java OOP 第一章 封装
  8. arctan查表法_查表法
  9. 中国的高级软件工程师你们难道上网只看技术吗?悲哀啊。
  10. 用一个空置U盘(大白菜)PE重装系统