01 | JSX 代码是如何“摇身一变”成为 DOM 的?
JSX 的本质是什么
JSX 是 JavaScript 的一种语法扩展,它和模板语言很接近,但是它充分具备 JavaScript 的能力,JSX 的本质是React.createElement这个 JavaScript 调用的语法糖。
JSX 是如何映射为 DOM
1. JSX 标签都被Babel转化成了 React.createElement 调用;
2. createElement 执行到最后会 return 一个针对 ReactElement 的调用;
3. ReactElement 把传入的参数按照一定的规范,“组装”进了 element 对象里,这个 ReactElement 对象实例,本质上是以 JavaScript 对象形式存在的对 DOM 的描述,也就是“虚拟 DOM”;
4. 通过ReactDOM.render最终渲染到DOM容器中;
如下图:
JSX 背后的功能模块是什么,这个功能模块都做了哪些事情?
createElement
入参:type、config、children;
出参:针对 ReactElement 的调用
函数体拆解:
1.特殊处理React 元素的属性key、ref、self、source ‘
2.遍历config ,提取属性到props;
3.提取子元素,推到props.children;
4.格式化defaultProps;
5.返回ReactElement 的调用 ;
如下图:
代码:
/**
101. React的创建元素方法
*/
export function createElement(type, config, children) {
// propName 变量用于储存后面需要用到的元素属性
let propName;
// props 变量用于储存元素属性的键值对集合
const props = {};
// key、ref、self、source 均为 React 元素的属性,此处不必深究
let key = null;
let ref = null;
let self = null;
let source = null;
// config 对象中存储的是元素的属性
if (config != null) {
// 进来之后做的第一件事,是依次对 ref、key、self 和 source 属性赋值
if (hasValidRef(config)) {
ref = config.ref;
}
// 此处将 key 值字符串化
if (hasValidKey(config)) {
key = '' + config.key;
}
self = config.__self === undefined ? null : config.__self;
source = config.__source === undefined ? null : config.__source;
// 接着就是要把 config 里面的属性都一个一个挪到 props 这个之前声明好的对象里面
for (propName in config) {
if (
// 筛选出可以提进 props 对象里的属性
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// childrenLength 指的是当前元素的子元素的个数,减去的 2 是 type 和 config 两个参数占用的长度
const childrenLength = arguments.length - 2;
// 如果抛去type和config,就只剩下一个参数,一般意味着文本节点出现了
if (childrenLength === 1) {
// 直接把这个参数的值赋给props.children
props.children = children;
// 处理嵌套多个子元素的情况
} else if (childrenLength > 1) {
// 声明一个子元素数组
const childArray = Array(childrenLength);
// 把子元素推进数组里
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
// 最后把这个数组赋值给props.children
props.children = childArray;
}
// 处理 defaultProps
if (type && type.defaultProps) {
const defaultProps = type.defaultProps;
for (propName in defaultProps) {
if (props[propName] === undefined) {
props[propName] = defaultProps[propName];
}
}
}
// 最后返回一个调用ReactElement执行方法,并传入刚才处理过的参数
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
ReactElement
入参:type, key, ref, self, source, owner, props
出参:element
函数体:ReactElement 把传入的参数按照一定的规范,“组装”进了 element 对象里,也就是虚拟dom,并把它返回给了 React.createElement
如下图:
代码:
const ReactElement = function(type, key, ref, self, source, owner, props) { const element = { // REACT_ELEMENT_TYPE是一个常量,用来标识该对象是一个ReactElement $$typeof: REACT_ELEMENT_TYPE, // 内置属性赋值 type: type, key: key, ref: ref, props: props, // 记录创造该元素的组件 _owner: owner, }; if (__DEV__) { // 这里是一些针对 __DEV__ 环境下的处理,对于大家理解主要逻辑意义不大,此处我直接省略掉,以免混淆视听 } return element; };
ReactDOM.render
入参:element,container,[callback]
出参:element会被挂载到container容器中。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· winform 绘制太阳,地球,月球 运作规律
2018-12-28 React-propsType和defaultProps