广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >一文详解Vue3中简单diff算法的实现
  • 896
分享到

一文详解Vue3中简单diff算法的实现

2024-04-02 19:04:59 896人浏览 独家记忆
摘要

目录简单Diff算法减少DOM操作例子结论实现DOM复用与key的作用例子虚拟节点的key实现找到需要移动的元素探索节点顺序关系实现如何移动元素例子实现添加新元素例子实现移除不存在的

简单Diff算法

核心Diff只关心新旧虚拟节点都存在一组子节点的情况

减少DOM操作

例子

// 旧节点
const oldVnode = {
  type: 'div',
  children: [
    { type: 'p', children: '1' },
    { type: 'p', children: '2' },
    { type: 'p', children: '3' }
  ]
}
// 新节点
const newVNode = {
  type: 'div',
  children: [
    { type: 'p', children: '4' },
    { type: 'p', children: '5' },
    { type: 'p', children: '6' }
  ]
}

如果直接去操作DOM,那么上面的更新需要6次DOM操作,卸载所有旧子节点,挂载所有新子节点。

但是观察上面新旧vNode的子节点可以发现:

  • 更新前后所有子节点都是 p 标签,即标签元素步变
  • 只有p标签的子节点发生变化了

所以最理想的更新方式是直接更新这个p标签的文本节点的内容,这样只需要一次DOM操作,即可完成一个p标签的更新。更新完所有节点只需要3次DOM操作就可以完成全部节点的更新。

上面的做法可以减少DOM操作次数,但问题也很明显,只有节点数量相同这个做法才能正常工作。但新旧两组子节点数量未必相同。

新的一组子节点数量少于旧的一组子节点的数量时,意味着有节点在更新后应该被卸载。(图二)

新的一组子节点数量多余旧的一组子节点的数量时,意味着有节点在更新后应该被新增并挂载。(图三)

结论

通过上面分析得出,进行新旧两组子节点的更新时,不应该总是遍历旧的一组子节点或新的一组子节点,而是应该遍历其中较短的一组。这样才能尽可能多的调用patch进行更新。接着对比新旧两组子节点的长度,如果新的一组子节点更长,说明有新节点需要挂载,否则说明有旧的子节点需要卸载。

实现

function easyDiff (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 获取新旧子节点列表的长度
  const oldLen = oldChildren.length
  const newLen = newChildren.length
  // 取得较小的一个(可以理解为两组子节点的公共长度)
  const commonLength = Math.min(oldLen, newLen)
  // 遍历 commonLength 次
  for (let i = 0; i < commonLength; i++) {
    patch(oldChildren[i], newChildren[i], container)
  }
  // 如果 newLen > oldLen,说明有新子节点需要挂载
  if (newLen > oldLen) {
    for (let i = commonLength; i < newLen; i++) {
      patch(null, newChildren[i], container)
    }
  }
  // 如果 oldLen > newLen,说明有旧节点需要卸载
  if (oldLen > newLen) {
    for (let i = commonLength; i < oldLen; i++) {
      unmount(oldChildren[i])
    }
  }
}

DOM复用与key的作用

例子

上面通过减少DOM操作次数提升了更新性能,但还存在可优化空间

const KEY = {
  oldVNode: [
    { type: 'p' },
    { type: 'div' },
    { type: 'span' }
  ],
  newVNode: [
    { type: 'span' },
    { type: 'p' },
    { type: 'div' }
  ]
}

针对这个例子,如果还使用上面的算法,则需要6次DOM操作。

调用 patch 在 p标签和span标签之间打补丁,由于不是相同标签,所以p标签被卸载,然后挂载span标签,需要两步操作,div - p,span - div同理。

很容易发现新旧两组子节点只是顺序不同。所以最优的处理方式是,通过DOM的移动来完成子节点的更新,这比不断执行卸载和挂载性能好得多。但是要通过移动DOM来完成更新,必须要保证新旧两组子节点的确存在可复用的节点。(如果新的子节点没有在旧的子节点中出现,则无法通过移动节点的方式完成更新操作。)

