记一次调试Vue3源码排查异常BUG


一、BUG描述

情况: 点击右侧字段列, 隐藏 “创建日期和更新日期”,再显示 “创建日期和更新日期”,操作栏的显示就错了。如果多点击几次,操作栏显示会恢复正常,且之后一直正常。

技术栈: vue3 + element-plus

二、element-plus的Table组件的封装代码

const defaultSlot = useSlots().default?.()
let canLookSlot = defaultSlot.filter(xxx)

// 拿到默认插槽,并通过filter方法过滤需要的数据,通过函数式组件渲染canLookSlot。

三、排查步骤

3.1 打印 “点击前后” 的slot数据

const vNodeList = computed(() => {
  const result = defaultSlot.filter((item: any, index:number) => {...})
  console.log('点击后数据:', defaultSlot, result)
  debugger
  return result
})

3.2 观察数据, 得出观点

a. debugger断点时,数据是正常的
b. 页面渲染后,数据是异常的
c. 观点: 在vue渲染时,Diff时应该有一些问题

截图1

3.3 让Vue3源码报错

a. 渲染后,slot数据中item.children.default被改了,就需要看vue源码是如何改的,将item.children.default设置为不可写,直接让源码报错

const vNodeList = computed(() => {
  const result = defaultSlot.filter((item: any, index:number) => {
    // 添加这段代码
    Object.defineProperty(item.children, 'default', {
      value: item.$default, // 初始值
      writable: false, // 不可写
      enumerable: true, // 可枚举
      configurable: false // 不可配置
    })
  })
  return result
})

b. 浏览器报错

截图2

3.4 调试源码

a. 依次把这些堆栈位置打上断点

截图3

b. 再加一个断点在业务文件,当业务文件修改数据后,再启动vue文件的断点。
PS: 因为渲染的时候,vue的断点会一直多次触发。

截图4

c. 经排查, 重新渲染diff时,“新的DOM” 把 “操作栏的DOM位置” 替换了,并重置了slot数据(操作栏的children.default函数)。

截图5

3.4 修复

a. 观察源码执行过程中, 有一个方法isSameVNodeType,内部判断了key,是否同一个元素

截图6

b. 为所有插槽数据添加随机key

let defaultSlot = useSlots().default?.().map((item) => {
    item.props.key = randomString(8) + randomNumber(8)
})

3.5 修复结果

3.6 其它修复方式

待深入排查有无其它解决方案,当前方式感觉并不优


文章作者: Alex
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Alex !
  目录