Skip to content

Commit 10801b9

Browse files
committed
refactor: handle class prop and vnode type check in Vue render process
1 parent a6fc361 commit 10801b9

File tree

2 files changed

+77
-15
lines changed

2 files changed

+77
-15
lines changed

books/Vue.js设计与实现/2025-2/render/index-render-plus.html

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,12 @@
3838
},
3939

4040
patchProps(el, key, prevValue, nextValue) {
41+
// 对 class 进行特殊处理
42+
if (key === 'class') {
43+
el.className = nextValue || '';
44+
}
4145
// 使用 shouldSetAsProps 函数判断是否应该作为 DOM Properties 设置
42-
if (shouldSetAsProps(el, key, nextValue)) {
46+
else if (shouldSetAsProps(el, key, nextValue)) {
4347
const type = typeof el[key];
4448
// 如果是布尔类型,并且 value 是空字符串,则将值矫正为 true
4549
if (type === 'boolean' && nextValue === '') {

books/Vue.js设计与实现/2025-2/render/render-plus.js

Lines changed: 72 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,24 @@ function createRenderer(options) {
66
* @param {*} container 容器
77
*/
88
function patch(oldVnode, newVnode, container) {
9-
// 如果 n1 不存在,意味着挂载,则调用 mountElemnet 函数完成挂载
10-
if (!oldVnode) {
11-
mountElement(newVnode, container);
12-
} else {
13-
// n1 存在,意味着打补丁
9+
// 如果 oldVnode 存在, 则对比 oldVnode 和 newVnode 的类型
10+
if (oldVnode && oldVnode.type !== newVnode.type) {
11+
// 如果新旧 vnode 的类型不同,则直接将旧 vnode 卸载
12+
unmount(oldVnode);
13+
oldVnode = null;
14+
}
15+
16+
// 代码运行到这里, 证明 oldVnode 和 newVnode 所描述的内容相同
17+
const { type } = newVnode;
18+
19+
if (typeof type === 'string') {
20+
// 如果 oldVnode 不存在,意味着挂载,则调用 mountElemnet 函数完成挂载
21+
if (!oldVnode) {
22+
mountElement(newVnode, container);
23+
} else {
24+
patchElement(oldVnode, newVnode);
25+
}
26+
} else if (typeof type === 'object') {
1427
}
1528
}
1629

@@ -20,7 +33,11 @@ function createRenderer(options) {
2033
// 处理子节点,,如果子节点是字符串,代表元素具有文本节点
2134

2235
if (typeof vnode.children === 'string') {
23-
el.textContent = vnode.children;
36+
options.setElementText(el, vnode.children);
37+
} else if (Array.isArray(vnode.children)) {
38+
vnode.children.forEach(child => {
39+
patch(null, child, el);
40+
});
2441
}
2542

2643
// 如果 vnode.props 存在才处理下
@@ -38,19 +55,27 @@ function createRenderer(options) {
3855
options.insert(el, container);
3956
}
4057

58+
function unmount(vnode) {
59+
const parent = vnode.el.parentNode;
60+
if (parent) {
61+
// eslint-disable-next-line unicorn/prefer-dom-node-remove
62+
parent.removeChild(vnode.el);
63+
}
64+
}
65+
4166
function render(vnode, container) {
4267
if (vnode) {
4368
// 新 vnode 存在,将其与旧 vnode 一起传递给 patch 函数,进行打补丁
44-
patch(container.__vnode, vnode, container);
69+
patch(container._vnode, vnode, container);
4570
} else {
46-
if (container.__vnode) {
47-
// 旧值 存在,且新 vnode 不存在,说明是卸载(unmount)操作
48-
// 只需要将 container 内的 DOM 清空即可
49-
container.innerHTML = '';
71+
// eslint-disable-next-line no-lonely-if
72+
if (container._vnode) {
73+
// 调用 unmount 函数卸载 vnode
74+
unmount(container._vnode);
5075
}
51-
52-
container.__vnode = vnode;
5376
}
77+
78+
container._vnode = vnode;
5479
}
5580

5681
function hydrate(vnode, container) {}
@@ -61,4 +86,37 @@ function createRenderer(options) {
6186
};
6287
}
6388

64-
export { createRenderer };
89+
/**
90+
* 处理 class 样式
91+
* @param {*} cls
92+
* @returns
93+
*/
94+
function normalizeClass(cls) {
95+
let result = '';
96+
97+
const normalizeObj = cls => {
98+
for (const [className, sign] of Object.entries(cls)) {
99+
if (sign) {
100+
result += ` ${className}`;
101+
}
102+
}
103+
};
104+
105+
if (typeof cls === 'string') {
106+
result = cls;
107+
} else if (Array.isArray(cls)) {
108+
for (const clsObj of cls) {
109+
if (typeof clsObj === 'string') {
110+
result += ` ${clsObj}`;
111+
} else if (typeof clsObj === 'object' && clsObj !== null) {
112+
normalizeObj(cls);
113+
}
114+
}
115+
} else {
116+
normalizeObj(cls);
117+
}
118+
119+
return result.trim();
120+
}
121+
122+
export { createRenderer, normalizeClass };

0 commit comments

Comments
 (0)