为什么使用虚拟DOM
虚拟DOM不会进行排版与重绘操作 虚拟DOM进行频繁修改,然后一次性比较并修改真实DOM中需要改的部分(注意!),最后并在真实DOM中进行排版与重绘,减少过多DOM节点排版与重绘损耗 真实DOM频繁排版与重绘的效率是相当低的 虚拟DOM有效降低大面积(真实DOM节点)的重绘与排版,因为最终与真实DOM比较差异,可以只渲染局部(同2) 使用虚拟DOM的损耗计算: 总损耗=虚拟DOM增删改+(与Diff算法效率有关)真实DOM差异增删改+(较少的节点)排版与重绘 直接使用真实DOM的损耗计算: 总损耗=真实DOM完全增删改+(可能较多的节点)排版与重绘 总之,一切为了减弱频繁的大面积重绘引发的性能问题,不同框架不一定需要虚拟DOM,关键看框架是否频繁会引发大面积的DOM操作。
虚拟DOM优化实战
如果我有1000条数据,我修改了其中两条,真实的DOM会重新渲染1000条数据,只要发生了变化,就会重新渲染全部数据,虚拟dom 会生成1000个对象 (它是不会被浏览器图形化渲染的),虚拟dom 里的东西会和真实dom绑定在一起,当数据发生变化 虚拟dom和之前的虚拟dom 会去做数据的比较,当数据发生变化时,才会去更新数据发生改变的那部分真实的dom元素
但是数组没有默认的标识,所以数组每次改变都要重新排序,性能影响较大,所以在实时侦听遍历数组数据时,需要引入key属性,用来标识数组数据,一般使用下标标识
虚拟dom的好处
虚拟DOM就是用JS对象来表示或者是模拟一个真实DOM的结构页面性能 通过虚拟DOM的对比,进行差异的更新能提升页面的性能.
如何使用虚拟dom
如何描述虚拟DOM(create)如何绘制虚拟DOM(render)如何差异化的更新虚拟DOM并且更新UI(update)
demo实战
其实就是一个JS的对象。我们先来简单的实现一个虚拟DOM。我们使用过React的都知道createElement这一个函数、Vue都知道render或者是“h”,这样一个函数。我们先来创建一个createElement函数。
/*** [createElement 用来创建DOM节点]* @param {[type]} type [元素类型(名称)]* @param {[type]} props [描述信息]* @param {[type]} children [子节点]* @return {[type]} [description]*/function createElement(type, props, children) {// 返回一个Element对象。return new Element(type, props, children);}复制代码
<div class="vdom">561651</div>type: divprops: class="vdom"children: 561651复制代码
元素对象(Element),用来表示一个元素。
class Element {constructor(type, props, children) {this.type = type;this.props = props;this.children = children;}}复制代码
let vDom = createElement("ul", {class: "dawd"}, [createElement("li", {class: "dawd"}, ["1"]),createElement("li", {class: "dawd"}, ["2"]),createElement("li", {class: "dawd"}, ["3"])]);复制代码
已经基本描述出了DOM的树形结构。下面我们来根据虚拟DOM创建真实的DOM。在这之前我们先创建元素节点(单个元素)。
/*** [createNode 创建单个元素]* @param {[type]} node [元素节点]* @return {[type]} [真实的DOM元素]*/function createNode(node){// 根据类型创建元素let el = document.createElement(node.type);for (key in node.props) {// 遍历属性if(key === "value"){// 只有input还有textarea需要value属性if(node.type.toUpperCase() === "INPUT" || node.type.toUpperCase() === "TEXTAREA"){el.value = node.props[key];}}else {// 设置属性el.setAttribute(key, node.props[key]);}}return el;}复制代码
根据单个元素组成DOM树
function createDom(node) {let root = createNode(node);if(node.children && node.children.length > 0){// 遍历子元素node.children.forEach( function(element) {if(element instanceof Element){// 节点root.appendChild( createDom(element) );}else {// 文本root.appendChild( document.createTextNode(element) );}});}return root;}复制代码
根据虚拟DOM生成的真实DOM
现在只是生成了真实的DOM但是还没有真正的挂载到DOM树上,没有显示。
let dom = createDom(vDom);document.getElementsByTagName("body")[0].appendChild(dom);复制代码

再试一下input元素
let vDom = createElement("ul", {class: "dawd"}, [createElement("li", {class: "dawd"}, [createElement("input", {type: "radio",value: "1651"},[]),createElement("input", {type: "text",value: "1651"},[])])]);复制代码
真实DOM
显示效果
我们再来一个复杂一点的来验证是否正确。
createElement("div", {class: "div"}, [createElement("ul", {class: "ul"}, [createElement("li", {class: "li"},[createElement("input", {type: "radio",value: "1651"},["单选"])]),createElement("li", {class: "li"},[createElement("input", {type: "text",value: "1651"},[])]),]),createElement("div", {class: "div"}, [createElement("p", {class: "p"},[createElement("span", {class: "span"},["我是span"])]),createElement("a", {class: "a",href: "https://juejin.im/editor/drafts/5cf3c75de51d45572c05fff3"},[createElement("span", {class: "span"},["我是超链接里面的span"])]),createElement("img", {class: "img",src: "http://g.hiphotos.baidu.com/image/h%3D300/sign=b5e4c905865494ee982209191df4e0e1/c2cec3fdfc03924590b2a9b58d94a4c27d1e2500.jpg",alt: "虚拟DOM图片",title: "虚拟的DOM"},[])]),]);复制代码

- 总结
- 虚拟DOM对我们的项目性能很有帮助,我们重点的是要了解他的思想和实现,不是一味的使用或者是照抄。
- 说到虚拟DOM就离不开DOM diff算法,我打算把他们分了两篇文章来写,首先一篇文章会太长。其次,我觉得这两个概念虽然戚戚相关但是我觉得还是两个东西。diff算法我觉得是对DOM更新等操作的优化,减少无用的更新,这样会带来更少的消耗更多的性能。
