Урок 8 :: Computed Properties :: Вычисленные свойства

Вот, что мы с Вами сделаем на этом уроке.
Vue Mastery
Пройдем по этой ссылке:
Computed Properties ➳

... Все внимательно послушаем, посмотрим, прочитаем.

Затем сделаем конспект (мой см ниже).

Конспект

Вычисленные свойства
В этом уроке мы рассмотрим концепцию вычисленных свойств.

Чтобы начать этот урок, вы можете либо получить программой git коммандой checkout исходный код в ветке
L8-start репозитория, либо перейти в CodePen, чтобы взять файлы там.

Примечание переводчика

Чтобы перейти на ветку с файлами начала урока 8 выполните команду:

git checkout L8-start

Наша Цель
Обновить в браузере пользователя картинку товара и сообщение есть этот товар или нет в наличии, используя концепцию вычисленных свойств (объект data, свойство computed).

Простое Вычисляемое Свойство
В исходном коде вы заметите, что у нас появилось новое свойство данных (data): brand.

📄 main.js
data() {
  return {
    product: 'Socks',
    brand: 'Vue Mastery'
}

Что, если бы мы хотели объединить бренд (brand) и продукт (product) в нашем шаблоне? Мы могли бы сделать это в таком выражении, как:

📄 index.html
<h1>{{ brand + ' ' + product }}</h1>

Если бы мы проверили это в браузере, мы бы увидели, что отображается “Vue mastery Socks". Но разве не было бы здорово, если бы вместо обработки этой логики во внутреннем HTML наше приложение имело возможность вычислить это значение для нас? Например, взять brand и product, сложить их вместе и вернуть этo новое значение.

Вычисляемые свойства в точности такие, как они звучат: свойства, которые мы можем добавить в приложение Vue, которое вычисляет значения для нас. Они помогают нам исключить вычислительную логику из шаблона и повысить производительность, о чем мы скоро расскажем. А пока давайте превратим этот простой пример в вычисляемое свойство. Мы изменим выражение h1 следующим образом:

📄 index.html
<h1>{{ title }}</h1>

Теперь title (заголовок) - это имя вычисляемого свойства, которое мы сейчас создадим. Сначала мы добавим в приложение параметр computed ("вычисляемый"), расположенный чуть ниже наших methods (методов), а затем создадим свойство title (заголовок).

📄 main.js
...
computed: {
  title() {
    return this.brand + ' ' + this.product
  }
}

Если мы проверим браузер, мы по-прежнему увидим “Vue Mastery Socks”, за исключением того, что теперь мы извлекли эту вычислительную логику из шаблона и аккуратно поместили ее в объект options.

Но как именно работают вычисляемые свойства? Давайте взглянем глубже.

Думайте о них как о калькуляторе
Мне нравится думать о вычисляемых свойствах как о калькуляторе, потому что они вычисляют или вычисляют значения для нас. Этот вычислительный калькулятор берет наши значения, brand и product (бренд и продукт), складывает их вместе и дает нам результат.
les08_pic01.jpg
Как я уже упоминал ранее, вычисляемые свойства обеспечивают нам повышение производительности. Это связано с тем, что они кэшируют вычисленное значение. Значение 'Vue Mastery Socks' ("Носки мастерства Vue") сохраняется и обновляется только тогда, когда это необходимо, при изменении одной из его зависимостей. Например, если бренд изменится с 'Vue Mastery' на 'Node Mastery', наше вычисляемое свойство получит эту новую зависимость от бренда, затем пересчитает и вернет новое значение: 'Node Mastery Socks' ("Носки мастерства Нода").

Теперь, когда мы начинаем понимать вычисляемые свойства, давайте реализуем более практический пример в нашем приложении Vue.

Вычисляем, какое должно быть изображение и подсчитываем количество.
Возвращаясь к нашему коду, давайте добавим новое свойство quantity в наши объекты variant.

📄 main.js
data() {
  return {
    ...
    variants: [
      { id: 2234, color: 'green', image: './assets/images/socks_green.jpg', quantity: 50 },
      { id: 2235, color: 'blue', image: './assets/images/socks_blue.jpg', quantity: 0 },
    ]
  }
}

Обратите внимание, что quantity (количество) зеленых носков составляет 50, а синих - 0. Другими словами, зеленые носки есть в наличии, а синих носков нет в наличии. Однако в настоящее время мы показываем “В наличии” или “Нет в наличии” на основе значения данных inStock, которое больше не отражает правду о нашем продукте и его различных количествах. Поэтому мы захотим создать вычисляемое свойство, которое мы сможем использовать для отображения “В наличии” или “Нет в наличии” на основе этих новых количеств.

Для начала, помните, как мы обновили изображение носков, основываясь на том, какой цвет носков выбран? Вместо этого события наведения курсора мыши mouseover, запускающего метод updateImage(), мы собираемся запустить новый метод, называемый updateVariant().

📄 index.html
<div 
  v-for="(variant, index) in variants" 
  :key="variant.id" 
  @mouseover="updateVariant(index)" <! -- new method -->
  class="color-circle" 
  :style="{ backgroundColor: variant.color }">
</div>

Обратите внимание, как мы передаем index текущего варианта: updateVariant(index). Мы получили доступ к этому индексу, добавив его в качестве второго параметра в нашу директиву v-for:

v-for="(variant, index) in variants"

Почему мы передаем index? Мы собираемся использовать его, чтобы сообщить нашему приложению, какой товар в данный момент находится в поле зрения, чтобы оно могло использовать эту информацию для запуска обновления как изображения, так и того, есть ли этот товар в наличии или нет.

Мы добавим новое свойство данных в наше приложение, которое будет обновлено, чтобы соответствовать этому индексу

📄 main.js
data() {
  return {
    ...
    selectedVariant: 0,
    ...
  }
}

Наш метод updateVariant() установит значение выбранного варианта (selectedVariant) равным индексу (index) текущего варианта.

📄 main.js
updateVariant(index) {
  this.selectedVariant = index
}

Теперь мы реализовали способ, с помощью которого наше приложение может узнать, с каким вариантом продукта используется, и мы можем использовать эту информацию для запуска вычисления того, какое изображение показывать и показывать ли “В наличии” (“In stock”) или “Нет в наличии” (“Out of stock”), в зависимости от того, какой вариант товара пользователь просматривает.

Теперь мы готовы удалить изображение image и inStock из наших данных и заменить их вычисляемыми свойствами с теми же именами.

📄 main.js
computed: {
  image() {
    return ??
  },
  inStock() {
    return ??
  }
}

Итак, как нам получить изображение и количество? Это будет выглядеть так:

📄 main.js
image() {
  return this.variants[this.selectedVariant].image
}

Мы нацеливаемся на первый или второй элемент нашего массива вариантов variants на основе выбранного варианта selectedVariant, который равен либо 0, либо 1, в зависимости от того, на каком цветовом круге варианта находится курсор. Затем мы просто используем точечную нотацию, чтобы захватить изображение (image) этого варианта.

Логика вычислений для свойства inStock почти идентична:

📄 main.js
inStock() {
  return this.variants[this.selectedVariant].quantity
}

Проверяя это в браузере, когда мы наводим курсор на цветные круги, мы не только обновляем изображение товара, но и показываем, есть ли этот товар в наличии или нет, используя его значение его количества (quantity).
les08_pic02.jpg
Обратите внимание, что кнопка button по-прежнему автоматически обновляется для нас, включается и отключается. Это потому, что в нашем шаблоне мы все еще используем значение inStock.

📄 index.html
<button 
  class="button" 
  :class="{ disabledButton: !inStock }" 
  :disabled="!inStock" 
  v-on:click="addToCart">
  Add to Cart
</button>

Теперь inStock больше не является свойством данных data; это новое вычисляемое свойство (computed).

Задача кодирования
Мы подошли к концу этого урока, теперь будем решать новые задачи кодирования.

Вот, что надо сделать.

Добавьте логическое свойство (boolean) onSale в наши данные (data).

Используйте вычисляемое свойство (computed property) для отображения строки: ’brand + ’ ’ product + ’ ’ is on sale’, когда onSale есть истина (true).

Вы можете найти код решения, загрузив L8-end ветвь репозитория или просмотрев решение в Codepen.

Чтобы перейти на ветку с файлами окончания урока 8 выполните команду:

git checkout L8-end

Код файла index.html данного урока

    <div id="app">
      <div class="nav-bar"></div>

      <div class="cart">Cart({{ cart }})</div>
      
      <div class="product-display">
        <div class="product-container">
          <div class="product-image">
            <img v-bind:src="image">
          </div>
          <div class="product-info">
            <h1>{{ title }}</h1>
            <!-- solution -->
            <p>{{ sale }}</p>
            <!-- solution -->
            <p v-if="inStock">In Stock</p>
            <p v-else>Out of Stock</p>
            <ul>
              <li v-for="detail in details">{{ detail }}</li>
            </ul>

            <div 
              v-for="(variant, index) in variants" 
              :key="variant.id" 
              @mouseover="updateVariant(index)" 
              class="color-circle" 
              :style="{ backgroundColor: variant.color }">
            </div>
            
            <button class="button" :class="{ disabledButton: !inStock }" :disabled="!inStock" v-on:click="addToCart">Add to Cart</button>
          </div>
        </div>
      </div>
    </div>

    <!-- Import App -->
    <script src="./main.js"></script>

    <!-- Mount App -->
    <script>
      const mountedApp = app.mount('#app')
    </script>


Код файла main.js данного урока

const app = Vue.createApp({
    data() {
        return {
            cart:0,
            product: 'Socks',
            brand: 'Vue Mastery',
            selectedVariant: 0,
            details: ['50% cotton', '30% wool', '20% polyester'],
            variants: [
              { id: 2234, color: 'green', image: '../assets/images/socks_green.jpg', quantity: 50 },
              { id: 2235, color: 'blue', image: '../assets/images/socks_blue.jpg', quantity: 0 },
            ],
            // solution
            onSale: true
            // solution
        }
    },
    methods: {
        addToCart() {
            this.cart += 1
        },
        updateVariant(index) {
            this.selectedVariant = index
        }
    },
    computed: {
        title() {
            return this.brand + ' ' + this.product
        },
        image() {
            return this.variants[this.selectedVariant].image
        },
        inStock() {
            return this.variants[this.selectedVariant].quantity
        },
        // solution
        sale() {
            if (this.onSale) {
                return this.brand + ' ' + this.product + ' is on sale.'
            }
            return ''
        }
        // solution
    }
})




Результат выполнения данного урока

Cart({{ cart }})

{{ title }}

{{ sale }}

In Stock

Out of Stock

  • {{ detail }}


Примечание переводчика

1. Я слегка меняю контент оригинальных файлов курса для тренировки, проверки русификации и т.д.

2. Код для HTML я размещаю в этом файле между тегами body.

3. Обратите внимание, чтобы в консоле браузера не было ошибок.

Теперь Нажмите здесь для перехода к следующему уроку ➳

... или ...


Ссылки по теме.