Skip to content

Commit 8f0d886

Browse files
committed
✨feat: 完善10.5_ 移除不存在的元素
1 parent 833857c commit 8f0d886

File tree

1 file changed

+300
-0
lines changed

1 file changed

+300
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,300 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="UTF-8">
6+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
7+
<title>Document</title>
8+
</head>
9+
10+
<body>
11+
<div id="app"></div>
12+
13+
<script>
14+
15+
function shouldSetAsProps (el, key, value) {
16+
if (key === 'form' && el.tagName === 'INPUT') return false
17+
return key in el
18+
}
19+
20+
function createRenderer (options) {
21+
22+
const {
23+
createElement,
24+
insert,
25+
setElementText,
26+
patchProps,
27+
createText,
28+
setText
29+
} = options
30+
31+
function mountElement (vnode, container, anchor) {
32+
const el = vnode.el = createElement(vnode.type)
33+
if (typeof vnode.children === 'string') {
34+
setElementText(el, vnode.children)
35+
} else if (Array.isArray(vnode.children)) {
36+
vnode.children.forEach(child => {
37+
patch(null, child, el)
38+
})
39+
}
40+
41+
if (vnode.props) {
42+
for (const key in vnode.props) {
43+
patchProps(el, key, null, vnode.props[key])
44+
}
45+
}
46+
47+
insert(el, container, anchor)
48+
}
49+
50+
function patchChildren (n1, n2, container) {
51+
if (typeof n2.children === 'string') {
52+
if (Array.isArray(n1.children)) {
53+
n1.children.forEach((c) => unmount(c))
54+
}
55+
setElementText(container, n2.children)
56+
} else if (Array.isArray(n2.children)) {
57+
patchKeyedChildren(n1, n2, container)
58+
} else {
59+
if (Array.isArray(n1.children)) {
60+
n1.children.forEach(c => unmount(c))
61+
} else if (typeof n1.children === 'string') {
62+
setElementText(container, '')
63+
}
64+
}
65+
}
66+
67+
function patchKeyedChildren (n1, n2, container) {
68+
const oldChildren = n1.children
69+
const newChildren = n2.children
70+
71+
let oldStartIdx = 0
72+
let oldEndIdx = oldChildren.length - 1
73+
let newStartIdx = 0
74+
let newEndIdx = newChildren.length - 1
75+
76+
let oldStartVNode = oldChildren[oldStartIdx]
77+
let oldEndVNode = oldChildren[oldEndIdx]
78+
let newStartVNode = newChildren[newStartIdx]
79+
let newEndVNode = newChildren[newEndIdx]
80+
81+
while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
82+
if (!oldStartVNode) {
83+
oldStartVNode = oldChildren[++oldStartIdx]
84+
} else if (!oldEndVNode) {
85+
oldEndVNode = oldChildren[--oldEndIdx]
86+
} else if (oldStartVNode.key === newStartVNode.key) {
87+
patch(oldStartVNode, newStartVNode, container)
88+
oldStartVNode = oldChildren[++oldStartIdx]
89+
newStartVNode = newChildren[++newStartIdx]
90+
} else if (oldEndVNode.key === newEndVNode.key) {
91+
patch(oldEndVNode, newEndVNode, container)
92+
oldEndVNode = oldChildren[--oldEndIdx]
93+
newEndVNode = newChildren[--newEndIdx]
94+
} else if (oldStartVNode.key === newEndVNode.key) {
95+
patch(oldStartVNode, newEndVNode, container)
96+
insert(oldStartVNode.el, container, newEndVNode.el.nextSibling)
97+
98+
oldStartVNode = oldChildren[++oldStartIdx]
99+
newEndVNode = newChildren[--newEndIdx]
100+
} else if (oldEndVNode.key === newStartVNode.key) {
101+
patch(oldEndVNode, newStartVNode, container)
102+
insert(oldEndVNode.el, container, oldStartVNode.el)
103+
104+
oldEndVNode = oldChildren[--oldEndIdx]
105+
newStartVNode = newChildren[++newStartIdx]
106+
} else {
107+
const idxInOld = oldChildren.findIndex(
108+
node => node.key === newStartVNode.key
109+
)
110+
if (idxInOld > 0) {
111+
const vnodeToMove = oldChildren[idxInOld]
112+
patch(vnodeToMove, newStartVNode, container)
113+
insert(vnodeToMove.el, container, oldStartVNode.el)
114+
oldChildren[idxInOld] = undefined
115+
} else {
116+
// 将 newStartVNode 作为新节点挂载到头部,使用当前头部节点 oldStartVNode.el 作为锚点
117+
patch(null, newStartVNode, container, oldStartVNode.el)
118+
}
119+
newStartVNode = newChildren[++newStartIdx]
120+
121+
}
122+
}
123+
if (oldEndIdx < oldStartIdx && newStartIdx <= newEndIdx) {
124+
// 如果满足条件,则说明有新的节点遗留,需要挂载它们
125+
for (let i = newStartIdx;i <= newEndIdx;i++) {
126+
patch(null, newChildren[i], container, oldStartVNode.el)
127+
}
128+
} else if (newEndIdx < newStartIdx && oldStartIdx <= oldEndIdx) {
129+
for (let i = oldStartIdx;i <= oldEndIdx;i++) {
130+
unmount(oldChildren[i])
131+
}
132+
}
133+
}
134+
135+
function patchElement (n1, n2) {
136+
const el = n2.el = n1.el
137+
const oldProps = n1.props
138+
const newProps = n2.props
139+
140+
for (const key in newProps) {
141+
if (newProps[key] !== oldProps[key]) {
142+
patchProps(el, key, oldProps[key], newProps[key])
143+
}
144+
}
145+
for (const key in oldProps) {
146+
if (!(key in newProps)) {
147+
patchProps(el, key, oldProps[key], null)
148+
}
149+
}
150+
151+
patchChildren(n1, n2, el)
152+
}
153+
154+
function unmount (vnode) {
155+
if (vnode.type === Fragment) {
156+
vnode.children.forEach(c => unmount(c))
157+
return
158+
}
159+
const parent = vnode.el.parentNode
160+
if (parent) {
161+
parent.removeChild(vnode.el)
162+
}
163+
}
164+
165+
function patch (n1, n2, container, anchor) {
166+
if (n1 && n1.type !== n2.type) {
167+
unmount(n1)
168+
n1 = null
169+
}
170+
171+
const { type } = n2
172+
173+
if (typeof type === 'string') {
174+
if (!n1) {
175+
mountElement(n2, container, anchor)
176+
} else {
177+
patchElement(n1, n2)
178+
}
179+
} else if (type === Text) {
180+
if (!n1) {
181+
const el = n2.el = createText(n2.children)
182+
insert(el, container)
183+
} else {
184+
const el = n2.el = n1.el
185+
if (n2.children !== n1.children) {
186+
setText(el, n2.children)
187+
}
188+
}
189+
} else if (type === Fragment) {
190+
if (!n1) {
191+
n2.children.forEach(c => patch(null, c, container))
192+
} else {
193+
patchChildren(n1, n2, container)
194+
}
195+
}
196+
}
197+
198+
function render (vnode, container) {
199+
if (vnode) {
200+
patch(container._vnode, vnode, container)
201+
} else {
202+
if (container._vnode) {
203+
unmount(container._vnode)
204+
}
205+
}
206+
container._vnode = vnode
207+
}
208+
209+
return {
210+
render
211+
}
212+
}
213+
214+
const renderer = createRenderer({
215+
createElement (tag) {
216+
return document.createElement(tag)
217+
},
218+
setElementText (el, text) {
219+
el.textContent = text
220+
},
221+
insert (el, parent, anchor = null) {
222+
parent.insertBefore(el, anchor)
223+
},
224+
createText (text) {
225+
return document.createTextNode(text)
226+
},
227+
setText (el, text) {
228+
el.nodeValue = text
229+
},
230+
patchProps (el, key, prevValue, nextValue) {
231+
if (/^on/.test(key)) {
232+
const invokers = el._vei || (el._vei = {})
233+
let invoker = invokers[key]
234+
const name = key.slice(2).toLowerCase()
235+
if (nextValue) {
236+
if (!invoker) {
237+
invoker = el._vei[key] = (e) => {
238+
console.log(e.timeStamp)
239+
console.log(invoker.attached)
240+
if (e.timeStamp < invoker.attached) return
241+
if (Array.isArray(invoker.value)) {
242+
invoker.value.forEach(fn => fn(e))
243+
} else {
244+
invoker.value(e)
245+
}
246+
}
247+
invoker.value = nextValue
248+
invoker.attached = performance.now()
249+
el.addEventListener(name, invoker)
250+
} else {
251+
invoker.value = nextValue
252+
}
253+
} else if (invoker) {
254+
el.removeEventListener(name, invoker)
255+
}
256+
} else if (key === 'class') {
257+
el.className = nextValue || ''
258+
} else if (shouldSetAsProps(el, key, nextValue)) {
259+
const type = typeof el[key]
260+
if (type === 'boolean' && nextValue === '') {
261+
el[key] = true
262+
} else {
263+
el[key] = nextValue
264+
}
265+
} else {
266+
el.setAttribute(key, nextValue)
267+
}
268+
}
269+
})
270+
271+
const Fragment = Symbol()
272+
const VNode1 = {
273+
type: 'div',
274+
children: [
275+
{ type: 'p', children: '1', key: 1 },
276+
{ type: 'p', children: '2', key: 2 },
277+
{ type: 'p', children: '3', key: 3 }
278+
]
279+
}
280+
renderer.render(VNode1, document.querySelector('#app'))
281+
282+
const VNode2 = {
283+
type: 'div',
284+
children: [
285+
{ type: 'p', children: '1', key: 1 },
286+
{ type: 'p', children: '3', key: 3 }
287+
]
288+
}
289+
290+
setTimeout(() => {
291+
console.log('update')
292+
renderer.render(VNode2, document.querySelector('#app'))
293+
}, 400);
294+
295+
296+
297+
</script>
298+
</body>
299+
300+
</html>

0 commit comments

Comments
 (0)