第二章 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'
})
})
]
},