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

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

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

北京

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

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

看不清楚,换张图片

免费获取短信验证码

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

今天给大家介绍一下怎么深入解析Vue3中的diff 算法。文章的内容小编觉得不错,现在给大家分享一下,觉得有需要的朋友可以了解一下,希望对大家有所帮助,下面跟着小编的思路一起来阅读吧。

1.0  diffkey子节点

在处理被标记为UNKEYED_FRAGMENT时。

  • 首先会通过新旧自序列获取最小共同长度commonLength

  • 对公共部分循环遍历patch

  • patch 结束,再处理剩余的新旧节点。

  • 如果oldLength > newLength,说明需要对旧节点进行unmount

  • 否则,说明有新增节点,需要进行mount;

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

这里贴下省略后的代码。

const patchUnkeyedChildren = (c1, c2,...res) => {    c1 = c1 || EMPTY_ARR    c2 = c2 || EMPTY_ARR    // 获取新旧子节点的长度    const oldLength = c1.length    const newLength = c2.length    // 1. 取得公共长度。最小长度    const commonLength = Math.min(oldLength, newLength)    let i    // 2. patch公共部分    for (i = 0; i < commonLength; i++) {       patch(...)    }    // 3. 卸载旧节点    if (oldLength > newLength) {      // remove old      unmountChildren(...)    } else {      // mount new      // 4. 否则挂载新的子节点      mountChildren(...)    }  }

从上面的代码可以看出,在处理无key子节点的时候,逻辑还是非常简单粗暴的。准确的说处理无key子节点的效率并不高。

因为不管是直接对公共部分patch,还是直接对新增节点进行mountChildren(其实是遍历子节点,进行patch操作),其实都是在递归进行patch,这就会影响到性能。

2.0 diffkey子节点序列

diffkey子序列的时候,会进行细分处理。主要会经过以下一种情况的判断:

  • 起始位置节点类型相同。

  • 结束位置节点类型相同。

  • 相同部分处理完,有新增节点。

  • 相同部分处理完,有旧节点需要卸载。

  • 首尾相同,但中间部分存在可复用乱序节点。

在开始阶段,会先生面三个指正,分别是:

  • i = 0,指向新旧序列的开始位置

  • e1 = oldLength - 1,指向旧序列的结束位置

  • e2 = newLength - 1,指向新序列的结束位置

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

let i = 0const l2 = c2.lengthlet e1 = c1.length - 1 // prev ending indexlet e2 = l2 - 1 // next ending index

下面开始分情况进行diff处理。

2.1 起始位置节点类型相同

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

  • 对于起始位置类型相同的节点,从左向右进行diff遍历。

  • 如果新旧节点类型相同,则进行patch处理

  • 节点类型不同,则break,跳出遍历diff

//  i <= 2 && i <= 3while (i <= e1 && i <= e2) {  const n1 = c1[i]  const n2 = c2[i]  if (isSameVNodeType(n1, n2)) {    // 如果是相同的节点类型,则进行递归patch    patch(...)  } else {    // 否则退出    break  }  i++}

上面上略了部分代码,但不影响主要逻辑。

从代码可以知道,遍历时,利用前面在函数全局上下文中声明的三个指针,进行遍历判断。

保证能充分遍历到开始位置相同的位置,i <= e1 && i <= e2

一旦遇到类型不同的节点,就会跳出diff遍历。

2.2 结束位置节点类型相同

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

开始位置相同diff 结束,会紧接着从序列尾部开始遍历 diff

此时需要对尾指针e1、e2进行递减。

//  i <= 2 && i <= 3// 结束后: e1 = 0 e2 =  1while (i <= e1 && i <= e2) {  const n1 = c1[e1]  const n2 = c2[e2]  if (isSameVNodeType(n1, n2)) {    // 相同的节点类型    patch(...)  } else {    // 否则退出    break  }  e1--  e2--}

从代码可以看出,diff逻辑与第一种基本一样,相同类型进行patch处理。

不同类型break,跳出循环遍历。

并且对尾指针进行递减操作。

2.3 相同部分遍历结束,新序列中有新增节点,进行挂载

经过上面两种情况的处理,已经patch完首尾相同部分的节点,接下来是对新序列中的新增节点进行patch处理。

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

在经过上面两种请款处理之后,如果有新增节点,可能会出现 i >  e1 && i <= e2的情况。

这种情况下意味着新的子节点序列中有新增节点。

这时会对新增节点进行patch

// 3. common sequence + mount// (a b)// (a b) c// i = 2, e1 = 1, e2 = 2// (a b)// c (a b)// i = 0, e1 = -1, e2 = 0if (i > e1) {  if (i <= e2) {    const nextPos = e2 + 1    // nextPos < l2,说明有已经patch过尾部节点,    // 否则会获取父节点作为锚点    const anchor = nextPos < l2 ? c2[nextPos].el : parentAnchor    while (i <= e2) {      patch(null, c2[i], anchor, ...others)      i++    }  }}

从上面的代码可以知道,patch的时候没有传第一个参数,最终会走mount的逻辑。

可以看这篇 主要分析patch的过程

https://mp.weixin.qq.com/s/hzpNGWFCLMC2vJNSmP2vsQ

patch的过程中,会递增i指针。

并通过nextPos来获取锚点。

如果nextPos < l2,则以已经patch的节点作为锚点,否则以父节点作为锚点。

2.4 相同部分遍历结束,新序列中少节点,进行卸载

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

如果处理完收尾相同的节点,出现i > e2 && i <= e1的情况,则意味着有旧节点需要进行卸载操作。

// 4. common sequence + unmount// (a b) c// (a b)// i = 2, e1 = 2, e2 = 1// a (b c)// (b c)// i = 0, e1 = 0, e2 = -1// 公共序列 卸载旧的else if (i > e2) {  while (i <= e1) {    unmount(c1[i], parentComponent, parentSuspense, true)    i++  }}

通过代码可以知道,这种情况下,会递增i指针,对旧节点进行卸载。

2.5 乱序情况

这种情况下较为复杂,但diff的核心逻辑在于通过新旧节点的位置变化构建一个最大递增子序列,最大子序列能保证通过最小的移动或者patch实现节点的复用。

下面一起来看下如何实现的。

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

2.5.1 为新子节点构建key:index映射

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

// 5. 乱序的情况// [i ... e1 + 1]: a b [c d e] f g// [i ... e2 + 1]: a b [e d c h] f g// i = 2, e1 = 4, e2 = 5const s1 = i // s1 = 2const s2 = i // s2 = 2// 5.1 build key:index map for newChildren// 首先为新的子节点构建在新的子序列中 key:index 的映射// 通过map 创建的新的子节点const keyToNewIndexMap = new Map()// 遍历新的节点,为新节点设置key// i = 2; i <= 5for (i = s2; i <= e2; i++) {  // 获取的是新序列中的子节点  const nextChild = c2[i];  if (nextChild.key != null) {    // nextChild.key 已存在    // a b [e d c h] f g    // e:2 d:3 c:4 h:5    keyToNewIndexMap.set(nextChild.key, i)  }}

结合上面的图和代码可以知道:

  • 在经过首尾相同的patch处理之后,i = 2,e1 = 4,e2 = 5

  • 经过遍历之后keyToNewIndexMap中,新节点的key:index的关系为 E : 2、D : 3 、C : 4、H : 5

  • keyToNewIndexMap的作用主要是后面通过遍历旧子序列,确定可复用节点在新的子序列中的位置

2.5.2 从左向右遍历旧子序列,patch匹配的相同类型的节点,移除不存在的节点

经过前面的处理,已经创建了keyToNewIndexMap

在开始从左向右遍历之前,需要知道几个变量的含义:

// 5.2 loop through old children left to be patched and try to patch// matching nodes & remove nodes that are no longer present// 从旧的子节点的左侧开始循环遍历进行patch。// 并且patch匹配的节点 并移除不存在的节点// 已经patch的节点个数let patched = 0// 需要patch的节点数量// 以上图为例:e2 = 5; s2 = 2; 知道需要patch的节点个数// toBePatched = 4const toBePatched = e2 - s2 + 1// 用于判断节点是否需要移动// 当新旧队列中出现可复用节点交叉时,moved = truelet moved = false// used to track whether any node has moved// 用于记录节点是否已经移动let maxNewIndexSoFar = 0// works as Map<newIndex, oldIndex>// 作新旧节点的下标映射// Note that oldIndex is offset by +1// 注意 旧节点的 index 要向右偏移一个下标// and oldIndex = 0 is a special value indicating the new node has// no corresponding old node.// 并且旧节点Index = 0 是一个特殊的值,用于表示新的节点中没有对应的旧节点// used for determining longest stable subsequence// newIndexToOldIndexMap 用于确定最长递增子序列// 新下标与旧下标的mapconst newIndexToOldIndexMap = new Array(toBePatched)// 将所有的值初始化为0// [0, 0, 0, 0]for (i = 0; i < toBePatched; i++) newIndexToOldIndexMap[i] = 0
  • 变量 patched 用于记录已经patch的节点

  • 变量 toBePatched 用于记录需要进行patch的节点个数

  • 变量 moved 用于记录是否有可复用节点发生交叉

  • maxNewIndexSoFar用于记录当旧的子序列中存在没有设置key的子节点,但是该子节点出现于新的子序列中,且可复用,最大下标。

  • 变量newIndexToOldIndexMap用于映射新的子序列中的节点下标 对应于 旧的子序列中的节点的下标

  • 并且会将newIndexToOldIndexMap初始化为一个全0数组,[0, 0, 0, 0]

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

知道了这些变量的含义之后 我们就可以开始从左向右遍历子序列了。

遍历的时候,需要首先遍历旧子序列,起点是s1,终点是e1

遍历的过程中会对patched进行累加。

卸载旧节点

如果patched >= toBePatched,说明新子序列中的子节点少于旧子序列中的节点数量。

需要对旧子节点进行卸载。

// 遍历未处理旧序列中子节点for (i = s1; i <= e1; i++) {    // 获取旧节点    // 会逐个获取 c d e    const prevChild = c1[i]    // 如果已经patch 的数量 >= 需要进行patch的节点个数        // patched刚开始为 0    // patched >= 4    if (patched >= toBePatched) {      // all new children have been patched so this can only be a removal      // 这说明所有的新节点已经被patch 因此可以移除旧的      unmount(prevChild, parentComponent, parentSuspense, true)      continue    }}

如果prevChild.key是存在的,会通过前面我们构建的keyToNewIndexMap,获取prevChild在新子序列中的下标newIndex

获取newIndex

// 新节点下标let newIndexif (prevChild.key != null) {  // 旧的节点肯定有key,   // 根据旧节点key  获取相同类型的新的子节点  在 新的队列中对应节点位置  // 这个时候 因为c d e 是原来的节点 并且有key  // h 是新增节点 旧节点中没有 获取不到 对应的index 会走else  // 所以newIndex在开始时会有如下情况     // 这里是可以获取到newIndex的  newIndex = keyToNewIndexMap.get(prevChild.key)}

以图为例,可以知道,在遍历过程中,节点c、d、e为可复用节点,分别对应新子序列中的2、3、4的位置。

newIndex可以取到的值为4、3、2

如果旧节点没有key怎么办?

// key-less node, try to locate a key-less node of the same type// 如果旧的节点没有key// 则会查找没有key的 且为相同类型的新节点在 新节点队列中 的位置// j = 2: j <= 5for (j = s2; j <= e2; j++) {  if (    newIndexToOldIndexMap[j - s2] === 0 &&    // 判断是否是新旧节点是否相同    isSameVNodeType(prevChild, c2[j])  ) {    // 获取到相同类型节点的下标    newIndex = j    break  }}

如果节点没有key,则同样会取新子序列中,遍历查找没有key且两个新旧类型相同子节点,并以此节点的下标,作为newIndex

newIndexToOldIndexMap[j - s2] === 0 说明节点没有该节点没有key。

如果还没有获取到newIndex,说明在新子序列中没有存在的与 prevChild 相同的子节点,需要对prevChild进行卸载。

if (newIndex === undefined) {  // 没有对应的新节点 卸载旧的  unmount(prevChild, parentComponent, parentSuspense, true)}

否则,开始根据newIndex,构建keyToNewIndexMap,明确新旧节点对应的下标位置。

时刻牢记newIndex是根据旧节点获取的其在新的子序列中的下标。

// 这里处理获取到newIndex的情况// 开始整理新节点下标 Index 对于 相同类型旧节点在 旧队列中的映射// 新节点下标从 s2=2 开始,对应的旧节点下标需要偏移一个下标// 0 表示当前节点没有对应的旧节点// 偏移 1个位置 i从 s1 = 2 开始,s2 = 2// 4 - 2 获取下标 2,新的 c 节点对应旧 c 节点的位置下标 3// 3 - 2 获取下标 1,新的 d 节点对应旧 d 节点的位置下标 4// 2 - 2 获取下标 0,新的 e 节点对应旧 e 节点的位置下标 5// [0, 0, 0, 0] => [5, 4, 3, 0]// [2,3,4,5] = [5, 4, 3, 0]newIndexToOldIndexMap[newIndex - s2] = i + 1// newIndex 会取 4 3 2 if (newIndex >= maxNewIndexSoFar) {  maxNewIndexSoFar = newIndex} else {  moved = true}

在构建newIndexToOldIndexMap的同时,会通过判断newIndexmaxNewIndexSoFa的关系,确定节点是否发生移动。

newIndexToOldIndexMap最后遍历结束应该为[5, 4, 3, 0]0说明有旧序列中没有与心序列中对应的节点,并且该节点可能是新增节点。

如果新旧节点在序列中相对位置保持始终不变,则maxNewIndexSoFar会随着newIndex的递增而递增。

意味着节点没有发生交叉。也就不需要移动可复用节点。

否则可复用节点发生了移动,需要对可复用节点进行move

遍历的最后,会对新旧节点进行patch,并对patched进行累加,记录已经处理过几个节点。

// 进行递归patchpatch(  prevChild,  c2[newIndex],  container,  null,  parentComponent,  parentSuspense,  isSVG,  slotScopeIds,  optimized)// 已经patch的patched++

经过上面的处理,已经完成对旧节点进行了卸载,对相对位置保持没有变化的子节点进行了patch复用。

接下来就是需要移动可复用节点,挂载新子序列中新增节点。

2.5.3 移动可复用节点,挂载新增节点

这里涉及到一块比较核心的代码,也是Vue3 diff效率提升的关键所在。

前面通过newIndexToOldIndexMap,记录了新旧子节点变化前后的下标映射。

这里会通过getSequence方法获取一个最大递增子序列。用于记录相对位置没有发生变化的子节点的下标。

根据此递增子序列,可以实现在移动可复用节点的时候,只移动相对位置前后发生变化的子节点。

做到最小改动。

那什么是最大递增子序列?

  • 子序列是由数组派生而来的序列,删除(或不删除)数组中的元素而不改变其余元素的顺序。

  • 而递增子序列,是数组派生的子序列,各元素之间保持逐个递增的关系。

  • 例如:

  • 数组[3, 6, 2, 7] 是数组 [0, 3, 1, 6, 2, 2, 7] 的最长严格递增子序列。

  • 数组[2, 3, 7, 101] 是数组 [10 , 9, 2, 5, 3, 7, 101, 18]的最大递增子序列。

  • 数组[0, 1, 2, 3] 是数组 [0, 1, 0, 3, 2, 3]的最大递增子序列。

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

已上图为例,在未处理的乱序节点中,存在新增节点N、I、需要卸载的节点G,及可复用节点C、D、E、F

节点CDE在新旧子序列中相对位置没有变换,如果想要通过最小变动实现节点复用,我们可以将找出F节点变化前后的下标位置,在新的子序列C节点之前插入F节点即可。

最大递增子序列的作用就是通过新旧节点变化前后的映射,创建一个递增数组,这样就可以知道哪些节点在变化前后相对位置没有发生变化,哪些节点需要进行移动。

Vue3中的递增子序列的不同在于,它保存的是可复用节点在 newIndexToOldIndexMap的下标。而并不是newIndexToOldIndexMap中的元素。

接下来我们看下代码部分:

// 5.3 move and mount// generate longest stable subsequence only when nodes have moved// 移动节点 挂载节点// 仅当节点被移动后 生成最长递增子序列// 经过上面操作后,newIndexToOldIndexMap = [5, 4, 3, 0]// 得到 increasingNewIndexSequence = [2]const increasingNewIndexSequence = moved  ? getSequence(newIndexToOldIndexMap)  : EMPTY_ARR// j = 0j = increasingNewIndexSequence.length - 1// looping backwards so that we can use last patched node as anchor// 从后向前遍历 以便于可以用最新的被patch的节点作为锚点// i = 3for (i = toBePatched - 1; i >= 0; i--) {  // 5 4 3 2  const nextIndex = s2 + i  // 节点 h  c  d  e   const nextChild = c2[nextIndex]  // 获取锚点  const anchor =    nextIndex + 1 < l2 ? c2[nextIndex + 1].el : parentAnchor  // [5, 4, 3, 0] 节点h会被patch,其实是mount  //  c  d  e 会被移动  if (newIndexToOldIndexMap[i] === 0) {    // mount new    // 挂载新的    patch(      null,      nextChild,      container,      anchor,      ...    )  } else if (moved) {    // move if:    // There is no stable subsequence (e.g. a reverse)    // OR current node is not among the stable sequence    // 如果没有最长递增子序列或者 当前节点不在递增子序列中间    // 则移动节点    //     if (j < 0 || i !== increasingNewIndexSequence[j]) {      move(nextChild, container, anchor, MoveType.REORDER)    } else {      j--    }  }}

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

从上面的代码可以知道:

  • 通过newIndexToOldIndexMap获取的最大递增子序列是[2]

  • j = 0

  • 遍历的时候从右向左遍历,这样可以获取到锚点,如果有已经经过patch的兄弟节点,则以兄弟节点作为锚点,否则以父节点作为锚点

  • newIndexToOldIndexMap[i] === 0,说明是新增节点。需要对节点进行mount,这时只需给patch的第一个参数传null即可。可以知道首先会对h节点进行patch

  • 否则会判断moved是否为true。通过前面的分析,我们知道节点C & 节点E在前后变化中发生了位置移动。

  • 故这里会移动节点,我们知道 j 此时为0i 此时为**2**,i !== increasingNewIndexSequence[j]true,并不会移动C节点,而是执行 j--

  • 后面因为 j < 0,会对 D、E节点进行移动。

至此我们就完成了Vue3 diff算法的学习分析。

这里为大家提供了一个示例,可以结合本文的分析过程进行练习:

可以只看第一张图进行分析,分析结束后可以与第二三张图片进行对比。

图一:

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

图二 & 三:

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

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

vue是什么

Vue是一套用于构建用户界面的渐进式JavaScript框架,Vue与其它大型框架的区别是,使用Vue可以自底向上逐层应用,其核心库只关注视图层,方便与第三方库和项目整合,且使用Vue可以采用单文件组件和Vue生态系统支持的库开发复杂的单页应用。

以上就是怎么深入解析Vue3中的diff 算法的全部内容了,更多与怎么深入解析Vue3中的diff 算法相关的内容可以搜索编程网之前的文章或者浏览下面的文章进行学习哈!相信小编会给大家增添更多知识,希望大家能够支持一下编程网!

免责声明:

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

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

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

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

下载Word文档

猜你喜欢

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

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

深入浅析Vue2中的Diff算法

diff算法是一种通过同层的树节点进行比较的高效算法,避免了对树进行逐层搜索遍历。那么大家对diff算法吗有多少了解?下面本篇文章就来带大家深入解析下vue2的diff算法,希望对大家有所帮助!
2023-05-14

深入了解Vue2中的的双端diff算法

双端Diff在可以解决更多简单Diff算法处理不了的场景,且比简单Diff算法性能更好。本文主要来和大家详细讲讲Vue2中的双端diff算法的实现与使用,需要的可以参考一下
2023-02-08

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

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

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

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

Vue2中的Diff算法怎么使用

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

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

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

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

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

深入解析Go语言的运算符用法

Go语言中运算符的高级用法解析Go语言作为一门现代化的编程语言,提供了丰富的运算符供开发者使用。除了常规的算术运算符和逻辑运算符外,Go语言还提供了一些高级的运算符,可以帮助开发者更加高效地进行编程。本文将对这些高级运算符进行解析,并给出相
深入解析Go语言的运算符用法
2024-01-18

如何深入理解Python中的Apriori关联分析算法

今天就跟大家聊聊有关如何深入理解Python中的Apriori关联分析算法,可能很多人都不太了解,为了让大家更加了解,小编给大家总结了以下内容,希望大家根据这篇文章可以有所收获。在美国有这样一家奇怪的超市,它将啤酒与尿布这样两个奇怪的东西放
2023-06-02

深入理解Golang中的除法运算符

深入理解Golang中的除法运算符在Golang中,除法运算符/是常见的数学运算符之一,用于计算两个数的商。但在实际编程中,对于除法运算符的使用可能会涉及到一些特殊情况和注意事项。本文将深入探讨Golang中的除法运算符,包括整数除法、浮
深入理解Golang中的除法运算符
2024-03-13

深入解析Go语言中%运算符的应用

Go语言中%运算符的用途详解,需要具体代码示例引言:在Go语言中,%运算符是一个常用的运算符之一。它的作用是求取两个数相除的余数。本文将详细讨论%运算符的用途,并给出一些具体的代码示例。一、%运算符的基本用法%运算符可以应用于整数类型的
深入解析Go语言中%运算符的应用
2024-01-18

深入解析Python中的浮点数输入方法

Python中浮点型的输入方法详解在Python编程中,我们经常需要从用户那里获取输入数据。当涉及到浮点型数据时,如何准确地读取和处理用户的输入就变得至关重要。本文将详细介绍Python中浮点型的输入方法,并提供具体代码示例。一、使用i
深入解析Python中的浮点数输入方法
2024-02-03

编程热搜

  • 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动态编译

目录