v-for上的v-show会产生不必要的返回过渡

时间:2019-01-10 14:28:00

标签: performance vue.js transition

我在大型阵列上遇到v-for的性能问题。 问题在于,每次更换过滤器时,v-for都会重新生成所有过滤的行。我们通过对v-show元素进行v-ifv-for的组合来解决此问题。效果很好,每次过滤数组时,我们可以节省约700毫秒的重新绘制时间。

这样做的缺点是transition-group(香蕉)会消失

在下面的示例中,您可以通过过滤某些内容,然后清除过滤器来查看此操作。通过使用v-show,这些元素从左上角开始进行怪异的过渡。这不是我所期望的。

new Vue({
  el: "#app",
  data() {
    return {
      filter: null,
      people: [],
      useVshow: true,
      settings: {
        opacityOnly: false
      }
    }
  },

  created() {
    /* Irerelevant for issue - Just some fancy example code: */
    let arr = [];
    for (let i = 0; i < 500; ++i) {
      arr.push({
        id: i,
        name: `person ${i}`,
        age: i,
        face: Math.floor(Math.random() * 9),
        show: true
      });
    }
    this.people = arr;
  },

  computed: {
    filteredPeople: function() {
      return this.filter ? this.people.filter(person => person.name.includes(this.filter)) : this.people
    }
  },

  watch: {
    filter() {
      for (const i of this.people) {
        i.show = this.filter ? i.name.includes(this.filter) : true;
      }
    }
  }
})
.wrapper {
  position: relative;
}

.card {
  transition: all 800ms ease-in-out;
}

.opacity-only {
  transition: opacity 800ms ease-in-out;
}

.list-leave-active {
  position: absolute;
}

.list-enter,
.list-leave-to {
  opacity: 0;
  transform: translateY(30px);
}

.container {
  max-width: 520px;
  margin: 0 auto;
}


/* Irerelevant for issue - Just some fancy example code: */

input {
  width: 150px;
}

.card {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  margin: 10px 0;
}

.card .info p {
  margin: 5px 10px;
}

.card .info .name {
  font-weight: 600;
  color: #48484c;
}

.card .info .age {
  font-size: 14px;
  color: #656565;
}

.card .avatar {
  height: 80px;
  border-radius: 50%;
  border: 5px solid #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.5.17/vue.js"></script>

<main id="app">
  <div class="container">
    <label for="filter">Filter by name:</label>
    <input name="filter" v-model="filter"> Use v-show: <input type="checkbox" v-model="useVshow">
    <br>Only trantition opacity: <input type="checkbox" v-model="settings.opacityOnly">

    <transition-group name="list" tag="div" class="wrapper">

      <template v-if="useVshow">
        <div v-for="person in people" v-show="person.show" :key="person.id" class="card" :class="{'opacity-only': settings.opacityOnly}">
          <img class="avatar" :src="`https://randomuser.me/api/portraits/lego/${person.face}.jpg`">
          <div class="info">
            <p class="name">{{person.name}}</p>
            <p class="age">{{person.age}} years old</p>
          </div>
        </div>
      </template>

      <template v-else>
        <div v-for="person in filteredPeople" :key="person.id" class="card">
          <img class="avatar" :src="`https://randomuser.me/api/portraits/lego/${person.face}.jpg`">
          <div class="info">
            <p class="name">{{person.name}}</p>
            <p class="age">{{person.age}} years old</p>
          </div>
        </div>
      </template>

    </transition-group>
  </div>
</main>

为什么会发生这种情况,并且有什么方法可以使返回转换与没有v-show的情况一样?

EDIT:添加了选项,仅用于@Sitethief指出的过渡不透明度

1 个答案:

答案 0 :(得分:0)

我认为问题在于transition: all对所有CSS属性都应用了过渡。其中包括position: absolute;,如果您在切换滤镜时在开发人员中进行检查,则可以实时看到它。如果您将转换更改为transition: opacity 800ms ease-in-out;之类的内容,列表项将不会在左上角开始。