vue 虚拟DOM的原理

为什么需要虚拟DOM?

  如果对前端工作进行抽象的话,主要就是维护状态和更新视图,而更新视图和维护状态都需要DOM操作。其实近年来,前端的框架主要发展方向就是解放DOM操作的复杂性。

  运行js的速度是很快的,大量的操作DOM就会很慢,时常在更新数据后会重新渲染页面,这样造成在没有改变数据的地方也重新渲染了DOM 节点,这样就造成了很大程度上的资源浪费。

  在jQuery出现以前,我们直接操作DOM结构,这种方法复杂度高,兼容性也较差。有了jQuery强大的选择器以及高度封装的API,我们可以更方便的操作DOM,jQuery帮我们处理兼容性问题,同时也使DOM操作变得简单。

  但是聪明的程序员不可能满足于此,各种MVVM框架应运而生,有angularJS、avalon、vue.js等,MVVM使用数据双向绑定,使得我们完全不需要操作DOM了,更新了状态,视图会自动更新。更新了视图数据状态也会自动更新,可以说MVVM使得前端的开发效率大幅提升。但是其大量的事件绑定使得其在复杂场景下的执行性能堪忧,有没有一种兼顾开发效率和执行效率的方案呢?由此引入Virtual DOM(虚拟DOM)。

  利用在内存中生成与真实DOM与之对应的数据结构,这个在内存中生成的结构称之为虚拟DOM 。

  当数据发生变化时,能够智能地计算出重新渲染组件的最小代价并应用到DOM操作上。

Virtual DOM 算法

  所谓的 Virtual DOM 算法。包括几个步骤:

  1.用 JavaScript 对象结构表示 DOM 树的结构;然后用这个树构建一个真正的 DOM 树,插到文档当中;

  2.当状态变更的时候,重新构造一棵新的对象树。然后用新的树和旧的树进行比较,记录两棵树差异;

  3.把2所记录的差异应用到步骤1所构建的真正的DOM树上,视图就更新了。

  Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存。

  既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)。

  所谓的virtual dom,也就是虚拟节点。它通过js的Object对象模拟DOM中的节点,然后再通过特定的render方法将其渲染成真实的DOM节点 dom。diff 则是通过JS层面的计算,返回一个patch对象,即补丁对象,在通过特定的操作解析patch对象,完成页面的重新渲染。

比较两棵虚拟DOM树的差异

  比较两棵DOM树的差异是 Virtual DOM 算法最核心的部分,这也是所谓的 Virtual DOM 的 diff 算法。

  两个树的完全的 diff 算法是一个时间复杂度为 O(n^3) 的问题。但是在前端当中,你很少会跨越层级地移动DOM元素。所以 Virtual DOM 只会对同一个层级的元素进行对比:

  上面的div只会和同一层级的div对比,第二层级的只会跟第二层级对比。这样算法复杂度就可以达到 O(n)。

  在实际的代码中,会对新旧两棵树进行一个深度优先的遍历,这样每个节点都会有一个唯一的标记,如下图所示:

Virtual DOM 算法实现

  Virtual DOM 算法得实现主要是用三个函数:element,diff,patch。然后就可以实际的进行使用,如下面代码所示:

// 1. 构建虚拟DOM
var tree = el('div', {'id': 'container'}, [
  el('h1', {style: 'color: blue'}, ['simple virtal dom']),
  el('p', ['Hello, virtual-dom']),
  el('ul', [el('li')])
])

// 2. 通过虚拟DOM构建真正的DOM
var root = tree.render()
document.body.appendChild(root)

// 3. 生成新的虚拟DOM
var newTree = el('div', {'id': 'container'}, [
  el('h1', {style: 'color: red'}, ['simple virtal dom']),
  el('p', ['Hello, virtual-dom']),
  el('ul', [el('li'), el('li')])
])

// 4. 比较两棵虚拟DOM树的不同
var patches = diff(tree, newTree)

// 5. 在真正的DOM元素上应用变更
patch(root, patches)

diff算法

用 三大策略 将O(n^3)复杂度 转化为 O(n)复杂度

  • 策略一(tree diff):

  Web UI中DOM节点跨层级的移动操作特别少,可以忽略不计。

  • 策略二(component diff):

  拥有相同类的两个组件 生成相似的树形结构,
  拥有不同类的两个组件 生成不同的树形结构。

  • 策略三(element diff):

  对于同一层级的一组子节点,通过唯一id区分。

