【经验总结】报错未显示具体代码位置 - 记一次bug排查

118 阅读2分钟

前言:简单记录一次bug的排查,思考和解决过程

一、发现问题

起因:项目发现页面loading未解除,打开控制台发现页面报错如下:

image.png

二、排查问题

2.1 查看接口状态及返回值,确认正常

2.2 搜索页面代码是否使用了parentElement,搜索不到,所以具体是编译以后的代码报错

2.3 在接口调用方法中,返回值的下方,增加一个debugger,

到浏览器调试面板中,一行一行执行代码,直到遇到错误;定位到一个子组件内

2.4 注释所有template内的HTML代码,发现错误消失了,排除错误不是本组件js代码造成的,是HTML代码部分的原因

开始逐步放开HTML代码,确定造成错误的具体代码位置

<el-button
   v-if="props.exportBtnShow"
   v-permission="'order:index:export'"
   type="primary"
   @click="handleExport">导出
</el-button>

是一个导出按钮的问题

2.5 确认是按钮的v-if和v-permission冲突造成的

导出按钮所在组件,是复用组件,一个页面有导出按钮,另一个页面无导出按钮,并且还需要根据接口返回的当前状态值,判断是否显示导出按钮,所以增加了v-if判断

业务上导出按钮需要根据角色分配导出权限,所以增加了v-permission

其中v-permission的指令代码简化如下:

import { createApp } from 'vue'
import { isAuth } from '@/utils'

export default {
  install(app: ReturnType<typeof createApp>): void {
    app.directive('permission', {
      mounted: (el, binding) => {
        const permission = binding.value
        if (!isAuth(permission)) {
          el?.parentNode?.removeChild(el)
        }
      },
    })
  },
}

初始状态props.exportBtnShow是true,接口返回结果后,props.exportBtnShow变为false,然后自定义指令执行removeChild时发生了错误

三、解决问题

思路理清以后,问题就好解决了。

3.1 方法一、简单直接,将v-if改成v-show,子元素永远存在

3.2 方法二、增加两个判断的逻辑顺序

既然有两个判断,应该有一个逻辑上的先后,增加一个包裹的div标签,将v-if写在div标签上。第一个判断过了,才会判断第二个,避免不必要的冲突

<div v-if="props.exportBtnShow">
    <el-button
       v-permission="'order:index:export'"
       type="primary"
       @click="handleExport">导出
    </el-button>
</div>

不能将div换成template,因为template编译以后,实际上还是将v-if指令,写在el-button上

3.3 方法三、修改复用组件,增加一个插槽。需要导出按钮的页面,利用插槽将按钮写入

3.3 方法四、增加一个变量,将复用组件控制按钮是否在页面的逻辑,和业务状态控制按钮是否显示的逻辑分开

props.exportBtnShow,用来控制按钮在不同页面的是否显示的逻辑

:disabled="props.exportDisabled",用来控制业务上按钮是否可置灰

v-permission指令保留

<div v-if="props.exportBtnShow">
    <el-button
        v-permission="'order:index:export'"
        :disabled="props.exportDisabled"
        @click="handleExport">
        导出
    </el-button>
</div>

此方法虽然改动最大,增加了数据状态,但是逻辑更清晰,各种状态该管的状态更明确