Блог

Уроки VueJS. Что такое Computed properties (Вычисляемые свойства)?

5 years ago
16K
Sergei Nikonov

 

В прошлых уроках курса VueJS я рассказал и показал, что такое свойства и методы. Во VueJS есть так называемые Computed Properties или Вычисляемые свойства. С вами Сергей Никонов и давайте рассмотрим примеры.

Смотрите видео про Вычисляемые свойства VueJS (Computed Properties )

 

Для лучшего понимания, я покажу пример функционала и ловушку в которую попадают практически все начинающие, кто осваивает фреймворк VueJS. Итак вот этот пример:
 
 
У нас есть профиль пользователя, с фотографией, именем, фамилией и прочим. Для бОльших возможностей, как правило, имя и фамилию хранят в разных полях в базе данных. Сверху у нас может быть какое-то приветствие с пользователем по имени, и ниже мы выводим его имя и фамилию в карточке.  Думаю, что с этим все понятно.  
 
Теперь давайте посмотрим немного на код:
 
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>FructCode VueJS</title>
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css">
    <link rel="stylesheet" href="style.css">
</head>

<body>
    <div id="app">
      <div class="wrapper">
        <div class="control-panel">
          <p class="profile-hello">
            Привет, {{ fullName }}!
            <input type="text" v-model="tempName">
            <button v-on:click="setName()"> Set </button>
            <button v-on:click="rndValue()"> {{rndNum}} </button>
          </p>  
        </div>

        <div class="profile-card">
            <div class="profile-header">
                <img :src="profile_back_img">
            </div>
            <div class="profile-body">
              <div class="user-img">
                <img :src="profile_img">
              </div>
             
              <div class="name">{{  first_name + ' ' + last_name; }}</div>

              <div class="bio">
                <p>{{ bio }}</p>
              </div>
              <div class="social-icon">
                <ul>
                  <li v-for="(item, key) in social_links">
                    <a :href="item" target="_blank">
                      <i class="fa" :class="key"></i>
                    </a>        
                  </li>
                </ul>  
              </div>
          </div>

        </div>

      </div>  
      
    </div>
    <script src="https://cdn.jsdelivr.net/npm/vue/dist/vue.js"></script>
    <script>
    var app = new Vue({
        el: '#app',
        data: {
            tempName: '',
            rndNum: 0,
            first_name: 'Sergei',
            last_name: 'Nikonov',
            bio: 'Автор курсов программирования и основатель проекта FructCode',
            profile_img: 'https://fructcode.com/uploads/user/photo/5a711ac714ad7.jpg',
            profile_back_img: 'https://fructcode.com/uploads/img/blog/13ee471281559f0812396e5153452c52/a768558507_fructcode.jpg',
            social_links: {
              'fa-facebook-f': 'https://www.facebook.com/fructcodecom/',
              'fa-vk': 'https://vk.com/fructcodecom',
              'fa-youtube': 'https://www.youtube.com/channel/UCXneQRoicIlzKJe_yKnYt4g',
              'fa-instagram': 'https://www.instagram.com/fructcodecom/',
            }
        }, 
        methods: {
          getFullName: function() {
            console.log('getFullName Method');
            return this.first_name.toUpperCase() + ' ' + this.last_name;
          },
          rndValue: function() {
            this.rndNum = Math.floor((Math.random() * 100) + 1);
          },
          setName: function() {
            this.fullName = this.tempName;
          }
        },
        computed: {
          fullName: {
            get: function() {
              return this.first_name.toUpperCase() + ' ' + this.last_name;
            },
            set: function(value) {
              let names = value.split(' ');
              this.first_name = names[0];
              this.last_name = names[names.length - 1];
            }
          }
        }
    })
    </script>
</body>

</html>
 
У нас есть data в которую мы записали данные о пользователе: Имя и Фамилия (first_name и last_name соответственно), bio - биография пользователя, profile_img и profile_back_img - это аватар пользователя и картинка бекграунд. И также у нас есть массив social_links - это ссылки на социальные сети. 
 
Живой пример вы можете найти на сайте FructCode в интерактивном задании курса VueJS
 
Как и в прошлых уроках я помещаю данные в верстку. Думаю, что если вы смотрели прошлые уроки, а также проходили интерактивные задания, в этом коде вы уже разбираетесь. Но если что-то непонятно, не стесняйтесь, задавайте вопросы в наше комьюнити на сайте FructCode, где более опытные участники помогут решить проблему.
 
Единственное пояснение, которое мне хочется сделать, это про генерацию иконок для соц. сетей в цикле v-for. Я использую библиотеку fonts awesome. С помощью нее можно вставлять различные иконки. Ключи массива генерируют нужный класс иконки и в тег <a> вставляется ссылка на соц. сеть:
 
<div class="social-icon">
    <ul>
        <li v-for="(item, key) in social_links">
            <a :href="item" target="_blank">
                <i class="fa" :class="key"></i>
            </a>        
       </li>
    </ul>  
</div>
 
Если с кодом разобрались, давайте перейдем к популярной ситуации при работе с  фреймворком VueJS. 
 
