用于计算属性重新渲染的 Vue.js 加载指示器 [英] Vue.js loading indicator for computed property re-render

查看:32
本文介绍了用于计算属性重新渲染的 Vue.js 加载指示器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个用 Vue.js 编写的小型 Web 应用程序,我试图在过滤和重新呈现项目列表时在页面上显示加载指示器.该列表不是异步获取的,它只是基于某些过滤器字段的计算属性中的项目列表,并随着过滤器的更改而更新.在 UI 中更新列表之前有几秒钟的延迟,因为列表有大约 1400 个项目,每个项目都是卡片样式的组件,带有图像、按钮、图标和一些文本.我试图在过滤器更改和新列表呈现之间显示正在加载..."消息,但似乎无法将其显示给用户.

我整理了一个简化的小提琴作为示例:https://jsfiddle.net/zm4z9657/1/

这是代码片段:

new Vue({el: '#app',数据: {甚至:假的,加载:假},计算:{数字:函数(){this.render();返回 this.even ?_.range(0,100000,2) : _.range(0,100000);}},方法: {重新渲染:函数(){var self = this;/* self.loading = true */;//this.$nextTick(() => {self.loading = true;console.log('重新渲染开始')this.$nextTick(() => {self.loading = false;console.log('重新渲染结束')})//})},}})

<script src="https://unpkg.com/vue"></script><script src="https://unpkg.com/lodash"></script><div id="应用程序"><input type="checkbox" value="1" v-model="even"/>甚至?<div v-if="loading" id="loading">正在加载...

<div v-else id="已加载"><div><div v-for="number in numbers" :key="number">{{number}}</div>

示例显示了数字 1-100000 的列表,当您单击偶数?"时复选框过滤器,它会导致列表只更新偶数.100000 用于显示新列表渲染的轻微延迟.如果您检查 DOM,您可以看到在选中/取消选中复选框后,<div id="loading"> 会在 DOM 中弹出一小段时间,然后切换到新的

中的列表.尽管加载

显示在 DOM 中,但它永远不会在 UI 中更新.

我有我希望在调用计算属性时显示加载"的代码,当列表完成渲染时,隐藏它并显示列表.为什么这不起作用?为什么 DOM 会改变,但 UI 却永远不会更新?UI 是否没有更新,因为所有动态元素都在同一个组件中,并且在列表完成渲染之前它不会刷新?有没有更好的方法来实现这一目标?

谢谢!

解决方案

您正在执行一个长时间的同步过程.这些类型的进程会占用 UI 线程,即您不能在选项卡中执行任何操作,也不能在此期间更新 DOM.

要像您一样执行长时间的流程,您应该将其放入 网络工作者.使用 Messaging api,您可以向工作人员发送一条消息,告诉它制作列表,并在完成后发回一条消息.这将使您的流程异步并且不会占用 UI.

旁注:您可能需要考虑使用分页来显示如此大的列表.由于隐藏/显示这么多元素也会导致浏览器锁定.

html

甚至?

worker.js

importScripts("lodash.js");self.addEventListener('message', function(e) {var 数据 = e.data;如果(数据.命令==开始"){让列表 = data.even ?_.range(0,100000,2) : _.range(0,100000);self.postMessage({"list":list});}}, 错误的);

app.js

var app,msgBus;var worker = new Worker('worker.js');//监听从worker返回的消息worker.addEventListener('message', function(e) {var 数据 = e.data;如果(数据.列表){//向你的 vue 应用程序发送一条消息,该列表//被制作msgBus.$emit('listGenerated',data.list);}}, 错误的);//为事件单独空vuemsgBus = new Vue();应用程序 = 新的 Vue({el: '#app',安装:功能(){//监听一个listGenerated事件//将数字设置为传递的列表msgBus.$on("listGenerated",(list)=>{this.numbers = 列表;this.loading = false;});},方法:{onEvenChanged:函数(){this.loading = true;//向worker发送启动命令//也传递偶数属性this.$nextTick(()=>{worker.postMessage({command:"start",even:this.even});});}}});

plunker 演示

I have a small web application written in Vue.js and I am trying to show a loading indicator on the page while a list of items is being filtered and re-rendered. The list is not fetched asynchronously, it is just a list of items from a computed property, based on some filter fields and is updated as the filters are changed. There is a delay of a few seconds before the list is updated in the UI because the list has about 1400 items and each item is a card-style component with an image, a button, icons, and some text. I have attempted to show a "Loading..." message between filter changes and new list rendering, but can't seem to get it to show to the user.

I have put together a simplified fiddle as an example: https://jsfiddle.net/zm4z9657/1/

EDIT: Here is the code snippet:

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>

The sample shows a list of numbers 1-100000, and when you click the "even ?" checkbox filter, it causes the list to update with only even numbers. 100000 was used to show the slight delay in the rendering of the new list. If you inspect the DOM, you can see that after checking/unchecking the checkbox, the <div id="loading"> pops into the DOM for a short bit, then changes over to the new list inside <div id="loaded">. Although the loading <div> is shown in the DOM, it is never updated in the UI.

I have code I hoped would show "loading" when the computed property is called, and when the list is done rendering, hide it and show the list. Why is this not working? Why does the DOM change, but the UI is never updated? Is the UI not updating because all the dynamic elements are in the same component and it is not flushed until the list is done rendering? Is there a better way to achieve this?

Thanks!

解决方案

You are doing a long synchronous process. These types of processes tie up the UI thread, ie you cannot do anything within the tab nor can the DOM be updated during that time.

To do long processes like you are doing you should put it into a Web Worker. Using the Messaging api you would then send a message to the worker telling it to make the list, and it would send a message back when it is done. This would make your process asynchronous and not tie up the UI.

Side note: you might want to think about using pagination for displaying such a large list. As the hiding/showing of that many elements will also cause the browser to lock up.

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});
            });
          }
        }
      });

Demo on plunker

这篇关于用于计算属性重新渲染的 Vue.js 加载指示器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
前端开发最新文章
热门教程
热门工具
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