Calculate properties and listener details

  • 2021-12-04 18:03:53
  • OfStack

Directory 1, Calculated Attribute 1.1 Basic Example 1.2 Calculated Attribute Cache vs Method 1.3 Calculated Attribute vs Listening Attribute 1.4 Calculated Attribute setter2, Listener

1. Calculate attributes

Expressions in templates are very convenient, but they were originally designed for simple operations. Putting too much logic into a template makes it overweight and difficult to maintain.

For example:


<div id="example">
  {{ message.split('').reverse().join('') }}
</div>


In this place, templates are no longer simple declarative logic. You have to look at it for a while to realize that this is the variable you want to display message Gets or sets the flipped string of the. When you want to include this flip string in multiple places in the template, it will be more difficult to handle.

Therefore, for any complex logic, you should use computed attributes.

1.1 Basic Examples


<div id="example">
  <p>Original message: "{{ message }}"</p>
  <p>Computed reversed message: "{{ reversedMessage }}"</p>
</div>
var vm = new Vue({
  el: '#example',
  data: {
    message: 'Hello'
  },
  computed: {
    //  Object of the computed property  getter
    reversedMessage: function () {
      // `this`  Point  vm  Instances 
      return this.message.split('').reverse().join('')
    }
  }
})


Results:

Original message: "Hello"

Computed reversed message: "olleH"

Here we declare a computed attribute reversedMessage . The function we provide will be used as property vm.reversedMessage Adj. getter Function:


console.log(vm.reversedMessage) // => 'olleH'
vm.message = 'Goodbye'
console.log(vm.reversedMessage) // => 'eybdooG'


You can open the browser console and modify the vm . vm.reversedMessage The value of always depends on the value of vm.message The value of.

You can bind like ordinary property 1. Bind the calculation attribute in the template. Vue knows vm.reversedMessage Depend on vm.message , so when vm.message When a change occurs, all dependencies vm.reversedMessage The binding of is also updated. And best of all, we have created this dependency declaratively: the getter The function is free of side effects (side effect), which makes it easier to test and understand.

1.2 vs Method for Computing Attribute Cache

You may have noticed that we can achieve the same effect by calling methods in expressions:


<p>Reversed message: "{{ reversedMessage() }}"</p>
//  In a component 
methods: {
  reversedMessage: function () {
    return this.message.split('').reverse().join('')
  }
}


We can define the same 1 function as a method instead of a calculated property. The final result of the two methods is exactly the same. However, the difference is that computed attributes are cached based on their responsive dependencies. They are re-evaluated only when the related responsive dependencies change. This means that as long as message Haven't changed yet, multiple visits reversedMessage The evaluated property immediately returns the previous evaluation results without having to execute the function again.

This also means that the following computed properties will not be updated because Date. now () is not a responsive dependency:


computed: {
  now: function () {
    return Date.now()
  }
}


In contrast, whenever a re-rendering is triggered, the calling method will always execute the function again.

Why do we need caching? Suppose we have a computational attribute with high performance overhead A It needs to traverse a huge array and do a lot of calculations. Then we may have other computed attributes that depend on A . Without caching, we will inevitably execute many times A Adj. getter ! If you don't want to have cache, please use method instead.

1.3 Computed Attributes vs Listening Attributes

Vue Provides a more general way to observe and respond Vue Data changes on the instance: listen for attributes. When you have one data that needs to change with other data, you are easy to abuse it watch --especially if you've used it before AngularJS . However, it is generally better to use computed attributes instead of imperative watch Callback.

Consider this example:


<div id="demo">{{ fullName }}</div>
var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar',
    fullName: 'Foo Bar'
  },
  watch: {
    firstName: function (val) {
      this.fullName = val + ' ' + this.lastName
    },
    lastName: function (val) {
      this.fullName = this.firstName + ' ' + val
    }
  }
})


The above code is imperative and repetitive. Compare it to the version of the calculated property:


var vm = new Vue({
  el: '#demo',
  data: {
    firstName: 'Foo',
    lastName: 'Bar'
  },
  computed: {
    fullName: function () {
      return this.firstName + ' ' + this.lastName
    }
  }
})

Much better, isn't it?

1.4 setter for Calculated Attributes

getter is the default calculation attribute, but you can also provide 1 setter when necessary:


// ...
computed: {
  fullName: {
    // getter
    get: function () {
      return this.firstName + ' ' + this.lastName
    },
    // setter
    set: function (newValue) {
      var names = newValue.split(' ')
      this.firstName = names[0]
      this.lastName = names[names.length - 1]
    }
  }
}
// ...

Run again now vm.fullName = 'John Doe' When, setter Will be called, vm.firstName And vm.lastName Will be updated accordingly.

2. Listeners

Although computed properties are more appropriate in most cases, a custom listener is sometimes required. That's why Vue Pass watch Option provides a more general way to respond to changes in data. This is most useful when you need to perform asynchronous or costly operations as the data changes.

For example:


<div id="watch-example">
  <p>
    Ask a yes/no question:
    <input v-model="question">
  </p>
  <p>{{ answer }}</p>
</div>
<!--  Because  AJAX  The ecology of libraries and general tools is quite rich, Vue  There is no duplication of core code  -->
<!--  Provide these functions to keep them lean. It also gives you the freedom to choose tools that you are more familiar with.  -->
<script src="https://cdn.jsdelivr.net/npm/axios@0.12.0/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/lodash@4.13.1/lodash.min.js"></script>
<script>
var watchExampleVM = new Vue({
  el: '#watch-example',
  data: {
    question: '',
    answer: 'I cannot give you an answer until you ask a question!'
  },
  watch: {
    //  If  `question`  Change, this function will run 
    question: function (newQuestion, oldQuestion) {
      this.answer = 'Waiting for you to stop typing...'
      this.debouncedGetAnswer()
    }
  },
  created: function () {
    // `_.debounce`  Yes 1 A pass  Lodash  A function that limits the frequency of operation. 
    //  In this example, we want to restrict access  yesno.wtf/api  Frequency of 
    // AJAX  The request will not be issued until the user has entered it. Want to know more about 
    // `_.debounce`  Function  ( And their close relatives  `_.throttle`)  Knowledge, 
    //  Please refer to: https://lodash.com/docs#debounce
    this.debouncedGetAnswer = _.debounce(this.getAnswer, 500)
  },
  methods: {
    getAnswer: function () {
      if (this.question.indexOf('?') === -1) {
        this.answer = 'Questions usually contain a question mark. ;-)'
        return
      }
      this.answer = 'Thinking...'
      var vm = this
      axios.get('https://yesno.wtf/api')
        .then(function (response) {
          vm.answer = _.capitalize(response.data.answer)
        })
        .catch(function (error) {
          vm.answer = 'Error! Could not reach the API. ' + error
        })
    }
  }
})
</script>

Results:

Ask a yes/no question:

I cannot give you an answer until you ask a question!

In this example, use the watch Option allows us to perform an asynchronous operation (access 1 API), limits how often we perform this operation, and sets an intermediate state until we get the final result. These are all things that computed attributes can't do.


Related articles: