{"id":19069583,"url":"https://github.com/srect/virtualdom","last_synced_at":"2025-10-20T11:43:28.177Z","repository":{"id":39550064,"uuid":"182818761","full_name":"sRect/virtualDOM","owner":"sRect","description":"虚拟DOM","archived":false,"fork":false,"pushed_at":"2022-12-09T20:28:24.000Z","size":1508,"stargazers_count":0,"open_issues_count":18,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2025-02-22T03:42:00.527Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sRect.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-04-22T15:45:34.000Z","updated_at":"2019-06-19T05:53:34.000Z","dependencies_parsed_at":"2022-09-20T05:42:05.269Z","dependency_job_id":null,"html_url":"https://github.com/sRect/virtualDOM","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/sRect/virtualDOM","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2FvirtualDOM","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2FvirtualDOM/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2FvirtualDOM/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2FvirtualDOM/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sRect","download_url":"https://codeload.github.com/sRect/virtualDOM/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sRect%2FvirtualDOM/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":260639894,"owners_count":23040462,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-11-09T01:14:49.881Z","updated_at":"2025-10-20T11:43:28.093Z","avatar_url":"https://github.com/sRect.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"virtualDOM\n---\n\n### 第一步：用JS创建虚拟DOM\n```javascript\nclass Element {\n  constructor(type, props, children) {\n    this.type = type;\n    this.props = props;\n    this.children = children;\n  }\n}\n\n// 创建虚拟DOM\nfunction createElement(type, props, children) {\n  return new Element(type, props, children)\n}\n\nlet vertualDom = createElement('ul', { class: 'list' }, [\n  createElement('li', { class: 'item' }, [\n    createElement('input', { class: 'input', value: 'vertualDOM' }, [])\n  ]),\n  createElement('li', { class: 'item' }, ['b']),\n  createElement('li', { class: 'item' }, ['c']),\n]);\n\n// 将虚拟DOM转为真实DOM\nfunction render(eleObj) {\n  let el = document.createElement(eleObj.type);\n\n  for (let key in eleObj.props) {\n    // 设置属性的方法\n    setAttr(el, key, eleObj.props[key]);\n  }\n\n  eleObj.children.forEach(child =\u003e {\n    // 判断child是否是一个元素\n    child = (child instanceof Element) ? render(child) : document.createTextNode(child);\n    el.appendChild(child);\n  })\n  return el;\n}\n\nfunction renderDOM(el, target) {\n  target.appendChild(el);\n}\n\n// 将真实DOM插入到页面中\nlet el = render(vertualDom);\nrenderDOM(el, document.body);\n```\n\n### 第二步：比较两棵虚拟DOM树的差异(深度先序)\n```javascript\n// 定义差异类型\nconst ATTRS = 'ATTRS'; // 属性变化\nconst TEXT = 'TEXT'; // 文本变化\nconst REMOVE = 'REMOVE'; // 节点删除\nconst REPLACE = 'REPLACE'; // 节点覆盖\n\nfunction diff(oldTree, newTree) {\n  let patches = {};\n  let index = 0;\n\n  walk(oldTree, newTree, index, patches);\n  return patches;\n}\n\n// 遍历树 找出差异 放入补丁包中\nfunction walk(oldNode, newNode, index, patches) {\n  let currentPatch = [];\n\n  if(!newNode) { // 节点删除\n    currentPatch.push({type: types.REMOVE, index});\n  } else if (isString(oldNode) \u0026\u0026 isString(newNode)) { // 判断文本变化\n    oldNode !== newNode ? currentPatch.push({\n      type: types.TEXT,\n      text: newNode\n    }) : undefined;\n  }else if(oldNode.type === newNode.type) {\n    // 比较属性差异\n    let attrs = diffAttr(oldNode.props, newNode.props);\n    if(Object.keys(attrs).length) { // 判断是否为一个空对象\n      currentPatch.push({type: types.ATTRS, attrs})\n    }\n\n    // 如果有子节点应该遍历子节点\n    diffChildren(oldNode.children, newNode.children, patches);\n  } else {\n    // 节点被替换\n    currentPatch.push({ type: REPLACE, index, newNode });\n  }\n\n  if (currentPatch.length) {\n    // 将当前元素补丁放入到大补丁中\n    patches[index] = currentPatch;\n  }\n} \n```\n\n### 第三步：把patches应用到真正的DOM树上\n```javascript\nfunction patch(node, patches) {\n  allPatches = patches;\n  // 给元素打补丁\n  // 此时的node是真实的DOM\n  walk(node);\n}\n\nfunction walk(node) {\n  let key = index++;\n  let currentPatch = null;\n  currentPatch = allPatches[key];\n  let childNodes = node.childNodes;\n  console.log(childNodes);\n  [...childNodes].forEach(child =\u003e walk(child)); // 深度先序\n\n  if (currentPatch \u0026\u0026 currentPatch.length) {\n    doPatch(node, currentPatch); // 打补丁是后序的\n  }  \n}\n\n// 进行打补丁\nfunction doPatch(node, patches) {\n  patches.forEach(patch =\u003e {\n    switch(patch.type) {\n      case types.ATTRS:\n        for(let key in patch.attrs) {\n          let value = patch.attrs[key];\n          if(value) {\n            setAttr(node, key, value);\n          } else {\n            node.removeAttribute(key);\n          }\n        }\n        break;\n      case types.TEXT:\n        node.textContent = patch.text;\n        break;\n      case types.REPLACE:\n        let newNode = (patch.newNode instanceof Element) ? render(patch.newNode) : document.createTextNode(patch.newNode);\n        node.parentNode.replaceChild(newNode, node);\n        break;\n      case types.REMOVE:\n        node.parentNode.removeChild(node);\n        break;\n      default:\n        break;\n    }\n  })\n}\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrect%2Fvirtualdom","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsrect%2Fvirtualdom","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsrect%2Fvirtualdom/lists"}