Обратите внимание, что как мы выводим имя и фамилию во View:
{{ first_name + ' ' + last_name }}

Так делать не очень хорошо, так как вьюшки должны быть понятными и хорошо читаться. Но представим ситуацию, если мы захотим при выводе имени и фамилии добавить какой-то еще алгоритм, например мы захотим, чтобы все наши буквы были в верхнем регистре или еще что-то. Понятно, что конкретно для верхнего регистра мы можем добавить css-стиль:

text-transform: uppercase;

но здесь для примера, мы сделаем все буквы в верхнем регистре с помощью javascript:

{{ first_name.toUpperCase() + ' ' + last_name }}!
 
Как вы видите наш вывод во view уже усложнился. А если мы добавим в вывод имени и фамилии еще какие-нибудь алгоритмы, наш код во view будет уже очень сложно-читаемым. И будет еще хуже ситуация, если мы будем выводить данный код в нескольких местах, например, мы захотим поприветствовать пользователя и использовать тот же формат вывода:
<p class="profile-hello">Здравствуйте, {{ first_name.toUpperCase() + ' ' + last_name }}!</p>  
И если мы захотим поменять формат вывода, нам нужно будет менять код на всех страницах, где мы его использовали. 
 
Думаю, что это знакомая ситуация для многих, кто имеет даже небольшой опыт в программировании и первое решение, которые приходит - это вынести код отображение из view куда-нибудь в методы и во view в нескольких местах мы просто вставим вызов этих методов и теперь наш код лучше читается и более гибкий для внесения изменений в формат вывода:
        methods: {
          getFullName: function() {
            return this.first_name + ' ' + this.last_name;
          }
        }
 
Именно в таком подходе и заключается ловушка для неопытных разработчиков VueJS. Дело в том, что при каких-либо изменениях, которые даже не касаются вывода имени и фамилии, VueJS будет заново каждый раз пересчитываться и это очень дорогая операция, так как Frontend часть в итоге будет тормозить.
 
И в подтверждение того, что VueJS будет каждый раз заново вызывать метод getFullName, давайте добавим еще один функционал. Это будет кнопка и у кнопки будет название какое-то случайное число, когда мы будет на эту кнопку нажимать. 
 
В data добавим rndNum и в нее будем записывать сгенерированное число
rndNum: 0,

Далее создадим метод, который будет генерировать случайное число от 1 до 100:

rndValue: function() {
    this.rndNum = Math.floor((Math.random() * 100) + 1);
},

Сейчас давайте создадим во view кнопку и поместим в нее этот метод:

<button  v-on:click="rndValue()">{{ rndNum }}</button>

И давайте добавим в метод getFullName console.log для того, чтобы в консоли разработчика отображался вызов нашего метода:

console.log('getFullName Method');
 
И если вы запустите код, наш метод getFullName будет вызываться каждый раз, даже если не затрагиваются first_name и last_name.   
Для такого функционала во VueJS нужно использовать Computed Properties (Вычисляемые свойства)
 

Использование Computed Properties (вычисляемые свойства)

Для того, чтобы создать вычисляемые свойства, ниже methods нужно добавить еще одно ключевое слово computed и в него поместить название нашего вычисляемого свойства:
fullName: function () {
    return this.first_name + ' ' + this.last_name;
}

 

И выводить его в верстке можно как обычное свойство:

<div class="name">{{ fullName }}</div>
И если мы также добавим console.log в вычитаемое свойство, для того, чтобы посмотреть сколько раз оно вызывается, по сравнению с вызовом метода, вы увидите, что вызов происходит один раз. 
 
Итак, основное различие между вызовом метода и вычисляемого свойства состоит в том, что вычисляемые свойства кешируются, основываясь на своих реактивных зависимостях и пересчитываются тогда, когда изменится одна из его реактивных зависимостей.
 
Также в вычисляемые свойства можно явно задать Getter и Setter и давайте сделаем возможность изменять Имя и Фамилию с помощью одного input:
        computed: {
          fullName: {
            get: function() {
              return this.first_name + ' ' + this.last_name;
            },
            set: function(value) {
              let names = value.split(' ');
              this.first_name = names[0];
              this.last_name = names[names.length - 1];
            }
          }
        }
 
Добавим в data еще одно свойство:
tempName: ''
 
И добавим во View:
<input v-model="tempName">

 

И сделаем еще одну кнопку set, которая будет вызывать метод для изменения имени:

<button  v-on:click="setName()">set</button>

 

И добавим еще один метод:

setName: function() {
    this.fullName = this.tempName;
},
 
И если вы запустите код, а также введете через пробел имя и фамилию и нажмете на кнопку set, вы увидите, что имя и фамилия изменится.
 

Вывод

Используйте computed properties, вместо методов в подобных ситуациях, чтобы ваша frontend-часть работала быстро и без ошибок. 
 
Живой пример кода вы можете найти на сайте FructCode в интерактивном задании курса VueJS
 
С вами был Сергей Никонов, 
Актуальные версии моих уроков и курсов вы можете найти только на сайте FructCode.com