├── 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 | 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 | 72 | 73 | 120 | -------------------------------------------------------------------------------- /src/components/Content.vue: -------------------------------------------------------------------------------- 1 | 196 | 197 | 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 | --------------------------------------------------------------------------------