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

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)

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

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Linux终端玩转bastet俄罗斯方块小游戏

Linux终端玩转bastet俄罗斯方块小游戏 一、bastet小游戏介绍1.1 bastet小游戏简介1.2 项目预览 二、本次实践介绍2.1 本地环境规划2.2 本次实践介绍 三、检查系统镜像源3.1 检查系统镜像源3.2 更新软件列表3.3 查询软件 四、安装bastet小游戏4.1 安装bastet4.2 启动bastet游戏 …

2024最新版DataGrip安装教程-全网最全教程!!!

1.DataGrip下载安装 1.打开DataGrip官网&#xff0c;选择自己需要的版本下载即可&#xff1a; 2.进行安装&#xff1a; 3.重启打开&#xff1a; 我这个是正版激活码激活的&#xff0c;需要教程可以关注留言

打字侠,中小学生暑期的打字练习神器

亲爱的家长们&#xff0c;暑假来临&#xff0c;孩子们又要开始“沙发上的咸鱼”模式了&#xff01;与其看着他们抱着手机、平板不放&#xff0c;不如让他们成为“打字侠”&#xff0c;在快乐中提升打字技能&#xff01; “打字侠”是一款为中小学生量身打造的打字练习神器。别…

[保姆级教程]uniapp自定义标签页切换组件

文章目录 导文样式改成动态列表切换点击效果加上点击自动滑动scroll-view加上切换组件效果 导文 unaipp自带的标签页和ui设计相差太大&#xff0c;直接修改组件比手写一个还麻烦&#xff0c;下面手写一个。 样式 先用scroll-view做一个滑动&#xff0c;不然多的话滑动不了。 &l…

【C++实验】多项式加减

题目&#xff1a;一元多项式运算 基本要求&#xff1a; &#xff08;1&#xff09; 输入并建立多项式; &#xff08;2&#xff09; 输出多项式; &#xff08;3&#xff09; 多项式加法 &#xff08;4&#xff09; 多项式减法。 测试数据&#xff1a; 代码展示&#xff1a; #i…

1.1 数据采集总览

正所谓巧妇难为无米之炊&#xff0c;数据采集是数据处理的第一步。 什么是数据采集 数据采集&#xff0c;也称为数据收集&#xff0c;是将原始数据从各种来源获取并存储起来的过程。这个过程是数据分析和数据仓库建设的第一步&#xff0c;涉及到从不同的数据源中提取数据&…

Redis-数据类型-Hash

文章目录 1、查看redis是否启动2、通过客户端连接redis3、切换到db3数据库4、插入新数据返回15、获取指定哈希&#xff08;hash&#xff09;对象的所有字段&#xff08;field&#xff09;名6、获取存储在指定哈希&#xff08;hash&#xff09;对象中的所有字段&#xff08;fiel…

51单片机STC89C52RC——6.3 定时器/计数器 实现计时功能(定时器+中断系统+LCD1602液晶显示器)

目录 目的/效果 一&#xff0c;STC单片机模块 二&#xff0c;定时器 中断系统LCD1602显示 三&#xff0c;创建Keil项目 四&#xff0c;代码 五&#xff0c;代码编译、下载到51单片机 ​ 目的/效果 用定时器实现系统中断&#xff0c;计时信息显示在LCD1602上。效果如下 …

最优化第六讲练习题

使用牛顿法 def f(vec):x1,x2vec[0],vec[1]return x1*x1/22*x2*x2def first_order(vec):x1,x2vec[0],vec[1]return np.array((x1,4*x2))x0np.array((2,1)) #初始点 secnp.array([[1,0],[0,4]]) #二阶导 try:invnp.linalg.inv(sec) except:print("矩阵不存在逆矩阵")…

如何让表格标题栏具有粘性?

让表格标题栏具有粘性 什么意思呢&#xff1f; 就是当表格的内容&#xff08;行数&#xff09;比较多的时候&#xff0c; 滚动屏幕&#xff0c;看下面的内容的时候&#xff0c; 表格标题栏可以一直显示在屏幕最上方&#xff0c; 以前呢&#xff0c; 我会通过JSCSS 的 pos…

ffmpeg音视频开发从入门到精通——ffmpeg实现音频抽取

文章目录 FFmpeg 实现音频流抽取1. 包含FFmpeg头文件与命名空间声明2. 主函数与参数处理3. 打开输入文件4. 获取文件信息5. 查找音频流6. 分配输出文件上下文7. 猜测输出文件格式8. 创建新的音频流9. 打开输出文件10. 写入文件头信息11. 读取并写入音频数据12. 写入文件尾部信息…

STM32读取芯片内部温度

基于stm32f103cbt6这款芯片&#xff0c;原理部分请参考其他文章&#xff0c;此文章为快速上手得到结果&#xff0c;以结果为导向。 1.基础配置 打开stm32cubemx只需要勾选中 ADC1 Temperature Sensor Channel 2.代码分析 /** 函数名&#xff1a;float GetAdcAnlogValue(voi…

05 - matlab m_map地学绘图工具基础函数 - 设置比例尺指北针

05 - matlab m_map地学绘图工具基础函数 - 设置比例尺指北针 0. 引言1. 关于m_scale2. 关于m_ruler3. 关于m_northarrow4. 结语 0. 引言 本篇介绍下m_map中添加指北针(m_northarrow)、比例尺(m_ruler)和进行比例缩放(m_scale)的函数及其用法 。 1. 关于m_scale m_scale用于图件…

python库离线安装方法(pyqt5离线安装方法)

在某些情况下&#xff0c;我们的计算机是无法联网的。 网上大部分方法&#xff1a; 这些方法都有个问题&#xff0c;就是库是需要依赖其它库的&#xff0c;你不知道它需要依赖什么库&#xff0c;就是提供了依赖库的列表也麻烦&#xff0c;依赖库也是有对应版本要求的&#xf…

C++程序设计基础实践:学生信息管理系统

目录 1 系统介绍 2 系统设计 3 设计结果 4 源代码 近来有空闲&#xff0c;把前几个学期做的实验上传上来。如有错误的地方欢迎大佬批评指正&#xff0c;有更好的方法也期待您的分享~ 实验要求 本课程要完成一个学生信息管理系统的设计与实现&#xff0c;可实现对于学生信息…

kafka(五)spring-kafka(2)详解与demo

一、简单的收发消息demo 父工程pom&#xff1a; <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation&qu…

Android面试题:App性能优化之Java和Kotlin常见的数据结构

本文首发于公众号“AntDream”&#xff0c;欢迎微信搜索“AntDream”或扫描文章底部二维码关注&#xff0c;和我一起每天进步一点点 Java常见数据结构特点 ArrayList ArrayList底层是基于数组实现add、删除元素需要进行元素位移耗性能&#xff0c;但查找和修改块适合不需要频…

全面国产化信创适配改造方案说明

一、概叙 系统的全面国产化适配改造需要从多个方面进行考虑&#xff0c;改造前需要进行充分的论证&#xff0c;在满足具体业务场景的前提下&#xff0c;以确保系统的稳定性和安全性&#xff0c;同时还要考虑技术的发展&#xff0c;不断优化和更新。因此全面国产化适配改造也面临…

沙奇里再造世界波,容声注定与经典结缘

足球的世界&#xff0c;就像人生一样&#xff0c;包罗万象&#xff0c;千人千面。有人天生就是王者&#xff0c;有人怎么努力也碌碌无为&#xff0c;而有的人&#xff0c;平时看似没有那么闪耀&#xff0c;却注定为大场面而生。 比如瑞士国脚沙奇里。在对阵苏格兰的欧洲杯小组…

【CT】LeetCode手撕—415. 字符串相加

目录 题目1- 思路2- 实现⭐415. 字符串相加——题解思路 3- ACM 实现 题目 原题连接&#xff1a;415. 字符串相加 1- 思路 模式识别&#xff1a;字符串相加 逆向遍历过程模拟 数据结构 ① String res &#xff1a;记录res 、② carry 记录进位值① 定义两个整数遍历 nums1 …