Хуки жизненного цикла (lifecycle hooks) — важная часть любого серьёзного компонента. Нам часто нужно знать, когда компонент был создан, добавлен в DOM, обновлён или уничтожен. Хуки жизненного цикла показывают нам, как работает «за кулисами» выбранная библиотека. Они часто вызывают у новичков трепет или беспокойство. К счастью, понять принцип работы хуков несложно, см. схему:



Создание (инициализация)


Первыми в вашем компоненте идут хуки создания. Они позволяют вам выполнять действия ещё до того, как компонент будет добавлен в DOM. В отличие от прочих, хуки создания также выполняются в ходе отрисовки на стороне сервера.


Используйте хуки создания, когда вам нужно вам нужно что-то настроить в компоненте в ходе отрисовки на стороне клиента и сервера. Внутри таких хуков у вас не будет доступа к DOM или целевому элементу (mounting element) (this.$el).


beforeCreate


Хук beforeCreate выполняется прямо во время инициализации компонента. Данные ещё не стали реактивными, а события не настроены.


Пример:


ExampleComponent.vue
<script>
export default {
  beforeCreate() {
    console.log('Nothing gets called before me!')
  }
}
</script>

created


В хуке created вы сможете получить доступ к реактивным данным и активным событиям. Шаблоны и виртуальный DOM ещё не встроены (mounted) и не отрисованы.


Пример:


ExampleComponent.vue
<script>
export default {
  data() {
    return {
      property: 'Blank'
    }
  },

  computed: {
    propertyComputed() {
      console.log('I change when this.property changes.')
      return this.property
    }
  },

  created() {
    this.property = 'Example property update.'
    console.log('propertyComputed will update, as this.property is now reactive.')
  }
}
</script>

Встраивание (вставка в DOM)


Хуки встраивания (mounting) — одни из самых часто используемых. Они позволяют получать доступ к компоненту непосредственно перед первой отрисовкой или сразу после неё. Однако эти хуки не выполняются в ходе отрисовки на стороне сервера.


Применяйте их, если непосредственно перед первичной отрисовкой или сразу после неё вам нужно отредактировать DOM компонента или получить к нему доступ.


Не применяйте эти хуки, если вам в ходе инициализации нужно извлечь данные для компонента. В этом случае используйте created (или created + activated для keep-alive-компонентов), особенно если эти данные вам нужны в ходе отрисовки на стороне сервера.


beforeMount


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


Пример:


ExampleComponent.vue
<script>
export default {
  beforeMount() {
    console.log(`this.$el doesn't exist yet, but it will soon!`)
  }
}
</script>

mounted


В хуке mounted вы получите полный доступ к реактивному компоненту, шаблонам и отрисованному DOM (через this.$el). Mounted — самый популярный хук жизненного цикла. Обычно его используют для извлечения данных для компонента (вместо этого применяйте created) и изменения DOM, зачастую ради интегрирования не-Vue библиотек.


Пример:


ExampleComponent.vue
<template>
  <p>I'm text inside the component.</p>
</template>

<script>
export default {
  mounted() {
    console.log(this.$el.textContent) // I'm text inside the component.
  }
}
</script>

Обновление (диффы и перерисовка)


Хуки обновления вызываются, когда изменилось реактивное свойство, используемое вашим компонентом, или, когда что-то ещё приводит к перерисовке. Эти хуки позволяют получить доступ к циклу «отслеживания-вычисления-отрисовки» компонента.


Применяйте их, если вам нужно узнать о перерисовке компонента, например, для отладки или профилирования.


Не применяйте их, если вам нужно узнать об изменении реактивного свойства в компоненте, вместо этого используйте вычисленные свойства или «наблюдателей» (watchers).


beforeUpdate


Хук beforeUpdate выполняется после изменения данных в компоненте и начала цикла обновления, сразу перед патчингом и перерисовкой DOM. Этот хук позволяет получить новое состояние любых реактивных данных в компоненте, прежде чем он будет отрисован.


Пример:


ExampleComponent.vue
<script>
export default {
  data() {
    return {
      counter: 0
    }
  },

  beforeUpdate() {
    console.log(this.counter) // Logs the counter value every second, before the DOM updates.
  },

  created() {
    setInterval(() => {
      this.counter++
    }, 1000)
  }
}
</script>

updated


Хук updated вызывается после изменения данных в компоненте и перерисовки DOM. Если вам нужно получить доступ к DOM после изменения свойства, такой хук — самое безопасное место для этого.


Пример:


ExampleComponent.vue
<template>
  <p ref=”dom-element”>{{counter}}</p>
</template>
<script>
export default {
  data() {
    return {
      counter: 0
    }
  },

  updated() {
    // Fired every second, should always be true
    console.log(+this.$refs[‘dom-element’].textContent === this.counter)
  },

  created() {
    setInterval(() => {
      this.counter++
    }, 1000)
  }
}
</script>

Уничтожение


Хуки уничтожения позволяют действовать, например, прибраться или отправить данные для аналитики, после уничтожения компонента. Эти хуки срабатывают при демонтаже элемента и его удалении из DOM.


beforeDestroy


beforeDestroy выполняется непосредственно перед монтажом. Ваш компонент ещё полностью функционирует. Если вам нужно очистить события или реактивные подписки, то beforeDestroy — самое подходящее для этого место.


Пример:


