简体   繁体   中英

Weird behaviour of methods in Vue.js

I have one data variable in my app, It is message , And also I have one method in methods which performs some cryptographic algorithms. Here is my code.

export default {
  data: () => ({
    message: ""
  }),
  methods: {
    click() {
      this.message = "Hello";
      console.log("this.message ", this.message); // Prints "Hello"

      // takes around 8 seconds
      var encryptedPassphrase = generateKeystore();
      console.log("this.message ", this.message); // Prints "Hello"
    }
  }
};

Above message variable I'm displaying in HTML tag, And method click gets called from Vuetify button. Below is HTML code,

<div>
    <p>{{message}}</p>
    <v-btn @click="click">Click</v-btn>
</div>

Issue
Now Problem is When click method is called first task it does is updates message variable, But this message variable update reflects in HTML after the full function execution is completed. So In click function next task is cryptographic computation which takes around 8 seconds, After this completes message reflects in HTML. I have no idea what is going on here. Just mentioning I'm using webpack here.

Update

  <v-btn @click="update(); click();">Click</v-btn>

Even this doesn't work, Here update method updates message variable, It updates after click function is completed.

If you want the message data field that's changed in the click method to reflect in the html before the method is complete, you have to wrap the cryptographic function in a Vue.nextTick . That will ensure that the logic inside the nextTick will execute on the next DOM update cycle. This will allow the DOM to be updated with the latest value for message and then it will execute the cryptographic function. Otherwise, Vue will wait for the click function to complete prior to the changes being reflected in the DOM .

Vue.nextTick(function() { //cryptographic function })

https://v2.vuejs.org/v2/api/#Vue-nextTick

Long story short, in the case, you should use Web Worker for intensive tasks such as your generateKeystore function. The reason your message didn't get rendered is that only one piece of Javascript ever executing at a time, basically your generateKeystore blocks the Vue rendering (watcher etc) until it finishes.

I've created a js fiddle to demonstrate the Vue - Web Worker usage https://jsfiddle.net/Fourzero/t9L1g2hj/17/ . You can also find the code below. In this example, the click handler has the same problem your described (changed message won't render until slow function is done), the click2 method handles it in a way that the message 'hello' get rendered immediately, and the slow function starts executing (takes a few seconds) and will return value once finished.

Html

<script id="worker1" type="javascript/worker">

  // This script won't be parsed by JS engines because its type is javascript/worker.
  self.onmessage = function(e) {
    console.log('slow function started execution inside worker with parameter: ', e.data)
    // execute slow function
    var sum = 1;
        for (let i = 1; i < 10000000000; i++) {
        sum = sum + i;
    }
    console.log('DONE!!slow function finished inside worker with result: ' + sum)
    self.postMessage(sum);
  };

</script>


<div id="app">
    <p ref="message">{{message}}</p>
    <p >slowFunctionResult: {{slowFunctionResult}}</p>
    <button @click="click">Click</button>
    <button @click="click2">Click (Run in worker)</button>
</div>

Javascript

var blob = new Blob([
  document.querySelector('#worker1').textContent
], { type: "text/javascript" })

// Note: window.webkitURL.createObjectURL() in Chrome 10+.
var worker = new Worker(window.URL.createObjectURL(blob));


new Vue({
  el: '#app',
  data: {
    message: 'xx',
    slowFunctionResult: ''
  },

  methods: {
    click() {
      this.message = 'hello';
      console.log("slow function start")
      this.slowFunction();
      console.log("slow function done")

      // the next tick won't happen until the slow function finished
    },

    click2() {
      this.message = 'hello';

      let that = this;
      worker.onmessage = function(e) {
        console.log("Received result from worker!: " + e.data);
        that.slowFunctionResult = e.data;
      }
      // DOM should be updated before the slow function execution finished
      worker.postMessage(this.message); // Start the worker.
    },


    slowFunction() {
        var sum = 1;
        for (let i = 1; i < 10000000000; i++) {
            sum = sum + i;
        }
    }
  }
})

Console output of clicking the Click (Run in worker) button

slow function started execution inside worker with parameter:  hello
// after a few seconds
DONE!!slow function finished inside worker with result: 49999999990067860000
Received result from worker!: 49999999990067860000

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM