В этом посте я расскажу об одной простой ошибке, которую часто совершают разработчики и которая наверняка потребует рефакторинга раньше, чем позже: брак зависимостей.

Что вы имеете в виду, говоря о женитьбе на иждивении?

В современном JavaScript (т. Е. С использованием импорта модуля JS и, возможно, сборщика) очень часто используется сторонний код из npm. Всякий раз, когда вы видите такую ​​строку:

import somePackage from 'some-package'

Вы действительно получаете это:

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

Этот пост не о выборе хорошего сторонника

Но я полагаю, что в последнее время эта тема требует большего внимания.

Когда вы делаете это import, упомянутое выше, вы немедленно наследуете все сторонние функции, это практично и здорово. Но на мой взгляд, это не только самая важная часть сделки.

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

Важны не только особенности, зависимость, с которой вы только что женились, имеет историю, ошибки, дорожную карту и кто знает, что еще она может скрывать от вас?

Но мне это нравится, что может пойти не так?

Что ж, так много всего, что мне придется составить список:

  • От пакета можно отказаться - JS - это настоящий ажиотаж, это означает, что новые пакеты будут появляться и исчезать в мгновение ока. Экосистема меняется слишком быстро, то, что кажется важным сегодня, завтра может устареть.
  • Пакет может перестать поддерживать единственную функцию, которую вы использовали из него - некоторые пакеты начинаются с какой-то конкретной цели, но по мере роста они начинают охватывать все больше, пока эта первоначальная цель не будет затенена второстепенными вещами. Теперь автор хочет вернуться к исходному курсу и решает отказаться от всего несущественного, то есть только той функции, которую вы используете.
  • Пакет толстеет: бывает. Много. Ваше приложение - спортсмен, ничего не весит и загружается очень быстро, но затем одна из ваших зависимостей становится толстой и больше не может успевать за вашей скоростью и весом.
  • Пакет, который вы потребляете, объединяет так много пакетов, что он тратит все свое время, пытаясь посоветоваться с другими пакетами. Пока что не может быть никакого прогресса.

Как уберечься от болезненного развода

Помимо аналогий, давайте приступим к практике.

Теперь мы рассмотрим некоторые приемы, которые продлят этот брак и, безусловно, помогут при разводе. Мы будем использовать https://github.com/simov/slugify в качестве реального примера пакета.

Первый шаг - всегда проксировать (или адаптировать) ваш брак через локальный файл:

export default from 'slugify'

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

Адаптация брака так, чтобы он работал в обоих направлениях

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

import slugify from 'slugify'
const mySlug = slugify(someString, { lower: true })

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

import slugify from 'slugify'
const makeSlug = (string, options) =>
  slugify(string, {
    lower: true,
    ...options,
  })
export default makeSlug

В приведенном выше фрагменте кода мы сохранили тот же API, но переключили параметр по умолчанию на свой вкус. Это очень гибко, потому что вы всегда можете переопределить lower, передав его как дополнительный параметр параметра и установить false.

Добавление к химии

Теперь предположим, что вы хотите ограничить длину строки заголовка, но сам пакет не предоставляет эту функцию. Что ты собираешься делать? Расставаться? Найдите другого сторонника?

Нет! Вы защищены, просто улучшите свою функцию-оболочку, чтобы она выполняла ее за вас. Используя оператор распространения ES6, вы можете безопасно сохранить тот же API, добавив больше опций поверх, если он.

import slugify from 'slugify'
const makeSlug = (string, { maxLength = MAX_SLUG_LENGTH, ...options } = {}) =>
  slugify(string, {
    lower: true,
    ...options,
  }).substr(0, maxLength)
export default makeSlug

Мы расширили второй параметр дополнительным maxLengthoption, сохранив совместимость.

Убедитесь, что он прошел тест

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

import slugify from './slugify'
test('slugify', () => {
  expect(slugify('UPPER CASE string')).toBe('upper-case-string')
  expect(slugify('UPPER CASE string', { lower: false })).toBe('UPPER-CASE-string')
  expect(
    slugify('something with more length then allowed which should be trimmed to the default maxlen')
      .length
  ).toBe(MAX_SLUG_LENGTH)
})

Мы просто не подходят друг другу

К сожалению, момент, к которому вы теперь готовы, настал: пакет, с которым вы поженились, стал частным, и вы не можете позволить себе его лицензию. Очень жаль?

Надеюсь, вы быстро найдете другого сторонника (не на основе ⭐️️️⭐️). Теперь у вас больше опыта и меньше шансов сделать неправильный выбор. Адаптация должна быть менее болезненной.

Подведение итогов

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