【从0实现React18】 (二) JSX 的转换 jsx到底是什么?React是如何把jsx转换为ReactElement?

news2024/11/27 0:39:29

react项目结构

  • React(宿主环境的公用方法)
  • React-reconciler(协调器的实现,宿主环境无关)
  • 各种宿主环境的包
  • shared(公用辅助方法,宿主环境无关)

当前实现的JSX转换属于 react****包

初始化react包

先创建react package并初始化

更新package.json文件:

{
  "name": "react",
  "version": "1.0.0",
  "description": "react公用方法",
  "module": "index.ts",
  "keywords": [],
  "author": "",
  "license": "ISC"
}

JSX转换是什么

jsx在线转换

包括两部分

  • 编译时
  • 运行时:jsx方法或react.createElement方法的实现(包括dev、prod两个环境)

实现运行时 jsx 转换

编译时由babel编译实现,我们实现运行时,工作量包括:

  • 实现jsx方法
  • 实现打包流程
  • 实现调试打包结果的环境
  1. 实现jsx方法

包括:

  • jsxDEV方法(dev环境)
  • jsx方法(prod环境)
  • React.craeteElement方法

实现:react/src/jsx.ts:

import { REACT_ELEMENT_TYPE } from '@/shared/ReactSymbols'
import {
  Type,
  Key,
  Ref,
  Props,
  ElementType,
  ReactElementType,
} from '@/shared/ReactTypes'

// ReactElement 构造函数实现
const ReactElement = function (
  type: Type,
  key: Key,
  ref: Ref,
  props: Props
): ReactElementType {
  const element = {
    $$typeof: REACT_ELEMENT_TYPE, // 内部字段, 指明当前字段是reactElement
    type,
    key,
    ref,
    props,
    __mark: 'khs', // 该字段是为了与真实的react项目区分开
  }

  return element
}

// jsx 函数实现
export const jsx = (type: ElementType, config: any, ...maybeChildren: any) => {
  let key: Key = null
  let ref: Ref = null
  const props: Props = {}

  // 遍历config
  for (const prop in config) {
    const val = config[prop]
    // 1. 单独找出 key和ref字段
    if (prop === 'key') {
      if (val !== undefined) {
        key = '' + val
      }
      continue
    }
    if (prop === 'ref') {
      if (val !== undefined) {
        ref = val
      }
      continue
    }
    // 2. 剩下的如果是config自身的prop, 则正常取出
    if ({}.hasOwnProperty.call(config, prop)) {
      props[prop] = val
    }
  }

  const maybeChildrenLength = maybeChildren.length
  if (maybeChildrenLength) {
    // [child] 或 [child, child, child]
    if (maybeChildrenLength === 1) {
      props.child = maybeChildren[0]
    } else {
      props.child = maybeChildren
    }
  }
  return ReactElement(type, key, ref, props)
}

// jsxDEV 函数实现
export const jsxDEV = (type: ElementType, config: any) => {
  let key: Key = null
  let ref: Ref = null
  const props: Props = {}

  // 遍历config
  for (const prop in config) {
    const val = config[prop]
    // 1. 单独找出 key和ref字段
    if (prop === 'key') {
      if (val !== undefined) {
        key = '' + val
      }
      continue
    }
    if (prop === 'ref') {
      if (val !== undefined) {
        ref = val
      }
      continue
    }
    // 2. 剩下的如果是config自身的prop, 则正常取出
    if ({}.hasOwnProperty.call(config, prop)) {
      props[prop] = val
    }
  }

  return ReactElement(type, key, ref, props)
}

react/index.tsx:

/**
 * 打包出的React包
 */

import { jsxDEV } from './src/jsx'
export default {
  version: '0.0.0',
  createElement: jsxDEV,
}

同时因为react引入了shared包,所以为react/package.json添加依赖:

{
  "name": "react",
  "version": "1.0.0",
  "description": "react公用方法",
  "module": "index.ts",
  "dependencies": {
    "shared": "workspace: *"  
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}
  1. 实现打包流程

对应上述3方法,打包对应文件

  • react/jsx-dev-runtime.js(dev环境)
  • react/jsx-runtime.js(prod环境)
  • react

1、安装rollup Plugin

  • 兼容commonjs: @rollup/plugin-commonjs
  • ts解析为js: rollup-plugin-typescript2
  • 生成package.json文件:rollup-plugin-generate-package-json
pnpm i -D -w rollup-plugin-typescript2

pnpm i -D -w @rollup/plugin-commonjs

pnpm i -D -w rollup-plugin-generate-package-json
  1. react包rollup打包配置:

scripts/rollup/react.config.js:

import { getBaseRollupPlugins, getPackageJSON, resolvePkgPath } from './utils'

import generatePackageJson from 'rollup-plugin-generate-package-json'

const { name, module } = getPackageJSON('react')
// react包的路径
const pkgPath = resolvePkgPath(name)
//react产物路径
const pkgDistPath = resolvePkgPath(name, true)

export default [
  // react 的包
  {
    input: `${pkgPath}/${module}`,
    output: {
      file: `${pkgDistPath}/index.js`,
      name: 'index.js',
      format: 'umd', // 该格式能够兼容commonjs
    },
    plugins: [
      ...getBaseRollupPlugins(),
      // 生成package.json文件
      generatePackageJson({
        inputFolder: pkgPath,
        outputFolder: pkgDistPath,
        baseContents: ({ name, description, version }) => ({
          name,
          description,
          version,
          main: 'index.js',
        }),
      }),
    ],
  },
  // jsx-runtime 和 jsx-dev-runtime 的包
  {
    input: `${pkgPath}/src/jsx.ts`,
    output: [
      // jsx-runtime
      {
        file: `${pkgDistPath}/jsx-runtime.js`,
        name: 'jsx-runtime',
        format: 'umd',
      },
      // jsx-dev-runtime
      {
        file: `${pkgDistPath}/jsx-dev-runtime.js`,
        name: 'jsx-dev-runtime.js',
        format: 'umd',
      },
    ],
    plugins: getBaseRollupPlugins(),
  },
]
  1. 测试打包

安装清除上次打包的文件工具:rimraf

pnpm i -D -w rimraf

添加脚本:

"build:dev": "rimraf dist && rollup --bundleConfigAsCjs --config scripts/rollup/react.config.js"

运行脚本

pnpm build:dev

打包结果:

  1. 调试打包结果

Pnpm link

npm link是一种把包链接到包文件夹的方式,即:可以在不发布npm模块的情况下,调试该模块,并且修改模块后会实时生效,不需要通过npm install进行安装

  • 优点:可以模拟实际项目引用React的清空
  • 缺点:略显繁琐,达不到热更新的效果

先去到模块目录,把它 link 到全局:

cd .\dist\node_modules\react\

pnpm link --global

在外部新建一个react项目,然后将我们实现的react link到该项目

npx create-react-app react-demo 
 
cd ./react-demo

pnpm link react --global

修改react-demo/index.js:

import React from 'react'

const jsx = (
  <div key={123} ref={'khs'}>
    hello
    <span>big-react</span>
  </div>
)

console.log(React)
console.log(jsx)

控制台打印调试后的结果:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1851984.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Redis源码学习:quicklist的设计与实现

为什么需要quicklist 假设你已经知道了ziplist的缺陷&#xff1a; 虽然节省空间&#xff0c;但是申请内存必须是连续的&#xff0c;如果内存占用比较多&#xff0c;申请效率低要存储大量数据&#xff0c;超过了ziplist的最佳上限后&#xff0c;性能有影响 借鉴分片思想&…

仓颉编程语言入门

华为在 2024 年 6 月 21 日的华为开发者大会上&#xff0c;华为终端 BG 软件部总裁龚体正式官宣了华为自研仓颉编程语言&#xff0c;并发布了 HarmonyOS NEXT 仓颉语言开发者预览版。 仓颉编程语言文件后缀名为 .cj, 以下是第一个入门代码输出&#xff1a;你好&#xff0c;仓颉…

理解 什么是 滚动更新,蓝绿部署,灰度发布 以及它们的区别

滚动更新&#xff0c;蓝绿部署&#xff0c;灰度发布 这3种 现代化的 发布模式相信很多人都听过&#xff0c; 但是并不是都能正确理解他们的作用和区别 滚动更新 Rolling Update 所谓滚动更新是for 那些多实例的service的。 假如1个 service 有n 个instance, 更新时并不是n 个…

xss.haozi.me靶场通关参考

url&#xff1a;https://xss.haozi.me/ 文章目录 0x000x010x020x030x040x050x060x070x080x090x0A0x0B0x0C00xD00xE00xF0x100x110x12 0x00 先看js代码&#xff0c;第一关给你热热手&#xff0c;没给你加过 payload&#xff1a; <script>alert(1)</script>0x01 这…

移动端 UI 风格,诠释精致

移动端 UI 风格&#xff0c;诠释精致

文华财经wh7关于mancd和KDJ预警指标公式源码

一、零轴下金叉和零轴上死叉的预警 MA5:MA(C,5); MA10:MA(C,10); DIFF:EMA(CLOSE,12) - EMA(CLOSE,26); DEA:EMA(DIFF,9); MACD:2*(DIFF-DEA); CROSS(MA5,MA10)&&MAX(DIFF,DEA)<0,NOTICE(零轴下金叉); CROSSDOWN(MA5,MA10)&&MIN(DIFF,DEA)>0,NOTI…

华为---理解OSPF Route-ID(五)

9.5 理解OSPF Route-ID 9.5.1 原理概述 一些动态路由协议要求使用Router-ID作为路由器的身份标示&#xff0c;如果在启动这些路由协议时没有指定Router-ID,则默认使用路由器全局下的路由管理Router-ID。 Router-ID选举规则为&#xff0c;如果通过Router-ID命令配置了Router-…

46、基于自组织映射神经网络的鸢尾花聚类(matlab)

1、自组织映射神经网络的鸢尾花聚类的原理及流程 自组织映射神经网络&#xff08;Self-Organizing Map, SOM&#xff09;是一种用于聚类和数据可视化的人工神经网络模型。在鸢尾花聚类中&#xff0c;SOM 可以用来将鸢尾花数据集分成不同的类别&#xff0c;同时保留数据间的拓扑…

centos7系统上安装MySQL8.4图文教程

本章教程&#xff0c;主要记录如何在CentOS7系统上安装MySQL8.4的详细步骤。 一、查看当前系统版本 cat /etc/centos-release二、安装步骤 1、创建mysql目录 cd /usr/local && mkdir mysql && cd mysql2、安装rpm包 yum install https://repo.mysql.com//m…

[FreeRTOS 基础知识] 互斥访问与回环队列 概念

文章目录 为什么需要互斥访问&#xff1f;使用队列实现互斥访问休眠和唤醒机制环形缓冲区 为什么需要互斥访问&#xff1f; 在裸机中&#xff0c;假设有两个函数&#xff08;func_A, func_B&#xff09;都要修改a的值&#xff08;a&#xff09;&#xff0c;那么将a定义为全局变…

Django数据驾驶舱

Django数据驾驶舱 1.项目介绍2.项目结构3.库表结构3.1 appcsdn的models3.2 appssq的models3.3 appweather的models3.4 appweibo的models 4.功能展示5.解决问题5.1 路由配置5.2 后端数据与前端echarts展示5.3 长图表丝滑滚动条 6.遗留问题7.资源分享 1.项目介绍 这里介绍本人最…

STM32读写备份寄存器和实时时钟

文章目录 1. 硬件电路 2. RTC操作注意事项 操作步骤 3. 代码实现 3.1 读写备份寄存器 3.1.1 main.c 3.2 实时时钟 3.2.1 MyRTC.c 3.2.2 MyRTC.h 3.2.3 main.c 1. 硬件电路 对于BKP备份寄存器和RTC实时时钟的详细解析可以看下面这篇文章&#xff1a; STM32单片机BKP备…

水果销售系统

摘 要 随着电子商务的快速发展&#xff0c;传统的实体销售模式面临着越来越多的挑战。在这个数字化的时代&#xff0c;消费者的购物习惯发生了翻天覆地的变化&#xff0c;消费者更倾向于在家中通过网络平台浏览并购买商品&#xff0c;这无疑给传统水果销售带来了极大的挑战。为…

【动态规划】1130. 叶值的最小代价生成树

1130. 叶值的最小代价生成树 难度&#xff1a;中等 力扣地址&#xff1a;https://leetcode.cn/problems/minimum-cost-tree-from-leaf-values/description/ 题目内容 给你一个正整数数组 arr&#xff0c;考虑所有满足以下条件的二叉树&#xff1a; 每个节点都有 0 个或是 2 个…

算法04 模拟算法之一维数组相关内容详解【C++实现】

大家好&#xff0c;我是bigbigli&#xff0c;模拟算法我们将分为几个章节来讲&#xff0c;今天我们只看一维数组相关的题目 目录 模拟的概念 训练&#xff1a;开关灯 解析 参考代码 训练&#xff1a;数组变化 解析 参考代码 训练&#xff1a;折叠游戏 解析 参考代码 …

韩顺平0基础学java——第29天

p592-599 线程 用户线程和守护线程 1.用户线程:也叫工作线程&#xff0c;当线程的任务执行完或通知方式结束 2守护线程:一般是为工作线程服务的&#xff0c;当所有的用户线选束&#xff0c;守护线程自动结束 3.常见的守护线程:垃圾回收机制 当我们希望当main线程结束后&…

C# Onnx Yolov5 水果识别,人员识别,物品识别 人工智能

目录 先上效果 来电废话&#xff0c;但实用 网络成功案例实践易失败的原因 万物检测涉及技术 下载合集 关键代码 全部代码 实操vs2022安装关键 YOLO V5核心库编译 编写自己识别软件 更新相关依赖 标注字库文件 测试效果 名词解释YOLO 名词解释ONNX 源码 直播教…

利用第三方服务对目标进行被动信息收集防止被发现(web安全白帽子)

利用第三方服务对目标进行被动信息收集防止被发现&#xff08;web安全白帽子&#xff09; 1 被动信息收集1.1 信息收集内容1.2 信息用途 2 信息收集-DNS2.1 DNS信息收集NSLOOKUP2.1.1 ping2.1.2 nslookup 2.2 DNS信息收集-DIG&#xff08;此命令查到的结果更复杂些&#xff0c;…

Apache IoTDB vs InfluxDB 开源版,架构性能全面对比!

分布式、端边云同步、读写查询性能&#xff0c;Apache IoTDB 与 InfluxDB 开源版的详尽对照&#xff01; 在物联网&#xff08;IoT&#xff09;领域&#xff0c;数据的采集、存储和分析是确保系统高效运行和决策准确的重要环节。随着物联网设备数量的增加和数据量的爆炸式增长&…

Golang | Leetcode Golang题解之第174题地下城游戏

题目&#xff1a; 题解&#xff1a; func calculateMinimumHP(dungeon [][]int) int {n, m : len(dungeon), len(dungeon[0])dp : make([][]int, n 1)for i : 0; i < len(dp); i {dp[i] make([]int, m 1)for j : 0; j < len(dp[i]); j {dp[i][j] math.MaxInt32}}dp[…