我有一个用Vue.js编写的小型Web应用程序,我正在尝试在页面上显示加载指示器,同时过滤和重新呈现项目列表。该列表不是异步提取的,它只是来自计算属性的项目列表,基于某些过滤器字段,并在更改过滤器时更新。在UI中更新列表之前有几秒钟的延迟,因为该列表包含大约1400个项目,并且每个项目都是具有图像,按钮,图标和一些文本的卡片样式组件。我试图展示一个" Loading ..."过滤器更改和新列表呈现之间的消息,但似乎无法向用户显示。
我把一个简化的小提琴放在一起作为例子:https://jsfiddle.net/zm4z9657/1/
编辑:以下是代码段:
new Vue({
el: '#app',
data: {
even: false,
loading: false
},
computed: {
numbers: function() {
this.rerender();
return this.even ? _.range(0,100000,2) : _.range(0,100000);
}
},
methods: {
rerender: function() {
var self = this;
/* self.loading = true */;
//this.$nextTick(() => {
self.loading = true;
console.log('re-render start')
this.$nextTick(() => {
self.loading = false;
console.log('re-render end')
})
//})
},
}
})

<script src="https://unpkg.com/vue"></script>
<script src="https://unpkg.com/lodash"></script>
<div id="app">
<input type="checkbox" value="1" v-model="even" /> even?
<div v-if="loading" id="loading">
Loading...
</div>
<div v-else id="loaded">
<div>
<div v-for="number in numbers" :key="number">{{number}}</div>
</div>
</div>
</div>
&#13;
示例显示数字列表1-100000,当您点击&#34;偶数?&#34;复选框过滤器,它使列表仅使用偶数更新。 100000用于显示新列表呈现的轻微延迟。如果您检查DOM,您可以看到在选中/取消选中该复选框后,<div id="loading">
会弹出一小段DOM,然后切换到<div id="loaded">
内的新列表。尽管在DOM中显示了加载<div>
,但它永远不会在UI中更新。
我有代码我希望能展示&#34; loading&#34;当调用计算属性时,当列表完成渲染时,隐藏它并显示列表。为什么这不起作用?为什么DOM会发生变化,但UI永远不会更新? UI是否未更新,因为所有动态元素都在同一个组件中,并且在列表完成渲染之前不会刷新它?有没有更好的方法来实现这一目标?
谢谢!
答案 0 :(得分:2)
您正在进行长时间的同步过程。这些类型的进程占用UI线程,即您不能在选项卡中执行任何操作,也不能在此期间更新DOM。
要像你一样做很长的过程,你应该把它放到Web Worker。然后,使用Messaging api,您将向工作人员发送一条消息,告诉它创建列表,并在完成后发送消息。这将使您的流程异步,而不会占用UI。
旁注:你可能想考虑使用分页来显示如此大的列表。由于隐藏/显示许多元素也会导致浏览器锁定。
HTML
<input type="checkbox" value="1" v-model="even" @change="onEvenChanged" /> even?
worker.js
importScripts("lodash.js");
self.addEventListener('message', function(e) {
var data = e.data;
if(data.command == "start"){
let list = data.even ? _.range(0,100000,2) : _.range(0,100000);
self.postMessage({"list":list});
}
}, false);
app.js
var app,msgBus;
var worker = new Worker('worker.js');
//listen for messages coming back from the worker
worker.addEventListener('message', function(e) {
var data = e.data;
if(data.list){
//emit a message to your vue app that the list
//was made
msgBus.$emit('listGenerated',data.list);
}
}, false);
//separate empty vue for events
msgBus = new Vue();
app = new Vue({
el: '#app',
mounted:function(){
//Listen for a listGenerated event
//set numbers to the passed list
msgBus.$on("listGenerated",(list)=>{
this.numbers = list;
this.loading = false;
});
},
methods:{
onEvenChanged:function(){
this.loading = true;
//send the start command to the worker
//passing also the even property
this.$nextTick(()=>{
worker.postMessage({command:"start",even:this.even});
});
}
}
});