您的位置:首页 > 游戏 > 游戏 > 【从0实现React18】 (二) JSX 的转换 jsx到底是什么?React是如何把jsx转换为ReactElement?

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

2024/11/18 8:24:19 来源:https://blog.csdn.net/m0_74989367/article/details/139885306  浏览:    关键词:【从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, // 内部字段, 指明当前字段是reactElementtype,key,ref,props,__mark: 'khs', // 该字段是为了与真实的react项目区分开}return element
}// jsx 函数实现
export const jsx = (type: ElementType, config: any, ...maybeChildren: any) => {let key: Key = nulllet ref: Ref = nullconst props: Props = {}// 遍历configfor (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.lengthif (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 = nulllet ref: Ref = nullconst props: Props = {}// 遍历configfor (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-typescript2pnpm i -D -w @rollup/plugin-commonjspnpm 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-demopnpm 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)

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

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com