用上面的例子来说,怎么确定新的一组节点中的第三个节点 { type: 'div' } 与旧的一组子节点中的第二个节点相同呢?可以通过vNode.type判断,但这种方式并不可靠。

  oldChildren: [
    { type: 'p', children: '1' },
    { type: 'p', children: '2' },
    { type: 'p', children: '3' }
  ],
  newChildren: [
    { type: 'p', children: '3' },
    { type: 'p', children: '1' },
    { type: 'p', children: '2' }
  ]

观察上面节点,可以发现,这个案例可以通过移动DOM的方式来完成更新,但是vNode.type的值都相同,导致无法确定新旧节点中的对应关系,就不能确定怎么移动DOM完成更新。

虚拟节点的key

因此,需要引入额外的 key 作为vNode的标识。

const KEY = {
  oldChildren: [
    { type: 'p', children: '1', key: '1' },
    { type: 'p', children: '2', key: '2' },
    { type: 'p', children: '3', key: '3' }
  ],
  newChildren: [
    { type: 'p', children: '3', key: '3' },
    { type: 'p', children: '1', key: '1' },
    { type: 'p', children: '2', key: '2' }
  ]
}

key 属性就像虚拟DOM的 身份证号,只要两个虚拟节点的type和key属性都相同,那么就可以认为它们是相同的;即可以进行DOM的复用。

但是DOM可复用并不意味着不需要更新

oldVNode: { type: 'p', children: 'text - 1', key: '1' }
newVNode: { type: 'p', children: 'text - 2', key: '1' }

两个节点有相同的key可type,但它们的文本内容不同,还是需要通过patch进行打补丁操作。

实现

function easyDiffV2 (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 遍历新的children
  for (let i = 0; i < newChildren.length; i++) {
    const newVNode = newChildren[i]
    for (let j = 0; j < oldChildren.length; j++) {
      const oldVNode = oldChildren[i]
      // 如果找到可复用的两个节点
      if (newVNode.key === oldVNode.key) {
        // 对可复用的两个节点打补丁
        patch(oldVNode, newVNode, container)
        // 一个新节点处理完后开始下一个新节点
        break
      }
    }
  }
}

外层循环遍历新的一组子节点,内层循环遍历旧的一组子节点。内层循环中对比新旧子节点的key值,在旧的子节点中找到可以复用的节点;一旦找到则调用 patch 打补丁。

找到需要移动的元素

现在已经可以通过key找到可复用的节点了,接下来要做的是判断一个节点是否需要移动

探索节点顺序关系

节点顺序不变 - 查找过程:

第一步:取新的一组子节点中的第一个节点 p - 1,它的key为1,在旧的一组子节点中找到具有相同key值的可复用节点,能够找到,并且该节点在旧的一组子节点中索引为0;p - 2、p = 3同理。

  • key 为 1 的节点在 旧节点列表中的索引为0
  • key 为 2 的节点在 旧节点列表中的索引为1
  • key 为 3 的节点在 旧节点列表中的索引为2

每一次查找可复用节点都会记录该可复用节点在旧的一组子节点中的位置索引,如果按照先后顺序排列,则可以得到一个序列:0、1、2,是一个递增序列。

节点顺序变化 - 查找过程

第一步:取新的一组子节点中的第一个节点 p - 3,它的key为3,在旧的一组子节点中找到具有相同key值的可复用节点,能够找到,并且该节点在旧的一组子节点中索引为2;

第二步:取新的一组子节点中的第一个节点 p - 1,它的key为1,在旧的一组子节点中找到具有相同key值的可复用节点,能够找到,并且该节点在旧的一组子节点中索引为0;

到了这一步发现递增的顺序被打破了。节点 p - 1 在旧的一组children 的索引为0,它小于 p - 3 在旧children中的索引2.这说明节点 p - 1 在旧children中排在 p - 3前面,但在新的children中,它排在节点 p - 3后面。因此得出:节点p - 1对应的真实DOM需要移动

第三步:取新的一组子节点中的第一个节点 p - 2,它的key为2,在旧的一组子节点中找到具有相同key值的可复用节点,能够找到,并且该节点在旧的一组子节点中索引为1;

