如何快速更新Vue被动内容?

时间:2017-07-11 15:31:03

标签: vue.js vuex

我遇到Vue在更新被动内容方面迟缓的问题。

我希望用户从列表中选择项目。选择项目时,应标记它们。对于测试我只是在所选项目周围设置边框。问题是,当我有多个项目时,我觉得Vue需要很长时间来更新(反应)所选项目上的课程。

所以我有一个简单的反应商店,如下所示:

export default new Vuex.Store({
  state: {
    selections: []
  },
  mutations: {
    set_selections (state, sel) {
      state.selections = sel;
    }
  }
})

我将此商店传递给我的组件,该组件呈现一个包含许多项目的简单列表。

  <p v-for="item in items">
    <span v-bind:class="{ 'is-selected': isSelected(item) }" v-on:click="onSelect(item)">
        {{ item.name }}
    </span>
  </p>

因此,每个项目都有一个唯一的ID,我在Vuex商店状态中添加/删除选项:

onSelect: function(item, event){
    let itemId = item._id;
    let sel = this.selections;
    if (sel.indexOf(itemId) !== -1) {
        var index = sel.indexOf(itemId);
        sel.splice(index, 1);
    } else {
        sel.push(itemId);
    }
    this.$store.commit("set_selections", sel);
},

其中,

selections: function() {
    return this.$store.state.selections;
}

是一个获取当前选择的计算属性。

检查项目是否被选中的方法,因此将“is-selected”类添加到DOM元素,如下所示:

isSelected: function(item){
    let itemId = item._id;
    let sel = this.selections;
    if (sel.indexOf(itemId) !== -1) {
        return true;
    }
    return false;
},

问题 当我的列表中有很多项目时,我觉得反应内容非常缓慢。当我点击一个项目时,它需要大约500ms到1秒才能标记该项目。 (注意我有很多项目)。我,我可能做错了什么?由于我循环使用v-for,因此我了解Vue必须为每个可能耗费时间的项目重新计算isSelected方法。

当然我可以直接在onClick事件上添加/删除类,但后来我放弃了使用Vue的全部意义。你会如何处理这个问题?

1 个答案:

答案 0 :(得分:1)

我认为您的列表更新速度很慢,因为如果您在千个组件/项目中执行此操作,则迭代您的选择数组可能会很昂贵。

同样如评论key中所述,绑定也可以提高速度(未测试的速度差异)。

但是在我的演示中,最好的结果是在创建了一个选择对象后,只需检查selected中的属性。

最好将selected检查为计算属性 - 它更快。

对于计算属性,选择具有1000个项目列表的项目大约需要200ms。使用选择方法需要大约450毫秒 - 不完全确定为什么它会慢得多。

以下是一个选择事件200ms的性能截图(在Chrome 59.0.3071.115(64位)中测试 - i5-6200 / 8GB RAM / Win10): Screenshot 200ms

目前,这是我最快的版本。我也开始用0.5到1秒来显示选择。

请查看下面的演示或fiddle

const listItem = {
	props: ['item'],
	template: `
  	<li :class="{ 'is-selected': selected }" @click="$emit('selected', item)">
        {{ item.name }}
    </li>
  `,
  computed: {
  	...Vuex.mapState(['selections']),
    selected () {
    	// 200ms time to mark item for click with 1000 list items - clicked Test 326
    	return this.selections[this.item.id] !== undefined; // object property check is fast
    }
  },
  methods: {
  	selected () {
    	// 450ms to mark selection
    	//console.log('selected', this.selections, !!this.selections[this.item.id]);
      // slightly slower than computed property
    	return this.selections[this.item.id] !== undefined; // object property check is fast
    	// array --> slow because another iteration for each component required
    	//this.selections.indexOf(this.item) !== -1
    }
  }
};

const list = {
	props: ['items'],
	template: `
  	<ul>
    	<list-item 
      	v-for="item in items" 
        :item="item" 
        @selected="select"
        :key="item.id"></list-item>
  	</ul>
  `,
  components: {
  	listItem
  },
  methods: {
  	select(item) {
    	this.$store.commit('set_selection', item)
    }
  }
};

const store = new Vuex.Store({
	state: {
    selections: []
  },
  mutations: {
    set_selection (state, item) {
      //state.selections = sel;
      console.log('clicked', item, state.selections[item.id]);
      
      if (state.selections[item.id]) {
      	// in object --> remove from selection
      	//let  {[item.id]: deleted, ...newState} = state;
        state.selections[item.id] = undefined;
      }
      else {
      	// not in object --> add item
      	state.selections = {
        	...state.selections,
        	[item.id]: item
        }
      }
      // console.log(state.selections, !!state.selections[item.id]);
      
      /*
      --> array approach is slow
      if (state.selections.indexOf(item) === -1) 
      {
      	// not in list --> push to list
      	state.selections.push(item);
      }
      else {
      	// in list --> remove selection
      	state.selections.pop(item);
      }*/
    }
  }
})

function createItems(count) {
	let items = [];
  for(let i=0; i< count; i++) {
  	items.push({
    	id: i,
      name: 'Test ' + i
    });
  }
  return items;
}

new Vue({
	el: '#app',
  store,
  data () {
  	let items = createItems(1000);
  	return {
    	items
    };
  },
  components: {
  	list
  }
})
.is-selected {
   background-color: red;
   border: 1px solid red;
}

ul {
  list-style-type: none;
}
li {
  cursor: pointer;
}

li:hover {
  border: 1px solid gray;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.3.4/vue.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vuex/2.3.1/vuex.js"></script>
<div id="app">
  <list :items="items"></list>
</div>