vue项目中table数据更新视图未更新原因及解决方法

186 阅读1分钟

现象:

<e-table :data="noRelateAppList"  style="width: 100%" v-loading="loading">
<e-table-column label="名称" width="200" show-overflow-tooltip>
  <template slot="header" slot-scope="{ row }">
    <e-checkbox
      v-model="checkAllNoRelate"
      :indeterminate="indeterminateNoRelate"
      @change="handleCheckAllNoRelateChange"
    ></e-checkbox
    >名称
    <div style="display: none">{{ row }}</div>
  </template>
  <template slot-scope="{ row }">
    <e-checkbox v-model="row.checked" @change="noRelateChange"></e-checkbox>
    {{ row.name }}
  </template>
</e-table-column>
...
</e-table>

<script>
data() {
    return {
      loading: false,
      size: 20,
      page: 1,
      total: 20,
      checkAllNoRelate: false,
      indeterminateNoRelate: false,
      noRelateAppListChecked: [], // 可选关联应用列表-已选状态
      noRelateAppList: [],
    }
  },
  mounted(){
    this.getList()
  },
    methods:{
      getList(){
         this.noRelateAppList = this.list.map((item, index) => {
              item.checked = false
              return item
            })
          }
    }
</script>

点击 checkbox 后 ,noRelateAppList 中点击那一项 checked值更新,视图checkbox 未重新渲染,checkbox组件prop未更新。

原因:

export class Observer {
  value: any;
  dep: Dep;
  vmCount: number; // number of vms that have this object as root $data

  constructor (value: any) {
    this.value = value
    this.dep = new Dep()
    this.vmCount = 0
    def(value, '__ob__', this)
    if (Array.isArray(value)) {
      if (hasProto) {
        protoAugment(value, arrayMethods)
      } else {
        copyAugment(value, arrayMethods, arrayKeys)
      }
      this.observeArray(value)
    } else {
      this.walk(value)
    }
  }

  /**
   * Walk through all properties and convert them into
   * getter/setters. This method should only be called when
   * value type is Object.
   */
  walk (obj: Object) {
    const keys = Object.keys(obj)
    for (let i = 0; i < keys.length; i++) {
      defineReactive(obj, keys[i])
    }
  }

  /**
   * Observe a list of Array items.
   */
  observeArray (items: Array<any>) {
    for (let i = 0, l = items.length; i < l; i++) {
      observe(items[i])
    }
  }
}
function observe (value, asRootData) {
  if (!isObject(value) || value instanceof VNode) {
    return
  }
  var ob;
  if (hasOwn(value, '__ob__') && value.__ob__ instanceof Observer) {
    ob = value.__ob__;
  } else if (
    shouldObserve &&
    !isServerRendering() &&
    (Array.isArray(value) || isPlainObject(value)) &&
    Object.isExtensible(value) &&
    !value._isVue
  ) {
    ob = new Observer(value);
  }
  if (asRootData && ob) {
    ob.vmCount++;
  }
  return ob
}

数组对象list 被 赋值给数组noRelateAppList ,在observe(item)时,由于 初始化时list中的item已经添加了__ob__属性,所以noRelateAppList item 执行 observe时,不会重新执行 new Observe,所以新添加的属性没有执行 defineReactive 方法,getter setter 没有被劫持,因此vue监听不到checked属性的变化,checkbox组件未更新。

解决方案:

  • 使用Vue.$set() 给数组重新赋值
  const temp = this.list.map((item, index) => {
    item.checked = false
    return item
  })
  this.$set(this.noRelateAppList, temp)
  • 深拷贝一个数组对象,将原数组置空,再赋值给原数组
  const test12 = _.cloneDeep(this.list)
  const temp = test12.map((item, index) => {
    item.checked = false
    return item
  })
  this.noRelateAppList = temp
  • map时 返回一个新对象
  this.noRelateAppList = test.map((item, index) => {
    item = {...item}
    item.checked = false
    return item
  })