В объектно-ориентированном программировании шаблон декоратора - это шаблон проектирования, который позволяет статически или динамически добавлять поведение к отдельному объекту, не влияя на поведение других объектов из того же класса. Он помогает решать повторяющиеся проблемы проектирования для разработки гибкого и многократно используемого объектно-ориентированного программного обеспечения, то есть объектов, которые легче реализовать, изменить, протестировать и повторно использовать. (Источник: Википедия)

Проще говоря, декораторы - это синтаксический сахар, помогающий вам сохранять ваш код читаемым и поддерживаемым, путем обертывания дополнительных или общих функций в отдельном файле, который может совместно использоваться разными компонентами / классами. Концепция декораторов была впервые представлена ​​в python, а сейчас они также становятся частью javascript.

ПРИМЕЧАНИЕ. Декораторы представляют собой предложение этапа 2 для JavaScript и доступны в качестве экспериментальной функции TypeScript. Декораторы - это экспериментальная функция, которая может измениться в будущих выпусках.

Что, почему и как используют декораторы в React:

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

Давайте посмотрим на один пример декоратора функции / метода:

function deprecatedFn(target, name, descriptor) {
  const newDescriptor = { ...descriptor };
  const originalFunction = descriptor.value;     
  function deprecatedFunctionWarning(args) {
   originalFunction.call(this, ...args);
   if(__DEV__) {
     console.warn("This function is about to be Deprecated");
    }
  }
  newDescriptor.value = deprecatedFunctionWarning;  
  return newDescriptor;
}
class SomeOldCodeBaseClass extends React.Component {
  @deprecatedFn
  someFunction() { // Some deprecated logic }
}

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

  • deprecatedFn: декоратор функции (т. Е. Переопределение функции / метода класса), имеет 3 аргумента: цель, имя и дескриптор.
  • target: класс, частью которого является свойство / функция / метод. (в нашем случае SomeOldCodeBaseClass)
  • name: имя свойства / функции / метода, переопределяемого декоратором. (в нашем случае someFunction)
  • дескриптор: дескриптор свойства, аналогичный объекту, переданному в Object.defineProperty (но не то же самое). Он имеет логические значения, такие как enumerable, configurable, value, writable
  • descriptor.value: Он ссылается на функцию, к которой применяется декоратор. (здесь someFunction)
  • Мы пишем другую функцию, которая вызывает исходную функцию (т.е. someFunction) с помощью метода call. Вы можете избавиться от call, если исходная функция не требует контекста this.
  • Наконец, мы назначаем дескриптору вновь созданную функцию, а затем возвращаем новый дескриптор. Теперь, когда React вызывает someFunction, управление переходит к нашему декоратору, который, в свою очередь, возвращает замещенную функцию.

Теперь посмотрим, как работают декораторы классов:

export default Child => {
  class GetExtraDataInProps extends React.Component {
    someHelperFunction() {
      // some logic here
    }
render() {
      const data = {
        // some dummy data
      };
      return <Child {...this.props} callbackFn={someHelperFunction} extraData={data} />;
    }
  }
  return GetExtraDataInProps;
};
@GetExtraDataInProps
export default class HelloWorld extends React.Component { ... }

Декоратор классов очень похож на наши React HOC, то есть оба принимают компонент в качестве входных данных и возвращают компонент таким же образом. Помимо синтаксических различий, давайте поговорим о производительности.

Я использовал react-addons-perf, чтобы сравнить два, где HOC и декоратор имели один и тот же код, единственная разница заключалась в том, как они связаны с Компонентом. Декораторы располагаются поверх Component при объявлении, тогда как HOC упаковываются, пока вы хотите их экспортировать. Я обнаружил, что HOC работают лучше, чем декораторы, поскольку время рендеринга HOC было быстрее, чем у класса декоратора.

Примечание. Производительность рассчитывалась с использованием Perf.start() перед setState() и Perf.end() внутри componentDidUpdate().

Настройка декораторов для вашего проекта:

Поскольку декораторы являются экспериментальной функцией в Typescript, они не являются частью вашей конфигурации Babel по умолчанию, поэтому ваш сборщик выдаст синтаксическую ошибку. Чтобы ваш бандлер был совместим с декораторами, включите плагин babel в свой devDependencies внутри package.json, или вы можете добавить плагин в свой babel.rc файл:

"@babel/plugin-proposal-decorators": "^7.3.0"

Чтобы автоматически привязать (привязать функции внутри класса к контексту класса) методы класса к вашим декораторам класса, включите следующее в свой dependencies внутри package.json:

"class-autobind-decorator": "^3.0.1"

Вывод:

Декораторы могут быть мощным шаблоном проектирования кода, позволяющим писать более чистые и удобные в обслуживании компоненты React, однако я все же рекомендую HOC вместо декораторов классов. Поскольку декораторы все еще находятся на стадии 2 предложения ECMA, они могут меняться со временем, поэтому используйте их с умом.

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

Использованная литература: