代码之家  ›  专栏  ›  技术社区  ›  haferje

Vue。用于计算属性重新渲染的js加载指示器

  •  1
  • haferje  · 技术社区  · 6 年前

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

    我以一把简化的小提琴为例: 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>

    该示例显示了1-100000的数字列表,当您单击“偶数”时复选框过滤器,它会导致列表仅更新为偶数。100000用于显示新列表呈现过程中的轻微延迟。如果检查DOM,可以看到在选中/取消选中复选框后 <div id="loading"> 在DOM中弹出一小段时间,然后切换到内部的新列表 <div id="loaded"> .虽然装载 <div> 在DOM中显示,在UI中永远不会更新。

    我有一些代码希望在调用computed属性时显示“loading”,在列表渲染完成后,隐藏它并显示列表。为什么这不起作用?为什么DOM会改变,但UI永远不会更新?UI是否没有更新,因为所有动态元素都在同一个组件中,并且在列表渲染完成之前它不会被刷新?有没有更好的方法来实现这一点?

    谢谢

    1 回复  |  直到 6 年前
        1
  •  2
  •   Patrick Evans    6 年前

    您正在执行一个长的同步过程。这些类型的进程会占用UI线程,也就是说,在此期间,您无法在选项卡内执行任何操作,也无法更新DOM。

    要像你现在这样做很长的过程,你应该把它放在一个 Web Worker 。然后,使用消息api,您将向工作人员发送一条消息,告诉其创建列表,完成后,工作人员将返回一条消息。这将使您的流程异步,而不会占用UI。

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

    html

    <input type="checkbox" value="1" v-model="even" @change="onEvenChanged" /> even?
    

    工人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);
    

    应用程序。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