Skip to content

Commit 8a5017a

Browse files
committed
chore: adjust patch
1 parent 48f0508 commit 8a5017a

File tree

5 files changed

+159
-6
lines changed

5 files changed

+159
-6
lines changed

demo/24-patch.html

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
<style>
2+
.red { color: red; }
3+
.green { color: green; }
4+
</style>
5+
6+
<body>
7+
<div id="app"></div>
8+
</body>
9+
10+
<script>
11+
function h(tag, props, children) {
12+
return {
13+
tag,
14+
props,
15+
children,
16+
}
17+
}
18+
19+
function mount(vnode, container) {
20+
const el = vnode.el = document.createElement(vnode.tag);
21+
vnode.parent = container
22+
// handle props
23+
if (vnode.props) {
24+
for (let key in vnode.props) {
25+
if (key.startsWith('on')) { // 事件绑定
26+
const eventName = key.slice(2).toLowerCase();
27+
el.addEventListener(eventName, vnode.props[key]);
28+
} else {
29+
el.setAttribute(key, vnode.props[key]);
30+
}
31+
}
32+
}
33+
// handle children
34+
if (vnode.children) {
35+
if (Array.isArray(vnode.children)) {
36+
vnode.children.forEach(child => {
37+
mount(child, el);
38+
});
39+
} else { // text node
40+
el.textContent = vnode.children;
41+
}
42+
}
43+
44+
container.appendChild(el);
45+
}
46+
47+
const vdom = h('div', { class: 'red' }, [
48+
h('span', null, 'hello')
49+
])
50+
51+
mount(vdom, document.getElementById('app'))
52+
53+
function patch(n1, n2) {
54+
// there are lots of assumption and only handle simpler cases
55+
56+
// patch tag
57+
if (n1.tag === n2.tag) {
58+
const el = n1.el
59+
// patch props
60+
const oldProps = n1.props || {}
61+
const nweProps = n2.props || {}
62+
for (const key in nweProps) {
63+
const oldValue = oldProps[key]
64+
const newValue = nweProps[key]
65+
if (oldValue !== newValue) {
66+
el.setAttribute(key, newValue)
67+
}
68+
}
69+
// patch children
70+
const oldChildren = n1.children
71+
const newChildren = n2.children
72+
if (typeof newChildren === 'string') {
73+
if (typeof oldChildren === 'string') {
74+
if (newChildren !== oldChildren) {
75+
el.textContent = newChildren
76+
}
77+
} else {
78+
// oldChildren is a array, but we can just override it
79+
el.textContent = newChildren
80+
}
81+
} else {
82+
if (typeof oldChildren === 'string') {
83+
// discard oldChildren
84+
el.innerHTML = ''
85+
// mount newChildren
86+
newChildren.forEach(child => {
87+
mount(child, el)
88+
})
89+
} else {
90+
// both old and new is a array
91+
92+
const commonLength = Math.min(oldChildren.length, newChildren.length)
93+
for (let i = 0; i < commonLength; i++) {
94+
patch(oldChildren[i], newChildren[i])
95+
}
96+
if (newChildren.length > oldChildren.length) {
97+
// to new node, just mount it
98+
newChildren.slice(oldChildren.length).forEach(child => {
99+
mount(child, el)
100+
})
101+
} else if (newChildren.length < oldChildren.length) {
102+
oldChildren.slice(newChildren.length).forEach(child => {
103+
// unmount old el
104+
el.removeChild(child.el)
105+
})
106+
}
107+
}
108+
}
109+
} else {
110+
// ... replace
111+
112+
// unmount old dom
113+
n1.parent.removeChild(n1.el)
114+
// mount new dom
115+
mount(n2, n1.parent)
116+
}
117+
}
118+
119+
const vdom2 = h('div', { class: 'green' }, [
120+
h('span', null, 'changed!')
121+
])
122+
123+
setTimeout(() => {
124+
patch(vdom, vdom2)
125+
}, 1000)
126+
127+
</script>
File renamed without changes.

public/patch.excalidraw.png

8.32 KB
Loading

slides.md

Lines changed: 32 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1351,7 +1351,7 @@ const ast = {
13511351
}
13521352
```
13531353

1354-
定义节点类型:
1354+
定义节点类型枚举:
13551355

13561356
```js
13571357
const NodeTypes = {
@@ -2415,9 +2415,36 @@ createApp({
24152415

24162416
---
24172417

2418+
`patch` 函数的实现如下:
2419+
2420+
```js
2421+
/**
2422+
* @param {*} n1 old vnode
2423+
* @param {*} n2 new vnode
2424+
*/
2425+
function patch(n1, n2) {
2426+
if (n1.tag === n2.tag) {
2427+
// ...
2428+
} else {
2429+
// ...
2430+
}
2431+
}
2432+
```
2433+
2434+
具体代码和demo见: `24-patch.html`
2435+
2436+
---
2437+
2438+
现在,我们已经实现了 **挂载****更新**,整合之前的代码,接下来再来看下 **计数器** demo 的效果。
2439+
2440+
---
2441+
24182442
## Counter 计数器
24192443

2420-
demo: `22-counter.html`
2444+
demo: `25-counter.html`
2445+
2446+
> 打开 DevTools 的 Elements 查看效果
2447+
24212448

24222449
<div grid="~ cols-2 gap-2">
24232450

@@ -2497,9 +2524,9 @@ function createApp(options = {}) {
24972524
<div id="app"></div>
24982525
```
24992526

2500-
```html
2501-
<script>
2502-
// demo: 24-render-function-options.html
2527+
```js
2528+
// demo: 26-render-function-options.html
2529+
25032530
const { createApp, ref, h } = MiniVue
25042531
createApp({
25052532
setup() {
@@ -2521,7 +2548,6 @@ createApp({
25212548
])
25222549
}
25232550
}).mount('#app')
2524-
</script>
25252551
```
25262552

25272553
</div>

0 commit comments

Comments
 (0)