我的编程空间,编程开发者的网络收藏夹
学习永远不晚

vue2中的VNode和diff算法怎么使用

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

vue2中的VNode和diff算法怎么使用

本文小编为大家详细介绍“vue2中的VNode和diff算法怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue2中的VNode和diff算法怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。

什么是 VNode

我们知道,render function 会被转化成 VNodeVNode 其实就是一棵以 JavaScript 对象作为基础的树,用对象属性来描述节点,实际上它只是一层对真实 DOM 的抽象。最终可以通过一系列操作使这棵树映射到真实环境上。

比如有如下template

<template>  <span class="demo" v-show="isShow"> This is a span. </span> </template>

它换成 VNode 以后大概就是下面这个样子

{  tag: "span",  data: {        directives: [      {                rawName: "v-show",        expression: "isShow",        name: "show",        value: true,      },    ],        staticClass: "demo",  },  text: undefined,  children: [        {      tag: undefined,      data: undefined,      text: "This is a span.",      children: undefined,    },  ],};

总的来说,VNode 就是一个 JavaScript 对象。这个JavaScript 对象能完整地表示出真实DOM

为什么vue要使用 VNode

笔者认为有两点原因

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

  • 减少操作DOM,任何页面的变化,都只使用VNode进行操作对比,只需要在最后一次进行挂载更新DOM,避免了频繁操作DOM,减少了浏览器的回流和重绘从而提高页面性能

diff算法

下面我们来看看组件更新所涉及到的diff算法

前面我们讲依赖收集的时候有说到,渲染watcher传递给Watcherget方法其实是updateComponent方法。

updateComponent = () => {  vm._update(vm._render(), hydrating)}new Watcher(vm, updateComponent, noop, {  before () {    if (vm._isMounted) {      callHook(vm, 'beforeUpdate')    }  }}, true )

所以组件在响应式数据发生变化的时候会再次触发该方法,接下来我们来详细分析一下updateComponent里面的_update方法。

_update

_update方法中做了初始渲染和更新的区分,虽然都是调用__patch__方法,但是传递的参数不一样。

// class="lazy" data-src/core/instance/lifecycle.jsVue.prototype._update = function (vnode: VNode, hydrating?: boolean) {  const vm: Component = this  const prevEl = vm.$el  const prevVnode = vm._vnode  vm._vnode = vnode  // 初次渲染没有 prevVnode,组件更新才会有  if (!prevVnode) {    // 初次渲染    vm.$el = vm.__patch__(vm.$el, vnode, hydrating, false )  } else {    // 更新    vm.$el = vm.__patch__(prevVnode, vnode)  }    // ...}

下面我们再来看看__patch__方法

__patch__

patch方法接收四个参数,由于初始渲染的时候oldVnodevm.$elnull,所以初始渲染是没有oldVnode

// class="lazy" data-src/core/vdom/patch.jsreturn function patch (oldVnode, vnode, hydrating, removeOnly) {  // 新节点不存在,只有oldVnode就直接销毁,然后返回  if (isUndef(vnode)) {    if (isDef(oldVnode)) invokeDestroyHook(oldVnode)    return  }  let isInitialPatch = false  const insertedVnodeQueue = []  // 没有老节点,直接创建,也就是初始渲染  if (isUndef(oldVnode)) {    isInitialPatch = true    createElm(vnode, insertedVnodeQueue)  } else {    const isRealElement = isDef(oldVnode.nodeType)    // 不是真实dom,并且是相同节点走patch    if (!isRealElement && sameVnode(oldVnode, vnode)) {      // 这里才会涉及到diff算法      patchVnode(oldVnode, vnode, insertedVnodeQueue, null, null, removeOnly)    } else {      if (isRealElement) {        // ...      }      // replacing existing element      const oldElm = oldVnode.elm      const parentElm = nodeOps.parentNode(oldElm)      // 1.创建一个新节点      createElm(        vnode,        insertedVnodeQueue,        // extremely rare edge case: do not insert if old element is in a        // leaving transition. Only happens when combining transition +        // keep-alive + HOCs. (#4590)        oldElm._leaveCb ? null : parentElm,        nodeOps.nextSibling(oldElm)      )      // 2.更新父节点占位符      if (isDef(vnode.parent)) {        let ancestor = vnode.parent        const patchable = isPatchable(vnode)        while (ancestor) {          for (let i = 0; i < cbs.destroy.length; ++i) {            cbs.destroy[i](ancestor)          }          ancestor.elm = vnode.elm          if (patchable) {            for (let i = 0; i < cbs.create.length; ++i) {              cbs.create[i](emptyNode, ancestor)            }                        const insert = ancestor.data.hook.insert            if (insert.merged) {              // start at index 1 to avoid re-invoking component mounted hook              for (let i = 1; i < insert.fns.length; i++) {                insert.fns[i]()              }            }          } else {            registerRef(ancestor)          }          ancestor = ancestor.parent        }      }      // 3.删除老节点      if (isDef(parentElm)) {        removeVnodes([oldVnode], 0, 0)      } else if (isDef(oldVnode.tag)) {        invokeDestroyHook(oldVnode)      }    }  }      //触发插入钩子  invokeInsertHook(vnode, insertedVnodeQueue, isInitialPatch)  return vnode.elm}

