虚拟dom总结

一、三个过程

  1. 用JavaScript模拟DOM树,并渲染这个DOM树
  2. 比较新老DOM树,得到比较的差异对象-----diff
  3. 把差异对象应用到渲染的DOM树。------patch:把这些变化用打补丁的方式更新到真实 dom 上去(其实第二步也属于patch)

patch:先找到差异对象(diff),再对其进行节点更新,就是创建新节点、移除等操作,将VDOM渲染成真正的DOM然后插入到容器里面

有一个问题:那个老dom树就是真实dom树吗,对其patch就是对真dom树直接进行操作吗?我觉得可以是真实dom树,如果是真实dom树,那就是边diff边更新                          有提到一点点

如果oldVnode是真实DOM节点或hydrating设置为true,需要用hydrate函数将虚拟DOM和真实DOM进行映射(下面),
然后将oldVnode设置为对应的虚拟dom,找到oldVnode.elm的父节点,根据vnode创建一个真实dom节点并插入到该父节点中oldVnode.elm的位置

 

浅析虚拟dom原理并实现

详解Vue中的虚拟DOM

  1. 具备跨平台的优势(由于 Virtual DOM 是以 JavaScript 对象为基础而不依赖真实平台环境,所以使它具有了跨平台的能力)
  2. 提升渲染性能

二、用JavaScript模拟DOM树,并渲染这个DOM树(就是渲染到页面上)

虚拟DOM,就是用JS对象结构的一种映射。用JS很容易模拟一个DOM树的结构。

  • Vue.js通过编译将template 模板转换成渲染函数(render) ,执行渲染函数就可以得到一个虚拟节点树

render函数的功能是把节点创建好,然后设置节点属性,最后递归创建。这样子我们就得到一个DOM树,然后插入(appendChild)到页面上。这里oldVnode就是真实node了。

三、patch

超详解diff(!!!!!重要!!!)   vdom

patch ,这个过程是对比新旧虚拟节点之间有哪些不同,然后根据对比结果找出需要更新的的节点进行更新。patch 的核心----diff 算法,找出本次DOM需要更新的节点来更新,其他的不更新

节点类型:相同的节点类型       

  • oldVnode: 旧的虚拟节点或旧的真实dom节点
  • vnode: 新的虚拟节点
  • elm 对应dom元素
  • oldCholdVnode的子节点,newCh为Vnode的子节点
  • sameVnode():节点相同,就是基本属性相同的情况
  • hydrating: 是否要跟真实dom混合

patch过程步骤: (应该说patch过程)

  1. 如果vnode不存在但oldVnode存在,则表示要移除旧的node,那么就进行销毁
  2. 如果oldVnode不存在但是vnode存在,说明是要创建新节点,那么就调用createElm来创建新节点
  3. 当vnode和oldVnode都存在时:
    1. 如果oldVnode与Vnode是同一节点,即基本属性相同的情况。基本属性相同的情况下才认为这个2个node只是局部发生了更新,然后才会对这2个node进行diff
    2. 如果2个node的基本属性存在不一致的情况,那么就会直接跳过diff的过程,进而依据vnode新建一个真实的dom,同时删除老的dom节点。
    3. 注:基本属性有:tag 属性即这个vnode的标签属性、data 属性包含了最后渲染成真实dom节点后,节点上的class,attribute,style以及绑定的事件、key 属性是vnode的标记,在diff过程中可以提高diff的效率等。就是元素节点、属性节点、文本节点相同

diff过程(patchVnode函数):

  1. 首先进行文本节点的判断,若oldVnode.text !== vnode.text,那么就会直接进行文本节点的替换;
    1. 注:其实就是上面的3.2,但文本节点的替换比新建更少耗性能,因为元素节点->属性节点->文本节点
  2. vnode没有文本节点的情况下,进入子节点的diff
    1. oldChch都存在且不相同的情况下,调用updateChildren对子节点进行diff
    2. oldCh不存在,ch存在,首先清空oldVnode的文本节点,同时调用addVnodes方法将ch添加到elm真实dom节点当中;
    3. oldCh存在,ch不存在,则删除elm真实节点下的oldCh子节点;
    4. oldVnode有文本节点,而vnode没有,那么就清空这个文本节点。

updateChildren(是整个diff过程中最重要的环节:):不带key

  1.  四个指针
  2. 如果这四个指针没有一对相同的节点,就把newChstartVnode被添加到oldStartVnode的前面,同时newStartIndex前移一位;
  3. 如果这四个指针有一对相同的节点,即满足sameVnode,就对这2个node进行diff,最后将patch打到oldStartVnode上,同时这一对的指针移动一位;
  4. 遍历结束条件:oldCh或者newChstartIndex >= endIndex。如果这个时候newStartIdx > newEndIdx,说明此时oldCh存在多余的节点,那么最后就需要将这些多余的节点删除。

 updateChildren:带key

  1. 当为vnode引入key属性后,在每一轮的diff过程中,当起始结束节点都没有找到sameVnode时,首先对oldCh中进行key值与索引的映射。用以将oldCh中的key属性作为,而对应的节点的索引作为
  2. 然后再判断在newStartVnode的属性中是否有key,且是否在oldKeyToIndx中找到对应的节点
    1. 上面两点的意思是:先给oldCh的每一个节点一个key值,然后再看newStartVnode这一个节点的key有没有在oldCh的key集合中。
    2. 如果不存在这个key,那么就将这个newStartVnode作为新的节点创建且插入到原有的root的子节点中
    3. 如果存在这个key,那么就取出oldCh中的存在这个keyvnode,然后再进行diff的过程(放到该放的地方)
  3. 指针移动,再次判断,直到遍历结束。

四、其他

1、v-once指令:这样模板只会在第一次更新时显示数据,此后再次更新该DOM里面引用的数据时,内容不会自动更新了。Vue内部把模板缓存起来了,把v-once对应的节点当作一个静态节点来看待,而不是一个响应式的数据(没有经过Object.defineproperty处理)。以后的更新渲染都只是从缓存中拿出结果而已。

2、如果旧节点就是真实dom树,那在更新的过程中就通过createElm、删除等操作完成了

如果旧节点还是vdom,则再将其转为真实dom树,如下:

模拟的JS进行转化为真实的DOM:通过递归

const createElement = (vnode) => {
  let tag = vnode.tag;
  let attrs = vnode.attrs || {};
  let children = vnode.children || [];
  if(!tag) {
    return null;
  }
  //创建元素
  let elem = document.createElement(tag);
  //属性
  let attrName;
  for (attrName in attrs) {
    if(attrs.hasOwnProperty(attrName)) {
      elem.setAttribute(attrName, attrs[attrName]);
    }
  }
  //子元素
  children.forEach(childVnode => {
    //给elem添加子元素
    elem.appendChild(createElement(childVnode));
  })

  //返回真实的dom元素
  return elem;
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值