💡持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天
💡 代码在github: github.com/babachao/Re…
1.1 先写个简单的main.jsx
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>react18源码学习</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="./src/main.jsx"></script>
</body>
</html>
// 入口文件
let element = (
<h1 ref="h1">
可爱的<span style={{ color: "#000" }}>羊巴鲁</span>
</h1>
);
console.log(element);
💡 Tips: 1、实现一个jsxDEV方法,将main.jsx内容编译成图1。
2、在项目中新建src文件夹,再再src中新建main.jsx文件和react文件夹。
例:src/main.jsx
1.2 jsx-dev-runtime.js
💡 Tip: 按照源码目录在src/react/jsx-dev-runtime.js
// babel会自动递归,生成ReactDOM
export { jsxDEV } from './src/jsx/ReactJSXElement';
1.3 ❗实现jsxDEV方法❗❗
1.3.1 公用方法:hasOwnProperty.js
💡 Tip: 在src新建---> src/shared/hasOwnProperty.js
// 将原型上的hasOwnProperty方法,结构出来使用
const { hasOwnProperty } = Object.prototype;
export default hasOwnProperty;
1.3.2 公用方法:ReactSymbols.js
💡 Tip: 在src新建---> src/shared/ReactSymbols.js
// 表示此虚拟DOM的类型是一个React元素
export const REACT_ELEMENT_TYPE = Symbol.for('react.element');
1.3.3 ReactJSXElement.js -核心内容
💡 Tip: 在src/react中新建---> src/react/src/jsx/ReactJSXElement.js
import hasOwnProperty from 'shared/hasOwnProperty';
import { REACT_ELEMENT_TYPE } from 'shared/ReactSymbols'
// 保留属性,为true的不会放到prop对象中
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
};
/**
* @description: 判断config中的key是否合法
* @param {*} config jsxDev方法中的config参数
*/
function hasValidKey(config) {
return config.key !== undefined;
}
/**
* @description: 判断config中的ref是否合法
* @param {*} config jsxDev方法中的config参数
*/
function hasValidRef(config) {
return config.ref !== undefined;
}
/**
* @description: 生成reactDOM,也是虚拟DOM
* */
function ReactElement(type, key, ref, props) {
return {
$$typeof: REACT_ELEMENT_TYPE, // 代表这个虚拟DOM的类型是reactDOM
type, // dom-> h1, div, span ~~
key, // 唯一标识
ref, // 作用是获取真实dom元素
props, // children, id, style
};
}
/**
* @description: 把jsx转换为虚拟DOM的方法, jsxDev() = createElement()
* @param {*} type tab标签名
* @param {*} config children, id, style 等属性
*/
export function jsxDEV(type, config) {
let propName; // 属性名
const props = {}; // 属性对象
let key = null; // 每个虚拟DOM可以有一个可选的key属性,用来区分一个父节点下的不同子节点
let ref = null; // 引入,后面可以通过这个获取真实DOM
// 判断config.key是否合法
if (hasValidKey(config)) {
key = config.key;
}
// 判断config.ref是否合法
if (hasValidRef(config)) {
ref = config.ref;
}
// 遍历config中的属性,将config上的属性拷贝到prop中
for (propName in config) {
// 1、在【RESERVED_PROPS】中将不需要的剔除
if (
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
return ReactElement(type, key, ref, props);
}
1.4 结构目录
react18源码学习
├─ index.html
├─ jsconfig.json
├─ package.json
├─ src
│ ├─ main.jsx
│ ├─ react
│ │ ├─ jsx-dev-runtime.js
│ │ └─ src
│ │ └─ jsx
│ │ └─ ReactJSXElement.js
│ └─ shared
│ ├─ ReactSymbols.js
│ └─ hasOwnProperty.js
└─ vite.config.js
1.5 总结
const babel = require('@babel/core');
const sourceCode = `
<h1 style="color:red;">
<p>hello</p><span style={{ color: "red" }}>world</span>
</h1>`
const result = babel.transform(sourceCode, {
// 老的为runtime: "classic"
// 新的为runtime: "automatic"
plugins: [["@babel/plugin-transform-react-jsx", { runtime: "classic" }]],
});
console.log(result.code);
1.5.1 React 17以前,babel转换是老的写法,转译结果:
1、可以发现React.createElement的参数是(标签,属性对象,子内容/子节点), 2、runtime参数的转换参数也是【classic】经典模式
/*#__PURE__*/
React.createElement("h1", {
style: "color:red;"
}, /*#__PURE__*/React.createElement("p", null, "hello"), /*#__PURE__*/React.createElement("span", {
style: {
color: "red"
}
}, "world"));
1.5.2 React17以后使用了新的转换写法
1、会自动导出jsx使用,runtime参数的转换参数也会改变为【automatic】自动模式 2、结构也变化为jsx(标签,属性对象+子节点),新转换的子节点都存放在children数组中,依此类推下去
import { jsx as _jsx } from "react/jsx-runtime";
import { jsxs as _jsxs } from "react/jsx-runtime";
/*#__PURE__*/
_jsxs("h1", {
style: "color:red;",
children: [
/*#__PURE__*/_jsx("p", {
children: "hello"
}),
/*#__PURE__*/_jsx("span", {
style: {
color: "red"
},
children: "world"
})]
});
1.5.3 过程
- React.createElement 函数所返回的就是一个虚拟 DOM
- 虚拟 DOM 就是一个描述真实 DOM 的纯 JS 对象