【问题标题】:How to make Vue reactive content update quickly?如何让 Vue 响应式内容快速更新?
【发布时间】:2017-12-15 18:26:40
【问题描述】:

我有一个问题是 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;
},

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

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

【问题讨论】:

  • 这几乎总是通过将列表项重构为一个组件来解决,这样 Vue 就不必重绘整个列表。
  • 但是你会怎么做呢?我仍然需要遍历每个项目才能呈现它们?如果“isSelected”是在组件中计算出来的,它仍然会在更新时对每个项目调用“isSelected”。
  • 当然,但只有更改的组件需要重绘。并非列表中的所有内容。用小提琴/笔将一个工作示例放在一起,我们可以进行重构。
  • 确保将key 添加到您正在使用v-for 的元素中。这可能会解决问题。
  • 谢谢。我会试试的。那么如果我不把每个项目都放在一个组件中呢? Vue 居然重新渲染了整个列表?

标签: vue.js vuex


【解决方案1】:

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

同样如cmets中提到的key绑定也可以提高速度(速度差异未测试)。

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

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

对于计算属性,选择包含 1000 个项目的项目大约需要 200 毫秒。使用选择方法大约需要 450 毫秒 - 不完全确定为什么它会这么慢。

这是一个选择事件 200 毫秒的性能截图(在 Chrome 59.0.3071.115 (64bit) - 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>

【讨论】:

  • 感谢您的回答!我将数组更改为字典,然后我还必须使用 Vue.set(state.selections, item.id, item) 让 Vue 对更改做出反应。现在速度有点快了。我修改了你的小提琴:jsfiddle.net/9o17yheq/1 但是我仍然不明白为什么它是字典时选择了大约 5 个项目后会这么慢?
  • 不客气。我看不到在 5 次或更多选择后它变慢了。 (我没有测量时间,但它似乎并不慢)。您在哪个浏览器中看到过这种行为?
  • 这就是真正加快速度的方法。启动组件时预分配字典中的所有键。这使一切变得不同。我现在根本看不到任何滞后-jsfiddle.net/9o17yheq/2
  • 我只在 Google Chrome 版本 59.0.3071.115(64 位)中测试过
  • 哇,酷。通过预分配,它可以完美运行。没有延误。
猜你喜欢
  • 1970-01-01
  • 2022-12-03
  • 1970-01-01
  • 2012-01-08
  • 2021-05-23
  • 2016-02-25
  • 2016-10-19
  • 1970-01-01
  • 2013-03-24
相关资源
最近更新 更多