webpack.config.js

module.exports = {    entry: {        index: './src/index.js'    },    output: {        path: __dirname + '/public',        filename: './js/[name].js'    },    devServer: {        contentBase: './public',        inline: true    }}

package.json

{  "name": "snabbdom",  "version": "1.0.0",  "description": "",  "main": "index.js",  "scripts": {    "test": "webpack-dev-server --open"  },  "keywords": [],  "author": "",  "license": "ISC",  "dependencies": {    "snabbdom": "^3.1.0",    "webpack": "5",    "webpack-cli": "3",    "webpack-dev-server": "3"  }}

index.js

import h from './dom/h'import patch from './dom/patch.js'let container = document.getElementById('container')let vNode = h('ui', {}, [    h('li', { key: 'a' }, 'a'),    h('li', { key: 'b' }, 'b'),    h('li', { key: 'c' }, 'c'),    h('li', { key: 'd' }, 'd'),    h('li', { key: 'e' }, 'e')])patch(container, vNode)let btn = document.getElementById('btn')console.log(container.nextSibling)let vNode2 = h('ui', {}, [    h('li', { key: 'c' }, 'c'),    h('li', { key: 'b' }, 'b'),    h('li', { key: 'a' }, 'a'),    h('li', { key: 'd' }, 'd'),    h('li', { key: 'e' }, 'e'),    h('li', { key: 'f' }, 'f'),])console.log(vNode2)btn.onclick = function () {    patch(vNode, vNode2)}

public/index.html

<!DOCTYPE html><html lang="en"><head>    <meta charset="UTF-8">    <meta http-equiv="X-UA-Compatible" content="IE=edge">    <meta name="viewport" content="width=device-width, initial-scale=1.0">    <title>Document</title></head><body>    <div id="container">        <div>1</div>        <div>2</div>        <div>3</div>    </div>    <button id="btn">按钮</button>    <script src="./js/index.js"></script></body></html>

dom/h.js

import vNode from './vNode.js'export default (sel, data, params) => {    if (typeof params == 'string') {        let text = params        return vNode(sel, data, undefined, text, undefined)    } else if (Array.isArray(params)) {        let children = []        for (const item of params) {            children.push(item)        }        return vNode(sel, data, children, undefined, undefined)    }}

dom/createElement.js

export default function createElement(vNode) {    let domNode = document.createElement(vNode.sel)    if (vNode.children == undefined || vNode.children.length == 0) {        domNode.innerText = vNode.text    } else if (Array.isArray(vNode.children)) {        for (const child of vNode.children) {            let childDom = createElement(child)            domNode.appendChild(childDom)        }    }    vNode.elm = domNode    return domNode}

dom/patch.js

import VNode from './vNode'import createElement from './createElement'export default (oldVNode, newVNode) => {    //实在dom转换为虚构dom    if (oldVNode.sel == undefined) {        let sel = oldVNode.tagName.toLowerCase()        let data = {}        let children = []        let text = undefined        oldVNode = VNode(sel, data, children, text, oldVNode)    }    //新旧tagName统一    if (oldVNode.sel === newVNode.sel) {        patchVNode(oldVNode, newVNode)    } else {        //新旧tagName不统一 间接删除旧的创立新的        //依据虚构dom 创立dom节点        let newVNodeElm = createElement(newVNode)        let oldVNodeElm = oldVNode.elm        // 插入新的dom        if (newVNodeElm) {            oldVNodeElm.parentNode.insertBefore(newVNodeElm, oldVNodeElm)        }        //删除旧dom        oldVNodeElm.parentNode.removeChild(oldVNodeElm)    }}function patchVNode(oldVNode, newVNode) {    //新的没有child 间接替换旧的内容    if (newVNode.children === undefined) {        if (newVNode.text !== oldVNode.text) {            oldVNode.elm.innerText = newVNode.text        }    } else {        //新旧都有子元素        if (oldVNode.children !== undefined && oldVNode.children.length > 0) {            let parentDom = oldVNode.elm            let oldCh = oldVNode.children            let newCh = newVNode.children            let oldStartIdx = 0 //旧前            let oldEndIdx = oldCh.length - 1 //旧后            let newStartIdx = 0 //新前            let newEndIdx = newCh.length - 1 //新后            let oldStartVNode = oldCh[0]            let oldEndVNode = oldCh[oldEndIdx]            let newStartVNode = newCh[0]            let newEndVNode = newCh[newEndIdx]            while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {                if (oldStartVNode == undefined) {                    oldStartVNode = oldCh[++oldStartIdx]                } else if (oldEndVNode == undefined) {                    oldEndVNode = oldCh[--oldEndIdx]                } else if (sameVNode(oldStartVNode, newStartVNode)) {                    console.log(1)                    patchVNode(oldStartVNode, newStartVNode)                    if (newStartVNode) {                        newStartVNode.elm = oldStartVNode?.elm                    }                    oldStartVNode = oldCh[++oldStartIdx]                    newStartVNode = newCh[++newStartIdx]                } else if (sameVNode(oldEndVNode, newEndVNode)) {                    console.log(2)                    patchVNode(oldEndVNode, newEndVNode)                    if (newEndVNode) {                        newEndVNode.elm = oldEndVNode?.elm                    }                    oldEndVNode = oldCh[--oldEndIdx]                    newEndVNode = newCh[--newEndIdx]                } else if (sameVNode(oldStartVNode, newEndVNode)) {                    console.log(3)                    patchVNode(oldStartVNode, newEndVNode)                    if (newEndVNode) {                        newEndVNode.elm = oldStartVNode?.elm                    }                    parentDom.insertBefore(                        oldStartVNode.elm,                        oldEndVNode.elm.nextSibling                    )                    oldStartVNode = oldCh[++oldStartIdx]                    newEndVNode = newCh[--newEndIdx]                } else if (sameVNode(oldEndVNode, newStartVNode)) {                    console.log(4)                    patchVNode(oldEndVNode, newStartVNode)                    if (newStartVNode) {                        newStartVNode.elm = oldEndVNode?.elm                    }                    parentDom.insertBefore(                        oldEndVNode.elm,                        oldStartVNode.elm.nextSibling                    )                    oldEndVNode = oldCh[--oldEndIdx]                    newStartVNode = newCh[++newStartIdx]                } else {                    const keyMap = {}                    for (let i = oldStartIdx; i <= oldEndIdx; i++) {                        const key = oldCh[i]?.key                        if (key) keyMap[key] = i                    }                    let idxInOld = keyMap[newStartVNode.key]                    if (idxInOld) {                        const elmMove = oldCh[idxInOld]                        patchVNode(elmMove, newStartVNode)                        oldCh[idxInOld] = undefined                        parentDom.insertBefore(elmMove.elm, oldStartVNode.elm)                    } else {                        parentDom.insertBefore(                            createElement(newStartVNode),                            oldStartVNode.elm                        )                    }                    newStartVNode = newCh[++newStartIdx]                }            }            if (oldStartIdx > oldEndIdx) {                const before = newCh[newEndIdx + 1]                    ? newCh[newEndIdx + 1].elm                    : null                for (let i = newStartIdx; i <= newEndIdx; i++) {                    parentDom.insertBefore(createElement(newCh[i]), before)                }            } else {                for (let i = oldStartIdx; i <= oldEndIdx; i++) {                    parentDom.removeChild(oldCh[i].elm)                }            }        } else {            //新的有子元素,循环插入            //清空旧节点            oldVNode.elm.innerHTML = ''            //便当插入新的子元素            for (let iterator of newVNode.children) {                let childDom = createElement(iterator)                oldVNode.elm.appendChild(childDom)            }        }    }}function sameVNode(vNode1, vNode2) {    return vNode1.key == vNode2.key}

dom/vNode.js

export default (sel, data, children, text, elm) => {    let key = data.key    return { sel, data, children, text, elm, key }}