React源码解析-JSX

简介: 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
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

目录
相关文章
|
1天前
HuggingFace Tranformers 源码解析(4)
HuggingFace Tranformers 源码解析
5 0
|
1天前
HuggingFace Tranformers 源码解析(3)
HuggingFace Tranformers 源码解析
5 0
|
1天前
|
开发工具 git
HuggingFace Tranformers 源码解析(2)
HuggingFace Tranformers 源码解析
6 0
|
1天前
|
并行计算
HuggingFace Tranformers 源码解析(1)
HuggingFace Tranformers 源码解析
7 0
|
1天前
|
JavaScript 前端开发
vue3中使用jsx报错React is not defined和h is not defined
vue3中使用jsx报错React is not defined和h is not defined
|
2天前
|
存储 前端开发 JavaScript
React Hooks实战:从useState到useContext深度解析
React Hooks 深度解析:useState用于函数组件的状态管理,通过初始化和更新状态实现渲染控制;useContext则提供跨组件数据传递。useState的状态更新是异步的,不支持浅比较,可结合useEffect处理副作用。useContext在多层组件间共享状态,但可能导致不必要的渲染。两者结合可创建复杂应用场景,如带主题切换的计数器。了解其工作原理和优化策略,能有效提升React应用性能。
|
2天前
PandasTA 源码解析(二十三)
PandasTA 源码解析(二十三)
41 0
|
2天前
PandasTA 源码解析(二十二)(3)
PandasTA 源码解析(二十二)
34 0
|
2天前
|
前端开发 测试技术 开发工具
探索前端框架React Hooks的优势与应用
本文将深入探讨前端框架React Hooks的优势与应用。通过分析React Hooks的特性以及实际应用案例,帮助读者更好地理解和运用这一现代化的前端开发工具。
|
2天前
|
开发框架 Dart 前端开发
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择
【4月更文挑战第30天】对比 Flutter(Dart,强类型,Google支持,快速热重载,高性能渲染)与 React Native(JavaScript,庞大生态,热重载,依赖原生渲染),文章讨论了开发语言、生态系统、性能、开发体验、学习曲线、社区支持及项目选择因素。两者各有优势,选择取决于项目需求、团队技能和长期维护考虑。参考文献包括官方文档和性能比较文章。
【Flutter前端技术开发专栏】Flutter与React Native的对比与选择

热门文章

最新文章

推荐镜像

更多