对vue虚拟dom的研究

  • Vue.js通过编译将template 模板转换成渲染函数(render ) ,执行渲染函数就可以得到一个虚拟节点树
  • 在对 Model 进行操作的时候,会触发对应 Dep 中的 Watcher 对象。Watcher 对象会调用对应的 update 来修改视图。这个过程主要是将新旧虚拟节点进行差异对比,然后根据对比结果进行DOM操作来更新视图。

   简单点讲,在Vue的底层实现上,Vue将模板编译成虚拟DOM渲染函数。结合Vue自带的响应系统,在状态改变时,Vue能够智能地计算出重新渲染组件的最小代价并应到DOM操作上。

 patch(也叫做patching算法):虚拟DOM最核心的部分,它可以将vnode渲染成真实的DOM,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。这点我们从单词含义就可以看出, patch本身就有补丁、修补的意思,其实际作用是在现有DOM上进行修改来实现更新视图的目的。Vue的Virtual DOM Patching算法是基于Snabbdom的实现,并在些基础上作了很多的调整和改进

Virtual DOM 其实就是一棵以 JavaScript 对象( VNode 节点)作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

虚拟DOM的最终目标是将虚拟节点渲染到视图上。但是如果直接使用虚拟节点覆盖旧节点的话,会有很多不必要的DOM操作。例如,一个ul标签下很多个li标签,其中只有一个li有变化,这种情况下如果使用新的ul去替代旧的ul,因为这些不必要的DOM操作而造成了性能上的浪费。

为了避免不必要的DOM操作,虚拟DOM在虚拟节点映射到视图的过程中,将虚拟节点与上一次渲染视图所使用的旧虚拟节点(oldVnode)做对比,找出真正需要更新的节点来进行DOM操作,从而避免操作其他无需改动的DOM。

其实虚拟DOM在Vue.js主要做了两件事:

  • 提供与真实DOM节点所对应的虚拟节点vnode
  • 将虚拟节点vnode和旧虚拟节点oldVnode进行对比,然后更新视图
  • 具备跨平台的优势

由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力,比如说浏览器平台、Weex、Node 等。

  • 操作 DOM 慢,js运行效率高。我们可以将DOM对比操作放在JS层,提高效率。

因为DOM操作的执行速度远不如Javascript的运算速度快,因此,把大量的DOM操作搬运到Javascript中,运用patching算法来计算出真正需要更新的节点,最大限度地减少DOM操作,从而显著提高性能。

Virtual DOM 本质上就是在 JS 和 DOM 之间做了一个缓存。可以类比 CPU 和硬盘,既然硬盘这么慢,我们就在它们之间加个缓存:既然 DOM 这么慢,我们就在它们 JS 和 DOM 之间加个缓存。CPU(JS)只操作内存(Virtual DOM),最后的时候再把变更写入硬盘(DOM)

  • 提升渲染性能

Virtual DOM的优势不在于单次的操作,而是在大量、频繁的数据更新下,能够对视图进行合理、高效的更新。

为了实现高效的DOM操作,一套高效的虚拟DOM diff算法显得很有必要。我们通过patch 的核心----diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新。

Vue的diff算法是基于snabbdom改造过来的,仅在同级的vnode间做diff,递归地进行同级vnode的diff,最终实现整个DOM树的更新。因为跨层级的操作是非常少的,忽略不计,这样时间复杂度就从O(n3)变成O(n)。

diff 算法的实现过程

diff 算法本身非常复杂,实现难度很大。本文去繁就简,粗略介绍以下两个核心函数实现流程:

  • patch(container,vnode) :初次渲染的时候,将VDOM渲染成真正的DOM然后插入到容器里面。
  • patch(vnode,newVnode):再次渲染的时候,将新的vnode和旧的vnode相对比,然后之间差异应用到所构建的真正的DOM树上。
