React.createElement
jsx 仅仅只是 React.createElement(component, props, ...children) 函数的语法糖,
所有的jsx最终都会通过babel被转换成React.createElement的函数进行调用
Hello Wolrd
React.createElement('h2', {
classname: 'foo'
}, 'Hello World')
React.createElement的源码
// 存储一些特殊的react属性值
const RESERVED_PROPS = {
key: true,
ref: true,
__self: true,
__source: true,
}
// createElement的部分源码
export function createElement(type, config, children) {
// ...
// props是实际存在内容的对象
const props = {};
// 属性不为null的时候,将属性依次挂载到props上
if (config != null) {
for (propName in config) {
if (
// 如果config自身有propName属性,且propName属性不是特殊的react属性的时候
// 将该属性挂载到props上
hasOwnProperty.call(config, propName) &&
!RESERVED_PROPS.hasOwnProperty(propName)
) {
props[propName] = config[propName];
}
}
}
// 从第三个参数开始就是子元素,使用arguments接收所有的实参
// 并将所有的子元素挂载到props.children上
// 也就是说子元素在传递的时候使用 React.createElement(type, config, child1, child2, child3, ...)
const childrenLength = arguments.length - 2;
if (childrenLength === 1) {
props.children = children;
} else if (childrenLength > 1) {
// const childArray = Array(childrenLength); === const childArray = new Array(childrenLength);
// 在使用Array构造器创建数组的时候,new关键字可以省略
const childArray = Array(childrenLength);
for (let i = 0; i < childrenLength; i++) {
childArray[i] = arguments[i + 2];
}
props.children = childArray;
}
// 调用ReactElement方法返回一个ReactElement对象,也就是vNode对象
return ReactElement(
type,
props,
// ...
);
}
hasOwnProperty
--- hasOwnProperty()
方法会返回一个布尔值,指示对象自身属性中是否具有指定的属性
const obj = {name: 'Klaus'};
obj.__proto__.age = 23
// hasOwnProperty的参数为对应的属性名
console.log(obj.hasOwnProperty('name')) // => true
console.log(obj.hasOwnProperty('age')) // => false
// 注意: 如果hasOwnProperty方法的参数是一个没有定义的对象,那么脚本并不会报错,而是直接返回false
console.log(obj.hasOwnProperty(name)) // => false
ReactElement
通过 React.createElement方法 最终创建出来一个 ReactElement对象
而ReactElement对象的props.children属性中记录这当前元素的所有子元素
因此ReactElement本质就是vNode,其实也是vDom
React利用ReactElement对象组成了一个JavaScript的对象树,而这个JS对象树就是大名鼎鼎的虚拟DOM(Virtual DOM)
jsx
Hello Wolrd
Lorem ipsum dolor sit amet.
经过编译后,会形成如下vDom对象
React会将jsx编译为在内存中运行的vDOM,随后在通过ReactDOM.render
将vDOM编译成对应的真实DOM
vdom的作用
1. 操作真实DOM性能较低
在JavaScript中,通过React.createElement
创建出来的js对象是一个非常庞大的对象
并且DOM操作会引起浏览器的回流和重绘,所以频繁的DOM操作必然会导致性能的损失
而传统的开发模式不可避免的会需要进行频繁的DOM操作
而vDom本质就是一个js对象,这个对象是对真实DOM在内存中的抽象和模拟
vDom在结构上和真实DOM是一一映射的
而vDOM是存在于内存中的,且vDOM比真实DOM更为的轻量级,操作起来性能也更高
因为vDOM是在内存中运行的,我们可以将所有的更新在内存中统一进行修改后,一次性映射到真实DOM中
这样就避免了频繁的操作DOM,引起的重绘和回流
2.很难跟踪状态发生的改变
传统的开发中,跟踪界面中状态的改变是十分麻烦的,只能通过debugger和console
但是有了vDOM后,React DevTool就可以通过新旧vDOM的对比,进行页面状态的改变的跟踪
3.vDOM便于跨平台
UI在真正渲染之前会先被编译为vdom,
此时就可以根据不同的平台渲染成不同的内容
例如:
在web端,使用web端的ReactDOM.render,将VDOM渲染为真实DOM
在移动端,使用移动端的ReactDOM.render, 将VDOM渲染为原生控件( 如IOS的UIButton,Android的Button)
4 声明式编程
使用了vDOM后,UI以一种理想化或者说虚拟化的方式保存在内存中,并且它是一个相对简单的JavaScript对象
我们可以通过ReactDOM.render让 虚拟DOM 和 真实DOM同步起来,这个过程中叫做协调(Reconciliation)
因为大部分的DOM操作都交给React来帮助我们进行管理
所以这就意味着我们只需要维护和定义对应的状态即可,React会确保DOM和这些状态是匹配的
我们不需要直接进行DOM操作,可以从手动更改DOM、属性操作、事件处理中解放出来
阶段案例
- 以表格形式显示书籍数据
- 底部显示书籍总价
- 点击+或-可以更改数据数量,如果数量为1的时候,静止减少
- 点击移除按钮可以移除对应数据,如果全部移除,显示购物车为空
Document