经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » JS/JS库/框架 » React » 查看文章
JSX 代码是如何“摇身一变”成为 DOM 的?
来源:cnblogs  作者:前端旧约  时间:2023/12/5 12:07:38  对本文有异议

JSX 是一种语法,并不是 React 中的内容,时下接入 JSX 语法的框架越来越多,但与之缘分最深的仍然是 React。本节来讲一下 React 是如何摇身一变成为 DOM 的。

我们平时在写React时会用 JSX 来描述组件的内容,例如下面的代码中,render 方法 return 的内容就是 JSX 代码。

  1. class App extends React.Component {
  2. render() {
  3. return (
  4. <div className="App">
  5. <h1 className="title">I am the title</h1>
  6. <p className="content">I am the content</p>
  7. </div>
  8. );
  9. }
  10. }

我们考虑以下三个问题:

  1. JSX 的本质是什么,它和 JS 之间到底是什么关系?
  2. React 为什么要用 JSX?
  3. JSX 是如何映射为 DOM 的?

这一节我们就将这三个问题一一解答。

1)JSX 的本质是什么?它和JS之间的到底是什么关系?

JSX 到底是什么,我们先来看看 React 官网给出的一段定义:

JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力。

“语法扩展”这一点在理解上几乎不会产生歧义,不过“它充分具备 JavaScript 的能力”这句,却总让人摸不着头脑,JSX 和 JS 怎么看也不像是一路人啊?这就引出了“JSX 语法是如何在 JavaScript 中生效的”这个问题。

JSX 是 JavaScript 的扩展,而不是 JavaScript 的某个版本,因此浏览器并不会天然支持,那么 JSX 是如何在 JavaScript 中生效的呢?

React 官网是这样的解释的:

JSX 会被编译为 React.createElement(), React.createElement() 将返回一个叫作“React Element”的 JS 对象。

那么 JSX 如何转换成 React.createElement() 的呢?答案就是通过 babel 转换。

我们直接打开 babel playground 来写一段 JSX 代码看一下 babel 转换后的结果。![image-20231204112041472](/Users/jiuyuezhang/Library/Application Support/typora-user-images/image-20231204112041472.png)

可以看到 JSX 代码都被转换成了 React.createElement 调用。

接下来我们总结一下来回答标题提到的两个问题。

JSX 是 JavaScript 的扩展,不是 JavaScipt 的某个版本,需要通过 Babel 进行转换成 JavaScript 代码。

JSX 会被 babel 转换为 React.CreateElement(...) 调用的形式,执行后返回的结果是一个对象。

2)React 为什么要用 JSX?

从上一节我们知道 JSX 等价于一次 React.createElement 调用,那么 React 官方为什么不直接引导我们用 React.createElement 来创建元素呢?

在实际功能效果一致的前提下,JSX 代码层次分明、嵌套关系清晰;而 React.createElement 代码则给人一种非常混乱的“杂糅感”,这样的代码不仅读起来不友好,写起来也费劲。

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

3)JSX 是如何映射为 DOM 的?

我们知道 JSX 经过babel转换后会变成 React.createElement(...) 的形式,接下来我们就来一起探讨一下 React.createElement(...) 是如何工作的?

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

我们先来看看方法的入参:

  1. export function createElement(type, config, children)

createElement 有 3 个入参,这 3 个入参囊括了 React 创建一个元素所需要知道的全部信息。

  • type:用于标识节点的类型。它可以是类似“h1”“div”这样的标准 HTML 标签字符串,也可以是 React 组件类型或 React fragment 类型。
  • config:以对象形式传入,组件所有的属性都会以键值对的形式存储在 config 对象中。
  • children:子节点,如果有多个子节点,那么依次往后写。

举个例子:

  1. <ul className="list">
  2. <li key="1">1</li>
  3. <li key="2">2</li>
  4. </ul>

经过 Babel 转换后的形式为:

注意:从第三个入参开始往后,传入的参数都是 children

  1. React.createElement("ul", {
  2. // 传入属性键值对
  3. className: "list"
  4. }, React.createElement("li", {
  5. key: "1"
  6. }, "1"), React.createElement("li", {
  7. key: "2"
  8. }, "2"));

3.2 出参解读:初识虚拟DOM

下面的代码是 React.createElement(...) 调用的返回值格式。

注意:这是 fiber节点之前的每个节点的格式。

  1. const ReactElement = function(type, key, ref, self, source, owner, props) {
  2. const element = {
  3. // REACT_ELEMENT_TYPE是一个常量,用来标识该对象是一个ReactElement
  4. $$typeof: REACT_ELEMENT_TYPE,
  5. // 内置属性赋值
  6. type: type,
  7. key: key,
  8. ref: ref,
  9. props: props,
  10. // 记录创造该元素的组件
  11. _owner: owner,
  12. };
  13. //
  14. if (__DEV__) {
  15. // 这里是一些针对 __DEV__ 环境下的处理,对于大家理解主要逻辑意义不大,此处我直接省略掉,以免混淆视听
  16. }
  17. return element;
  18. };

举个例子

  1. const AppJSX = (<div className="App">
  2. <h1 className="title">I am the title</h1>
  3. <p className="content">I am the content</p>
  4. </div>)
  5. console.log(AppJSX)

输出为:

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

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

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

复制代码

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

ReactDOM.render 方法可以接收 3 个参数,其中第二个参数就是一个真实的 DOM 节点这个真实的 DOM 节点充当“容器”的角色,React 元素最终会被渲染到这个“容器”里面去。比如,示例中的 App 组件,它对应的 render 调用是这样的:

  1. const rootElement = document.getElementById("root");
  2. ReactDOM.render(<App />, rootElement);

注意,这个真实 DOM 一定是确实存在的。比如,在 App 组件对应的 index.html 中,已经提前预置 了 id 为 root 的根节点:

  1. <body>
  2. <div id="root"></div>
  3. </body>

原文链接:https://www.cnblogs.com/zhangguicheng/p/17876753.html

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号