React源码解析-JSX

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: React源码解析-JSX

React是世界上最好的前端工程师们历经多年打造的UI库
他的宗旨是:learn once, write anywhere
得益于优秀的设计,react可以开发:

web
native app
vr
桌面应用
3d应用

在每个平台上,我们都可以使用JSX
一. 什么是JSX
JSX是js的一种扩展,也是一种表达式。 React认为渲染逻辑 本质上 与 UI 是耦合的。
javascript复制代码const App =


您好

显然浏览器,是不认识的,这个时候,我们需要babel
二. transform JSX
preset-react
preset-react是babel在react下的解决方案
preset-react包会包含以下3个包:

@babel/plugin-syntax-jsx
让babel认识jsx语法

@babel/plugin-transform-react-jsx
转化jsx为函数调用
需要注意的是jsx并不是只能被编译成createElement函数,此插件可以自由配置

@babel/plugin-transform-react-display-name
处理react class displayName

旧版jsx转换
先看个🌰:
js复制代码function App() {
return

1

}

export default App;

当我们运行时,会报错:React is not defined。(内心一万匹马在奔腾)

在react17之前,jsx转换都会转换为React.createElement调用, 所以我们必须在第一行加上:
js复制代码import React from "react"

新版jsx转换
17版本之后,react升级了支持了jsx runtime,从此我们再也不需要担心 react is not defined
转化前代码:
js复制代码function App() {
return

1

}

转化后代码:
js复制代码import { jsx } from 'react/jsx-runtime';

function App() {
return jsx("div", { children: "1" })
}

注意:不要手动引入 import { jsx } from "react/jsx-runtime"
transform result
本质上,react中的jsx会转化为createElement 或者 jsx函数调用。
我们以createElement为例,先看源码方法定义:
js复制代码function createElement(type, config, children) {
// ...
}

为了了解入参,我们先看几个🌰:

  1. 单一元素
    jsx:
    html复制代码

    hello world

结果:
js复制代码React.createElement(
"div",
{
"data-id": "test-id"
},
"hello world"
);

  1. 嵌套标签
    jsx:
    html复制代码

    hello world

    hello span

结果:
js复制代码React.createElement(
"div",
{
"data-id": "test-id"
},
"hello world",
React.createElement(
"span",
{
className: "span"
},
"hello span"
)
);

不难发现,createElement方法只定义了3个参数,而本次转化结果 顶层createElement方法 有4个参数,为什么呢? 其实createElement后面可以写n个参数,内部会使用arguments对象做children集合。这个我们后面分析createElement源码再聊。

  1. 组件标签
    jsx:
    html复制代码

    hello world

    hello span


结果:
js复制代码React.createElement(
"div",
{
"data-id": "test-id"
},
"hello world",
React.createElement(
"span",
{
className: "span"
},
"hello span"
),
React.createElement(TestComponent, {
name: "trump"
})
);

由此可看出,createElement第一个参数type,可以是div, span,也可以是Component。
jsx -> createElement过程的源码实现,我们后面单独开章节分析,总的概括是 AST -> Optimize -> Generate createElement,17版本之前之前和之后做了较大的改动。
这里,我们只要记住,babel利用相关插件,将jsx转化为一系列函数调用,若不指定jsx runtime,默认使用createElement函数调用,反之jsx函数调用
三. createElement
核心代码
js复制代码function createElement(type, config, children) {
// ...
const props = {};
let key = null;
let ref = null;

// 将config属性赋值给props
...
// 处理childrens,可以传入n个参数
const childrenLength = arguments.length - 2;
if(childrenLength === 1) {
// 只有1个children直接赋给props
props.children = children
}else (childrenLength > 1) {
// 利用arguments对象将多个children,依次循环组装成一个childrens数组
// 将childrens数组挂载到props对象上
...
}

// 处理组件的defaultProps,如果props上对应的key不存在,那么使用defaultProps值
// 注意props可以是函数组件,可以是div,可以是class component
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}

return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}

说明
总的来说,createElement函数,做一些初始化工作,定义props,挂载key, ref, children,处理defaultProps,最后返回ReactElement函数调用。
四. ReactElement
核心代码
js复制代码function ReactElement(type, key, ref, self, source, owner, props) {
const element = {
$$typeof: REACT_ELEMENT_TYPE, type: type, key: key, ref: ref, props: props, _owner: owner, }; // dev环境的处理,主要解决element对象的_self, _source属性不可以写,另外冻结element和props // ... return element; } 对象说明 reactElement会返回一个元素对象,其中: $$typeof: 是一个Symbol,为了防止xss攻击而设置(因为json不支持symbol)
type: 元素类型,可以是div,可以是函数组件,可以是类组件
key: props传入的key
ref: props传入的ref
props: 属性
_owner: 记录负责创建这个元素的组件,类型后期实际是个Fiber

五. 总结
jsx本质上是描述元素的一种数据结构,最终将被babel转化为createElement函数或jsx函数调用,最后返回element对象。他本身不包括组件的state, 也不包括lane模型,不包括调和阶段信息
jsx在babel层面的解析,我将在后面的章节单独分享。下面的文章,我们开启react源码解析之旅吧~ 标签: React.js

作者:GW_刘振
链接:https://juejin.cn/post/7084958413284704292
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

目录
相关文章
|
12天前
|
前端开发 JavaScript
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
52 17
|
1月前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
75 2
|
18天前
|
前端开发 UED
React 文本区域组件 Textarea:深入解析与优化
本文介绍了 React 中 Textarea 组件的基础用法、常见问题及优化方法,包括状态绑定、初始值设置、样式自定义、性能优化和跨浏览器兼容性处理,并提供了代码案例。
44 8
|
23天前
|
缓存 监控 Java
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
51 12
|
19天前
|
PyTorch Shell API
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。
|
1月前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
28天前
|
前端开发 JavaScript
React Hooks 深入解析
React Hooks 深入解析
25 0
|
7月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
382 0
|
7月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
132 0

推荐镜像

更多