Virtual DOM 的原理与实现

只贴代码 不解释过程 勿喷

博客 文章地址;
github地址

环境搭建

1.克隆

1$ git clone https://github.com/cvgellhorn/webpack-boilerplate.git 2$ npm install 3$ npm install @babel/plugin-transform-react-jsx --save-dev

2.配置babel

1"plugins": [ 2 ...其他配置 3 [ 4 "@babel/plugin-transform-react-jsx", 5 { 6 "pragma": "dom" // 这里表示生成的jsx 函数 7 } 8 ] 9 ...其他配置 10]

代码 index.js

1 2/* 3 * @Author: bucai 4 * @Date: 2019-10-16 20:54:45 5 * @LastEditors: bucai 6 * @LastEditTime: 2019-10-17 10:11:38 7 * @Description: vdom 8 */ 9 10// 生成的虚拟dom的节点 11function dom(type, props, ...children) { 12 return { 13 type, 14 props, 15 children 16 } 17} 18 19/** 20 * 生成真实的dom树 21 * @param {object} domObj node节点 22 */ 23function generateDom(domObj) { 24 let $el; 25 if (domObj.type) { 26 $el = document.createElement(domObj.type); 27 } else { 28 $el = document.createTextNode(domObj); 29 } 30 31 if (domObj.props) { 32 Object.keys(domObj.props).forEach(key => { 33 $el.setAttribute(key, domObj.props[key]); 34 }); 35 } 36 37 if (domObj.children) { 38 domObj.children.forEach(child => { 39 $el.appendChild(generateDom(child)); 40 }); 41 } 42 43 return $el; 44} 45/** 46 * 对比节点是否改变 47 * @param {object} nodea 节点A 48 * @param {object} nodeb 节点B 49 */ 50// node 发生改变 51function isNodeChange(nodea, nodeb) { 52 // 如果nodea.type 都不为空表示是 元素节点 53 if (nodea.type !== undefined && nodeb.type !== undefined) { 54 return nodea.type !== nodeb.type; 55 } 56 // 如果是其中又一个是文本节点就判断字符串 57 return nodea !== nodeb; 58} 59/** 60 * 是否参数改变 61 * @param {object} propa attrlist 62 * @param {*} propb attrlist 63 */ 64// props change 65function isPropsChange(propa, propb) { 66 // 统一化 67 const oldProps = propa || {}; 68 const newProps = propb || {}; 69 // 获取参数的key 70 const oldKeys = Object.keys(oldProps); 71 const newKeys = Object.keys(newProps); 72 // 长度不同就说明改变了 73 if (oldKeys.length !== newKeys.length) { 74 return true; 75 } 76 // 当长度一致的时候 77 if (oldKeys.length === 0) { 78 return false; 79 } 80 // 遍历key 来对比是否改变 81 for (let i = 0; i < oldKeys.length; i++) { 82 const oldkey = oldKeys[i]; 83 const newkey = newKeys[i]; 84 // 对key进行遍历 85 // if (oldkey !== newkey) { // // 这里没有意义 86 // return true; 87 // } 88 if (oldProps[oldkey] !== newProps[newkey]) { 89 return true; 90 } 91 } 92 // 如果上面都不符合就说是没有更改的 93 return false; 94} 95 96/** 97 * 虚拟DOM对比函数 98 * @param {HTMLElement} $parent 父节点 99 * @param {object} oldNode 旧的节点对象 100 * @param {object} newNode 新的节点对象 101 * @param {number} index 子节点的下标 102 */ 103function vDom($parent, oldNode, newNode, index = 0) { 104 const $currlenEl = $parent.childNodes[index]; 105 // 旧的不存在就直接添加到dom中 106 if (!oldNode) { 107 // append 108 return $parent.appendChild(generateDom(newNode)); 109 } 110 // 新的不存在就直接移除旧的节点 111 if (!newNode) { 112 // REMOVE 113 return $parent.removeChild($currlenEl); 114 } 115 // 判断节点是否改变 116 // node change 117 if (isNodeChange(oldNode, newNode)) { 118 return $parent.replaceChild(generateDom(newNode), $currlenEl); 119 } 120 // 节点相同就没问题 121 // no change textNode 122 if (oldNode === newNode) { 123 return; 124 } 125 126 // change props 127 const oldProps = oldNode.props || {}; 128 const newProps = newNode.props || {}; 129 130 if (isPropsChange(oldProps, newProps)) { 131 const oldPropsKey = Object.keys(oldProps); 132 const newPropsKey = Object.keys(newProps); 133 134 // 如果新的props 为空就将old全部移除 135 if (newPropsKey.length === 0) { 136 oldPropsKey.forEach(key => { 137 $currlenEl.removeAttribute(key); 138 }); 139 } else { 140 141 const propsKeySet = new Set([...oldPropsKey, ...newPropsKey]); 142 143 propsKeySet.forEach(key => { 144 145 if (oldProps[key] === undefined) { 146 $currlenEl.setAttribute(key, newProps[key]); 147 } else if (newProps[key] === undefined) { 148 $currlenEl.removeAttribute(key); 149 } else if (newProps[key] !== oldProps[key]) { 150 $currlenEl.setAttribute(key, newProps[key]); 151 } 152 153 }); 154 } 155 } 156 // 子节点 157 // children change 158 const oldChildren = oldNode.children || []; 159 const newChildren = newNode.children || []; 160 161 if (oldChildren.length || newChildren.length) { 162 const maxLen = Math.max(oldChildren.length, newChildren.length); 163 for (let i = 0; i < maxLen; i++) { 164 vDom($currlenEl, oldChildren[i], newChildren[i], i); 165 } 166 } 167 168} 169 170// 原来的dom树 171const profile = ( 172 <div> 173 <h3 class="aaaa">123</h3> 174 <p>123</p> 175 </div> 176); 177// 新的dom树 178const profileChange = ( 179 <div class="b"> 180 <h3 class="bucai" data-user-id="{a:1}">222</h3> 181 <span>xxxxx</span> 182 </div> 183); 184// 先生成一个真实的dom树 并将他添加到html中 185const $el = generateDom(profile); 186const $app = document.querySelector('#app'); 187$app.appendChild($el); 188 189// 延时,方便查看 190setTimeout(() => { 191 // 调用虚拟dom 对比两棵树 192 vDom($app, profile, profileChange); 193}, 3000); 194 195
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

一篇文章带你了解JavaScript日期

日期对象允许您使用日期(年、月、日、小时、分钟、秒和毫秒)。一、JavaScript的日期格式一个JavaScript日期可以写为一个字符串:ThuFeb02201909:59:51GMT0800(中国标准时间)或者是一个数字:1486000791164写数字的日期,指定的毫秒数自1970年1月1日00:00:00到现在。1\.显示日期使用

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )

Virtual DOM 的原理与实现 - HelloWorld