React 虚拟DOM创建渲染(粗略)
Galloping_Leo 2021-03-21 React
# React 虚拟 DOM 创建及渲染
本文涉及
React
的虚拟DOM
创建及渲染的部分简化逻辑,只体现其思想,并不是完全的源码。
# 虚拟 DOM 的结构
一个名为 Test 的组件实例结构如图所示
这里我们只涉及 props
的 以及标识组件类型的 type
属性。
{
props:{/**/},
type: /**/
}
1
2
3
4
2
3
4
首先是 React.createElement(type, config, ...children)
函数,他接收多个参数如下表,它负责将组件的描述对象的值拷贝到 props
上。
参数 | 描述 |
---|---|
type | 该虚拟DOM 的类型,可能的值有普通的html标签值,还有 React 函数组件、类组件。 |
config | 该虚拟DOM 的描述对象,该对象可以有 className 、style 等属性,其余的可随意传。 |
...children | 该虚拟DOM 的子组件。 |
assemble
函数将 type
和 props
整合成一个对象用来描述 DOM。
Component
组件为 React
组件的父组件,该组件提供 isReactComponent
静态属性,标识它是一个类组件。
实现代码如下:
function createElement(type, config, ...children) {
let props = {};
if (typeof config === "object" && config !== null) {
for (const key in config) {
props[key] = config[key];
}
}
if (children.length === 1) {
props.children = children[0];
} else if (children.length > 1) {
props.children = Array.from(children);
}
return assemble(type, props);
}
class Component {
static isReactComponent = true;
constructor(props) {
this.props = props;
}
// ......
}
function assemble(type, props) {
return {
type,
props,
};
}
// .....
export default { createElement, Component };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
// 根据 组件的 `type` 值不同惊醒不同的渲染,
function render(vElement, domParent) {
if (typeof vElement === "string" || typeof vElement === "number") {
domParent.appendChild(document.createTextNode(vElement));
return;
}
let type = vElement.type;
let props = vElement.props;
// 为类组件
if (type.isReactComponent) {
let element = new type(props).render();
type = element.type;
props = element.props;
}else if (typeof type === "function") {
//函数组件
let element = type(props);
type = element.type;
props = element.props;
}
// 字面量形式组件
let domElement = document.createElement(type);
for (const key in props) {
//添加样式
if (key === "style") {
let styles = props[key];
let stylesString = "";
for (const key in styles) {
const newKey = transStyleName(key);
let str = `${newKey}:${styles[key]};`;
stylesString += str;
}
domElement.setAttribute("style", stylesString);
} else if (key === "children") {
// 添加子组件
const children =
props.children instanceof Array ? props.children : [props.children];
for (let i = 0; i < children.length; i++) {
const element = children[i];
// 递归 render 生成组件
render(element, domElement);
}
} else if (key === "className") {
// 添加class 属性
domElement.setAttribute("class", props.className);
} else {
// 添加其他自定义属性
domElement.setAttribute(key, props.className);
}
}
// 挂载到目标组件元素上
domParent.appendChild(domElement);
}
//该驼峰命名为 a-b-c 形式
function transStyleName(name) {
return name.replace(/[(A-Z)]/g, function (target) {
return "-" + target.toLowerCase();
});
}
export default { render };
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
// 测试正确性
import React from "./React/react";
import ReactDom from "./React/react-dom";
let vElement1 = React.createElement(
"div",
{
className: "box",
style: {
color: "red",
},
},
"hello",
React.createElement("span", null, "world")
);
function Test1(props) {
return React.createElement(
"div",
{
className: "functiontest",
id: "functiontest",
style: {
color: "hotpink",
fontSize: "30px",
},
},
props.a,
React.createElement("div", null, props.b)
);
}
let vElement2 = React.createElement(Test1, { a: "hello", b: "world" });
class Test2 extends React.Component {
render() {
return React.createElement(
"div",
{
className: "functiontest",
id: "functiontest",
style: {
color: "hotpink",
fontSize: "30px",
},
},
this.props.a,
React.createElement("div", null, this.props.b)
);
}
}
let vElement3 = React.createElement(Test2, { a: "hello", b: "world" });
ReactDom.render(vElement3, document.getElementById("app"));
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53