JSX 本质
JSX 仅仅是 React.createElement(type, config, children) 函数的语法糖。
注:语法糖(Syntactic sugar),指计算机语言中添加的某种语法,这种语法对语言的功能没有影响,但是更方便程序员使用。语法糖让程序更加简洁,有更高的可读性。
所有的 JSX 最后会被转换为 React.createElement() 的函数调用。
转换示例:
JSX代码:
<div>
<p>plan01:</p>
<h2>{this.state.message}</h2>
<ul>{moviesList}</ul>
<p>plan02:</p>
<h2>{this.state.message}</h2>
<ul>
{this.state.moviesList.map(item => {
return <li>{item}</li>;
})}
</ul>
</div>
对应的 React.createElement() 书写方式:
/*#__PURE__*/
React.createElement("div", null,
/*#__PURE__*/React.createElement("p", null, "plan01:"),
/*#__PURE__*/React.createElement("h2", null, (void 0).state.message),
/*#__PURE__*/React.createElement("ul", null, moviesList), /*#__PURE__*/React.createElement("p", null, "plan02:"),
/*#__PURE__*/React.createElement("h2", null, (void 0).state.message),
/*#__PURE__*/React.createElement("ul", null, (void 0).state.moviesList.map(item => {
return /*#__PURE__*/React.createElement("li", null, item);
})));
React.createElement(type, config, children) 参数解析
- type
- 当前 ReactElement 的类型
- HTML 原生元素,使用元素名称表示,比如:“div”,“span”等
- 自定义组件元素,使用自定义组件名称表示
- config
- 所有 JSX 中 HTML 元素属性在 config 中以对象形式的键值对存放
- children
- 存放 type 的元素内容
children 参数处理
阅读 React.createElement() 函数表达式可知,children 参数只能接收一个元素内容,那么如果元素内容包含多个元素该如何区分呢?
实际上,当元素内容只是一段文本或是单个元素时,children 正常匹配,然后接收。
当元素内容为多个元素时,children 将以数组格式存放。
具体见 React.createElement() 源码处理(只截取了 处理 children 参数部分):
export function createElement(type, config, children) {
let propName;
// Reserved names are extracted
const props = {};
let key = null;
let ref = null;
let self = null;
let source = null;
// Children can be more than one argument, and those are transferred onto
// the newly allocated props object.
// 当接收多个参数时,刨去 type, config 剩余参数即对应 chilren
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
if (__DEV__) {
if (Object.freeze) {
Object.freeze(childArray);
}
}
props.children = childArray;
}
return ReactElement(
type,
key,
ref,
self,
source,
ReactCurrentOwner.current,
props,
);
}
Virtual DOM(虚拟DOM)
React.createElement() 函数最终会返回一个 ReactElement 对象(见上述代码末尾)。
ReactElement 对象作用:React 利用 ReactElement 对象组成一个 JavaScript 对象树,即 Virtual DOM (虚拟 DOM)。
浏览器内查看 ReactElement 对象:
与下述代码对比查看:
<div>
<p>plan01:</p>
<h2>{this.state.message}</h2>
<ul>{moviesList}</ul>
<p>plan02:</p>
<h2>{this.state.message}</h2>
<ul>
{this.state.moviesList.map(item => {
return <li>{item}</li>;
})}
</ul>
</div>
JSX 到 真实 DOM 过程
为什么使用 Virtual DOM
使用 真实 DOM 开发的弊端:
- 使用真实 DOM 很难跟踪状态
- 操作真实 DOM 的性能较低
- document.createElement() 创建的复杂对象不方便操作
- 真实 DOM 操作会引起浏览器的回流和重绘,降低浏览器的渲染效率
使用 Virtual DOM 可以有效解决上述问题。
Virtual DOM 是一种编程概念。在此概念里,UI 以一种理想化或者说虚拟化的方式被保存在内存中,通过如 ReactDOM 等类库使之与真实 DOM 同步。这一过程称为协调(Reconciliation)。
Virtual DOM 可以帮助程序员的开发模式,从命令式编程转换为声明式编程。
数据发生变化时,React 通过操作相对简单的 Virtual DOM 对象,借用 Virtual DOM 与 真实 DOM 的映射,进而批量修改页面元素。