Правильний спосіб змусити Vue перерендерити компонент

  • 6 апреля, 16:44
  • 3761
  • 0

Іноді буває так, що реактивності Vue не вистачає, і вам потрібно перерендерити компонент. Або вам потрібно скинути DOM і почати спочатку.

Так як же правильним чином змусити Vue перерендерити компонент?

Відповідь проста. Правильний шлях - використовувати :key в компоненті. Коли вам потрібно перерендерити компонент, ви просто міняєте значення ключа і Vue оновить компонент.

Правильний спосіб змусити Vue перерендерити компонентПравильний спосіб змусити Vue перерендерити компонент

Досить просто, чи не так? Є також і інші способи:

  1. Найгірший варіант: оновити всю сторінку,
  2. Поганий варіант: використовувати  v-if хак,
  3. Трохи краще: використовувати вбудований в Vue метод  forceUpdate,
  4. Кращий варіант: використовувати зміну ключа.

Зверніть увагу!  Якщо ви зіткнулися з ситуацією, в якій вам потрібно перерендерити компонент, ймовірно, є більш правильний спосіб вирішити задачу. Швидше за все, ви не до кінця розібралися з чимось з цього: 1. Реактивність Vue, 2. Computed-властивості, 3. Watch-властивості, 4. Не використовуєте  :key атрибут для  v-for.

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

Найгірший варіант: перезавантажити всю сторінку

Цей спосіб - приблизно те ж саме, що перезавантаження комп'ютера кожен раз, коли ви хочете закрити який-небудь додаток. Звучить дивно, правда? Колись це вам справді може допомогти. Однак це жахливе рішення, тому не використовуйте його. Зовсім.

Краще подивіться, що ви можете використовувати ще.

Поганий варіант: використовувати  v-if хак

Vue містить директиву v-if, яка дозволяє рендерити компонент або частину компонента тільки тоді, коли умова повертає істину. Якщо в умові брехня, то все що всередині не відрендериться і не буде існувати в DOM.

Давайте подивимося, як це виглядає на практиці.

Додайте v-if в ваш шаблон:

<Template>
  <my-component v-if = "renderComponent" />
</ Template>

У секції  script додайте метод, який використовує функцію  nextTick:

<Script>
  export default {
    data ( ) { 
      return {
        renderComponent: true,
      } ;
    } ,
    methods: {
      forceRerender ( ) { 
        // Спочатку приховуємо компонент
        this.renderComponent = false;

        this. $ nextTick ( ( ) => {
          // А потім покажемо знову
          this.renderComponent = true;
        } ) ;
      }
    }
  } ;
</ Script>

Ось що відбувається в цьому коді:

  1. Спочатку  renderComponent встановлено в true, тому компонент  my-component рендерится
  2. Коли ми викликаємо  forceRerender, ми відразу встановлюємо renderComponent в false
  3. Ми припиняємо рендеринг  my-component, тому що  v-if директива містить false
  4. На наступному кроці ( nextTick) ми повертаємо  renderComponent в true
  5. Тепер  v-if містить істину, тому ми знову рендиримо компонент my-component

У цьому прикладі є два важливих моменти, які важливі для розуміння того, як це працює. Перше - ми повинні дочекатися nextTick, інакше ми не побачимо ніяких змін.

В Vue tick - це один цикл поновлення DOM. Vue збирає всі оновлення в одному тіку, і в кінці цього тіка оновлює те, що відображається в DOM в процесі цих оновлень. Якщо ми не дочекаємося наступного тіка, то наші зміни властивості  renderComponent перезапишуть самі себе і нічого не зміниться. Тому ми використовуємо  nextTick.

Друге, Vue створить абсолютно новий компонент, коли ми відрендиримо його вдруге. Vue знищить перший компонент і створить новий. Це означає, що наш новий  my-component пройде всі хукі життєвого циклу компонента -  created,  mounted і т.д.

До речі, ви можете використовувати  nextTick з промісами:

forceRerender ( ) { 
  // Видаляємо компонент з DOM
  this.renderComponent = false;
  // Якщо віддаєте перевагу промісам, можете використовувати їх так
  this. $ nextTick ( ) . then ( ( ) => {
    // Повертаємо компонент назад
    this.renderComponent = true;
  } ) ;
}

Тим не менш, це не дуже гарне рішення. Швидше, це хак. Тому давайте подивимося, що ще ми можемо використовувати.

Спосіб краще: можна використовувати forceUpdate

Це один з двох способів, який відмінно вирішує проблему. При цьому, він офіційно підтримується Vue і не є хаком або чимось подібним.

Зазвичай Vue реагує на зміни в залежностях, оновлюючи представлення. Однак, коли ви запускаєте forceUpdate, ви можете примусово запустити оновлення представлення, навіть якщо залежності не змінилися.

І ось тут більшість людей роблять безліч серйозних помилок.

Якщо Vue автоматично оновлює все коли щось змінюється, то навіщо нам самим змушувати його щось оновлювати ?

Причина в тому, що іноді реактивність Vue може збивати з пантелику. Ми думаємо, що Vue відреагує на зміни властивості або змінної, але цього не відбувається. Є також певні ситуації, коли  реактивність Vue взагалі не розпізнає ваші зміни.

Варто відзначити, що forceUpdate все-таки не кращий спосіб вирішення завдання. Використовуйте його акуратно.

// Глобально
import Vue from 'vue' ;
Vue. forceUpdate ( ) ;

// Всередині вашого компонента
export default {
  methods: {
    methodThatForcesUpdate ( ) { 
      // ...
      this. $ forceUpdate ( ) ; // Зауважте, що ми використовуємо $ в якості префікса
      // ...
    }
  }
}

Важливо:  цей спосіб не оновлює computed-властивості. Виклик  forceUpdate тільки  змушує шаблон перерендеритися

Найкращий спосіб: зміна ключа

Що за ключ? Зараз розберемося. Взагалі, існує багато ситуацій, коли перерендеринг може дійсно вам знадобитися.

Для того, щоб зробити це належним чином, ми зв'яжемо наш компонент з атрибутом  key. Так Vue зв'яже компонент з окремим шматком даних. І якщо ключ при змінах залишиться колишнім, то Vue не оновить компонент, а ось якщо ключ зміниться - Vue позбудеться від старого компонента і створить новий. Те, що нам потрібно!

Давайте спершу пройдемось по тому, що таке  key і чому нам потрібно його використовувати.

Навіщо потрібен атрибут key в Vue?

Давайте уявимо, що ви рендерите список компонентів, у яких є одна або декілька з цих властивостей:

  1. У компонента є  локальний стейт 
  2. У компонента є  логіка ініціалізації: щось, що використовує mounted або created хукі
  3. Не реактивні  DOM-маніпуляції: зазвичай це jQuery-рішення або стандартні JS API.

Якщо ви відсортуєте цей список або оновите його будь-яким чином, вам потрібно буде повторно відрендерити всі частини цього списку. Навряд ви захочете перерендерити взагалі всі елементи цього списку, краще праювати лише з тим, що змінилося.

Для допомоги Vue у відстеженні таких змін ми впроваджуємо атрибут key. Використання індексу масиву тут не допоможе, оскільки індекс не прив'язаний до конкретних об'єктів в нашому списку.

Here is an example list that we have:

const people = [
  { name: 'Evan', age: 34 },
  { name: 'Sarah', age: 98 },
  { name: 'James', age: 45 },
];

Якщо ми відрендеримо список з використанням індексів, то отримаємо це:

<ul>
  <li v-for="(person, index) in people" :key="index">
    {{ person.name }} - {{ index }}
  </li>
</ul>

// Outputs
Evan - 0
Sarah - 1
James - 2

Якщо ми видалимо Сару, то отримаємо:

Evan - 0
James - 1

Індекс, який був пов'язаний з Джеймсом змінився, хоча Джеймс все ще Джеймс. При цьому, він перерендерится, хоча ми цього не хотіли.

У нашій ситуації підійде якийсь унікальний ID, давайте додамо його.

const people = [
  { id: 'this-is-an-id', name: 'Evan', age: 34 },
  { id: 'unique-id', name: 'Sarah', age: 98 },
  { id: 'another-unique-id', name: 'James', age: 45 },
];


<ul>
  <li v-for="person in people" :key="person.id">
    {{ person.name }} - {{ person.id }}
  </li>
</ul>

Коли ми видаляли Сару з нашого списку, Vue видалив компоненти для Сари і Джеймса, а потім створив новий компонент для Джеймса. Тепер, коли Vue знає ID, він збереже два компонента Евана і Джеймса, і видалить лише компонент Сари.

Якщо ми додамо ще когось в список, то Vue знає, що може зберегти поточні компоненти і просто створити один додатковий. Це дійсно корисно, коли у вас є більш складні компоненти з логікою, власним станом, мають логіку ініціалізації або виконують якісь маніпуляції з DOM.

А тепер давайте продовжимо з найкращим методом для примусового повторного рендеринга компонентів в Vue.

Зміна ключа для примусового повторного рендеринга компонента

Ви застосовуєте цю стратегію призначення ключів дочірнім елементам, але кожен раз, коли ви хочете повторно відрендерити компонент, ви просто оновлюєте ключ.

Ось простий приклад того, як це працює:

<Template>
  <component-to-re-render: key = "componentKey" />
</ Template>
<Script>
export default {
  data ( ) { 
    return {
      componentKey: 0 ,
    } ;
  } ,
  methods: {
    forceRerender ( ) { 
      this.componentKey + = 1 ;  
    }
  }
}
</ Script>

Кожен раз, коли ми в компоненті викликаємо  forceRerender, наш пропс componentKey змінюється. Це в свою чергу призводить до того, що Vue дізнається коли потрібно знищити інстанси компонента і створити новий.

В результаті ви отримуєте дочірній компонент, який здатний сам себе переініціалізувати. Простий і елегантний спосіб вирішення проблеми.

І пам'ятайте, що якщо ви зіткнулися з ситуацією, коли вам потрібно примусово перерендерити компонент, то ймовірно ви робите щось не кращим чином. Якщо все ж вам потрібно якось перерендерити компонент, то вибирайте спосіб з прив'язкою ключа.

Джерело перекладу


0 комментариев
Сортировка:
Добавить комментарий