节点 p - 2 在旧的一组children 的索引为0,它小于 p - 3 在旧children中的索引2.这说明节点 p - 2 在旧children中排在 p - 3前面,但在新的children中,它排在节点 p - 3后面。因此得出:**节点p - 2对应的真实DOM需要移动

可以将节点 p - 3 在旧children中的索引定义为:在旧children中寻找具有相同key值节点的过程中,遇到的最大索引值

如果后续寻找过程中,存在比当前遇到的最大索引值还要小的节点,则意味着该节点需要移动。

实现

function easyBigIndex (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 用来存储寻找过程中遇到的最大索引值
  let lastIndex = 0
  for (let i = 0; i < newChildren.length; i++) {
    const newVNode = newChildren[i]
    for (let j = 0; j < oldChildren; j++) {
      const oldVNode = oldChildren[j]
      if (newVNode.key === oldVNode.key) {
        patch(oldVNode, newVNode, container)
        if (j < lastIndex) {
          // 需要移动
        } else {
          // 更新lastIndex的值(lastIndex要保持当前已查找的索引中的最大值)
          lastIndex = j
        }
        break
      }
    }
  }
}

如何移动元素

移动节点指的是,移动一个虚拟节点所对应的真实DOM节点,并不是移动虚拟节点本身。既然移动的是真实DOM节点,就需要取得它的引用,其对应的真实DOM节点会存储到它的vNode.el属性中

例子

引用上面的案例:

取新的一组子节点中的第一个节点 p - 3,它的key 为3,在旧的虚拟节点列表中找到具有相同 key 值的可复用节点。发现能够找到,并且该节点在旧的一组子节点中的素引为2。此时变量 lastIndex 的值为 0,索引2 不小于0,所以节点 p - 3对应的真实DOM 不需要移动,但需要更新变量 lastIndex 的值为 2。

第二步:取新的一组子节点中第二个节点 p - 1,它的key 为1,在旧的一组子节点中找到具有相同 key 值的可复用节点。发现能够找到,并且该节点在日的一组子节点中的索引为0。此时变量 lastIndex 的值为 2,索引0小于 2,所以节点p-1对应的真实 DOM需要移动

到了这一步,我们发现,节点p - 1对应的真实 DOM 需要移动,但应该移动到哪里呢?新children 的顺序其实就是更新后真实 DOM 节点应有的顺序。所以节点 p-1在新 children 中的位置就代表了真实 DOM 更新后的位置。由于节点 p - 1在新 children 中排在节点p - 3后面,所以我们应该把节点p - 1所对应的真实 DOM移动到节点p - 3所对应的真实 DOM 后面。这样操作之后,此时真实 DOM 的顺序为 p-2、p-3、p-1。

第三步:取新的一组子节点中第三个节点 p-2,它的key 为2。尝试在旧的一组子节点中找到具有相同 key 值的可复用节点。发现能够找到,并且该节点在旧的一组子节点中的素引为1。此时变量 lastIndex 的值为 2,索引1小于2,所以节点p-2对应的真实 DOM需要移动

第二步操作完成后 新 / 旧 / 虚拟 节点之间的对应关系

实现

function easyMove (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 用来存储寻找过程中遇到的最大索引值
  let lastIndex = 0
  for (let i = 0; i < newChildren.length; i++) {
    const newVNode = newChildren[i]
    for (let j = 0; j < oldChildren; j++) {
      const oldVNode = oldChildren[j]
      if (newVNode.key === oldVNode.key) {
        patch(oldVNode, newVNode, container)
        if (j < lastIndex) {
          // 需要移动
          // 获取当前vNode的前一个vNode
          const prevVNode = newChildren[i - 1]
          // 如果 prevVNode 不存在,说明当前vNode是第一个节点,它不需要移动
          if (prevVNode) {
            // 由于要将newVNode对用的真实DOM移动到prevVNode对应的真实DOM后面,
            // 所以需要获取prevVNode对应的真实节点的下一个兄弟节点,并将其作为锚点
            const anchor = prevVNode.el.nextSibling
            // 调用insert将newVNode对应真实DOM插入到锚点元素前面
            // insert 是通过 el.insertBefore 插入元素的
            insert(newVNode.el, container, anchor)
          }
        } else {
          // 更新lastIndex的值(lastIndex要保持当前已查找的索引中的最大值)
          lastIndex = j
        }
        break
      }
    }
  }
}

添加新元素

例子

在新的一组子节点中,多出来一个 p - 4,它的key值为4,该节点在旧的一组字节点中不存在,因此应该将其视为新增节点。对于新增节点,更新时应该正确地将其挂载:

  • 找到新增节点
  • 将新增节点挂载到正确位置

第一步:取新的一组子节点中第一个节点p - 3,它的key值为3,在旧的一组子节及中找到可复用的节点。发现能找到,并且该节点在旧的一组子节点中的索引值为2。此时,变量lastIndex的值为0,所以节点 p - 3 对应的真实 DOM 不需要移动,但是需要将变量 lastIndex 的值更新为 2。

第二步:取新的一组子节点中第一个节点p - 1,它的key值为1,在旧的一组子节及中找到可复用的节点。发现能找到,并且该节点在旧的一组子节点中的索引值为1。此时变量lastIndex的值为2,所以节点 p - 1对应的真实DOM需要移动,并且应该移动到节点 p - 3对应的真实DOM后面。

第三步:取新的一组子节点中第一个节点p - 4,它的key值为4,在旧的一组子节及中找到可复用的节点。没有key值为4的节点,因此渲染器会把节点 p - 4 看作新增节点并挂载它。应该挂载到什么地方呢?观察p - 4在新的一组子节点中的位置。由于 p - 4出现在节点 p - 1后面,所以应该把 p - 4 挂载到节点 p - 1 对应的真实DOM后面。

第四步:取新的一组子节点中第一个节点p - 2,它的key值为2,在旧的一组子节及中找到可复用的节点。发现能找到,并且该节点在旧的一组子节点中的索引值为1。此时,变量lastIndex的值为2,索引值1小于lastIndex的值2,所以节点 p - 2对应的真实DOM需要移动,并且应该移动到节点 p - 4对应的真实DOM后面。

第二步操作完成后的节点对应关系

第三步操作完成后的节点对应关系

实现

function easyMount (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 用来存储寻找过程中遇到的最大索引值
  let lastIndex = 0
  for (let i = 0; i < newChildren.length; i++) {
    const newVNode = newChildren[i]

    // 定义变量 find,代表是否在旧的一组子节点中找到可复用的节点,初始值为false - 没找到
    let find = false
    for (let j = 0; j < oldChildren; j++) {
      const oldVNode = oldChildren[j]
      if (newVNode.key === oldVNode.key) {
        // 一旦找到可复用的节点,将变量find设置为true
        find = true
        patch(oldVNode, newVNode, container)
        if (j < lastIndex) {
          // 需要移动
          // 获取当前vNode的前一个vNode
          const prevVNode = newChildren[i - 1]
          // 如果 prevVNode 不存在,说明当前vNode是第一个节点,它不需要移动
          if (prevVNode) {
            // 由于要将newVNode对用的真实DOM移动到prevVNode对应的真实DOM后面,
            // 所以需要获取prevVNode对应的真实节点的下一个兄弟节点,并将其作为锚点
            const anchor = prevVNode.el.nextSibling
            // 调用insert将newVNode对应真实DOM插入到锚点元素前面
            // insert 是通过 el.insertBefore 插入元素的
            insert(newVNode.el, container, anchor)
          }
        } else {
          // 更新lastIndex的值(lastIndex要保持当前已查找的索引中的最大值)
          lastIndex = j
        }
        break
      }
    }
    // 这里find如果还是false,说明当前newVNode没有在旧的一组子节点中找到可复用的节点
    // 也就是说当前 newVNode 是新增节点,需要挂载
    if (!find) {
      // 为了将节点挂载到正确位置,需要先获取锚点元素
      // 首先获取当前newVNode的前一个vNode节点
      const prevVNode = newChildren[i - 1]
      let anchor = null
      if (prevVNode) {
        // 如果有前一个vNode节点,则使用它的下一个兄弟节点作为锚点元素
        anchor = prevVNode.el.nextSibling
      } else {
        // 如果没有前一个vNode节点,说明即将挂载的新节点是第一个子节点
        // 这是使用容器元素的firstChild作为锚点
        anchor = container.firstChild
      }
      // 挂载 newVNode
      patch(null, newVNode, container, anchor)
    }
  }
}

