记录一次升级ant-design-vue的遇见的bug

记录一次升级ant-design-vue的遇见的bug

使用版本:
"version": "2.5.2"
"ant-design-vue": "1.4.2",

vue模板内容

<template>
  <div>
    <a-table :columns="columns" :dataSource="data" :rowSelection="rowSelection" :locale="{emptyText:'sdfsd'}"/>
  </div>
</template>
<script>
  export default {
    data() {
      return {
        data:[ {
          key: 1,
          address: 'New York No. 1 Lake Park',
        }] ,
        columns: [
          {
            title: 'Address',
            dataIndex: 'address',
             '100%',
            key: 'address',
          },
        ],
        rowSelection:{
          onChange: () => {},
          onSelect: () => {},
          onSelectAll: () => {},
        }
      };
    },
  };
</script>

chrome控制台显示如下:
353BC814-E933-485C-8BF1-C280B2D

打开Sources看到是_traverse方法报错

function _traverse (val, seen) {
  var i, keys;
  var isA = Array.isArray(val);
  if ((!isA && !isObject(val)) || !Object.isExtensible(val)) {
    return
  }
  if (val.__ob__) {
    var depId = val.__ob__.dep.id;
    if (seen.has(depId)) {
      return
    }
    seen.add(depId);
  }
  if (isA) {
    i = val.length;
    while (i--) { _traverse(val[i], seen); }
  } else {
    keys = Object.keys(val);
    i = keys.length;
    while (i--) { _traverse(val[keys[i]], seen); }
  }
}

该方法存在于vue项目src/core/observer/traverse.js中,
traverse 的逻辑也很简单,它实际上就是对一个对象做深层递归遍历,因为遍历过程中就是对一个子对象的访问,会触发它们的 getter 过程,这样就可以收集到依赖,也就是订阅它们变化的 watcher
下面的代码就会触发traverse:

watch: {
  a: {
    deep: true,  // deep属性为true是关键
    handler(newVal) {
      console.log(newVal)
    }
  }
}

在 watcher 执行 get 求值的过程中有一段逻辑:

get() {
  let value = this.getter.call(vm, vm)
  // ...
  if (this.deep) {
    traverse(value)
  }
}

在traverse中打断点,打印出递归的traverse的参数name,发现异常:
358EE1FB-CF32-4EA8-86B7-5647A38B3302

如果深度遍历一个vnode节点,每一个vnode都保存有父节点和子节点的引用,遍历所有的vnode确实会导致栈溢出,但是怎么会遍历到一个vnode节点呢?
再看前面遍历到的一个对象,在ant-design-vue文档中查到是渲染rowSelection用到的,于是在ant-desing-vue的源码查到了 renderRowSelection方法:

renderRowSelection: function renderRowSelection(prefixCls, locale) {
    ......
    selectionColumn.title = selectionColumn.title 
    || h(SelectionCheckboxAll, {
        attrs: {
          store: this.store,
          locale: locale,
          data: data,
          getCheckboxPropsByItem: this.getCheckboxPropsByItem,
          getRecordKey: this.getRecordKey,
          disabled: checkboxAllDisabled,
          prefixCls: prefixCls,
    
          selections: rowSelection.selections,
          hideDefaultSelections: rowSelection.hideDefaultSelections,
          getPopupContainer: this.generatePopupContainerFunc()
        },
        on: {
          'select': this.handleSelectRow
        }
      });
    ......

SelectionCheckboxAll组件的locale属性就包含了emptyText,filterReset等,找到了位置,调试一下在renderTable中发现如下代码:

// locale至少是一个空对象
var locale = _extends({}, contextLocale, this.locale);   

if (!locale || !locale.emptyText) {
    mergedLocale.emptyText = renderEmpty(h, 'Table');
}
 ......
var columns = this.renderRowSelection(prefixCls, mergedLocale);

问题在于如果locale,经过排查international的LocaleReceiver.js,发现locale中有数据,又不存在emptyText字段,导致emptyText赋值为renderEmpty方法返回的组件,渲染SelectionCheckboxAll组件的时候,对属性遍历就会报栈溢出。

解决方案比较简单:给a-table组件加一个locale属性,同时locale的属性emptyText不为空。

之前的项目中为什么没有报错?

经过对比package.json和node_modules中的版本,发现旧项目ant-design-vue的版本是1.2.4,回退回去之后,问题消失了。

官网上为什么能够正常使用呢?

自己下载了一个浏览器版本的vue和antd-vue,测试正常,怀疑是vue版本的问题,对比了traverse方法

function _traverse (val: any, seen: SimpleSet) {
  let i, keys
  const isA = Array.isArray(val)
  if ((!isA && !isObject(val)) || Object.isFrozen(val) || val instanceof VNode) {
    return
  }
  if (val.__ob__) {
    const depId = val.__ob__.dep.id
    if (seen.has(depId)) {
      return
    }
    seen.add(depId)
  }
  if (isA) {
    i = val.length
    while (i--) _traverse(val[i], seen)
  } else {
    keys = Object.keys(val)
    i = keys.length
    while (i--) _traverse(val[keys[i]], seen)
  }
}

多了 val instanceof VNode这句判断,问题就解决了。

看看vue升级记录:
fix: do not traverse VNodes when regsitering dependencies

最终的解决方案:升级vue版本到2.6.x.
本文结束

原文地址:https://www.cnblogs.com/walkermag/p/ji-lu-yi-ci-sheng-jiantdesignvue-de-yu-jian-debug.html