Computing attributes
Expressions within templates are convenient, but they are actually only used for simple operations.Placing too much logic in a template can overweight and make it difficult to maintain.For example:
<div id="example"> {{ message.split('').reverse().join('') }} </div>
In this case, the template is no longer simple and clear.Before realizing that this is a reverse display of the message, you have to confirm it again.The problem gets worse when you want to reverse the message several times in the template
This is why computational attributes should be used for any complex logic
Basic examples
<div id="example"> <p>Original message: "{{ message }}"</p> <p>Computed reversed message: "{{ reversedMessage }}"</p> </div> <script> var vm = new Vue({ el: '#example', data: { message: 'Hello' }, computed: { reversedMessage: function () { // a computed getter return this.message.split('').reverse().join('') // `this` points to the vm instance } } }) </script>
Here we declare a computed property, reversedMessage.The function we provide will be used as a getter for the property vm.reversedMessage
console.log(vm.reversedMessage) // -> 'olleH' vm.message = 'Goodbye' console.log(vm.reversedMessage) // -> 'eybdooG'
The value of vm.reversedMessage always depends on the value of vm.message.Therefore, when vm.message changes, all bindings that depend on vm.reversedMessage will also be updated.And best of all, we've created this dependency declaratively: getter s that compute attributes have no side effects, making it easy to test and infer
Compute Cache vs Methods
<p>Reversed message: "{{ reversedMessage() }}"</p> <script> methods: { // in component reversedMessage: function () { return this.message.split('').reverse().join('') } } </script>
We can define the same function as a method instead of a calculated property.For the final result, the two approaches are indeed the same.However, the difference is that computed attributes are cached based on their dependencies.A computed property is reevaluated only if its dependent dependencies change.This means that as long as the message has not changed, multiple access to the reversedMessage calculation property immediately returns the previous calculation without having to execute the function again.
This also means that if reverseMessage is paid to a click event, in the computed method, multiple clicks transform the order only once.In contrast, whenever a re-rendering occurs, the method call always executes the function
Why do we need a cache?Suppose we have a computational attribute A with a high performance overhead that requires traversing a very large array and doing a lot of computations.Then we may have other computational properties that depend on A.If there is no cache, we will inevitably execute A getter more than once!If you don't want a cache, use method instead
Computed attribute vs Watched attribute
Vue does provide a more general way to observe and respond to data changes on Vue instances: the watch property.When you have some data that needs to change with other data, it's easy to abuse Watch - especially if you've used Angular JS before.However, it is generally better to use the computed attribute instead of the command watch callback.Think about this example:
<div id="demo">{{ fullName }}</div> <script> 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 } } }) </script>
The above code is commandal and repetitive.Compare it to the version of the computed property:
<script> var vm = new Vue({ el: '#demo', data: { firstName: 'Foo', lastName: 'Bar' }, computed: { fullName: function () { return this.firstName + ' ' + this.lastName } } }) </script>
Calculate setter
By default, a getter is the only computed property, but you can also provide a setter if you want:
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] } } }
Now when running vm.fullName ='John Doe', setter is called, and vm.firstName and vm.lastName are updated accordingly
Watchers
Although computational attributes are more appropriate in most cases, sometimes a custom watcher is also required.This is why Vue provides a more general way to respond to changes in data through the watch option.It is useful to perform asynchronous or expensive operations when you want to respond to data changes
<div id="watch-example"> <p>Ask a yes/no question: <input v-model="question"> </p> <p>{{ answer }}</p> </div> <script src="https://unpkg.com/axios@0.12.0/dist/axios.min.js"></script> <script src="https://unpkg.com/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: { question: function (newQuestion) { // If the question changes, the function will run this.answer = 'Waiting for you to stop typing...' this.getAnswer() } }, methods: { getAnswer: _.debounce( // _.debounce is a function that limits the frequency of operations by lodash.In this example, we want to limit access to yesno.wtf/api.An ajax request will not be made until the user has finished typing function () { var vm = this if (this.question.indexOf('?') === -1) { vm.answer = 'Questions usually contain a question mark. ;-)' return } vm.answer = 'Thinking...' 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 }) }, 500 // This is the number of milliseconds we waited for the user to stop typing ) } }) </script>
The use of the watch option allows us to perform asynchronous operations (access an API), limits how often we perform the operation, and sets the intermediate state before we get the final result.This is not possible with computed attributes.
In addition to the watch option, you can also use the vm.$watch API command