├── README.md
├── .vscode
└── extensions.json
├── .prettierrc
├── vite.config.js
├── src
├── main.js
├── assets
│ ├── 箭头_向上.svg
│ └── 箭头_向下.svg
├── App.vue
├── store.js
├── components
│ ├── Sidebar.vue
│ └── Content.vue
└── doubleEndedDiff
│ └── index.js
├── .gitignore
├── index.html
├── package.json
└── public
└── vite.svg
/README.md:
--------------------------------------------------------------------------------
1 |
2 | # 虚拟DOM的diff算法动画演示
3 |
4 | 目前只支持双端diff算法,后续会增加快速diff算法。
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | "recommendations": ["Vue.volar"]
3 | }
4 |
--------------------------------------------------------------------------------
/.prettierrc:
--------------------------------------------------------------------------------
1 | semi: false
2 | singleQuote: true
3 | printWidth: 80
4 | trailingComma: 'none'
5 | arrowParens: 'avoid'
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import vue from '@vitejs/plugin-vue'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [vue()]
7 | })
8 |
--------------------------------------------------------------------------------
/src/main.js:
--------------------------------------------------------------------------------
1 | import { createApp } from 'vue'
2 | import App from './App.vue'
3 | import { createPinia } from 'pinia'
4 |
5 | const pinia = createPinia()
6 | const app = createApp(App)
7 |
8 | app.use(pinia)
9 | app.mount('#app')
10 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + Vue
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vnode-visualization",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview"
10 | },
11 | "dependencies": {
12 | "codemirror": "^5.65.9",
13 | "pinia": "^2.0.23",
14 | "vue": "^3.2.37"
15 | },
16 | "devDependencies": {
17 | "@vitejs/plugin-vue": "^3.1.0",
18 | "less": "^4.1.3",
19 | "less-loader": "^11.1.0",
20 | "vite": "^3.1.0"
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/src/assets/箭头_向上.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/assets/箭头_向下.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/App.vue:
--------------------------------------------------------------------------------
1 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
27 |
34 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { defineStore } from 'pinia'
2 |
3 | export const useStore = defineStore('store', {
4 | state: () => {
5 | return {
6 | oldVNode: `[
7 | {
8 | "tag": "div",
9 | "children": "a",
10 | "data": {
11 | "key": 1
12 | }
13 | },
14 | {
15 | "tag": "div",
16 | "children": "b",
17 | "data": {
18 | "key": 2
19 | }
20 | },
21 | {
22 | "tag": "div",
23 | "children": "g",
24 | "data": {
25 | "key": 3
26 | }
27 | },
28 | {
29 | "tag": "div",
30 | "children": "c",
31 | "data": {
32 | "key": 4
33 | }
34 | },
35 | {
36 | "tag": "div",
37 | "children": "d",
38 | "data": {
39 | "key": 5
40 | }
41 | },
42 | {
43 | "tag": "div",
44 | "children": "h",
45 | "data": {
46 | "key": 6
47 | }
48 | },
49 | {
50 | "tag": "div",
51 | "children": "e",
52 | "data": {
53 | "key": 7
54 | }
55 | },
56 | {
57 | "tag": "div",
58 | "children": "f",
59 | "data": {
60 | "key": 8
61 | }
62 | }
63 | ]`,
64 | newVNode: `[
65 | {
66 | "tag": "div",
67 | "children": "b",
68 | "data": {
69 | "key": 2
70 | }
71 | },
72 | {
73 | "tag": "div",
74 | "children": "e",
75 | "data": {
76 | "key": 7
77 | }
78 | },
79 | {
80 | "tag": "div",
81 | "children": "c",
82 | "data": {
83 | "key": 4
84 | }
85 | },
86 | {
87 | "tag": "div",
88 | "children": "d",
89 | "data": {
90 | "key": 5
91 | }
92 | },
93 | {
94 | "tag": "div",
95 | "children": "i",
96 | "data": {
97 | "key": 9
98 | }
99 | },
100 | {
101 | "tag": "div",
102 | "children": "a",
103 | "data": {
104 | "key": 1
105 | }
106 | },
107 | {
108 | "tag": "div",
109 | "children": "f",
110 | "data": {
111 | "key": 8
112 | }
113 | }
114 | ]`
115 | }
116 | }
117 | })
118 |
--------------------------------------------------------------------------------
/src/components/Sidebar.vue:
--------------------------------------------------------------------------------
1 |
50 |
51 |
52 |
71 |
72 |
73 |
120 |
--------------------------------------------------------------------------------
/src/components/Content.vue:
--------------------------------------------------------------------------------
1 |
196 |
197 |
198 |
199 |
200 |
214 |
215 |
216 |
217 |
218 |
219 |
225 |
{{ item.name }}
226 |
{{ item.value }}
227 |

228 |
229 |
230 |
231 |
232 |
233 |
旧的VNode列表
234 |
235 |
236 |
249 |
{{ item ? item.children : '空' }}
250 |
251 |
252 |
253 |
254 |
255 |
256 |
新的VNode列表
257 |
258 |
259 |
272 |
{{ item.children }}
273 |
274 |
275 |
276 |
277 |
278 |
{{ info }}
279 |
280 |
281 |
282 |
288 |

289 |
{{ item.value }}
290 |
{{ item.name }}
291 |
292 |
293 |
294 |
295 |
真实DOM列表
296 |
297 |
298 |
303 |
{{ item.children }}
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
316 | {{ item.children }}
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
340 |
517 |
--------------------------------------------------------------------------------
/src/doubleEndedDiff/index.js:
--------------------------------------------------------------------------------
1 | let callbacks = {}
2 | let time = 3000
3 |
4 | /**
5 | * javascript comment
6 | * @Author: 王林25
7 | * @Date: 2022-10-26 09:55:20
8 | * @Desc: 等待函数
9 | */
10 | const wait = t => {
11 | return new Promise(resolve => {
12 | setTimeout(
13 | () => {
14 | resolve()
15 | },
16 | t === undefined ? time : t
17 | )
18 | })
19 | }
20 |
21 | /**
22 | * javascript comment
23 | * @Author: 王林25
24 | * @Date: 2021-06-28 14:05:31
25 | * @Desc: 创建vNode对象
26 | */
27 | export const h = (tag, data = {}, children) => {
28 | let text = ''
29 | let el
30 | let key
31 | // 文本节点
32 | if (typeof children === 'string' || typeof children === 'number') {
33 | text = children
34 | children = undefined
35 | } else if (!Array.isArray(children)) {
36 | children = undefined
37 | }
38 | if (data && data.key) {
39 | key = data.key
40 | }
41 | return {
42 | tag, // 元素标签
43 | children, // 子元素
44 | text, // 文本节点的文本
45 | el, // 真实dom
46 | key,
47 | data
48 | }
49 | }
50 |
51 | /**
52 | * javascript comment
53 | * @Author: 王林25
54 | * @Date: 2021-06-28 16:24:56
55 | * @Desc: vnode->dom
56 | */
57 | const createEl = vnode => {
58 | let el = document.createElement(vnode.tag)
59 | vnode.el = el
60 | if (vnode.children && vnode.children.length > 0) {
61 | vnode.children.forEach(item => {
62 | el.appendChild(createEl(item))
63 | })
64 | }
65 | if (vnode.text) {
66 | el.appendChild(document.createTextNode(vnode.text))
67 | }
68 | return el
69 | }
70 |
71 | /**
72 | * javascript comment
73 | * @Author: 王林25
74 | * @Date: 2021-06-29 13:42:55
75 | * @Desc: 判断是否是同一个节点
76 | */
77 | const isSameNode = (a, b) => {
78 | return a.key === b.key && a.tag === b.tag
79 | }
80 |
81 | /**
82 | * javascript comment
83 | * @Author: 王林25
84 | * @Date: 2021-06-29 15:12:24
85 | * @Desc: 在节点列表里寻找同个节点,返回索引
86 | */
87 | const findSameNode = (list, node) => {
88 | return list.findIndex(item => {
89 | return item && isSameNode(item, node)
90 | })
91 | }
92 |
93 | /**
94 | * javascript comment
95 | * @Author: 王林25
96 | * @Date: 2021-06-29 09:28:42
97 | * @Desc: diff算法
98 | */
99 | const diff = async (el, oldChildren, newChildren) => {
100 | // 指针
101 | let oldStartIdx = 0
102 | let oldEndIdx = oldChildren.length - 1
103 | let newStartIdx = 0
104 | let newEndIdx = newChildren.length - 1
105 | // 节点
106 | let oldStartVNode = oldChildren[oldStartIdx]
107 | let oldEndVNode = oldChildren[oldEndIdx]
108 | let newStartVNode = newChildren[newStartIdx]
109 | let newEndVNode = newChildren[newEndIdx]
110 | callbacks.updatePointers(oldStartIdx, oldEndIdx, newStartIdx, newEndIdx)
111 | while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {
112 | let stop = false
113 | let _isSameNode = false
114 | if (oldStartVNode === null) {
115 | callbacks.updateInfo('')
116 | oldStartVNode = oldChildren[++oldStartIdx]
117 | stop = true
118 | }
119 | if (!stop && oldEndVNode === null) {
120 | callbacks.updateInfo('')
121 | oldEndVNode = oldChildren[--oldEndIdx]
122 | stop = true
123 | }
124 | if (!stop && newStartVNode === null) {
125 | callbacks.updateInfo('')
126 | newStartVNode = oldChildren[++newStartIdx]
127 | stop = true
128 | }
129 | if (!stop && newEndVNode === null) {
130 | callbacks.updateInfo('')
131 | newEndVNode = oldChildren[--newEndIdx]
132 | stop = true
133 | }
134 | if (!stop) {
135 | callbacks.updateInfo('头-头比较')
136 | callbacks.updateCompareNodes(oldStartIdx, newStartIdx)
137 | _isSameNode = isSameNode(oldStartVNode, newStartVNode)
138 | if (_isSameNode) {
139 | callbacks.updateInfo(
140 | 'key值相同,可以复用,进行patch打补丁操作。新旧节点位置相同,不需要移动对应的真实DOM节点'
141 | )
142 | }
143 | await wait()
144 | }
145 | if (!stop && _isSameNode) {
146 | // 头-头
147 | patchVNode(oldStartVNode, newStartVNode)
148 | // 更新指针
149 | oldStartVNode = oldChildren[++oldStartIdx]
150 | newStartVNode = newChildren[++newStartIdx]
151 | stop = true
152 | }
153 | if (!stop) {
154 | callbacks.updateInfo('头-尾比较')
155 | callbacks.updateCompareNodes(oldStartIdx, newEndIdx)
156 | _isSameNode = isSameNode(oldStartVNode, newEndVNode)
157 | if (_isSameNode) {
158 | callbacks.updateInfo(
159 | 'key值相同,可以复用,进行patch打补丁操作。新旧节点位置不同,需要移动对应的真实DOM节点'
160 | )
161 | }
162 | await wait()
163 | }
164 | if (!stop && _isSameNode) {
165 | // 头-尾
166 | patchVNode(oldStartVNode, newEndVNode)
167 | // 把oldStartVNode节点移动到最后
168 | el.insertBefore(oldStartVNode.el, oldEndVNode.el.nextSibling)
169 | callbacks.moveNode(oldStartIdx, oldEndIdx + 1)
170 | // 更新指针
171 | oldStartVNode = oldChildren[++oldStartIdx]
172 | newEndVNode = newChildren[--newEndIdx]
173 | stop = true
174 | }
175 | if (!stop) {
176 | callbacks.updateInfo('尾-头比较')
177 | callbacks.updateCompareNodes(oldEndIdx, newStartIdx)
178 | _isSameNode = isSameNode(oldEndVNode, newStartVNode)
179 | if (_isSameNode) {
180 | callbacks.updateInfo(
181 | 'key值相同,可以复用,进行patch打补丁操作。新旧节点位置不同,需要移动对应的真实DOM节点'
182 | )
183 | }
184 | await wait()
185 | }
186 | if (!stop && _isSameNode) {
187 | // 尾-头
188 | patchVNode(oldEndVNode, newStartVNode)
189 | // 把oldEndVNode节点移动到oldStartVNode前
190 | el.insertBefore(oldEndVNode.el, oldStartVNode.el)
191 | callbacks.moveNode(oldEndIdx, oldStartIdx)
192 | // 更新指针
193 | oldEndVNode = oldChildren[--oldEndIdx]
194 | newStartVNode = newChildren[++newStartIdx]
195 | stop = true
196 | }
197 | if (!stop) {
198 | callbacks.updateInfo('尾-尾比较')
199 | callbacks.updateCompareNodes(oldEndIdx, newEndIdx)
200 | _isSameNode = isSameNode(oldEndVNode, newEndVNode)
201 | if (_isSameNode) {
202 | callbacks.updateInfo(
203 | 'key值相同,可以复用,进行patch打补丁操作。新旧节点位置相同,不需要移动对应的真实DOM节点'
204 | )
205 | }
206 | await wait()
207 | }
208 | if (!stop && _isSameNode) {
209 | // 尾-尾
210 | patchVNode(oldEndVNode, newEndVNode)
211 | // 更新指针
212 | oldEndVNode = oldChildren[--oldEndIdx]
213 | newEndVNode = newChildren[--newEndIdx]
214 | stop = true
215 | }
216 | callbacks.updateCompareNodes(-1, -1)
217 | if (!stop) {
218 | callbacks.updateInfo(
219 | '头尾比较没有找到可复用节点,直接在旧的VNode列表里搜索是否有可复用节点'
220 | )
221 | callbacks.updateCompareNodes(-1, newStartIdx)
222 | await wait()
223 | let findIndex = findSameNode(oldChildren, newStartVNode)
224 | // newStartVNode在旧列表里不存在,那么是新节点,创建插入
225 | if (findIndex === -1) {
226 | callbacks.updateInfo('该节点在旧列表里不存在,需要创建DOM节点并插入')
227 | await wait()
228 | el.insertBefore(createEl(newStartVNode), oldStartVNode.el)
229 | callbacks.insertNode(newStartVNode, oldStartIdx)
230 | await wait()
231 | } else {
232 | // 在旧列表里存在,那么进行patch,并且移动到oldStartVNode前
233 | callbacks.updateInfo(
234 | '该节点在旧的VNode列表里存在可复用节点,进行path打补丁操作,并且移动对应的真实DOM节点。然后将该VNode置空'
235 | )
236 | callbacks.updateCompareNodes(findIndex, newStartIdx)
237 | await wait()
238 | callbacks.updateCompareNodes(-1, -1)
239 | let oldVNode = oldChildren[findIndex]
240 | patchVNode(oldVNode, newStartVNode)
241 | el.insertBefore(oldVNode.el, oldStartVNode.el)
242 | callbacks.moveNode(findIndex, oldStartIdx, true)
243 | oldChildren[findIndex] = null
244 | await wait()
245 | }
246 | newStartVNode = newChildren[++newStartIdx]
247 | }
248 | callbacks.updateCompareNodes(-1, -1)
249 | callbacks.updateInfo('')
250 | callbacks.updatePointers(oldStartIdx, oldEndIdx, newStartIdx, newEndIdx)
251 | await wait()
252 | }
253 | // 旧列表里存在新列表里没有的节点,需要删除
254 | if (oldStartIdx <= oldEndIdx) {
255 | callbacks.updateInfo(
256 | '新的VNode列表已比较完毕,开始删除旧的VNode列表里不再需要的节点'
257 | )
258 | await wait()
259 | for (let i = oldStartIdx; i <= oldEndIdx; i++) {
260 | if (oldChildren[i]) {
261 | callbacks.updateInfo('该节点在新列表里不存在,需要删除')
262 | callbacks.updateDeleteNode(i)
263 | await wait()
264 | callbacks.updateInfo('')
265 | callbacks.updateDeleteNode(-1)
266 | removeEvent(oldChildren[i])
267 | el.removeChild(oldChildren[i].el)
268 | callbacks.removeChild(i)
269 | } else {
270 | callbacks.updateInfo('空的VNode,跳过')
271 | callbacks.updateDeleteNode(i)
272 | await wait()
273 | callbacks.updateInfo('')
274 | callbacks.updateDeleteNode(-1)
275 | }
276 | }
277 | } else if (newStartIdx <= newEndIdx) {
278 | callbacks.updateInfo(
279 | '旧的VNode列表已比较完毕,开始添加旧的VNode列表里不存在的节点'
280 | )
281 | await wait()
282 | let before = newChildren[newEndIdx + 1]
283 | ? newChildren[newEndIdx + 1].el
284 | : null
285 | for (let i = newStartIdx; i <= newEndIdx; i++) {
286 | callbacks.updateInfo('该节点在旧列表里不存在,需要添加')
287 | callbacks.updateAddNode(i)
288 | await wait()
289 | callbacks.updateInfo('')
290 | callbacks.updateAddNode(-1)
291 | el.insertBefore(createEl(newChildren[i]), before)
292 | callbacks.insertNode(newChildren[i], before ? newEndIdx + 1 : -1, true)
293 | }
294 | }
295 | callbacks.updateDeleteNode(-1)
296 | callbacks.updateAddNode(-1)
297 | callbacks.updateInfo('大功告成')
298 | callbacks.done()
299 | }
300 |
301 | /**
302 | * javascript comment
303 | * @Author: 王林25
304 | * @Date: 2021-06-29 16:58:44
305 | * @Desc: 更新类名
306 | */
307 | const updateClass = (el, newVNode) => {
308 | if (!el) {
309 | return
310 | }
311 | el.className = ''
312 | if (newVNode.data && newVNode.data.class) {
313 | let className = ''
314 | Object.keys(newVNode.data.class).forEach(cla => {
315 | if (newVNode.data.class[cla]) {
316 | className += cla + ' '
317 | }
318 | })
319 | el.className = className
320 | }
321 | }
322 |
323 | /**
324 | * javascript comment
325 | * @Author: 王林25
326 | * @Date: 2021-06-29 17:10:21
327 | * @Desc: 更新样式
328 | */
329 | const updateStyle = (el, oldVNode, newVNode) => {
330 | if (!el) {
331 | return
332 | }
333 | let oldStyle = (oldVNode && oldVNode.data.style) || {}
334 | let newStyle = newVNode.data.style || {}
335 | // 移除旧节点里存在新节点里不存在的样式
336 | Object.keys(oldStyle).forEach(item => {
337 | if (newStyle[item] === undefined || newStyle[item] === '') {
338 | el.style[item] = ''
339 | }
340 | })
341 | // 添加旧节点不存在的新样式
342 | Object.keys(newStyle).forEach(item => {
343 | if (oldStyle[item] !== newStyle[item]) {
344 | el.style[item] = newStyle[item]
345 | }
346 | })
347 | }
348 |
349 | /**
350 | * javascript comment
351 | * @Author: 王林25
352 | * @Date: 2021-06-29 17:23:51
353 | * @Desc: 更新属性
354 | */
355 | const updateAttr = (el, oldVNode, newVNode) => {
356 | if (!el) {
357 | return
358 | }
359 | let oldAttr = oldVNode && oldVNode.data.attr ? oldVNode.data.attr : {}
360 | let newAttr = newVNode.data.attr || {}
361 | // 移除旧节点里存在新节点里不存在的属性
362 | Object.keys(oldAttr).forEach(item => {
363 | if (newAttr[item] === undefined || newAttr[item] === '') {
364 | el.removeAttribute(item)
365 | }
366 | })
367 | // 添加旧节点不存在的新属性
368 | Object.keys(newAttr).forEach(item => {
369 | if (oldAttr[item] !== newAttr[item]) {
370 | el.setAttribute(item, newAttr[item])
371 | }
372 | })
373 | }
374 |
375 | /**
376 | * javascript comment
377 | * @Author: 王林25
378 | * @Date: 2021-06-29 17:44:01
379 | * @Desc: 移除所有事件
380 | */
381 | const removeEvent = oldVNode => {
382 | if (oldVNode && oldVNode.data && oldVNode.data.event) {
383 | Object.keys(oldVNode.data.event).forEach(item => {
384 | oldVNode.el.removeEventListener(item, oldVNode.data.event[item])
385 | })
386 | }
387 | }
388 |
389 | /**
390 | * javascript comment
391 | * @Author: 王林25
392 | * @Date: 2021-06-29 17:41:37
393 | * @Desc: 更新事件
394 | */
395 | const updateEvent = (el, oldVNode, newVNode) => {
396 | if (!el) {
397 | return
398 | }
399 | let oldEvent = oldVNode && oldVNode.data.event ? oldVNode.data.event : {}
400 | let newEvent = newVNode.data.event || {}
401 | // 移除旧节点里存在新节点里不存在的事件
402 | Object.keys(oldEvent).forEach(item => {
403 | if (newEvent[item] === undefined || oldEvent[item] !== newEvent[item]) {
404 | el.removeEventListener(item, oldEvent[item])
405 | }
406 | })
407 | // 添加旧节点不存在的新事件
408 | Object.keys(newEvent).forEach(item => {
409 | if (oldEvent[item] !== newEvent[item]) {
410 | el.addEventListener(item, newEvent[item])
411 | }
412 | })
413 | }
414 |
415 | /**
416 | * javascript comment
417 | * @Author: 王林25
418 | * @Date: 2021-06-28 16:42:15
419 | * @Desc: 打补丁
420 | */
421 | const patchVNode = (oldVNode, newVNode) => {
422 | if (oldVNode === newVNode) {
423 | return
424 | }
425 | // 元素标签相同,进行patch
426 | if (oldVNode.tag === newVNode.tag) {
427 | // 元素类型相同,那么旧元素肯定是进行复用的
428 | let el = (newVNode.el = oldVNode.el)
429 | updateClass(el, newVNode)
430 | updateStyle(el, oldVNode, newVNode)
431 | updateAttr(el, oldVNode, newVNode)
432 | updateEvent(el, oldVNode, newVNode)
433 | // 新节点的子节点是文本节点
434 | if (newVNode.text) {
435 | // 移除旧节点的子节点
436 | if (oldVNode.children) {
437 | oldVNode.children.forEach(item => {
438 | removeEvent(item)
439 | el.removeChild(item.el)
440 | })
441 | }
442 | // 文本内容不相同则更新文本
443 | if (oldVNode.text !== newVNode.text) {
444 | el.textContent = newVNode.text
445 | }
446 | } else {
447 | // 新旧节点都存在子节点,那么就要进行diff
448 | if (oldVNode.children && newVNode.children) {
449 | diff(el, oldVNode.children, newVNode.children)
450 | } else if (oldVNode.children) {
451 | // 新节点不存在子节点,那么移除旧节点的所有子节点
452 | oldVNode.children.forEach(item => {
453 | removeEvent(item)
454 | el.removeChild(item.el)
455 | })
456 | } else if (newVNode.children) {
457 | // 新节点存在子节点
458 | // 旧节点存在文本节点则移除
459 | if (oldVNode.text) {
460 | el.textContent = ''
461 | }
462 | // 添加新节点的子节点
463 | newVNode.children.forEach(item => {
464 | el.appendChild(createEl(item))
465 | })
466 | } else if (oldVNode.text) {
467 | // 新节点啥也没有,旧节点存在文本节点
468 | el.textContent = ''
469 | }
470 | }
471 | } else {
472 | // 不同使用newNode替换oldNode
473 | let newEl = createEl(newVNode)
474 | updateClass(newEl, newVNode)
475 | updateStyle(newEl, null, newVNode)
476 | updateAttr(newEl, null, newVNode)
477 | removeEvent(oldNode)
478 | updateEvent(newEl, null, newVNode)
479 | let parent = oldVNode.el.parentNode
480 | parent.insertBefore(newEl, oldVNode.el)
481 | parent.removeChild(oldVNode.el)
482 | }
483 | }
484 |
485 | /**
486 | * javascript comment
487 | * @Author: 王林25
488 | * @Date: 2021-06-28 15:55:23
489 | * @Desc: 入口方法
490 | */
491 | export const patch = (oldVNode, newVNode, handles, speed) => {
492 | callbacks = handles
493 | time = speed
494 | // dom元素转换成vnode
495 | if (!oldVNode.tag) {
496 | let el = oldVNode
497 | el.innerHTML = ''
498 | oldVNode = h(oldVNode.tagName.toLowerCase())
499 | oldVNode.el = el
500 | }
501 | patchVNode(oldVNode, newVNode)
502 | return newVNode
503 | }
504 |
505 | /*
506 |
507 | let preVNode = patch(document.getElementById('app'), h('div',{
508 | class: {
509 | btn: true
510 | },
511 | style: {
512 | fontSize: '30px'
513 | },
514 | attr: {
515 | id: 'a'
516 | },
517 | event: {
518 | mouseover: () => {
519 | alert('移入我')
520 | }
521 | }
522 | }, '旧'))
523 | setTimeout(() => {
524 | let newVNode = h('div', {
525 | class: {
526 | btn: true,
527 | warning: false,
528 | bg: true
529 | },
530 | style: {
531 | fontWeight: 'bold',
532 | fontStyle: 'italic'
533 | },
534 | event: {
535 | click: () => {
536 | alert('点了我')
537 | }
538 | }
539 | }, '新')
540 | console.log(preVNode, newVNode)
541 | patch(preVNode, newVNode)
542 | }, 10000);
543 | */
544 |
--------------------------------------------------------------------------------