patch方法大概流程如下:

  • 没有新节点只有老节点直接删除老节点。

  • 只有新节点没有老节点直接添加新节点。

  • 既有新节点又有老节点则判断是不是相同节点,相同则进入pathVnodepatchVnode我们后面会重点分析。

  • 既有新节点又有老节点则判断是不是相同节点,不相同则直接删除老节点添加新节点。

我们再来看看它是怎么判断是同一个节点的。

// class="lazy" data-src/core/vdom/patch.jsfunction sameVnode (a, b) {  return (    a.key === b.key &&    a.asyncFactory === b.asyncFactory && (      (        a.tag === b.tag &&        a.isComment === b.isComment &&        isDef(a.data) === isDef(b.data) &&        sameInputType(a, b)      ) || (        isTrue(a.isAsyncPlaceholder) &&        isUndef(b.asyncFactory.error)      )    )  )}function sameInputType (a, b) {  if (a.tag !== 'input') return true  let i  const typeA = isDef(i = a.data) && isDef(i = i.attrs) && i.type  const typeB = isDef(i = b.data) && isDef(i = i.attrs) && i.type  return typeA === typeB || isTextInputType(typeA) && isTextInputType(typeB)}

判断两个VNode节点是否是同一个节点,需要同时满足以下条件

  • key相同

  • 都有异步组件工厂函数

  • tag(当前节点的标签名)相同

  • isComment是否同为注释节点

  • 是否data(当前节点对应的对象,包含了具体的一些数据信息,是一个VNodeData类型)

  • 当标签是<input>的时候,type必须相同

当两个VNodetag、key、isComment都相同,并且同时定义或未定义data的时候,且如果标签为input则type必须相同。这时候这两个VNode则算sameVnode,可以直接进行patchVnode操作。

patchVnode

下面我们再来详细分析下patchVnode方法。

