zoukankan      html  css  js  c++  java
  • 记录一次升级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.
    本文结束

  • 相关阅读:
    Next Permutation
    Substring with Concatenation of All Words
    Divide Two Integers
    Remove Duplicates from Sorted Array
    3sum closest
    ThreadPoolExecutor参数与拒绝策略
    多线程情况下ArrayList 如何解决线性安全问题
    ArrayList扩容机制jdk1.8
    SpringCloud--工作流程(好文)
    Java面试——TCP与HTTP
  • 原文地址:https://www.cnblogs.com/walkermag/p/ji-lu-yi-ci-sheng-jiantdesignvue-de-yu-jian-debug.html
Copyright © 2011-2022 走看看