Стратегии рефакторинга

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

Ключевая особенность Stencil — генерация стандартных Web Components. Это накладывает требования к чистоте API компонентов, стабильности атрибутов и минимизации побочных эффектов.

Основные цели рефакторинга:

  • уменьшение связности компонентов;
  • упрощение жизненного цикла;
  • сокращение дублирования логики;
  • повышение тестируемости;
  • улучшение производительности при рендеринге.

Декомпозиция крупных компонентов

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

Признаки необходимости декомпозиции:

  • большое количество @State() и @Watch();
  • сложный метод render() с вложенными условиями;
  • повторяющиеся фрагменты JSX;
  • логика, не связанная напрямую с визуальным представлением.

Стратегия:

  • выделение дочерних компонентов с чётким контрактом через @Prop();
  • перенос части состояния вверх или вниз по иерархии;
  • отказ от передачи функций через props в пользу событий.

Пример разбиения:

  • контейнерный компонент отвечает за данные и события;
  • презентационные компоненты — за разметку и стили.

Выделение бизнес-логики из компонентов

Компоненты Stencil не предназначены для хранения сложной бизнес-логики. Рефакторинг предполагает вынос такой логики в отдельные модули.

Подходы:

  • utility-функции без побочных эффектов;
  • сервисные классы без зависимости от DOM;
  • кастомные хелперы для обработки данных.

Преимущества:

  • повторное использование логики между компонентами;
  • упрощение тестирования без среды браузера;
  • уменьшение объёма компонента.

Компонент в идеале содержит:

  • декларацию props и state;
  • реакцию на события;
  • минимальную координацию между данными и представлением.

Оптимизация состояния и реактивности

Stencil использует реактивную модель на основе @State() и @Prop(). Ошибки в управлении состоянием часто приводят к лишним перерендерам.

Типичные проблемы:

  • избыточное состояние;
  • хранение производных данных в @State();
  • частые изменения объектов и массивов.

Стратегии рефакторинга:

  • вычисление производных значений на лету;
  • использование иммутабельных обновлений;
  • сокращение количества @Watch().

Плохая практика:

@State() filteredItems: Item[];

Предпочтительный вариант:

get filteredItems() {
  return this.items.filter(...);
}

Упрощение жизненного цикла компонента

Stencil предоставляет хуки:

  • componentWillLoad
  • componentDidLoad
  • componentWillUpdate
  • componentDidUpdate
  • disconnectedCallback

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

Рефакторинг включает:

  • удаление дублирующей логики между хуками;
  • перенос асинхронной инициализации в componentWillLoad;
  • отказ от componentWillUpdate при возможности вычислений в render.

Хорошая практика — минимальное количество хуков с чётким назначением каждого.


Рефакторинг API компонентов

Публичный API веб-компонента — его props, события и методы, помеченные @Method().

Причины для рефакторинга API:

  • неоднозначные имена props;
  • props с двойным назначением;
  • события с неявным смыслом;
  • методы, напрямую меняющие состояние.

Стратегии:

  • переименование props в декларативном стиле (isOpen, hasError);
  • замена boolean-флагов на enum-подобные значения;
  • унификация структуры CustomEvent.detail;
  • сокращение количества публичных методов.

Важно сохранять обратную совместимость или проводить рефакторинг в мажорной версии.


Работа со слотами и композиция

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

Признаки необходимости рефакторинга:

  • логика, зависящая от наличия слота;
  • сложные проверки this.el.querySelector('slot');
  • неочевидное поведение при отсутствии контента.

Решения:

  • использование именованных слотов;
  • явная документация ожиданий компонента;
  • fallback-разметка внутри <slot>.

Пример:

<slot name="header">
  <default-header />
</slot>

Оптимизация render-функции

Метод render() должен оставаться максимально декларативным.

Антипаттерны:

  • побочные эффекты в render;
  • сложные вычисления;
  • изменения состояния.

Рефакторинг включает:

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

Допустимо:

const hasError = this.errorMessage !== '';

Недопустимо:

this.state = computeState();

Рефакторинг событий и коммуникации

Stencil использует @Event() для общения между компонентами.

Проблемы:

  • избыточное количество событий;
  • события с неструктурированными данными;
  • использование событий для внутренней коммуникации.

Стратегии:

  • события только для внешнего взаимодействия;
  • строгая типизация EventEmitter<T>;
  • единый стиль именования (componentAction).

Пример:

@Event() userSelected: EventEmitter<User>;

Улучшение тестируемости компонентов

Рефакторинг под тесты — важная часть поддержки кода.

Подходы:

  • уменьшение зависимости от глобального состояния;
  • отказ от прямой работы с document;
  • использование dependency injection через props.

Компоненты с минимальной логикой легче тестируются через @stencil/core/testing.


Очистка и стандартизация кода

Финальный этап рефакторинга:

  • удаление неиспользуемых props, state и методов;
  • выравнивание стиля именования;
  • упрощение типов;
  • согласованное использование readonly.

Единый стиль компонентов повышает скорость понимания кода и снижает стоимость сопровождения.


Рефакторинг как непрерывный процесс

В проектах на Stencil рефакторинг не является разовым действием. Архитектура компонентов должна эволюционировать вместе с требованиями, оставаясь простой, предсказуемой и соответствующей стандартам Web Components.