記錄一次升級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',
            width: '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.
本文結束

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM