React源码解析-JSX

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

目录
相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
15 2
|
29天前
|
前端开发 JavaScript
React Hooks 全面解析
【10月更文挑战第11天】React Hooks 是 React 16.8 引入的新特性,允许在函数组件中使用状态和其他 React 特性,简化了状态管理和生命周期管理。本文从基础概念入手,详细介绍了 `useState` 和 `useEffect` 的用法,探讨了常见问题和易错点,并提供了代码示例。通过学习本文,你将更好地理解和使用 Hooks,提升开发效率。
64 4
|
1月前
|
前端开发
深入解析React Hooks:构建高效且可维护的前端应用
本文将带你走进React Hooks的世界,探索这一革新特性如何改变我们构建React组件的方式。通过分析Hooks的核心概念、使用方法和最佳实践,文章旨在帮助你充分利用Hooks来提高开发效率,编写更简洁、更可维护的前端代码。我们将通过实际代码示例,深入了解useState、useEffect等常用Hooks的内部工作原理,并探讨如何自定义Hooks以复用逻辑。
|
3天前
|
存储 安全 Linux
Golang的GMP调度模型与源码解析
【11月更文挑战第11天】GMP 调度模型是 Go 语言运行时系统的核心部分,用于高效管理和调度大量协程(goroutine)。它通过少量的操作系统线程(M)和逻辑处理器(P)来调度大量的轻量级协程(G),从而实现高性能的并发处理。GMP 模型通过本地队列和全局队列来减少锁竞争,提高调度效率。在 Go 源码中,`runtime.h` 文件定义了关键数据结构,`schedule()` 和 `findrunnable()` 函数实现了核心调度逻辑。通过深入研究 GMP 模型,可以更好地理解 Go 语言的并发机制。
|
16天前
|
消息中间件 缓存 安全
Future与FutureTask源码解析,接口阻塞问题及解决方案
【11月更文挑战第5天】在Java开发中,多线程编程是提高系统并发性能和资源利用率的重要手段。然而,多线程编程也带来了诸如线程安全、死锁、接口阻塞等一系列复杂问题。本文将深度剖析多线程优化技巧、Future与FutureTask的源码、接口阻塞问题及解决方案,并通过具体业务场景和Java代码示例进行实战演示。
36 3
|
1月前
|
存储
让星星⭐月亮告诉你,HashMap的put方法源码解析及其中两种会触发扩容的场景(足够详尽,有问题欢迎指正~)
`HashMap`的`put`方法通过调用`putVal`实现,主要涉及两个场景下的扩容操作:1. 初始化时,链表数组的初始容量设为16,阈值设为12;2. 当存储的元素个数超过阈值时,链表数组的容量和阈值均翻倍。`putVal`方法处理键值对的插入,包括链表和红黑树的转换,确保高效的数据存取。
53 5
|
1月前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
108 5
|
算法 JavaScript 前端开发
|
6月前
|
设计模式 前端开发 数据可视化
【第4期】一文了解React UI 组件库
【第4期】一文了解React UI 组件库
357 0
|
6月前
|
资源调度 前端开发 JavaScript
React 的antd-mobile 组件库,嵌套路由
React 的antd-mobile 组件库,嵌套路由
121 0

推荐镜像

更多