Компонент: скрипт Options API

Мы познакомились с устройством шаблона компонента. Теперь перейдём к блоку script — месту, где сосредоточена логика компонента.

Vue предоставляет два подхода к написанию компонентов:

  • с помощью Options API,
  • с помощью Composition API.

В этой статье мы рассмотрим первый подход.

Options API хорошо знаком разработчикам, работавшим с Vue 2. С его помощью логика компонента описывается через объект параметров, включающий такие свойства, как data, computed, methods и другие.

<script>
  import Component1 from './component/Component1.vue'

  export default {
    name: 'TestComponent',
    components: {
      Component1
    },
    data () {
      return {
        tasks: [],
        filters: {
          status: '',
        }
        ...
      }
    },

    computed: {
      filteredTasks () {
        if (!this.filters.status) {
          return this.tasks
        } else {
          return this.tasks.filter(task => task.status === this.filters.status)
        }
      }
    },

    watch: {
      filters: {
        immediate: true,
        deep: true,
        handler (newFilters, oldFilters) {
          console.log('Filters have been changed!')
        }
      }
    },

    methods: {
      getTasks () {
        this.tasks = … // API call to get new tasks
      }
    }
  }
</script>

name

Свойство name применяется при отладке приложения. Во Vue devtools компоненты отображаются в виде дерева, используя имена, указанные в name.

components

Свойство components служит для регистрации дочерних компонентов. Для этого нужно импортировать компонент, указать его в опции components и затем использовать в шаблоне.

Длинная нотация:

import Component1 from './component/Component1.vue'

export default {
  components: {
    Component1: Component1
  }
}

Сокращённая запись:

import Component1 from './component/Component1.vue'

export default {
  components: {
    Component1
  }
}

data

data — функция, возвращающая объект с данными экземпляра Vue. При создании экземпляра Vue все свойства из data включаются в систему реактивности — свойства верхнего уровня становятся доступны через this на экземпляре компонента.

В template нет необходимости использовать this, (кроме динамических свойств), так как шаблон по умолчанию ссылается на экземпляр компонента.

При изменении значений свойств данные в шаблоне обновляются автоматически благодаря реактивности.

Опция data в однофайловом компоненте Vue.js должна быть именно функцией, возвращающей объект. Благодаря этому каждый экземпляр компонента получает собственное независимое состояние. Если определить data как простой объект, все экземпляры будут разделять одни и те же данные.

Методы определения вычисляемых свойств и хуки жизненного цикла создаются единожды, но исполняются для каждого экземпляра компонента.

methods

methods — методы, которые подмешиваются к экземпляру Vue. Их можно вызывать напрямую из экземпляра или использовать в директивах.

this внутри методов указывает на экземпляр Vue. К методам можно обращаться из других методов, хуков жизненного цикла и шаблона. При вызове из методов и хуков используется this, например this.getTasks(). В шаблоне this не указывается, например @click="getTasks". При вызове метода без параметров круглые скобки в шаблоне тоже можно опустить.

computed

computed — вычисляемые свойства, являющиеся производными от других данных. В примере выше filteredTasks зависит от tasks и filters, обеспечивая постоянный доступ к отфильтрованным задачам.

Computed-свойства реактивны: при каждом обновлении массива задач или фильтров filteredTasks автоматически пересчитывается.

computed записывается как функция, однако используется как обычное свойство — this.filteredTasks — без круглых скобок.

Можно было бы оформить filteredTasks как метод, вызывая его с круглыми скобками. Однако помимо удобства записи без скобок у вычисляемых свойств есть ключевое преимущество перед методами: они кешируются на основе своих реактивных зависимостей. Если исходные данные не изменились, повторное обращение к computed-свойству вернёт закешированный результат без повторного выполнения функции.

Обратите внимание: следующее вычисляемое свойство никогда не обновится, поскольку Date.now() не является реактивной зависимостью:

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

