编译阶段-jsx-ast
构建AST
jsx语法的html结构通过babel解析转换为ast语法树结构
- react 18中:使用 babel将jsx代码转换为root.render()调用,此时得到转化后的Ast树结构
- react17中 使用 babel将jsx代码转换为React.createElement()调用,此时得到转化后的Ast树结构
简单实例1:babel将jsx代码转换为React.createElement()调用
测试:https://www.babeljs.cn/repl
// jsx
const element = <h1 style = {{"color": "red"}}>123
<div>test</div>
</h1>
// babel 编译过后生成的代码
const element = /*#__PURE__*/React.createElement("h1", {
style: {
"color": "red"
}
}, "123", /*#__PURE__*/React.createElement("div", null, "test"));
简单实例2:直接用转化后的代码去调用React.createElement
渲染代码:example-code\例-1-jsx-ast-r17.html
<script>
const container = document.getElementById('root');
const element = React.createElement(
'div',
{ className: 'red' },
'Click Me'
)
console.log('createElement 转化后ast树的结构:', element)
ReactDOM.render(element, container)
/*
{
$$typeof: Symbol(react.element)
key: null
props:
children: "Click Me"
className: "red"
[[Prototype]]: Object
ref: null
type: "div"
_owner: null
_store: {validated: false}\
}
*/
</script>
简单实例3:使用babel
渲染代码:example-code\例4-在html-使用babel转义jsx.html
<script type="text/babel">
function Test() {
return (
<div className="red">Click Me</div>
)
}
ReactDOM.render(<Test />, document.getElementById('root'))
</script>
完整测试例子
1.测试代码
例子基于react18,先看babel 的执行函数
function transform(code, options) {
console.log('=Babel-transform-参数:', { code })
const babel_transform = Babel.transform(code, processOptions(options));
console.log('=Babel-返回:', babel_transform)
return babel_transform
}
<script type="text/babel">
console.log('=Babel:', Babel)
function Test() {
console.log('test-render')
const [data, setData] = React.useState('改变我')
const [showDiv, setShowDiv] = React.useState(false)
const onClickText = () => {
console.log('=useState=onClick');
setData('努力哦')
setShowDiv(!showDiv)
}
const onClickText2 = () => {
console.log('=useState=onClick:', data);
}
React.useEffect(() => {
console.log('=副作用-useEffect-->运行');
}, [])
React.useLayoutEffect(() => {
console.log('=副作用-useLayoutEffect-->运行');
}, [])
return (
<div id='div1' className='c1'>
<button onClick={onClickText} className="btn">Hello world,Click me</button>
<span>{data}</span>
{showDiv && <div>被你发现了</div>}
<div id='div2' className='c2'>
<p>测试子节点</p>
</div>
</div>
)
}
const root = ReactDOM.createRoot(document.getElementById('root'))
console.log("=app=root:", root)
root.render(<Test />);
</script>
2.render阶段-babel生成的AST.json
{
"metadata":"",
"options":"",
"ignored":"",
"code":"",
"ast":"",
"map":""
}
3.code函数
每个节点都调用createElement(),并且嵌套结构都是和html节点一样
'use strict';
var _slicedToArray = function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i["return"]) _i["return"](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError("Invalid attempt to destructure non-iterable instance"); } }; }();
console.log('=Babel:', Babel);
function Test() {
console.log('test-render');
var _React$useState = React.useState('改变我'),
_React$useState2 = _slicedToArray(_React$useState, 2),
data = _React$useState2[0],
setData = _React$useState2[1];
var _React$useState3 = React.useState(false),
_React$useState4 = _slicedToArray(_React$useState3, 2),
showDiv = _React$useState4[0],
setShowDiv = _React$useState4[1];
var onClickText = function onClickText() {
console.log('=useState=onClick');
setData('努力哦');
setShowDiv(!showDiv);
};
var onClickText2 = function onClickText2() {
console.log('=useState=onClick:', data);
};
React.useEffect(function () {
console.log('=副作用-useEffect-->运行');
}, []);
React.useLayoutEffect(function () {
console.log('=副作用-useLayoutEffect-->运行');
}, []);
return React.createElement(
'div',
{ id: 'div1', className: 'c1' },
React.createElement(
'button',
{ onClick: onClickText, className: 'btn' },
'Hello world,Click me'
),
React.createElement(
'span',
null,
data
),
showDiv && React.createElement(
'div',
null,
'\u88AB\u4F60\u53D1\u73B0\u4E86'
),
React.createElement(
'div',
{ id: 'div2', className: 'c2' },
React.createElement(
'p',
null,
'\u6D4B\u8BD5\u5B50\u8282\u70B9'
)
)
);
}
var root = ReactDOM.createRoot(document.getElementById('root'));
console.log("=app=root:", root);
root.render(React.createElement(Test, null));
createElement()
function createElement(type, config, children) {
// console.log('=development调用createElement构建Ast树:', { type, config, children })
console.log('%c=development调用createElement-1:type', 'color:blueviolet', type, { config, children })
var propName; // Reserved names are extracted
var props = {};
var key = null;
var ref = null;
var self = null;
var source = null;
if (config != null) {
if (hasValidRef(config)) {
ref = config.ref;
{
warnIfStringRefCannotBeAutoConverted(config);
}
}
if (hasValidKey(config)) {
{
checkKeyStringCoercion(config.key);
}
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source; // Remaining properties are added to a new props object
for (propName in config) {
if (hasOwnProperty.call(config, propName) && !RESERVED_PROPS.hasOwnProperty(propName)) {
props[propName] = config[propName];
}
}
} // Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
var childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
var childArray = Array(childrenLength);
for (var i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
{
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
} // Resolve default props
if (type && type.defaultProps) {
var defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
{
if (key || ref) {
var displayName = typeof type === 'function' ? type.displayName || type.name || 'Unknown' : type;
if (key) {
defineKeyPropWarningGetter(props, displayName);
}
if (ref) {
defineRefPropWarningGetter(props, displayName);
}
}
}
const reactElementRes = ReactElement(type, key, ref, self, source, ReactCurrentOwner.current, props);
console.log('=development调用createElement-2:返回:', reactElementRes)
return reactElementRes
}