我遇到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的全部意义。你会如何处理这个问题?
答案 0 :(得分:1)
我认为您的列表更新速度很慢,因为如果您在千个组件/项目中执行此操作,则迭代您的选择数组可能会很昂贵。
同样如评论key
中所述,绑定也可以提高速度(未测试的速度差异)。
但是在我的演示中,最好的结果是在创建了一个选择对象后,只需检查selected
中的属性。
最好将selected
检查为计算属性 - 它更快。
对于计算属性,选择具有1000个项目列表的项目大约需要200ms。使用选择方法需要大约450毫秒 - 不完全确定为什么它会慢得多。
以下是一个选择事件200ms的性能截图(在Chrome 59.0.3071.115(64位)中测试 - i5-6200 / 8GB RAM / Win10):
目前,这是我最快的版本。我也开始用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>