移除不存在的元素

例子

在新的一组节点中,节点 p - 2 不存在了,说明该节点被删除,渲染器应该能找到那些需要删除的节点并正确地将其删除。

找到需要删除的节点 - 步骤:

  • 第一步:取新的一组子节点中的第一个节点p - 3,它的key 值为3,在旧的一组子节点中寻找可复用的节点。发现能够找到,并且该节点在旧的一组子节点中的索引值为2。此时变量 lastIndex 的值为0,索引2不小于lastIndex 的值0,所以节点p - 3对应的真实 DOM 不需要移动,但需要更新变量 lastIndex 的值为 2。
  • 第二步:取新的一组子节点中的第二个节点 p - 1,它的key 值为1。尝试在旧的一组子节点中寻找可复用的节点。发现能够找到,并且该节点在旧的一组子节点中的索引值为0。此时变量 lastIndex 的值为2,索引0小于 lastIndex 的值 2, 所以节点p-1对应的真实 DOM需要移动,并且应该移动到节点p-3对应的真实 DOM 后面经过这一步的移动操作后,发现 p - 3和p - 1都有了对应的真实DOM节点。
  • 至此更新结束,但 p - 2 对应的真实DOM仍然存在,所以需要增加额外的逻辑来删除遗留节点。当基本的更新结束时,需要遍历旧的一组子节点,然后去新的一组子节点中寻找具有相同key值的节点。如果找不到,说明应该删除该节点。

p - 2与任何newVNode没有对应关系

实现

function easyUnmount (n1, n2, container) {
  // 取出新旧子节点列表
  const oldChildren = n1.children
  const newChildren = n2.children
  // 用来存储寻找过程中遇到的最大索引值
  let lastIndex = 0
  for (let i = 0; i < newChildren.length; i++) {
    const newVNode = newChildren[i]

    // 定义变量 find,代表是否在旧的一组子节点中找到可复用的节点,初始值为false - 没找到
    let find = false
    for (let j = 0; j < oldChildren; j++) {
      const oldVNode = oldChildren[j]
      if (newVNode.key === oldVNode.key) {
        // 一旦找到可复用的节点,将变量find设置为true
        find = true
        patch(oldVNode, newVNode, container)
        if (j < lastIndex) {
          // 需要移动
          // 获取当前vNode的前一个vNode
          const prevVNode = newChildren[i - 1]
          // 如果 prevVNode 不存在,说明当前vNode是第一个节点,它不需要移动
          if (prevVNode) {
            // 由于要将newVNode对用的真实DOM移动到prevVNode对应的真实DOM后面,
            // 所以需要获取prevVNode对应的真实节点的下一个兄弟节点,并将其作为锚点
            const anchor = prevVNode.el.nextSibling
            // 调用insert将newVNode对应真实DOM插入到锚点元素前面
            // insert 是通过 el.insertBefore 插入元素的
            insert(newVNode.el, container, anchor)
          }
        } else {
          // 更新lastIndex的值(lastIndex要保持当前已查找的索引中的最大值)
          lastIndex = j
        }
        break
      }
    }
    // 这里find如果还是false,说明当前newVNode没有在旧的一组子节点中找到可复用的节点
    // 也就是说当前 newVNode 是新增节点,需要挂载
    if (!find) {
      // 为了将节点挂载到正确位置,需要先获取锚点元素
      // 首先获取当前newVNode的前一个vNode节点
      const prevVNode = newChildren[i - 1]
      let anchor = null
      if (prevVNode) {
        // 如果有前一个vNode节点,则使用它的下一个兄弟节点作为锚点元素
        anchor = prevVNode.el.nextSibling
      } else {
        // 如果没有前一个vNode节点,说明即将挂载的新节点是第一个子节点
        // 这是使用容器元素的firstChild作为锚点
        anchor = container.firstChild
      }
      // 挂载 newVNode
      patch(null, newVNode, anchor)
    }
  }

  // 更新操作完成后,遍历旧的一组子节点
  for (let i = 0; i < oldChildren.length; i++) {
    const oldVNode = oldChildren[i]
    // 拿旧子节点oldVNode去新的一组子节点中寻找具有相同key值的节点
    const has = newChildren.find(
      vNode => vNode.key === oldVNode.key
    )
    if (has) {
      // 如果没找到具有相同key值的节点,则说明需要删除该节点,调用unmount函数将其卸载
      unmount(oldVNode)
    }
  }
}

总结

遍历新旧子结点中较少的一组,逐个调用patch进行打补丁,然后比较新旧两组子节点的数量,如果新的一组子节点数量更多,说明有新节点需要挂载;否则说明在旧的一组子节点中,有节点需要卸载。

引入key属性,就像虚拟节点的身份证号,通过key找到可复用的节点,然后尽可能通过DOM移动操作来完成更新,避免过多地对DOM元素进行销毁和重建。

简单Diff算法地核心逻辑是,拿新的一组子节点中地节点去旧的一组子节点中寻找可复用地节点,如果找到了,则记录该节点地位置索引。在整个更新过程中,如果一个节点地索引值小于最大索引,则说明该节点对应地真实DOM元素需要移动。

以上就是一文详解vue3中简单diff算法的实现的详细内容,更多关于Vue简单diff算法的资料请关注编程网其它相关文章!

--结束END--

本文标题: 一文详解Vue3中简单diff算法的实现

本文链接: https://www.lsjlt.com/news/166768.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

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

下载Word文档
猜你喜欢
  • 一文详解Vue3中简单diff算法的实现
    目录简单Diff算法减少DOM操作例子结论实现DOM复用与key的作用例子虚拟节点的key实现找到需要移动的元素探索节点顺序关系实现如何移动元素例子实现添加新元素例子实现移除不存在的...
    99+
    2022-11-13
  • 一文详解Vue 的双端 diff 算法
    目录前言diff 算法简单 diff双端 diff总结前言 Vue 和 React 都是基于 vdom 的前端框架,组件渲染会返回 vdom,渲染器再把 vdom 通过增删改的 ap...
    99+
    2022-11-13
  • Vue3组件更新中的DOM diff算法示例详解
    目录同步头部节点同步尾部节点添加新的节点删除多余节点处理未知子序列移动子节点建立索引图更新和移除旧节点移动和挂载新节点最长递增子序列总结总结在vue的组件更新过程中,新子节点数组相对...
    99+
    2022-11-13
  • 详解Python中位运算的简单实现
    目录简介应用场景案例源码简介 程序中的数在计算机内存中都是以二进制的形式存在的,位运算就是直接对整数在内存中对应的二进制位进行操作,一般是将数字化为二进制数后进行操作。 应用场景 在...
    99+
    2022-11-12
  • 详解C/C++高精度算法的简单实现
    目录前言一、基本原理二、辅助方法1、字符串转高精度2、整型转高精度3、比较4、打印三、算法实现1、加法2、减法3、乘法4、除法四、使用示例1、加法2、减法3、乘法4、除法总结前言 由...
    99+
    2022-12-15
    C++实现高精度算法 C++高精度算法 C语言 高精度算法
  • 一文详解vue3中使用JSX的方法
    在 Vue 3 的项目开发中,template 是 Vue 3 默认的写法。虽然 template 长得很像 HTML,但 Vue 其实会把 template 解析为 render 函数,之后,组件运行的时候通过 render 函数去返回虚...
    99+
    2022-11-25
    JSX Vue vue3
  • 一文详解Vue3中的script setup语法糖
    在添加了setup的script标签中,我们不必声明和方法,这种写法会自动将所有顶级变量、函数,均会自动暴露给模板(template)使用这里强调一句 “暴露给模板,跟暴露给外部不是一回事”TIP:说的通俗一点,就是在使用 Vue 3 生命...
    99+
    2022-11-22
    前端 Vue.js
  • Matlab实现简单扩频语音水印算法详解
    目录一、实验背景1.实验目的2.实验环境3.原理简介二、基础知识1.PN序列2.时域到频域变换的原因3.三种时域到频域变换的区别三、算法源码1.PN产生函数2.隐藏算法3.提取算法4...
    99+
    2022-11-12
  • Python中怎么实现一个简单遗传算法
    今天就跟大家聊聊有关Python中怎么实现一个简单遗传算法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。遗传算法遗传算法是模仿自然选择过程的优化算法。 他们没有使用"数学技...
    99+
    2023-06-16
  • python简单实现整数反转的画解算法
    题目描述 给你一个 32 位的有符号整数 x ,返回将 x 中的数字部分反转后的结果。 如果反转后整数超过 32 位的有符号整数的范围 [−231, 231 −...
    99+
    2022-11-12
  • vue实现一个简单的分页功能实例详解
    这是一个简单的分页功能,只能够前端使用,数据不能通过后台服务器进行更改,能容已经写死了。 下面的内容我是在做一个关于婚纱项目中用到的,当时好久没用vue了,就上网区找了别人的博客来看...
    99+
    2022-12-24
    vue分页功能 vue实现一个简单的分页功能
  • 在 Golang 中实现一个简单的Http中间件过程详解
    本文主要针对Golang的内置库 net/http 做了简单的扩展,通过添加中间件的形式实现了管道(Pipeline)模式,这样的好处是各模块之间是低耦合的,符合单一职责原则,可以很...
    99+
    2022-11-12
  • 详解如何实现一个简单的Node.js脚手架
    原因 在工作中,需要开发一个脚手架,用于给相关用户提供相关的开发便利性。 适合人群 对前端、Node操作有一定的了解,同时向了解脚手架开发过程或者需要自己实现一个脚手架的开发者。 目标 开发一个简...
    99+
    2022-06-04
    脚手架 如何实现 详解
  • Python实现的文本简单可逆加密算法示例
    本文实例讲述了Python实现的文本简单可逆加密算法。分享给大家供大家参考,具体如下: 其实很简单,就是把一段文本每个字符都通过某种方式改变(比如加1) 这样就实现了文本的加密操作,解密就是其逆运算 #...
    99+
    2022-06-04
    示例 加密算法 文本
  • C++实现一个简单消息队列的示例详解
    目录前言一、如何实现1、接口定义2、用到的对象3、基本流程二、完整代码三、使用示例线程通信总结前言 消息队列在多线程的场景有时会用到,尤其是线程通信跨线程调用的时候,就可以使用消息队...
    99+
    2022-12-15
    C++实现消息队列 C++消息队列
  • 基于Java快速实现一个简单版的HashMap详解
    目录1.示例图2.分析需求3.代码实现3.运行结果简单实现一个底层数据结构为数组 + 链表的HashMap,不考虑链表长度超过8个时变为红黑树的情况。 1.示例图 2.分析需求 p...
    99+
    2023-02-08
    Java实现HashMap Java HashMap
  • 详解Java中日志跟踪的简单实现
    目录一、前言二、MDC介绍三、实现方案1、基本思路2、实现(以SpringBoot为例)四、总结一、前言 在编码过程中,常常需要写打印日志语句,我们期望的是同一个业务的日志都在一块,...
    99+
    2022-11-13
  • 详解JavaScript实现简单的词法分析器示例
    目录正文什么是词法分析器?实现一个简单的词法分析器总结正文 词法分析是编译器的一项重要工作,其目的是将源代码转换成单个单词(token)的序列,方便后续语法分析器(parser)对...
    99+
    2023-03-10
    JavaScript词法分析器 JavaScript 分析器
  • nodejs 实现简单的文件上传功能(示例详解)
    首先需要大家看一下目录结构,然后开始一点开始我们的小demo。 文件上传总计分为三种方式: 1.通过flash,activeX等第三方插件实现文件上传功能。 2.通过html的fo...
    99+
    2022-11-13
  • 利用Python中unittest实现简单的单元测试实例详解
    前言 单元测试的重要性就不多说了,可恶的是Python中有太多的单元测试框架和工具,什么unittest, testtools, subunit, coverage, testrepository, nos...
    99+
    2022-06-04
    详解 实例 单元测试
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作