bug复盘之Vite2项目在线上报错,修复后却意外发现Table组件的几个bug
0
阅: - 评:0 - 积分:摘要:2周前,@老王在测试供应商后台项目时发现了一个bug。属于那种经典类型的,特征是:本地开发正常,一线上就报错。经过一番折腾,最终解决问题并收获意外惊喜!遂记录一篇笔记,供自己日后参考。
一、项目环境
还是上次的那个Vite2 + Vue3 + Element-Plus@2.x项目,主要配置如下:
{
"dependencies": {
"element-plus": "^2.2.16",
"vue": "3.2.2",
"vue-router": "4.0.4",
"vuex": "4.0.0"
},
"devDependencies": {
"@vitejs/plugin-legacy": "^1.8.2",
"@vitejs/plugin-vue": "1.1.4",
"@vue/compiler-sfc": "3.2.2",
"@vue/test-utils": "2.0.0-rc.6",
"vite": "2.3.8",
"vite-plugin-mock": "2.3.0",
"vue-jest": "5.0.0-alpha.7"
}
}
二、测试提了一个bug
2周前,@老王在测试上述项目时,发现了一个关于添加多规格的bug。
一听有bug,我当时就不困了。亲眼看着他操作了一遍,删除规格名后下方的表格没有变化。打开控制台也没有任何报错,这就尴尬了。
三、排查原因
跟老王核对了信息,是预上线环境出现了问题。我立即启动了本地的预上线环境,使用老王的号及密码登录了进去,找到了那款商品进行了。按流程操作了一遍,一切正常。打包放线上,问题又出现了。
反反复复打包了几次,线上始终有问题。
这就难搞啊,要想解决问题,控制台得有日志信息才行。可现在的控制台,空空如也,啥都么有!
3.1、想办法显示日志
首先想到的是修改vite.config.js
配置文件,修改如下:
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ command, mode }) => {
return {
build: {
terserOptions: {
// 更多配置见→→→ https://terser.org/docs/api-reference#minify-options
compress: {
drop_console: false, // 打包时删除console
drop_debugger: false, // 打包时删除 debugger
pure_funcs: null, // ['console.log']
},
},
},
...... // 省略一些未修改的配置项
}
})
上面主要修改了3个配置项,分别是drop_console
,drop_debugger
和pure_funcs
。
3.2、终于出现了报错信息→→→ Cannot read properties of null (reading 'insertBefore')
修改完配置项后,再使用npm run build:pre
打包命令进行打包。打包结束后进入打包后的目录,使用http-server
即可启动本地一个web服务器。在浏览器中访问127.0.0.1:8080
即可访问项目。
打开控制台后继续操作规格名,突然间控制台报红了!看来有希望了,当时报错信息如下。
Uncaught (in promise) TypeError: Cannot read properties of null (reading 'insertBefore')
at insert (@vue.20221121_152517.js:1:86997)
at _ (@vue.20221121_152517.js:1:49039)
at y (@vue.20221121_152517.js:1:48620)
at D (@vue.20221121_152517.js:1:57414)
at L (@vue.20221121_152517.js:1:55755)
at A (@vue.20221121_152517.js:1:51170)
at $ (@vue.20221121_152517.js:1:49404)
at y (@vue.20221121_152517.js:1:48728)
at D (@vue.20221121_152517.js:1:56185)
at L (@vue.20221121_152517.js:1:55755)
一条条分析后,我有些懵逼!
3.3、定位问题所在
无果之下,在网上搜索了报错信息。最终找到了一篇相似文章--《vue3 生产环境报Cannot read property 'insertBefore' of null》。
经过几次调试,终于定位到了问题所在。
<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
<el-table ref="multipleTable" :data="tableData" @selection-change="handleSelectionChange">
<el-table-column type="selection" width="55"> </el-table-column>
<template v-for="(item, index) in activeList">
<el-table-column v-if="item.name" :key="index" :label="item.name" width="100">
<template #default="scope">
{{ scope.row.skus[index] && scope.row.skus[index].v }}
</template>
</el-table-column>
</template>
...
</el-table>
万万没想到的是,问题出在了第7行,即:scope.row.skus[index] && scope.row.skus[index].v
。当我在前面再加一个&&
后,一切正常了。
<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
{{ scope.row.skus && scope.row.skus[index] && scope.row.skus[index].v }}
3.4、百思不得其解
让人很费解的是:这个组件曾应用到多个Vue2项目中,日常也有使用,均未发现异常。只在这个Vue3项目中,打包后出现异常......
再来看看这段代码,用了2次&&
才正常。也就是说,当代码执行到scope.row.skus
时就已经出问题了。而scope.row
是el-table
自带的插槽方法,可访问到表格当前行的数据。这个skus
是一个数组格式,难不成在这里发生了变化???
3.5、尝试打印scope.row,有了新的发现!
修改了上面的代码,用一个函数来接收scope.row
,看看结果是否符合预期吧。
<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
<!-- 把上述第7行代码修改为下方代码↓↓↓ -->
{{ getScopeRow(scope.row) }}
// 打印scope.row
function getScopeRow(row) {
console.log('getScopeRow:', row, ';row.skus=', row.skus, ';row.id=', row.id)
}
现在再来操作,控制台的打印让我大吃一惊!getScopeRow:
竟然打印了多次!!!如下图↓↓↓
我又在另外几个Vue2的项目上做了同样的测试,结果均只打印了一次。如下图↓↓↓
接下来的内容就是意外惊喜了,各位看官注意仔细看哦!
四、意外发现Table组件的几个bug
为了更直接的找到问题,我又重新用一个干净的页面做测试,这次只添加一个精简的表格代码,意外惊喜接踵而至!
4.1、bug1:即使表格数据长度为0,getScopeRow()函数也会触发一次
第一个意外惊喜就是:即使表格无数据,getScopeRow()函数也会触发一次!完整代码见下方
<!-- 友情提示:因编码原因,文章中的双括号{}均做了转码处理,复制后需要替换成英文状态下的{ }才能运行 -->
<template>
<div>
<el-button @click="handleAdd">添加一条数据</el-button>
<el-table :data="tableData">
<el-table-column label="id" prop="id" width="100"></el-table-column>
<el-table-column label="标题" prop="name" width="100">
<template #default="scope">
{{ getScopeRow(scope.row) }}
</template>
</el-table-column>
<el-table-column label="价格" prop="price" width="100"></el-table-column>
</el-table>
</div>
</template>
<script setup>
import { ref } from 'vue'
const tableData = ref([]) // 表格数据
// 获取scope.row的内容
function getScopeRow(row) {
console.log('getScopeRow:', row, ';row.name=', row.name, ';row.price=', row.price)
}
// 点击新增按钮
function handleAdd() {
const id = tableData.value.length + 1
tableData.value.push({ name: '商品1', price: 25.8, num: 3, id: id })
console.log(`第${id}次点击添加`, Date.now(), ';tableData.value.length=', tableData.value.length)
}
</script>
<style lang="scss" scoped></style>
万万没想到的是:在初始化的时候,控制台竟然打印了getScopeRow:
的信息!如下图↓↓↓
更让我震惊的是:第一次点击添加按钮,getScopeRow:
会打印2次! 实际上,tableData
的长度此时为1
。Gif动图如下↓↓↓
4.2、bug2:神奇的width属性,竟然会让日志翻倍打印!
当我看到控制台打印了多次后,原本懵逼的我此时更加懵逼了!
考虑到有可能是版本的问题,我把Element-Plus
从2.2.16
升级到目前最新的2.2.26
。
再次操作,bug仍然存在!
当我删除掉部分列的width
后,首次点击按钮,没有重复执行了!日志打印如下图↓↓↓
经过几次测试,我发现这个width
属性还真能触发bug!规律如下:
- 当所有列
<el-table-column>
均添加width
且值大于0时,第一次插入数据,打印两条日志;以后的每次插入,均会翻倍打印...... - 当某一列
<el-table-column>
不添加width
属性,或者添加了width
且值为auto
或0
或空字符串时,每次打印均正常。 - 当所有列
<el-table-column>
均不添加width
时,每次打印均正常。
4.3、bug3:当视图区变化时,方法会被频繁触发!
这个发现还真是个意外!
起初我是在录动图的时候发现有时第二次插入数据时,日志会翻倍打印,有时就正常。这让我很疑惑,也找不到原因,我甚至怀疑自己起来。
直到某次调试,我拖动控制台为了显示更多日志时,突然意外发现getScopeRow:
会被频繁触发!我这才确定不是自己眼睛花了,是这个组件确实是有问题啊!!!gif动图如下↓↓↓
我只是拖拽调试器窗口来回调整大小,搜索结果竟然从1次叠加到了10次!
怎么样?!是不是很意外?没想到吧,这样也能触发日志打印多次!
你以为这样就结束了吗?我再告诉你一个新的发现吧!
现在我们把上面的4.2
和4.3
结合起来,你会发现在某一临界点,控制台日志数量突然翻倍!如下图↓↓↓
可以看到,当有9条数据时,控制台中有45条结果,这是没问题的。当我再点击一次按钮,此时搜索结果变成了65
条!也就是说,此时打印了20条数据,本身应该打印10条才对。而引起这一结果变化,就是因为页面出现了滚动条,4.3
的结论被再次印证。
有兴趣的看官可以复制上述代码或用Element-Plus官方最新版进行测试,截止目前发稿为止(2022年12月07日),上述发现的bug仍然存在。
五、结束语
这个Element-Plus组件库,在前面已经坑过我好几次了。加上这篇,算是第四篇了。有兴趣的看官可以访问往期文章↓↓↓
《Vue3踩坑系列之把Element-UI升级到Element-Plus时遇到的若干坑》、
《Vue3踩坑系列之Element-Plus库中upload上传组件的坑(下)》、
《Vue3踩坑系列之Element-Plus库中upload上传组件的坑》
最后,希望官方能够早日修复一些bug。同时也在告诫自己,写代码还是要严谨些。
转载声明:
若亲想转载本文到其它平台,请务必保留本文出处!
本文链接:/xwzj/2022-12-07/element-plus-table-bug.html
若亲不想直保留地址,含蓄保留也行。艺灵不想再看到有人拿我的技术文章到他的地盘或者是其它平台做教(装)程(B)而不留下我的痕迹。文章你可以随便转载,随便修改,但请尊重艺灵的劳动成果!谢谢理解。
亲,扫个码支持一下艺灵呗~
Tag: Vite2 Vue3 Vue2 Element-Plus Table组件 bug
上一篇: 工具分享之图片压缩 下一篇: Vite2项目优化之线上功能有异常,如何快速排查问题