function createElement(vnode) {    
var tag = vnode.tag  
var attrs = vnode.attrs || {}    
var children = vnode.children || []    
if (!tag) {       return null  }    
// 创建真实的 DOM 元素    
var elem = document.createElement(tag)   // 属性    
var attrName    
for (attrName in attrs) {    if (attrs.hasOwnProperty(attrName)) { // 给 elem 添加属性elem.setAttribute(attrName, attrs[attrName])}}// 子元素children.forEach(function (childVnode) {// 给 elem 添加子元素,如果还有子节点,则递归的生成子节点。elem.appendChild(createElement(childVnode))  // 递归})    // 返回真实的 DOM 元素   return elem
}

2.patch(vnode,newVnode)

这里我们只考虑vnode与newVnode如何对比的情况:

function updateChildren(vnode, newVnode) {var children = vnode.children || []var newChildren = newVnode.children || []// 遍历现有的childrenchildren.forEach(function (childVnode, index) {var newChildVnode = newChildren[index]// 两者tag一样if (childVnode.tag === newChildVnode.tag) {// 深层次对比,递归updateChildren(childVnode, newChildVnode)} else { // 两者tag不一样replaceNode(childVnode, newChildVnode) }}
)}

 

转载于:https://www.cnblogs.com/zhouyideboke/p/11208638.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/247440.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

element-ui之dialog组件title插槽的使用

dialog对话框组件title属性的slot使用方法 使用背景 需要单独控制title中某个数据显示及样式&#xff0c;footer也一样 <el-dialog// 也可以这样写,但是没有办法单独控制name age的显示// title"name age"title"提示":visible.sync"dialogVisi…

css3自适应布局单位vw,vh

视口单位(Viewport units) 什么是视口&#xff1f; 在桌面端&#xff0c;视口指的是在桌面端&#xff0c;指的是浏览器的可视区域&#xff1b;而在移动端&#xff0c;它涉及3个视口&#xff1a;Layout Viewport&#xff08;布局视口&#xff09;&#xff0c;Visual Viewport…

python 操作 elasticsearch-7.0.2 遇到的问题

错误一&#xff1a;TypeError: search() got an unexpected keyword argument doc_type&#xff0c;得到不预期外的参数 解决方法&#xff1a;elasticsearch7里不用文档类型&#xff0c;所以去掉 doc_typecredit_data 错误二&#xff1a;RequestError(400, illegal_argument_ex…

用到的Shell

sed 1i 添加的内容 file #这是在第一行前添加字符串 sed $i 添加的内容 file #这是在最后一行行前添加字符串 sed $a添加的内容 file #这是在最后一行行后添加字符串 sed -i s/.*/行首添加内容&行尾添加内容/ 文件名 //每一行 sed -i $a新增的一行 tars_build_tar.sh a…

如何解决浏览器缩小出现横向滚动条时网页背景图出现空白的问题

原因&#xff1a; 当窗口缩小时&#xff0c;浏览器默认100%宽度为浏览器窗口的宽度。而忽略了下部内容层固定宽度(1024px)。从而出现了固定宽度大于100%宽度的现象。浏览以此理解来解析页面&#xff0c;就出现了容器宽度理解上的差异&#xff0c;出现了一个非常奇特的BUG。 解…

前端设计模式

1. 单例模式 2.装饰器模式 转载于:https://www.cnblogs.com/lyraLee/p/11210985.html

区别 (function($){...})(jQuery)、$(function(){ })和$.fn

一、(function($){…})(jQuery) 首先function(arg){...}定义了一个匿名函数&#xff0c;参数为arg,而调用时需要在函数后面写上括号和实参&#xff0c;由于操作符的优先级&#xff0c;函数本身也需要括号&#xff0c;也就成了&#xff1a; &#xff08;function(arg){...}&…

git 清除缓存

清除git缓存 git config --local --unset credential.helpergit config --global --unset credential.helpergit config --system --unset credential.helper保存git缓存 git config --global credential.helper store转载于:https://www.cnblogs.com/zhouyideboke/p/11211650.…

网页里如何使用js禁用控制台

网页里如何禁用右击事件&#xff1f;使用jQuery&#xff0c;几句代码就可以搞定了 document.oncontextmenu function(){return false;} 简单示例&#xff1a; js实现&#xff1a; <!DOCTYPE html> <html><head><meta charset"UTF-8"><…

Java生鲜电商平台-订单中心服务架构与异常订单逻辑

Java生鲜电商平台-订单中心服务架构与异常订单逻辑 订单架构实战中阐述了订单系统的重要性&#xff0c;并从订单系统的信息架构和流程上对订单系统有了总体认知&#xff0c;同时还穿插着一些常见的订单业务规则和逻辑。上文写到订单的拆单部分时搁置了&#xff0c;现在接上文继…

Vuex的全面用法总结

1. vuex简介 vuex是专门用来管理vue.js应用程序中状态的一个插件。他的作用是将应用中的所有状态都放在一起&#xff0c;集中式来管理。需要声明的是&#xff0c;这里所说的状态指的是vue组件中data里面的属性。了解vue的同学应该是明白data是怎么回事的吧&#xff0c;如果不懂…

vue中通过第三方代理解决跨域问题

最近在学node&#xff0c;遇到了跨域的问题&#xff0c;来记录下方法 首页服务端的框架是通过express-generator 搭建起来的 npm install -g express-generator 具体接下来的细节不多说&#xff0c;今天主要说跨域 的问题 左侧为服务端项目结构&#xff0c;www为可执行文件&am…

使用V-chart时配置踩过的一些坑

如何配置图表信息 echart的配置项可谓是相当的海量&#xff0c;能不看就不看。而v-chart对其进行了不少的简化&#xff0c;所以我们想要自定义一个图表时&#xff0c;最好按照以下步骤来检查&#xff1a; 图表私有属性 v-chart每一个图表都有自己独有的设置项&#xff0c;想…

ZDOzMRVAOq

11 转载于:https://www.cnblogs.com/wc529065/p/11212226.html

vue 出现Elements in iteration expect to have 'v-bind:key' directives

是由于eslint检测出现bug 解决方法有两种 v-for 后添加 :keyitem <li v-for"item in list" :key"item"> 在build处关闭eslint检测 ...(config.dev.useEslint ? [createLintingRule()] : []),

requestAnimationFram

window.requestAnimationFrame() 告诉浏览器——你希望执行一个动画&#xff0c;并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数&#xff0c;该回调函数会在浏览器下一次重绘之前执行 注意&#xff1a;若你想在浏览器下次重绘之…

vue/return-in-computed-property Enforce that a return statement is present in computed property

此规则强制return语句在computed属性中得完整存在。 <script> export default {computed: {/* ✓ GOOD */foo () {if (this.bar) {return this.baz} else {return this.baf}},bar: function () {return false},/* ✗ BAD */baz () {if (this.baf) {return this.baf}},ba…

vue-cli3 一直运行 /sockjs-node/info?t= 解决方案

首先 sockjs-node 是一个JavaScript库&#xff0c;提供跨浏览器JavaScript的API&#xff0c;创建了一个低延迟、全双工的浏览器和web服务器之间通信通道。 服务端&#xff1a;sockjs-node&#xff08;https://github.com/sockjs/sockjs-node&#xff09; 客户端&#xff1a;so…

Java生鲜电商平台-生鲜供应链(采购管理)

Java生鲜电商平台-生鲜供应链(采购管理) 在生鲜供应链系统中采购中心这一模块&#xff0c;它是电商公司管理采购的模块&#xff0c;包含供应商管理&#xff0c;采购订单管理&#xff0c;采购商品管理&#xff0c;在该模块中采购订单是采购中心的核心模块。在其他的比如供应商的…

链式调用setTimeout()与setInterval()的区别

使用 setTimeout()和 setInterval()创建的定时器可以用于实现有趣且有用的功能。执行时机是不能保证的&#xff0c;因为在页面的生命周期中&#xff0c;不同时间可能有其他代码在控制 JavaScript 进程。在页面下载完后的代码运行、事件处理程序、Ajax 回调函数都必须使用同样的…