0%

React

CreateElement

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
// 虚拟 DOM 结构
const element = {
type: "div", // 标签名
props: {
// 节点属性,包含 children
title: "foo", // title 属性
children: "hello", // 子节点,注:实际上这里应该是数组结构,帮助我们存储更多子节点
},
};

const createElement = (type, props, ...children) => {
return {
type,
props: {
...props,
children: children.map((child) =>
typeof child === "object" ? child : createTextElement(child)
),
},
};
};

const createTextElement = (text) => {
return {
type: "TYPE_TEXT",
props: {
nodeValue: text,
children: [],
},
};
};

Render

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
// 虚拟Dom,添加到真实节点
function render(element, container) {
const dom =
element.type === "TEXT_ELEMENT"
? document.createTextNode("")
: document.createElement(element.type);
// 遍历所有子节点,并进行渲染
element.props.children.forEach((child) => render(child, dom));
const isProperty = (key) => key !== "children";
// 将element的props属性添加到dom
Object.keys(element.props)
.filter(isProperty)
.forEach((name) => {
dom[name] = element.props[name];
});
container.appendChild(dom);
}

并发模式 (requestIdleCallback)

fiber

虚拟 Dom

构建虚拟 Dom

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
console.log(
el("div", { id: "container" }, [
el("h1", { style: "color: red" }, ["simple virtal dom"]),
])
);

function Element(tagName, props = {}, children = []) {
// 标签名
this.tagName = tagName;
// 属性对象
this.props = props;
// 子节点
this.children = children;
// key标志
const { key = void 666 } = this.props;
this.key = key;

// 子节点数量
let count = 0;
this.children.forEach((child, index) => {
if (child instanceof Element) {
count += child.count;
}
count++;
});
this.count = count;
}

虚拟 Dom 转换为真实 Dom

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
Element.prototype.render = function () {
// 根据tag创建一个标签
const el = document.createElement(this.tagName);
const props = this.props;
// 遍历props给标签赋属性
for (const propName in props) {
const propValue = props[propName];
_.setAttr(el, propName, propValue);
}
// 遍历子结点
this.children.forEach((child) => {
let childEl;
// 如果子结点的原型指向El 继续创建标签 属性赋值
if (child instanceof Element) {
// 进行递归子结点 el("h1",{style:"color:red"})
childEl = child.render();
} else {
// 子结点非指向EL,就创建文本标签
childEl = document.createTextNode(child);
}
el.appendChild(childEl);
});

return el;
};

填充页面

1
2
3
4
5
document.body.appendChild(
el("div", { id: "container" }, [
el("h1", { style: "color: red" }, ["simple virtal dom"]),
]).render()
);

对比两棵树的差距

当新旧节点都是字符串类型时,直接替换

1
2
3
if (_.isString(oldNode) && _.isString(newNode)) {
currentNode.push({ type: patch.type, content: newNode });
}

新旧节点标签名和 key 相同,对比 porps 和 children

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
if (oldNode.tagName === newNode.tagName && oldNode.key === newNode.key) {
// Diff props
var propsPatches = diffProps(oldNode, newNode);
if (propsPatches) {
currentPatch.push({ type: patch.PROPS, props: propsPatches });
}
// Diff children. If the node has a `ignore` property, do not diff children
if (!isIgnoreChildren(newNode)) {
diffChildren(
oldNode.children,
newNode.children,
index,
patches,
currentPatch
);
}
}

对真实 DOM 进行修改

据 diff 结果,对真实 DOM 进行修改。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function patch(node, patches) {
var walker = { index: 0 };
dfsWalk(node, walker, patches);
}

function dfsWalk(node, walker, patches) {
var currentPatches = patches[walker.index];

var len = node.childNodes ? node.childNodes.length : 0;
for (var i = 0; i < len; i++) {
var child = node.childNodes[i];
walker.index++;
dfsWalk(child, walker, patches);
}

if (currentPatches) {
applyPatches(node, currentPatches);
}
}

hooks