ExampleComponent.vue
<script>
export default {
  data() {
    return {
      someLeakyProperty: 'I leak memory if not cleaned up!'
    }
  },

  beforeDestroy() {
    // Perform the teardown procedure for someLeakyProperty.
    // (In this case, effectively nothing)
    this.someLeakyProperty = null
    delete this.someLeakyProperty
  }
}
</script>

destroyed


К тому моменту, как вы добрались до хука destroyed, от вашего компонента мало что осталось. Всё, что было к нему прикреплено, уже уничтожено. Вы можете использовать хук для последней очистки или, словно подлый тихушник, проинформировать удалённый сервер об уничтожении компонента. <_<


Пример:


ExampleComponent.vue
<script>
import MyCreepyAnalyticsService from './somewhere-bad'

export default {
  destroyed() {
    console.log(this) // There's practically nothing here!
    MyCreepyAnalyticsService.informService('Component destroyed. All assets move in on target on my mark.')
  }
}
</script>

Прочие хуки


Есть два других хука, activated и deactivated. Они уже предназначены для keep-alive-компонентов, которые выходят за рамки нашей статьи. Достаточно сказать, что эти хаки позволяют определять, включён ли компонент, обёрнутый в теги <keep-alive></keep-alive>. Можете применять их для извлечения данных для компонента или обработки изменений состояния. Эти хаки будут вести себя как created и beforeDestroy, но без необходимости полностью пересобирать компонент.

Комментарии (12)


  1. PYXRU
    12.03.2018 14:43

    Я правильно понял, что данные с api запрашивать выгоднее всего на created(), а не mounted()?


  1. kolesoffac
    12.03.2018 15:11

    Все зависит от цели. Но в основном лучше на создании, съэкономит немного времени.


    1. mmMike
      12.03.2018 15:24

      Ради интереса попробовал в рабочем проекте активировать загрузку в created() (было в mounted).
      Результат ожидаемый. Разницы не заметил никакой.
      Все равно загрузка через axios и результат приходит всяко после того как все "отрисовано".


      Так все же, а в чем суть и причина этой рекомендации?


      1. kolesoffac
        12.03.2018 17:12

        Причина: запрос ушел, пошел рендер, а не наоборот. Я же не сказал, что производительность подскочит на 200%, я только хотел сказать, что в разрезе последовательности выполнения лучше начать запрос данных как можно раньше.


        1. mmMike
          13.03.2018 06:14

          Ради интереса посмотрел dt между created и mounted для одной и той же страницы в разных браузерах (последние версии на Windows) на одном компе:


          • Chrome — среднее 180ms. деривация 5ms
          • IE 11 — среднее 400ms. деривация 15ms
          • Opera — 152ms. деривация 4ms
          • FF — 300ms. деривация 10ms
            Это на относительно дохленьком компе для средне насыщенной страницы (таблица + диаграммы и пр… по мелочи).

          Субъективно разница в 0.5 сек не улавливается. Поэтому и запихивал загрузку ресурсов в уже вызываемый (для тонкой настройки) блок mounted.


          1. lexxor
            13.03.2018 09:15

            Важнее разница между created и mounted в порядке вызова этих хуков в компонентах-родителях и детях. mounted родителя вызывается после mounted ребенка. Соотвественно если вы в mounted родителя меняете что то, что приводит к ререндеру ребенка (назначаете дефолтные значения фильтров для списка, например), компоненты-дети будут отрисованы дважды. Это редко где освещается, но иногда приводит к неожиданным последствиям.
            подробнее medium.com/@brockreece/vue-parent-and-child-lifecycle-hooks-5d6236bd561f

            А вообще не очень понятно, зачем вам так часто что то в mounted приходится тонко настраивать. У меня в среднего размера проекте этот хук используется в двух местах, где пришлось поковырять dom через ref — это внешние зависимости плохо совместимые с vue. И они спрятаны в переиспользуемых компонентах.


            1. mmMike
              15.03.2018 13:30

              Часто, это я сгоряча… фактически настройки то же только в паре месте пришлось делать.
              Для для диаграмм и графиков (сhart.js).
              Возможно не правильный способ. Но я с ходу самый простой что бы понять размер и подкорректировать размеры canvas под фактический размер ($el.getBoundingClientRect())
              Правильный не правильный… но некогда было искать другие решения. работает и ладно.


      1. Serg_de_Adelantado
        12.03.2018 20:42

        Основное применение created, которое доводилось встречать — SSR, для загрузки данных при выполнении скрипта на стороне сервера.


  1. kolesoffac
    12.03.2018 15:14

    Автор, подскажите, пожалуйста, чем ваша статья кардинально отличается от достаточно качественной документации на официальном сайте vue? ru.vuejs.org/v2/guide/instance.html


    1. Fragster
      12.03.2018 17:41

      Судя по вопросам на тостере — никто не читает документацию :)


    1. kuftachev
      13.03.2018 01:29

      Вот меня тот же вопрос мучает!
      Почему люди не пытаются принести какую-то свою "добавочную стоимость"? Встречал даже курсы, включая платные, которые тупо читают документацию в вольном стиле.


      Ещё видел курсы по книге.


      Также интересно, что смотрел штук под 10 книг по Angular 2+ и не одна не даёт ничего больше, чем документация.


    1. marsdenden
      13.03.2018 10:09

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