逻辑判断中条件数据均不变,但执行结果一会儿真一会儿假,你会不会怀疑人生?

187 阅读3分钟

如题,对于开发者而言,这可能是最预料不到和最诡异的事情,此Bug不知道多少小伙伴正在坑里,因为它来自当前最新版本 Vite<= 5.4.11)和其依赖 rollup<= 4.27.2),以下为最简复现 Demo

<template>
    <div>
        <el-button @click="onTest('get')">测试 - get</el-button>
        <el-button @click="onTest('post')">测试 - post</el-button>
    </div>
</template>

<script setup lang="ts">
const onTest = (method: string) => {
    const opt = method === 'get' ? { get: true } : { get: false }

    // 这行代码中的逻辑判断,完全是从 opt 赋值语句中复制下来的
    console.log(`method=${method},此时 opt 应该是:`, method === 'get' ? { get: true } : { get: false })

    // 打印一下 opt 的实际值
    console.log(`method=${method},此时 opt 最终是:`, opt)
}
</script>

以上代码很简单,当我点击 测试 - get测试 - post,输出应该是:

# 点击 "测试 - get" 按钮
method=get,此时 opt 应该是: { get: true }
method=get,此时 opt 最终是: { get: true }

# 点击 "测试 - post" 按钮
method=post,此时 opt 应该是: { get: false }
method=post,此时 opt 最终是: { get: false }

在开发环境中,这一切正常,但是在编译后的生产环境中,它的输出是:

# 点击 "测试 - get" 按钮
method=get,此时 opt 应该是: { get: true }
method=get,此时 opt 最终是: { get: true }

# 点击 "测试 - post" 按钮
method=post,此时 opt 应该是: { get: false }
method=post,此时 opt 最终是: {}

如果你在仔细看一遍上面的 Demo,你就会发觉这代码的执行结果离谱到了什么程度:

  1. opt 的实际值与预期不符,应该同开发环境和开发者想要的结果一致,也就是 { get: false }
  2. console.logopt 的赋值语句完全一致,但是 log 中的值是 { get: false },赋值结果却是 {},同一个赋值语句,数据未变,执行结果却变了!

排查

以上是最简复现 Demo,实际上的代码肯定复杂很多,作为一个合格的开发者,肯定是优先怀疑自己的问题,多次修改和确认传参->重新编译无果后,开始打断点和重写代码,你猜猜他们把代码编译成了什么样子?

const o = (t) => {
    const e = t === 'get' ? { get: !0 } : {}
    console.log(`method=${t},此时 opt 应该是:`, t === 'get' ? { get: !0 } : { get: !1 })
    console.log(`method=${t},此时 opt 最终是:`, e)
}

是的,编译产物中,不等于 get 时,直接赋值 {},这完全背离了原代码的逻辑,神奇的是在 log 中它是正常的...

解决

  1. 目前确定这个Bug是在使用三元运算符时才会发生,如果使用多行的if else则不会有问题,所以最简单的办法是修改代码,但是三元运算符并不方便全局搜索和替换,只能继续寻找原因
  2. Bug是最近突然产生的,在前几天还正常,今天的编译结果却出了问题,优先怀疑依赖问题,但是项目的依赖全部指定了固定版本(没有使用~ ^ < >=等符号,直接指定了固定版本),但是还有一种可能,依赖的依赖没有固定版本

首先找到正常时期的 pnpm-lock.yaml 文件,复制过来,利用 BuildAdmin 内置的WEB终端快速安装依赖和重新发布,测试结果回归正常!

1.png

2.png

虽然正常了,但是笔者任想寻根究底,因为不习惯使用以前的 pnpm-lock.yaml 虽然很多开源仓库和项目部署都有上传这个文件,以保证依赖一致,但是它的内容总是被自动修改,是一个令人不喜的文件(笔者为开源作者,部署时任然建议上传它)。

接下来,使用针对此问题秘制的 pnpm-lock.yaml 分析工具,对出问题和不出问题的 pnpm-lock.yaml 进行分析对比,找到了一个比较明显的依赖,立刻找一个它次级版本固定起来测试

3.png

4.png

重新编译后,代码执行结果回到了我预想的样子,看来罪魁祸首就是它,再检查一下 Vite 的代码仓库,果然并未固定版本,欢迎有时间的大佬上去提交 PR,当然最好是向 rollup 提交修复这个 bugPR,最后,如果以上内容有帮助到你,笔者在此邀请你使用我倾心打造的开源后台管理系统 BuildAdmin,内置WEB终端,支持可视化CRUD,非常棒!