第二章 JSX转换

100 阅读1分钟

第二章 JSX转换

代码仓库

React项目结构

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

jsx就属于react包

创建 package/react

cd package/react
pnpm init

@package/react/package.json 注意第5行,采用module作为入口

{
  "name": "react",
  "version": "1.0.0",
  "description": "react 公用方法",
  // "main": "index.js", // 默认commjs入口,这里使用rollup打包,此处使用 "module": "index.ts",代替
  "module": "index.ts",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

1. JSX转换是什么

包括两部分:

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

编译时由babel编译实现,我们主要实现运行时,工作内容包括

  • 实现jsx方法 即(React.createElement())
  • 实现打包流程
  • 实现调试打包结果的环境

1.1 实现jsx方法

包括:

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

1.2 实现打包流程

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

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

相关文件目录结构

📁big-react
├─ 📄.commitlintrc.js
├─ 📄.eslintrc.json
├─ 📄.gitignore
├─ 📄.prettierrc.json
├─ 📄package.json
├─ 📄pnpm-lock.yaml
├─ 📄pnpm-workspace.yaml
├─ 📄README.md
├─ 📄tsconfig.json
├─ 📁.husky
│  ├─ 📄commit-msg
│  ├─ 📄pre-commit
│  └─ 📁_
│     ├─ 📄.gitignore
│     └─ 📄husky.sh
├─ 📁script
│  └─ 📁rollup
│     ├─ 📄react.config.js
│     └─ 📄utils.js
├─ 📁dist
│  └─ 📁node_modules
├─ 📁packages
│  ├─ 📄test.js
│  ├─ 📁shared
│  │  ├─ 📄package.json
│  │  ├─ 📄ReactSymbols.ts
│  │  └─ 📄ReactTypes.ts
│  └─ 📁react
│     ├─ 📄index.ts
│     ├─ 📄package.json
│     ├─ 📁src
│     └─ 📁node_modules

2.1 代码实现jsx,主要实现运行时

packages/react/index.ts

用于导出react包

// React包
import { jsx } from '@/react/src/jsx';

export default {
	version: '0.0.0',
	createElement: jsx
};

@/react/src/jsx

运行时dev&prod的包,这里不做区别

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

// ReactElement

const ReactElement = function (
	type: Type,
	key: Key,
	ref: Ref,
	props: Props
): ReactElementType {
	const element = {
		// This tag allows us to uniquely identify this as a React Element
		$$typeof: REACT_ELEMENT_TYPE,
		type, // Built-in properties that belong on the element
		key,
		ref,
		props, // Record the component responsible for creating this element.
		__Mark: 'owner'
	};

	return element;
};
export const jsx = (type: ElementType, config: Props, ...children: any[]) => {
	const props: Props = {};
	// 作用: 用于判断是否是自定义组件
	let key: Key = null;
	let ref: Ref = null;

	// 作用: 将config中的属性添加到props中
	for (const prop in config) {
		const val = config[prop]; // 作用: 获取config中的属性值
		if (prop === 'key') {
			// 作用: 判断是否是key属性
			if (val !== undefined) {
				key = '' + val; // 作用: 将key属性值转换为字符串
			}
			continue;
		}
		if (prop === 'ref') {
			if (val !== undefined) {
				ref = val;
			}
			continue;
		}
		// 作用: 将config中的属性添加到props中, 但是不包括key和ref
		if ({}.hasOwnProperty.call(config, prop)) {
			props[prop] = val;
		}
	}
	// 作用: 将children添加到props中
	const childrenLength = children.length;
	if (childrenLength) {
		if (childrenLength === 1) {
			props.children = children[0];
		} else {
			props.children = children;
		}
	}
	return ReactElement(type, key, ref, props);
};

export const jsxDEV = jsx;

其中rollup-plugin包括

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

@/package.json

  "scripts": {
    "lint": "eslint --ext .js,.ts,.jsx,.tsx --fix --quiet ./packages",
    "build:dev": "rimraf dist && rollup --bundleConfigAsCjs -c script/rollup/react.config.js"
  },

rimraf 用于删除旧的dist

--bundleConfigAsCjs 采用commonjs进行打包

问题:此时打包,在dist文件夹里不包含package.json

解决: pnpm i -D -w rollup-plugin-generate-package-json


import generationPackageJson from 'rollup-plugin-generate-package-json';

{
    input: `${pkgPath}/${module}`,
    output: {
        file: `${distPath}/index.js`,
        name: 'index.js',
        format: 'umd'
    },
    plugins: [
        ...getBaseRollupPlugins(),
        // 增加以下代码
        generationPackageJson({
            inputFolder: pkgPath,
            outputFolder: distPath,
            baseContents: ({ name, description, version }) => ({
                name,
                description,
                version,
                main: 'index.js'
            })
        })
    ]
},