使用 v-model 更改单个值 / full table 被重绘

changing a single value using v-model / full table is redrawn

我正在构建一个 editable table,当行数开始达到 运行 的 100 行时,它开始爬行停止。这促使我调查发生了什么。

在下面的例子中,当改变输入中的值时,整个table被重新绘制,并且ifFunction()函数被触发了4次。

为什么会这样? Vue 不应该能够重绘相应的单元格吗?我是不是按键绑定有问题?

<template>
    <div id="app">

        <table border="1" cellpadding="10">
            <tr v-for="(row, rowKey) in locations" :key="`row_+${rowKey}`">
                <td v-for="(column, columnKey) in row" :key="`row_+${rowKey}+column_+${columnKey}`">
                    <span v-if="ifFunction()">{{ column }}</span>
                </td>
            </tr>
        </table>

        <input v-model="locations[0][1]">

    </div>
</template>

<script>

    export default {

        data() {
            return {
                locations: [
                    ["1","John"],
                    ["2","Jake"]
                ], // TODO : locations is not generic enough.
            }
        },

        methods: {
            ifFunction() {
                console.log('ifFunction');
                return true;
            },
        }
    }
</script>

data 属性 定义反应元素 - 如果您更改其中的一部分,将重新计算依赖于那部分 data 的所有内容。

您可以将 computed 属性用于 "cache" 值,并且只更新真正需要更新的那些。

我重建了您的组件,因此可以在整个过程中使用计算属性:创建了一个 cRow 和一个 cCell 组件("custom row" 和 "custom cell")并重新构建了 table 来自这些组件。行和单元格组件每个都有一个计算的 属性 "proxies" prop 到模板 - 因此也缓存它。

在第一次渲染时,您会看到四次 ifFunction()(这是基于 Vue[=35 中的 data 属性 的单元格数量=] 实例),但是如果您使用输入字段更改值,您只会看到一次(每次更新;您可能必须单击 "Full page" 才能更新值)。

Vue.component('cCell', {
  props: {
    celldata: {
      type: String,
      required: true
    },
    isInput: {
      type: Boolean,
      required: true
    },
    coords: {
      type: Array,
      required: true
    }
  },
  data() {
    return {
      normalCellData: ''
    }
  },
  watch: {
    normalCellData: {
      handler: function(value) {
        this.$emit('cellinput', {
          coords: this.coords,
          value
        })
      },
      immediate: false
    }
  },
  template: `<td v-if="ifFunction()"><span v-if="!isInput">{{normalCellData}}</span> <input v-else type="text" v-model="normalCellData" /></td>`,
  methods: {
    ifFunction() {
      console.log('ifFunction');
      return true;
    },
  },
  mounted() {
    this.normalCellData = this.celldata
  }
})
Vue.component('cRow', {
  props: {
    rowdata: {
      type: Array,
      required: true
    },
    rownum: {
      type: Number,
      required: true
    }
  },
  template: `
  <tr>
    <td
      is="c-cell"
      v-for="(item, i) in rowdata"
      :celldata="item"
      :is-input="!!(i % 2)"
      :coords="[i, rownum]"
      @cellinput="reemit"
     ></td>
  </tr>`,
  methods: {
    reemit(data) {
      this.$emit('cellinput', data)
    }
  }
})
new Vue({
  el: "#app",
  data: {
    locations: [
      ["1", "John"],
      ["2", "Jake"]
    ], // TODO : locations is not generic enough.
  },
  methods: {
    updateLocations({
      coords,
      value
    }) {
      // creating a copy of the locations data attribute
      const loc = JSON.parse(JSON.stringify(this.locations))
      loc[coords[1]][coords[0]] = value
      // changing the whole locations data attribute to preserve
      // reactivity
      this.locations = loc
    }
  }
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<div id="app">
  <table border="1" cellpadding="10">
    <tbody>
      <tr v-for="(row, i) in locations" is="c-row" :rowdata="row" :rownum="i" @cellinput="updateLocations"></tr>
    </tbody>
  </table>
  <!-- <input v-model="locations[0][1]">
  <input v-model="locations[1][1]">-->
  {{locations}}
</div>