最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 虚拟DOM与diff学习总结

    正文概述 掘金(菜居居)   2021-02-20   436

    Snabbdom

    ​ 是著名的虚拟DOM库,是diff算法的鼻祖,Vue源码借鉴了Snabbdom;

    虚拟DOM

    ​ 用javaScript对象描述DOM的层次结构.DOM中的一切属性都在虚拟DOM中有对应的属性.

    diff是发生在虚拟DOM上的

    ​ 新虚拟DOM和老虚拟DOM进行diff(精细化比较),算出应该如何最小量更新,最后反映到真正的DOM上

    虚拟DOM是如何被渲染函数(h函数)产生的

    1. h函数用来产生虚拟节点(vnode)

    2. h函数:

      ​ 第一个参数为标签节点

      ​ 第二个参数为一个props对象,里面放html标签属性对象

      ​ 第三个参数为标签文字

    3. 一个虚拟节点有哪些属性

      {
         
      	children:undefined,       //子元素
         
      	data:{ },	     	  //属性,样式
         
      	elm:undefined, 		  //真正DOM节点,如果为undefined则表示该节点还没有上树
         
      	key:undefined,		  //该节点的唯一标识
         
      	sel:'"div",		  //选择器
         
      	text:"我是一个盒子"  //文字
         
      }
      

      ​ 而vnode函数的功能非常简单,就是把传入的5个参数组合成对象返回

      ​ vnode源码

       export function vnode(sel, data, children, text, elm) {
          const key = data === undefined ? undefined : data.key;
          return { sel, data, children, text, elm, key };
       }
      
    4. h函数可以嵌套使用,从而得到虚拟DOM树

    diff算法原理

    (2个虚拟DOM如何进行差异化比较)

    diff心得

    1. 最小量更新,key是节点的唯一标识,告诉diff算法,在更改前后 他们是否为同一个DOM节点
    2. 只能是同一个虚拟节点,才进行精细化比较.否则就暴力删除旧的DOM节点,插入新的DOM节点.
      1. 延伸问题:如何定义是否为同一虚拟节点?? 答:选择器相同且key相同
    3. 只进行同层级比较,不进行跨层级比较.即使是同一片虚拟节点,但是如果跨层级了,不会进行diff精细化比较,而是暴力删除旧DOM节点,重新插入新的DOM节点

    diff处理新旧节点不是同一个节点时

    patch(oldVnode,element) 源码; oldVnode 老节点; element新节点

    return function patch (oldVnode: VNode | Element, vnode: VNode): VNode {
        let i: number, elm: Node, parent: Node
        const insertedVnodeQueue: VNodeQueue = []
        for (i = 0; i < cbs.pre.length; ++i) cbs.pre[i]()
    
        if (!isVnode(oldVnode)) {
          oldVnode = emptyNodeAt(oldVnode)
        }
    	//源码中sameVnode(oldVnode,vnode)方法 判断2个节点是否为同一个节点,返回一个布尔值
        if (sameVnode(oldVnode, vnode)) {
          //如果是 再调用patchVnode()精细化比较
          patchVnode(oldVnode, vnode, insertedVnodeQueue)
        } else {
          //如果不是 暴力删除老节点,创建新节点重新插入
          elm = oldVnode.elm!
          parent = api.parentNode(elm) as Node
    	  //创建新节点
          createElm(vnode, insertedVnodeQueue)
    
          if (parent !== null) {
            api.insertBefore(parent, vnode.elm!, api.nextSibling(elm))
            //删除老节点
            removeVnodes(parent, [oldVnode], 0, 0)
          }
        }
    	//一些生命周期的钩子,暂不深入研究
        for (i = 0; i < insertedVnodeQueue.length; ++i) {
          insertedVnodeQueue[i].data!.hook!.insert!(insertedVnodeQueue[i])
        }
        for (i = 0; i < cbs.post.length; ++i) cbs.post[i]()
        return vnode
      }
    

    如何定义同一个节点

    源码中sameVnode(oldVnode,vnode)方法 判断2个节点是否为同一个节点,返回一个布尔值

    判断旧节点的key要和新节点的key相同 且 旧节点的选择器要和新节点的选择器相同

        function sameVnode (vnode1: VNode, vnode2: VNode): boolean {
          return vnode1.key === vnode2.key && vnode1.sel === vnode2.sel
        }
    

    ​ 如果是 再调用patchVnode()精细化比较

    ​ 如果不是 则暴力删除,重新创建新的直接点.所有新建的子节点都是需要递归出来的

    ​ (createElm源码递归创建新子节点部分)

     if (is.array(children)) {
            for (i = 0; i < children.length; ++i) {
              const ch = children[i]
              if (ch != null) {
                api.appendChild(elm, createElm(ch as VNode, insertedVnodeQueue))
              }
            }
          }
    

    源码:createKeyToOldIdx() 缓存key

    ​ 作用:就是把从开始的idx与结束的idx进行遍历存到一个map里,让map中的key等于下标 i 的项目

    function createKeyToOldIdx (children: VNode[], beginIdx: number, endIdx: number): 			KeyToIndexMap {
            const map: KeyToIndexMap = {}
            for (let i = beginIdx; i <= endIdx; ++i) {
            const key = children[i]?.key
            if (key !== undefined) {
            map[key] = i
        }
    }
    

    流程图

    虚拟DOM与diff学习总结

    经典的diff算法优化策略

    四种命中查找:

    1. 新前与旧前 (此种情况发生,指针要往后移动)
    2. 新后与旧后 (此种情况发生,指针要往前移动)
    3. 旧后与新前 (此种情况发生涉及移动节点,那么新前指向的节点,移动到旧后节点之后)
    4. 新前与旧后 (此种情况发生涉及移动节点,那么新前指向的节点,移动到旧前节点之前)

    命中一种就不再进行命中判断了.

    如果都没有命中,就需要用循环来寻找了.


    起源地下载网 » 虚拟DOM与diff学习总结

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元