tree diff

(1)通过updateDepth对Virtual DOM树进行层级控制。
(2)对树分层比较,两棵树只对同一层次节点进行比较。如果该节点不存在时,则该节点及其子节点会被完全删除,不会再进一步比较。
(3)只需遍历一次,就能完成整棵DOM树的比较。

  diff只简单考虑同层级的节点位置变换,如果是跨层级的话,只有创建节点和删除节点的操作。

如上图所示,以A为根节点的整棵树会被重新创建,而不是移动,因此官方建议不要进行DOM节点跨层级操作,可以通过CSS隐藏、显示节点,而不是真正地移除、添加DOM节点。

以上就是vue 虚拟DOM的原理的详细内容,更多关于vue 虚拟DOM的资料请关注我们其它相关文章!

(0)

相关推荐

  • vue 源码解析之虚拟Dom-render

    vue 源码解析 --虚拟Dom-render instance/index.js function Vue (options) { if (process.env.NODE_ENV !== 'production' && !(this instanceof Vue) ) { warn('Vue is a constructor and should be called with the `new` keyword') } this._init(options) } renderMixin

  • 解决vue中虚拟dom,无法实时更新的问题

    碰到的问题:使用jq获取元素节点的个数时一直为0 解决方法:使用vue的nextTick()函数即可解决 原理:nextTick可以在下一次更新dom之后进行回调,我的问题在于,在页面加载完成时无法获取虚拟dom,而使用回调函数后就可以获取到正确的dom数量,所以只需要在nextTick函数中执行jq函数就可以正确获取了. self.$nextTick(function () { // DOM 更新了 $("#myCarousel").carousel(0); }) 以上这篇解决vue

  • vue2.0的虚拟DOM渲染思路分析

    1.为什么需要虚拟DOM 前面我们从零开始写了一个简单的类Vue框架(文章链接),其中的模板解析和渲染是通过Compile函数来完成的,采用了文档碎片代替了直接对页面中DOM元素的操作,在完成数据的更改后通过appendChild函数将真实的DOM插入到页面. 虽然采用的是文档碎片,但是操作的还是真实的DOM. 而我们知道操作DOM的代价是昂贵的,所以vue2.0采用了虚拟DOM来代替对真实DOM的操作,最后通过某种机制来完成对真实DOM的更新,渲染视图. 所谓的虚拟DOM,其实就是 用JS来模

  • vue 虚拟dom的patch源码分析

    本文介绍了vue 虚拟dom的patch源码分析,分享给大家,具体如下: 源码目录:src/core/vdom/patch.js function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) { let oldStartIdx = 0 let newStartIdx = 0 let oldEndIdx = oldCh.length - 1 let oldStartVnode = oldCh[0]

  • 探究Vue.js 2.0新增的虚拟DOM

    你可能早就已经听说了 Vue.js 2.0.一个主要的令人兴奋的新特性就是更新页面的"虚拟DOM"的加入. 虚拟 DOM 可以做什么? React 和 Ember 都使用了虚拟DOM来提升页面的刷新速度.为了理解其如何工作,让我们先讨论一下几个概念: 更新DOM的花费时间非常长 当我们使用 JavaScript 来改变页面的时候,浏览器不得不做一些工作来找到需要的DOM节点,并且做出类似这样的改变: document.getElementById('myId').appendChild

  • 深入理解Vue2.x的虚拟DOM diff原理

    前言 经常看到讲解Vue2的虚拟Dom diff原理的,但很多都是在原代码的基础上添加些注释等等,这里从0行代码开始实现一个Vue2的虚拟DOM 实现VNode src/core/vdom/Vnode.js export class VNode{ constructor ( tag, //标签名 children,//孩子[VNode,VNode], text, //文本节点 elm //对应的真实dom对象 ){ this.tag = tag; this.children = children

  • Vue使用虚拟dom进行渲染view的方法

    前提 vue版本:v2.5.17-beta.0 触发render vue在数据更新后会自动触发view的render工作,其依赖于数据驱动:在数据驱动的工作下,每一个vue的data属性都被监听,并且在set触发时,派发事件,通知收集到的依赖,从而触发对应的操作,render工作就是其中的一个依赖,并且被每一个data属性所收集,因此每一个data属性改变后,都会触发render. vue更新监听 看一段代码 // 来自mountComponent函数 updateComponent = fun

  • 一篇文章带你搞懂Vue虚拟Dom与diff算法

    前言 使用过Vue和React的小伙伴肯定对虚拟Dom和diff算法很熟悉,它扮演着很重要的角色.由于小编接触Vue比较多,React只是浅学,所以本篇主要针对Vue来展开介绍,带你一步一步搞懂它. 虚拟DOM 什么是虚拟DOM? 虚拟DOM(Virtual   Dom),也就是我们常说的虚拟节点,是用JS对象来模拟真实DOM中的节点,该对象包含了真实DOM的结构及其属性,用于对比虚拟DOM和真实DOM的差异,从而进行局部渲染来达到优化性能的目的. 真实的元素节点: <div id="wr

  • vue 虚拟DOM的原理

    为什么需要虚拟DOM? 如果对前端工作进行抽象的话,主要就是维护状态和更新视图,而更新视图和维护状态都需要DOM操作.其实近年来,前端的框架主要发展方向就是解放DOM操作的复杂性. 运行js的速度是很快的,大量的操作DOM就会很慢,时常在更新数据后会重新渲染页面,这样造成在没有改变数据的地方也重新渲染了DOM 节点,这样就造成了很大程度上的资源浪费. 在jQuery出现以前,我们直接操作DOM结构,这种方法复杂度高,兼容性也较差.有了jQuery强大的选择器以及高度封装的API,我们可以更方便的

  • vue 虚拟DOM快速入门

    虚拟 DOM 什么是虚拟 dom dom 是文档对象模型,以节点树的形式来表现文档. 虚拟 dom 不是真正意义上的 dom.而是一个 javascript 对象. 正常的 dom 节点在 html 中是这样表示: <div class='testId'> <p>你好</p> <p>欢迎光临</p> </div> 而在虚拟 dom 中大概是这样: { tag: 'div', attributes:{ class: ['testId']

  • 关于Vue虚拟dom问题

    一.什么是虚拟dom? 虚拟dom本质上就是一个普通的JS对象,用于描述视图的界面结构 在vue中,每个组件都有一个render函数, 没有render找template,没有template找el,有el就会把el.outHTML作为template,然后把这串字符串编译成render函数. 有template就不往下找了.有render同理. 每个render 函数都会返回一个虚拟dom树,这也就意味着每个组件都对应一棵虚拟DOM树. 也就是说render目的就是创建虚拟dom,这个组件到底

  • Vue虚拟Dom到真实Dom的转换

    再有一颗树形结构的Javascript对象后, 我们需要做的就是讲这棵树跟真实Dom树形成映射关系.我们先回顾之前的mountComponnet 方法: export function mountComponent(vm, el) { vm.$el = el ... callHook(vm, 'beforeMount') ... const updateComponent = function () { vm._update(vm._render()) } ... } 我们已经执行完了vm._r

  • Vue虚拟DOM详细介绍

    目录 一.什么是虚拟DOM 二.为什么需要虚拟DOM 三.虚拟DOM介绍 一.什么是虚拟DOM 虚拟DOM是对真是DOM的抽象,以JavaScript对象(VNode节点)作为基础的树,用对象的属性来描述节点,最终可以通过一系列操作使这棵树映射到真实环境上. Javascript对象中,虚拟DOM表现为一个Object对象,并且最少包含标签名(tag).属性(attrs)和子元素对象(children)三个属性. 创建虚拟DOM就是为了更好将虚拟的节点渲染到页面视图中,所以虚拟DOM对象的节点与

  • Vue虚拟dom被创建的方法

    先来看生成虚拟dom的入口文件: ... import { parse } from './parser/index' import { optimize } from './optimizer' import { generate } from './codegen/index' ... const ast = parse(template.trim(), options) if (options.optimize !== false) { optimize(ast, options) } c

  • Vue源码分析之虚拟DOM详解

    为什么需要虚拟dom? 虚拟DOM就是为了解决浏览器性能问题而被设计出来的.例如,若一次操作中有10次更新DOM的动作,虚拟DOM不会立即操作DOM,而是将这10次更新的diff内容保存到本地一个JS对象中,最终将这个JS对象一次性attch到DOM树上,再进行后续操作,避免大量无谓的计算量.简单来说,可以把Virtual DOM 理解为一个简单的JS对象,并且最少包含标签名( tag).属性(attrs)和子元素对象( children)三个属性. ----- 元素节点: 元素节点更贴近于我们

随机推荐