Метод, напротив, будет выполняться заново при каждом обращении.

Зачем необходимо кеширование, и почему не использовать просто метод?

В примере с filteredTasks выполняется фильтрация задач по статусу — один проход по массиву. Но представьте ситуацию с тысячами задач и множеством фильтров. Вариантов усложнения вычислений масса, и операция может оказаться весьма затратной. В таких случаях кеширование становится мощным инструментом оптимизации.

Что использовать

Вычисляемые свойства — когда обновление значения требуется лишь при изменении реактивных зависимостей.

Метод — когда значение необходимо вычислять заново при каждом обращении.

Сеттеры вычисляемых свойств

По умолчанию вычисляемые свойства доступны только для чтения. Напрямую присвоить им новое значение нельзя — при создании свойства мы задаём логику вычисления и при использовании обращаемся к результату.

Если требуется возможность вручную обновлять вычисляемое свойство, для него можно определить сеттер. Рассмотрим на примере с задачами:

computed: {
  filteredTasks () {
    get () {
      if (!this.filters.status) {
        return this.tasks
      } else {
        return this.tasks.filter(task => task.status === this.filters.status)
      }
    },
    set ({ tasks, filters }) {
      this.tasks = tasks
      this.filters = filters
    }
  }
}

В качестве параметра передаётся значение для обновления зависимостей вычисляемого свойства. Параметр меняется — свойство пересчитывается.

Обращение к сеттеру: this.filteredTasks = { tasks, filters }. Если сеттер не определён, подобное присвоение вызовет ошибку.

Важный момент о computed

Computed предназначен исключительно для получения результата вычислений. Внутри не должно быть побочных эффектов. Это всегда синхронная операция — она не изменяет другие свойства и не генерирует события. Только вычисление результата.

Как быть, если при изменении свойства нужно инициировать событие или вызвать определённые методы? Как справиться с асинхронными операциями? Для этого во Vue предусмотрен watch.

Watch

Watch — механизм наблюдения за изменениями указанного свойства, срабатывающий при каждом его обновлении. Это объект, ключами которого являются выражения для отслеживания, а значениями — колбэки, вызываемые при изменении. Значениями также могут быть строки с именами методов или объекты с дополнительными опциями.

Экземпляр Vue выполнит $watch() для каждого ключа объекта при своей инициализации. Ключи (выражения для наблюдения) — это имена свойств компонента, за которыми ведётся отслеживание.

Варианты записи watch

Наиболее распространённый вариант — запись в виде функции:

watch: {
  filters (newFilters, oldFilters) {
    console.log('Filters have been changed!')
  }
}

Запись в виде объекта с опциями:

watch: {
  filters: {
    immediate: true,
    deep: true,
    handler (newFilters, oldFilters) {
      console.log('Filters have been changed!')
    }
  }
}

Сокращённая запись с указанием имени метода (используется редко):

watch: {
  filters: 'handleFilters' // указываем название метода-обработчика
}

Параметры функции

Функция принимает два параметра: новое значение после изменения и предыдущее значение — это удобно, когда необходимо сравнить данные до и после обновления.

Опция deep

Для отслеживания изменений во вложенных объектах необходимо указать deep: true в параметрах. Для массивов это не требуется.

Опция immediate

При значении immediate: true колбэк будет вызван сразу после начала наблюдения с текущим значением. Это полезно, когда нужно выполнить логику ещё до первого изменения свойства.

handler

Это имя метода, используемое при объектном синтаксисе. Он выступает в роли функции-наблюдателя.

Watch, в отличие от computed, может быть асинхронным. Поэтому в нём допускается использование конструкций async/await и then/catch.

Watch — отличное место для запроса данных с сервера, например при изменении id или других параметров.

Проверьте себя

1.В чём ключевое преимущество computed-свойств перед методами?

2.Почему опция data в однофайловом компоненте должна быть функцией, а не объектом?

3.Какие утверждения о watch верны?

Несколько вариантов