React源码解析-JSX

本文涉及的产品
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
全局流量管理 GTM,标准版 1个月
简介: 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
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

目录
打赏
0
0
1
0
53
分享
相关文章
React 步骤条组件 Stepper 深入解析与常见问题
步骤条组件是构建多步骤表单或流程时的有力工具,帮助用户了解进度并导航。本文介绍了在React中实现简单步骤条的方法,包括基本结构、状态管理、样式处理及常见问题解决策略,如状态管理库的使用、自定义Hook的提取和CSS Modules的应用,以确保组件的健壮性和可维护性。
89 17
React音频播放控制组件开发深度解析
本文介绍了构建React音频控制组件时遇到的关键问题及优化方案。主要包括: 1. **状态同步难题**:解决播放按钮与音频状态不同步的问题,通过双向绑定机制确保一致。 2. **跨浏览器兼容性**:处理Safari和Chrome预加载策略差异,确保`duration`属性正确获取。 3. **进度控制优化**:避免使用`setInterval`,采用`requestAnimationFrame`提升性能;优化拖拽交互,防止音频卡顿。 4. **音量控制进阶**:实现渐变音量调节和静音状态同步。
63 15
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
创建型模式的主要关注点是“怎样创建对象?”,它的主要特点是"将对象的创建与使用分离”。这样可以降低系统的耦合度,使用者不需要关注对象的创建细节。创建型模式分为5种:单例模式、工厂方法模式抽象工厂式、原型模式、建造者模式。
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
行为型模式用于描述程序在运行时复杂的流程控制,即描述多个类或对象之间怎样相互协作共同完成单个对象都无法单独完成的任务,它涉及算法与对象间职责的分配。行为型模式分为类行为模式和对象行为模式,前者采用继承机制来在类间分派行为,后者采用组合或聚合在对象间分配行为。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象行为模式比类行为模式具有更大的灵活性。 行为型模式分为: • 模板方法模式 • 策略模式 • 命令模式 • 职责链模式 • 状态模式 • 观察者模式 • 中介者模式 • 迭代器模式 • 访问者模式 • 备忘录模式 • 解释器模式
【23种设计模式·全精解析 | 行为型模式篇】11种行为型模式的结构概述、案例实现、优缺点、扩展对比、使用场景、源码解析
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
结构型模式描述如何将类或对象按某种布局组成更大的结构。它分为类结构型模式和对象结构型模式,前者采用继承机制来组织接口和类,后者釆用组合或聚合来组合对象。由于组合关系或聚合关系比继承关系耦合度低,满足“合成复用原则”,所以对象结构型模式比类结构型模式具有更大的灵活性。 结构型模式分为以下 7 种: • 代理模式 • 适配器模式 • 装饰者模式 • 桥接模式 • 外观模式 • 组合模式 • 享元模式
【23种设计模式·全精解析 | 创建型模式篇】5种创建型模式的结构概述、实现、优缺点、扩展、使用场景、源码解析
mindspeed-llm源码解析(一)preprocess_data
mindspeed-llm是昇腾模型套件代码仓,原来叫"modelLink"。这篇文章带大家阅读一下数据处理脚本preprocess_data.py(基于1.0.0分支),数据处理是模型训练的第一步,经常会用到。
61 0
React 文本区域组件 Textarea:深入解析与优化
本文介绍了 React 中 Textarea 组件的基础用法、常见问题及优化方法,包括状态绑定、初始值设置、样式自定义、性能优化和跨浏览器兼容性处理,并提供了代码案例。
115 8
Java线程池提交任务流程底层源码与源码解析
【11月更文挑战第30天】嘿,各位技术爱好者们,今天咱们来聊聊Java线程池提交任务的底层源码与源码解析。作为一个资深的Java开发者,我相信你一定对线程池并不陌生。线程池作为并发编程中的一大利器,其重要性不言而喻。今天,我将以对话的方式,带你一步步深入线程池的奥秘,从概述到功能点,再到背景和业务点,最后到底层原理和示例,让你对线程池有一个全新的认识。
87 12
Ascend Extension for PyTorch的源码解析
本文介绍了Ascend对PyTorch代码的适配过程,包括源码下载、编译步骤及常见问题,详细解析了torch-npu编译后的文件结构和三种实现昇腾NPU算子调用的方式:通过torch的register方式、定义算子方式和API重定向映射方式。这对于开发者理解和使用Ascend平台上的PyTorch具有重要指导意义。

推荐镜像

更多
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等