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

Допустим, вы находитесь на пути /compA и загрузили компонент A в DOM, и теперь, когда вы измените маршрут на /compB, который загрузит компонент B,

  • компонент A будет уничтожен, что означает, что он будет удален из DOM, и его соответствующий экземпляр компонента также будет удален, (мы можем проверить это, проверив, вызывается ли хук ngOnDestroy или нет ).
  • компонент B будет заново инициализирован и отрендерен в DOM(мы можем проверить это, проверив, вызывается ли хук ngOnInit).

Теперь снова, если мы попытаемся перейти от компонента Bк компоненту A, описанный выше процесс повторится (компонент Bбудет уничтожен, и компонент A будет инициализирован).

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

Проблема

Инициализация и уничтожение компонента — довольно дорогостоящий процесс, потому что весь JavaScript должен быть выполнен снова, и DOM должен быть отрендерен снова.

Это может постепенно привести к ухудшению производительности, если размер компонента большой.

Решение

При переходе к другому компоненту, вместо уничтожения текущего компонента, что, если мы сможем сохранить компонент где-нибудь и повторно использовать его при повторном обращении к нему?

Возможно ли это?
Да
, мы можем сделать это, используя Angular Стратегии повторного использования маршрута

Что такое стратегия повторного использования маршрута?

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

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

Поведение по умолчанию

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

Таким образом, это означает, что каждый раз angular будет уничтожать компонент и воссоздавать его снова при повторном посещении.

Мы можем переопределить это поведение по умолчанию, внедрив собственную RouteReuseStrategy.

Как реализовать собственную стратегию RouteReuseStrategy?

Для этого нам нужно предоставить наш собственный класс в качестве значения существующего класса RouteReuseStrategy, который содержит поведение angular по умолчанию.

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

Существует пять методов, которые будут вызываться один за другим в определенном порядке при любом изменении пути маршрутизации.

1. следует отсоединить

@param1 — ActivatedRouteSnapshot
returnType-boolean

  • Это будет вызываться первым при переходе с одного маршрута на другой, чтобы определить, можно ли отсоединить компонент (сохранить вместо его уничтожения) для повторного использования.
  • Мы можем получить доступ к сведениям о маршруте, используя параметр ActivatedRouteSnapshot, и мы можем вернуть true, если мы хотим отсоединиться, или вернуть false.

2. хранить

@param1 — ActivatedRouteSnapshot , @param2 — DetachedRouteHandle ,
returnType-boolean

  • Когда shouldDetach возвращает true, это означает, что этот метод будет вызван, и здесь у нас может быть собственная логика для хранения где-то экземпляра компонента.
  • Мы можем получить сведения о текущем маршруте из параметра ActivatedRouteSnapshot, а экземпляр его компонента — из DetachedRouteHandle.
  • Как мы сохраняем, полностью зависит от нас, мы можем сохранить его в объекте, карте, сервисе или чем-то еще, (мы должны иметь возможность получить сохраненное значение в другом методе с именем retrieve, который мы увидим ниже)
  • Путь маршрута и соответствующий экземпляр компонента (DetachedRouteHandle) рекомендуется хранить в паре ключ-значение, чтобы при необходимости их можно было легко получить.

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

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

3. следует прикрепить

@param1 — ActivatedRouteSnapshot
returnType-boolean

  • Этот метод используется для определения того, следует ли повторно использовать компонент, который мы пытаемся загрузить (вместо того, чтобы создавать его заново) или нет.
  • Верните true, если мы хотим повторно использовать, или верните false.

4. получить

@param1 — ActivatedRouteSnapshot
returnType-DetachedRouteHandle

  • Когда shouldAttach возвращает true означает, что этот метод будет вызван, и здесь нам нужно получить и вернуть экземпляр компонента (DetachedRouteHandle), который мы сохранили в методе store() для этого путь маршрута (мы можем получить доступ к пути, используя ActivatedRouteSnapshotparam.

5. следует повторно использовать маршрут

  • Этот метод используется для определения того, следует ли повторно использовать маршрут или нет.
  • Это должно вернуть true, чтобы заставить работать как стратегию Angular по умолчанию, так и нашу собственную стратегию.

Давайте создадим нашу собственную стратегию повторного использования маршрута для нашего приложения, где мы настроили маршрутизацию /compA и /compB для загрузки компонентов component A и component B соответственно.

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

Мы реализуем нашу RouteReuseStrategy таким образом, чтобы оба компонента использовались повторно.

Шаг 1. Создайте новый класс, реализуя RouteReuseStrategyабстрактный класс.

  • RouteReuseStrategy — это абстрактный класс, в котором будут объявлены все методы конфигурации, которые мы обсуждали выше, поэтому нам нужно определить эти методы в новом классе, который мы создаем.

Шаг 2. Определите метод shouldDetach

  • Нам нужно вернуть true здесь, если путь маршрута compA или compB, чтобы этот компонент не был уничтожен при выходе.
  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    const path = route.routeConfig.path;
    return path && ['compA', 'compB'].includes(path);
  }

Шаг 3.определение метода store

  • Нам нужно сохранить экземпляр компонента в виде DetachedRouteHandle для повторного использования.
private routeStore = new Map<string, DetachedRouteHandle>();

store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.routeStore.set(route.routeConfig.path, handle);
  }

Шаг 4: определите метод shouldAttach

  • Нам нужно вернуть true, если путь маршрута равен compA или compB и в нашем routerStore есть любое значение DetachedRouteHandle для этого пути маршрута.
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const path = route.routeConfig.path;
    return (
      path && ['compA', 'compB'].includes(path) && !!this.routeStore.get(path)
    );
  }

Шаг 5: определите метод retrive

  • Нам нужно вернуть DetachedRouteHandle, который мы сохранили для этого пути маршрута в методе store().
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const path = route.routeConfig.path;
    return this.routeStore.get(path);
  }

Шаг 6: определите метод shouldReuseRoute

  • Этот метод используется для определения того, следует ли повторно использовать маршруты или нет, он будет вызываться при каждом изменении маршрута, если мы попытаемся перейти к тому же компоненту, в котором мы сейчас находимся, он будет использоваться повторно.
  • curr — маршрут, с которого мы уходим,
    future — маршрут, на который пытаемся перейти
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }

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

import { ActivatedRouteSnapshot, DetachedRouteHandle, RouteReuseStrategy,} from '@angular/router';

export class CustomRouteReuseStrategy implements RouteReuseStrategy {
  private routeStore = new Map<string, DetachedRouteHandle>();

  shouldDetach(route: ActivatedRouteSnapshot): boolean {
    const path = route.routeConfig.path;
    return path && ['compA', 'compB'].includes(path);
  }
  store(route: ActivatedRouteSnapshot, handle: DetachedRouteHandle): void {
    this.routeStore.set(route.routeConfig.path, handle);
  }
  shouldAttach(route: ActivatedRouteSnapshot): boolean {
    const path = route.routeConfig.path;
    return (
      path && ['compA', 'compB'].includes(path) && !!this.routeStore.get(path)
    );
  }
  retrieve(route: ActivatedRouteSnapshot): DetachedRouteHandle {
    const path = route.routeConfig.path;
    return this.routeStore.get(path);
  }
  shouldReuseRoute(future: ActivatedRouteSnapshot, curr: ActivatedRouteSnapshot): boolean {
    return future.routeConfig === curr.routeConfig;
  }
}

Шаг 7. Предоставьте нашему классу значение по умолчанию RouteReuseStrategy

  • После всех настроек нам нужно указать Angular использовать этот класс в качестве значения, чтобы определить, как повторно использовать маршрутизацию. мы можем сделать это, зарегистрировав этот класс в массиве провайдера как значение существующего класса RouteReuseStrategy по умолчанию Angular:
// app.module.ts  
 providers: [
    {
      provide: RouteReuseStrategy,
      useClass: CustomRouteReuseStrategy,
    },
 ],

Вот и все, теперь мы реализовали собственную стратегию повторного использования маршрута для нашего приложения.

Посмотрите приведенный ниже GIF, чтобы понять поведение маршрутизации до и после реализации RouteReuseStrategy.

Посмотрите живой пример здесь, на stackblitz.

⚠️ Примечание.
Если мы передаем какие-либо данные по маршруту с использованием средств queryParams или params, нам нужно написать пользовательскую логику внутри метода shouldAttach, чтобы проверить, присутствуют ли params или queryParams в сохраненном маршрут и текущий маршрут совпадают или нет, если оба path и params одинаковы, то нам нужно вернуть только true.

Спасибо за прочтение, надеюсь, эта статья поможет вам хоть как-то обогатить свои знания.

Подпишитесь на меня в Linkedin и Medium, чтобы узнать больше такого контента.

Продолжайте учиться и продолжайте программировать!

Создавайте приложения с повторно используемыми компонентами, такими как Lego.

Инструмент с открытым исходным кодом Bit помогает более чем 250 000 разработчиков создавать приложения с компонентами.

Превратите любой пользовательский интерфейс, функцию или страницу в компонент многократного использования — и поделитесь им со своими приложениями. Легче сотрудничать и строить быстрее.

Подробнее

Разделите приложения на компоненты, чтобы упростить разработку приложений, и наслаждайтесь наилучшими возможностями для рабочих процессов, которые вы хотите:

Микро-интерфейсы

Система дизайна

Совместное использование кода и повторное использование

Монорепо

Узнать больше