// class="lazy" data-src/core/vdom/patch.jsfunction patchVnode (  oldVnode,  vnode,  insertedVnodeQueue,  ownerArray,  index,  removeOnly) {  // 两个vnode相同则直接返回  if (oldVnode === vnode) {    return  }  if (isDef(vnode.elm) && isDef(ownerArray)) {    // clone reused vnode    vnode = ownerArray[index] = cloneVNode(vnode)  }  const elm = vnode.elm = oldVnode.elm  if (isTrue(oldVnode.isAsyncPlaceholder)) {    if (isDef(vnode.asyncFactory.resolved)) {      hydrate(oldVnode.elm, vnode, insertedVnodeQueue)    } else {      vnode.isAsyncPlaceholder = true    }    return  }    if (isTrue(vnode.isStatic) &&    isTrue(oldVnode.isStatic) &&    vnode.key === oldVnode.key &&    (isTrue(vnode.isCloned) || isTrue(vnode.isOnce))  ) {    vnode.componentInstance = oldVnode.componentInstance    return  }  let i  const data = vnode.data    if (isDef(data) && isDef(i = data.hook) && isDef(i = i.prepatch)) {    i(oldVnode, vnode)  }  // 获取新老虚拟节点的子节点  const oldCh = oldVnode.children  const ch = vnode.children  if (isDef(data) && isPatchable(vnode)) {    for (i = 0; i < cbs.update.length; ++i) cbs.update[i](oldVnode, vnode)    if (isDef(i = data.hook) && isDef(i = i.update)) i(oldVnode, vnode)  }    // 新节点不是文本节点  if (isUndef(vnode.text)) {        if (isDef(oldCh) && isDef(ch)) {      if (oldCh !== ch) updateChildren(elm, oldCh, ch, insertedVnodeQueue, removeOnly)        } else if (isDef(ch)) {      if (process.env.NODE_ENV !== 'production') {        checkDuplicateKeys(ch)      }      if (isDef(oldVnode.text)) nodeOps.setTextContent(elm, '')      addVnodes(elm, null, ch, 0, ch.length - 1, insertedVnodeQueue)        } else if (isDef(oldCh)) {      removeVnodes(oldCh, 0, oldCh.length - 1)        } else if (isDef(oldVnode.text)) {      nodeOps.setTextContent(elm, '')    }  // 新节点是文本节点,如果文本不一样就设置新的文本    } else if (oldVnode.text !== vnode.text) {    nodeOps.setTextContent(elm, vnode.text)  }    if (isDef(data)) {    if (isDef(i = data.hook) && isDef(i = i.postpatch)) i(oldVnode, vnode)  }}

patchVnode方法大概流程如下:

新老节点相同就直接返回。

如果新旧VNode都是静态的,同时它们的key相同(代表同一节点),并且新的VNode是clone或者是标记了once(标记v-once属性,只渲染一次),那么只需要替换componentInstance即可。

新节点不是文本节点,新老节点均有children子节点,则对子节点进行diff操作,调用updateChildren,这个updateChildrendiff算法的核心,后面我们会重点说。

新节点不是文本节点,如果老节点没有子节点而新节点存在子节点,先清空老节点DOM的文本内容,然后为当前DOM节点加入子节点。

新节点不是文本节点,当新节点没有子节点而老节点有子节点的时候,则移除该DOM节点的所有子节点。

新节点不是文本节点,并且新老节点都无子节点的时候,只需要将老节点文本清空。

新节点是文本节点,并且新老节点文本不一样,则进行文本的替换。

updateChildren(diff算法核心)

updateChildrendiff算法的核心,下面我们来重点分析。

vue2中的VNode和diff算法怎么使用

这两张图代表旧的VNode与新VNode进行patch的过程,他们只是在同层级的VNode之间进行比较得到变化(相同颜色的方块代表互相进行比较的VNode节点),然后修改变化的视图,所以十分高效。所以Diff算法是:深度优先算法。 时间复杂度:O(n)

function updateChildren (parentElm, oldCh, newCh, insertedVnodeQueue, removeOnly) {  let oldStartIdx = 0  let newStartIdx = 0  let oldEndIdx = oldCh.length - 1  let oldStartVnode = oldCh[0]  let oldEndVnode = oldCh[oldEndIdx]  let newEndIdx = newCh.length - 1  let newStartVnode = newCh[0]  let newEndVnode = newCh[newEndIdx]  let oldKeyToIdx, idxInOld, vnodeToMove, refElm  const canMove = !removeOnly  if (process.env.NODE_ENV !== 'production') {    checkDuplicateKeys(newCh)  }  while (oldStartIdx <= oldEndIdx && newStartIdx <= newEndIdx) {    if (isUndef(oldStartVnode)) {      oldStartVnode = oldCh[++oldStartIdx] // Vnode has been moved left    } else if (isUndef(oldEndVnode)) {      oldEndVnode = oldCh[--oldEndIdx]    // 老 VNode 节点的头部与新 VNode 节点的头部是相同的 VNode 节点,直接进行 patchVnode,同时 oldStartIdx 与 newStartIdx 向后移动一位。    } else if (sameVnode(oldStartVnode, newStartVnode)) {      patchVnode(oldStartVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)      oldStartVnode = oldCh[++oldStartIdx]      newStartVnode = newCh[++newStartIdx]    // 两个 VNode 的结尾是相同的 VNode,同样进行 patchVnode 操作。并将 oldEndVnode 与 newEndVnode 向前移动一位。    } else if (sameVnode(oldEndVnode, newEndVnode)) {      patchVnode(oldEndVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)      oldEndVnode = oldCh[--oldEndIdx]      newEndVnode = newCh[--newEndIdx]    // oldStartVnode 与 newEndVnode 符合 sameVnode 的时候,    // 将 oldStartVnode.elm 这个节点直接移动到 oldEndVnode.elm 这个节点的后面即可。    // 然后 oldStartIdx 向后移动一位,newEndIdx 向前移动一位。    } else if (sameVnode(oldStartVnode, newEndVnode)) { // Vnode moved right      patchVnode(oldStartVnode, newEndVnode, insertedVnodeQueue, newCh, newEndIdx)      canMove && nodeOps.insertBefore(parentElm, oldStartVnode.elm, nodeOps.nextSibling(oldEndVnode.elm))      oldStartVnode = oldCh[++oldStartIdx]      newEndVnode = newCh[--newEndIdx]    // oldEndVnode 与 newStartVnode 符合 sameVnode 时,    // 将 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。    // oldEndIdx 向前移动一位,newStartIdx 向后移动一位。    } else if (sameVnode(oldEndVnode, newStartVnode)) { // Vnode moved left      patchVnode(oldEndVnode, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)      canMove && nodeOps.insertBefore(parentElm, oldEndVnode.elm, oldStartVnode.elm)      oldEndVnode = oldCh[--oldEndIdx]      newStartVnode = newCh[++newStartIdx]    } else {      // createKeyToOldIdx 的作用是产生 key 与 index 索引对应的一个 map 表      if (isUndef(oldKeyToIdx)) oldKeyToIdx = createKeyToOldIdx(oldCh, oldStartIdx, oldEndIdx)      idxInOld = isDef(newStartVnode.key)        ? oldKeyToIdx[newStartVnode.key]        : findIdxInOld(newStartVnode, oldCh, oldStartIdx, oldEndIdx)      // 如果没有找到相同的节点,则通过 createElm 创建一个新节点,并将 newStartIdx 向后移动一位。      if (isUndef(idxInOld)) { // New element        createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)      } else {        vnodeToMove = oldCh[idxInOld]        // 如果找到了节点,同时它符合 sameVnode,则将这两个节点进行 patchVnode,将该位置的老节点赋值 undefined        // 同时将 newStartVnode.elm 插入到 oldStartVnode.elm 的前面        if (sameVnode(vnodeToMove, newStartVnode)) {          patchVnode(vnodeToMove, newStartVnode, insertedVnodeQueue, newCh, newStartIdx)          oldCh[idxInOld] = undefined          canMove && nodeOps.insertBefore(parentElm, vnodeToMove.elm, oldStartVnode.elm)        } else {          // 如果不符合 sameVnode,只能创建一个新节点插入到 parentElm 的子节点中,newStartIdx 往后移动一位。          createElm(newStartVnode, insertedVnodeQueue, parentElm, oldStartVnode.elm, false, newCh, newStartIdx)        }      }      newStartVnode = newCh[++newStartIdx]    }  }  // 当 while 循环结束以后,如果 oldStartIdx > oldEndIdx,说明老节点比对完了,但是新节点还有多的,  // 需要将新节点插入到真实 DOM 中去,调用 addVnodes 将这些节点插入即可。  if (oldStartIdx > oldEndIdx) {    refElm = isUndef(newCh[newEndIdx + 1]) ? null : newCh[newEndIdx + 1].elm    addVnodes(parentElm, refElm, newCh, newStartIdx, newEndIdx, insertedVnodeQueue)  // 如果满足 newStartIdx > newEndIdx 条件,说明新节点比对完了,老节点还有多,  // 将这些无用的老节点通过 removeVnodes 批量删除即可。  } else if (newStartIdx > newEndIdx) {    removeVnodes(oldCh, oldStartIdx, oldEndIdx)  }}

vue2diff算法采用的是双端比较,所谓双端比较就是新列表旧列表两个列表的头与尾互相对比,在对比的过程中指针会逐渐向内靠拢,直到某一个列表的节点全部遍历过,对比停止。

首尾对比的四种情况

我们首先来看看首尾对比的四种情况。

  • 使用旧列表的头一个节点oldStartNode新列表的头一个节点newStartNode对比

  • 使用旧列表的最后一个节点oldEndNode新列表的最后一个节点newEndNode对比

  • 使用旧列表的头一个节点oldStartNode新列表的最后一个节点newEndNode对比

  • 使用旧列表的最后一个节点oldEndNode新列表的头一个节点newStartNode对比

首先是 oldStartVnodenewStartVnode 符合 sameVnode 时,说明老 VNode 节点的头部与新 VNode 节点的头部是相同的 VNode 节点,直接进行 patchVnode,同时 oldStartIdxnewStartIdx 向后移动一位。

vue2中的VNode和diff算法怎么使用

其次是 oldEndVnodenewEndVnode 符合 sameVnode,也就是两个 VNode 的结尾是相同的 VNode,同样进行 patchVnode 操作并将 oldEndVnodenewEndVnode 向前移动一位。

vue2中的VNode和diff算法怎么使用

接下来是两种交叉的情况。

先是 oldStartVnodenewEndVnode 符合 sameVnode 的时候,也就是老 VNode 节点的头部与新 VNode 节点的尾部是同一节点的时候,将 oldStartVnode.elm 这个节点直接移动到 oldEndVnode.elm 这个节点的后面即可。然后 oldStartIdx 向后移动一位,newEndIdx 向前移动一位。

vue2中的VNode和diff算法怎么使用

同理,oldEndVnodenewStartVnode 符合 sameVnode 时,也就是老 VNode 节点的尾部与新 VNode 节点的头部是同一节点的时候,将 oldEndVnode.elm 插入到 oldStartVnode.elm 前面。同样的,oldEndIdx 向前移动一位,newStartIdx 向后移动一位。

vue2中的VNode和diff算法怎么使用

最后是当以上情况都不符合的时候,这种情况怎么处理呢?

查找对比

那就是查找对比。

首先通过createKeyToOldIdx方法生成oldVnodekeyindex 索引对应的一个 map 表。

然后我们根据newStartVnode.key,可以快速地从 oldKeyToIdxcreateKeyToOldIdx 的返回值)中获取相同 key 的节点的索引 idxInOld,然后找到相同的节点。

这里又分三种情况

  • 如果没有找到相同的节点,则通过 createElm 创建一个新节点,并将 newStartIdx 向后移动一位。

  • 如果找到了节点,同时它符合 sameVnode,则将这两个节点进行 patchVnode,将该位置的老节点赋值 undefined(之后如果还有新节点与该节点key相同可以检测出来提示已有重复的 key ),同时将 newStartVnode.elm 插入到 oldStartVnode.elm 的前面。同理,newStartIdx 往后移动一位。

vue2中的VNode和diff算法怎么使用

  • 如果不符合 sameVnode,只能创建一个新节点插入到 parentElm 的子节点中,newStartIdx 往后移动一位。

vue2中的VNode和diff算法怎么使用

添加、删除节点

最后一步就很容易啦,当 while 循环结束以后,如果 oldStartIdx > oldEndIdx,说明老节点比对完了,但是新节点还有多的,需要将新节点插入到真实 DOM 中去,调用 addVnodes 将这些节点插入即可。

vue2中的VNode和diff算法怎么使用

同理,如果满足 newStartIdx > newEndIdx 条件,说明新节点比对完了,老节点还有多,将这些无用的老节点通过 removeVnodes 批量删除即可。

vue2中的VNode和diff算法怎么使用

总结

Diff算法是一种对比算法。对比两者是旧虚拟DOM和新虚拟DOM,对比出是哪个虚拟节点更改了,找出这个虚拟节点,并只更新这个虚拟节点所对应的真实节点,而不用更新其他数据没发生改变的节点,实现精准地更新真实DOM,进而提高效率和性能

精准主要体现在,diff 算法首先就是找到可复用的节点,然后移动到正确的位置。当元素没有找到的话再来创建新节点。

扩展

vue中为什么需要使用key,它的作用是什么?

keyVuevnode 的唯一标记,通过这个 keydiff 操作可以更准确、更快速。

  1. 更准确:因为带 key 就不是就地复用了,在 sameNode 函数 a.key === b.key 对比中可以避免就地复用的情况。所以会更加准确。

  2. 更快速:利用 key 的唯一性生成 map 对象来获取对应节点,比遍历方式更快。

为什么不推荐使用index作为key

当我们的列表只涉及到 展示,不涉及到排序、删除、添加的时候使用index作为key是没什么问题的。因为此时的index在每个元素上是唯一的。

但是如果涉及到排序、删除、添加的时候就不能再使用index作为key了,因为每个元素key不再唯一了。不唯一的key,对diff算法没有任何帮助,写和没写是一样的。

读到这里,这篇“vue2中的VNode和diff算法怎么使用”文章已经介绍完毕,想要掌握这篇文章的知识点还需要大家自己动手实践使用过才能领会,如果想了解更多相关内容的文章,欢迎关注编程网行业资讯频道。

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

vue2中的VNode和diff算法怎么使用

下载Word文档到电脑,方便收藏和打印~

下载Word文档

猜你喜欢

vue2中的VNode和diff算法怎么使用

本文小编为大家详细介绍“vue2中的VNode和diff算法怎么使用”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue2中的VNode和diff算法怎么使用”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。什么是
2023-07-04

深入理解vue2中的VNode和diff算法

虚拟dom和diff算法是vue学习过程中的一个难点,也是面试中必须掌握的一个知识点。这两者相辅相成,是vue框架的核心。今天我们再来总结下vue2中的虚拟dom 和 diff算法。
2022-11-22

Vue2中的Diff算法怎么使用

这篇文章主要介绍了Vue2中的Diff算法怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇Vue2中的Diff算法怎么使用文章都会有所收获,下面我们一起来看看吧。为什么要用 Diff 算法虚拟 DOM因为
2023-07-05

vue2的diff算法怎么使用

这篇文章主要介绍“vue2的diff算法怎么使用”,在日常操作中,相信很多人在vue2的diff算法怎么使用问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”vue2的diff算法怎么使用”的疑惑有所帮助!接下来
2023-07-04

Vue中的双端diff算法怎么应用

这篇文章主要讲解了“Vue中的双端diff算法怎么应用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“Vue中的双端diff算法怎么应用”吧!Vue 和 React 都是基于 vdom 的前端
2023-07-02

react的diff方法怎么使用

今天小编给大家分享一下react的diff方法怎么使用的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。react的diff方法
2023-07-04

怎么深入解析Vue3中的diff 算法

今天给大家介绍一下怎么深入解析Vue3中的diff 算法。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。1.0 diff 无key子节点在处理被标记为UNKE
2023-06-26

vue2中怎么使用tailwindcss方法

本文小编为大家详细介绍“vue2中怎么使用tailwindcss方法”,内容详细,步骤清晰,细节处理妥当,希望这篇“vue2中怎么使用tailwindcss方法”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。1.
2023-07-02

怎么在react中实现一个虚拟dom和diff算法

这篇文章将为大家详细讲解有关怎么在react中实现一个虚拟dom和diff算法,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。虚拟DOM,见名知意,就是假的DOM,我们真实的DOM挂载在页面上
2023-06-14

vue2中的@hook怎么使用

这篇文章主要讲解了“vue2中的@hook怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“vue2中的@hook怎么使用”吧!前言@hook是什么?用来监听组件生命周期的回调函数。这和
2023-07-05

diff和patch命令怎么在shell中使用

diff和patch命令怎么在shell中使用?针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。shell中常用的命令:diff(比较文件内容)和patch(打补丁)1.dif
2023-06-09

怎样深入理解vue中的虚拟DOM和Diff算法

怎样深入理解vue中的虚拟DOM和Diff算法,针对这个问题,这篇文章详细介绍了相对应的分析和解答,希望可以帮助更多想解决这个问题的小伙伴找到更简单易行的方法。真实DOM的渲染在讲虚拟DOM之前,先说一下真实DOM的渲染。浏览器真实DOM渲
2023-06-22

vue2.x版本中computed和watch怎么使用

这篇文章主要介绍了vue2.x版本中computed和watch怎么使用的相关知识,内容详细易懂,操作简单快捷,具有一定借鉴价值,相信大家阅读完这篇vue2.x版本中computed和watch怎么使用文章都会有所收获,下面我们一起来看看吧
2023-07-02

JavaScript的运算符和方法怎么使用

这篇文章主要介绍“JavaScript的运算符和方法怎么使用”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“JavaScript的运算符和方法怎么使用”文章能帮助大家解决问题。1.JS的类型按照数据类
2023-06-26

java中GC算法和垃圾收集器怎么使用

在Java中,GC(垃圾回收)算法和垃圾收集器是自动管理内存的关键组件。以下是关于如何使用GC算法和垃圾收集器的一些基本指南:1. 了解GC算法:Java中常见的GC算法有标记-清除(Mark and Sweep)、复制(Copying)、
2023-08-24

C++ STL中常用算法怎么使用

这篇文章主要讲解了“C++ STL中常用算法怎么使用”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“C++ STL中常用算法怎么使用”吧!前言在C++中使用STL算法都要包含一个算法头文件 #
2023-06-21

怎么使用python中的pycrypto算法加密

今天小编给大家分享一下怎么使用python中的pycrypto算法加密的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。一、安装
2023-06-30

php中怎么使用位运算符中的^和&

这篇文章给大家分享的是有关php中怎么使用位运算符中的^和&的内容。小编觉得挺实用的,因此分享给大家做个参考,一起跟随小编过来看看吧。位操作是程序设计中对位模式按位或二进制数的一元和二元操作。在许多古老的微处理器上,位运算比加减运算略快,
2023-06-20

怎么在Python中使用dHash算法

这篇文章将为大家详细讲解有关怎么在Python中使用dHash算法,文章内容质量较高,因此小编分享给大家做个参考,希望大家阅读完这篇文章后对相关知识有一定的了解。python主要应用领域有哪些1、云计算,典型应用OpenStack。2、WE
2023-06-14

编程热搜

  • Python 学习之路 - Python
    一、安装Python34Windows在Python官网(https://www.python.org/downloads/)下载安装包并安装。Python的默认安装路径是:C:\Python34配置环境变量:【右键计算机】--》【属性】-
    Python 学习之路 - Python
  • chatgpt的中文全称是什么
    chatgpt的中文全称是生成型预训练变换模型。ChatGPT是什么ChatGPT是美国人工智能研究实验室OpenAI开发的一种全新聊天机器人模型,它能够通过学习和理解人类的语言来进行对话,还能根据聊天的上下文进行互动,并协助人类完成一系列
    chatgpt的中文全称是什么
  • C/C++中extern函数使用详解
  • C/C++可变参数的使用
    可变参数的使用方法远远不止以下几种,不过在C,C++中使用可变参数时要小心,在使用printf()等函数时传入的参数个数一定不能比前面的格式化字符串中的’%’符号个数少,否则会产生访问越界,运气不好的话还会导致程序崩溃
    C/C++可变参数的使用
  • css样式文件该放在哪里
  • php中数组下标必须是连续的吗
  • Python 3 教程
    Python 3 教程 Python 的 3.0 版本,常被称为 Python 3000,或简称 Py3k。相对于 Python 的早期版本,这是一个较大的升级。为了不带入过多的累赘,Python 3.0 在设计的时候没有考虑向下兼容。 Python
    Python 3 教程
  • Python pip包管理
    一、前言    在Python中, 安装第三方模块是通过 setuptools 这个工具完成的。 Python有两个封装了 setuptools的包管理工具: easy_install  和  pip , 目前官方推荐使用 pip。    
    Python pip包管理
  • ubuntu如何重新编译内核
  • 改善Java代码之慎用java动态编译

目录