Всем привет. Сегодня я вернулся с отпускной поездки, проснулся, сделал себе кофеек, заказал завтрак, и решил глянуть наконец-то вышедший State Of JS 2023.
Читаю, открываю секцию библиотек - и первое, что я вижу
33% момента тоже ужасают (про jquery молчу) - но давайте поговорим про лодаш.
Я последние годы являюсь его ярым антагонистом. Я считаю, что многие либо используют его неправильно, либо используют без смысла - а для новичков он даже может быть вреден. Попробую объяснить свою позицию - поехали!
1. Установка
Первое, чем нас встречает лодаш - набором библиотек. У вас есть выбор:
Установить основной лодаш целиком (но он может криво работать на ласт вебпаке и тем более Vite - и ругаться на ESM)
Установить отдельно пакеты - но они не обновляются и потом будут депрекейтнуты
Установить lodash-es, но он тоже может криво работать на разных версиях вебпака или окружениях (SSR/CSR), иногда придется пострадать со странными ошибками - просто другими, нежели с обычной версией
lodash-fp, но он депрекейтет 8 лет назад
lodash-amd и использовать AMD лоадер (кто-то еще так делает?). И пакет не обновлялся еще больше основного
Помимо этого, вам, в идеале, надо настроить плагин бейбла и вебпак) На выбор представлено множество вариантов кода
Итого, вы скорее всего не сможете установить и использовать лодаш одной командой инсталла - на всех проектах уж точно.
Кроме того, вот эти отдельные бандлы вызывают проблемы - иногда хочется инстиктивно установить только то, что нужно, но они могли не обновляться более пяти лет - и содержать уязвимости. А предупреждения от разработчиков никто не читает, конечно.
А если сможете - см. пункт 2.
2. Использование
Итак, что нам рекомендует документация:
Если мы будем следовать этой рекомендации, мы подключим целиком весь бандл лодаша. Минифицированный бандл на CDN весит 72 килобайта. Вроде и немного, но грузить все методы браузеру тоже будет грустновато.
Ну ладно! Что у нас там дальше:
Итак, что из этого нам выбрать?
Полную сборку грузить, наверное, не очень
Неплохо выглядит core, но в документации нет информации, что туда входит
Для чего нужен FP - никто не поймет
Категории методов грузить удобно, но там их тоже куча + надо смотреть доку
К чему я веду. Представьте - вы новичок и зашли сюда, вам нужно по бырику взять метод глубокого клонирования. Вы смотрите на доку и ничего не понимаете. Что вы сделаете?
Правильно. Скорее всего вы подгрузите весь lodash, в лучшем случае - core. Также допустимы ошибки по типу загрузить отдельный npm пакет - к чему это может привести писал в конце первого раздела.
По итогу, большинство проектов грузят весь лодаш целиком, хотя им нужны оттуда методов пять. При условии корректной настройки babel, webpack (или другого сборщика), у вас есть шанс получить работающий tree-shaking, которого нет из коробки по умолчанию. lodash-es должен работать нормально, но это при условии, что не возникнет проблем при его установке - или разработчик не сдастся их решать.
Неконсистентность пакетов и подключения вызывает такие запросы в топе гугла по запросу "lodash esm":
Круто, не правда ли? Но мы идем дальше!
3. Вы используете из него 5 методов
Конечно, это применимо не ко всем проектам - в основном, к старым. Но невероятно част тот кейс, когда разработчик подключает лодаш ради... одной-двух функций.
В современном стеке, ОЧЕНЬ много методов лодаша уже было портировано. Я люблю задавать вопрос на собесе - как глубоко склонировать объект? Больше половины, особенно новичков, называют "лодаш дипклон" - что, на мой взгляд, является проблемой, при условии, что существует structuredClone, который имеет неплохую поддержку и полифиллы, а также на крайняк куча легковесных современных библиотек.
По итогу, lodash во многих случаях позволяет вам делать то, что УЖЕ поддерживается в JS... но хуже. Понятно, что некоторые методы "незаменимы", но возьмем несколько примеров:
array.drop -> slice
array.concat -> filter
array.fill -> splice
findIndex/findLastIndex/indexOf/join/reverse/slice -> натив
flatten -> flatMap
Ну и так далее. Но вы скажете, что же насчет незаменимых методов? Давайте посмотрим на те, что я видел чаще всего:
debounce -> это несколько строк кода. Вы можете написать свой крохотный метод с удобным API и так, как вам нужно
delay -> это вообще setTimeout, лучше написать свой асинхронный sleep из 5 строк кода
-
clone -> structuredClone покрывает почти всё, кроме чего-то реактивного
Это один из тех методов, который я не могу рекомендовать как 100% заменимый - я бы его заменял библиотекой для клонирования - по типу https://www.npmjs.com/package/klona
difference -> нормальных замен нет, но есть замены, по типу deep-object-diff и других (и там не очень много кода! Мы, например, написали свой метод на основе deep-object-diff)
В остальном, по моим наблюдением, другие методы либо не используются, либо могут быть весьма легко заменены нативными.
4. modern - это ложь
На сайте lodash указано, что он modern - что далеко от истины. Лодаш не использует современные сборщики, не обновляется годами, множество методов есть в нативе (и нативные, вероятно, будут быстрее - и не жрать место/память как минимум).
Кроме того, при использовании аналогов из лодаша вместо натива вы упускаете возможность следить за тем, что добавляется в натив - а там много всего очень интересного! Всякие toSorted, новые методы коллекций - можно использовать в ноде, можно обмазаться полифиллами (смотря какие версии браузеров вам нужны). Использование этих методов в том числе прокачивает ваше знание как джаваскриптера. И для этой прокачки вам точно не нужен лодаш.
Резюме
Лодаш:
Имеет множество методов установки, каждый из которых может вызывать проблемы и головную боль с подключением
Легко подключается целиком вместо нескольких нужных вам методов
Не обновляется
Не нужен в 95% случаев
Мешает пониманию действительно современных методов
Вам не нужен лодаш. Пройдитесь по своим методам, выкиньте доступные в нативе, замените недоступные (если они вам точно нужны).
А если уж вы его используете:
Используйте es-версию с костылями под TS, если потребуются, или обычную с плагинами для tree-shaking
Не используйте эти отдельные пакеты - только
lodash
илиlodash-es
Не увеличивайте его процент использования в вашем коде. Пожалуйста
Вы может быть еще и Moment используете? Тоже пора обновляться!
Хороший пример
Хотел бы показать прекрасных ребят из UnJS, создавшие набор un-iversal библиотек: https://unjs.io
Почему это топ:
Действительно современный и очень легковесный код с минимум сборки
Декомпозированные пакеты - берете то, что нужно
Не нужно ставить всякие плагины babel/ts - всё работает из коробки, как только импортнете. Установил и используй!
Я давненько хотел сделать эту статью. Есть тут любители lodash? Поделитесь, продолжите ли вы его использовать после всех пунктов выше - и почему? А если вы давно хотели уйти, но не хватало аргументов - ловите :)
И читаем State Of JS 2023: https://2023.stateofjs.com/en-US (я написал резюме на русском в своем тг канале: https://t.me/webdanil/137. Это же можно так делать, да? Вроде не пиарюсь... почти...)
Комментарии (46)
aleks_raiden
21.06.2024 09:10+2Вот я как пример пользователя лодаша (правда на сервер-сайде). Использую: _.noop, _.clone, _.clamp, _.min / _.max, _.compact а также более удобные обертки типа isNaN / isInteger. Удобно и минималистично ) зачем мне самописно заменять все, если одна маленькая и многолетне вылизанная либа все покрывает )
daniluk4000 Автор
21.06.2024 09:10+3Не сказал бы что совсем маленькая) Минмакс есть в жс, isnan есть в Number.isnan, за остальное не подскажу с ходу, но все же - смысла не вижу
artptr86
21.06.2024 09:10+1Ещё одна, не упомянутая в статье, проблема лодаша — по наследству от андерскора функции в нём перегружены для множества различных типов. То есть, условный
filter
будет обязательно содержать код для фильтрации и объектов, и массивов, да ещё и, из-за перегрузки типа второго аргумента, — код для фильтрации по явному предикату, если будет передана функция, либо по множеству ключей-значений черезmatches
, если будет передан объект; по ключу-значению черезmatchesProperty
, если будет передан массив-кортеж; по значению поля черезproperty
. Притом этот код не может быть удалён тришейком.Но вы скажете, что же насчет незаменимых методов? Давайте посмотрим на те, что я видел чаще всего
По моим прикидкам, чаще всего из «незаменимого» встречаются debounce и sortBy.
daniluk4000 Автор
21.06.2024 09:10Спасибо!
Про методы: дебаунс совсем уж легко заменить, sortBy... Не знаю с ходу отличий от обычного сорта, но как будто мы не так часто сортируем, а когда да, прописывание логики по типу (a,b) => a - b даже помогает в понимании (имхо)
artptr86
21.06.2024 09:10+1Отличие в том, что sortBy позволяет делать сортировку по нескольким полям одновременно. Например,
sortBy(users, ['lastName', 'firstName'])
artptr86
21.06.2024 09:10Можно написать собственную версию где-нибудь в utils, но даже этот код можно долго улучшать для поддержки направлений сортировки, чисел параллельно со строками или для натуральной сортировки строк с числами:
function comparator(keys) { const coll = new Intl.Collator('ru'); return (a, b) => keys.reduce((acc, key) => (acc || coll.compare(a[key], b[key])), 0); } // users.toSorted(comparator(['lastName', 'firstName']))
Так что это хороший кандидат на выделение в библиотеку.
daniluk4000 Автор
21.06.2024 09:10+1Тут соглашусь, звучит не так легко. Просто сам никогда этим не пользовался, но фишка удобная
sfi0zy
21.06.2024 09:10+7В целом я со всем согласен, но
Вы можете написать свой крохотный метод с удобным API и так, как вам нужно
Приводит к тому, что в разных местах проекта плодятся крохотные, почти одинаковые, но не совсем, методы, от разных авторов, которые делают вроде одно и то же, но не совсем. Это такой зоопарк, от которого увеличивается вероятность логических ошибок, которые сложно отлавливать. Нужно постоянно в новый контекст влезать. Плюс дублирующийся код, который порой сложно просто так сократить в дальнейшем из-за едва различимой разницы во внутренней логике. Тут нужно сильно заморачиваться с ревью и документацией, чтобы этого не допустить. Но это тоже время, да и вечные споры, как какую помогалку лучше сделать и назвать. Библиотеки - сборники утилит, вроде lodash или D3, дают тот же эффект, что и инструменты по типу prettier - тимлид приходит, бъет кулаком в стол и говорит "мы делаем так!". И все, больше никакого зоопарка. Это далеко не идеальный вариант, но тем не менее проблему синхронизации всех участников решает.
artptr86
21.06.2024 09:10Это не совсем решает вопрос с наличием аналогичных встроенных методов. Например, делать map/filter/toSorted массива через нативную версию или через Lodash?
daniluk4000 Автор
21.06.2024 09:10+2Скажу как тимлид, ударивший кулаком: написали свой UI Kit c утилитами и юзаем. Полет - прекрасный)
Lazytech
21.06.2024 09:10+7dom1n1k
21.06.2024 09:10Первая же функция в списке реализована адски неэффективно
Lazytech
21.06.2024 09:10+1Каюсь, читал по верхам. Да, согласен, многократное применение деструктуризации массива не способствует высокой производительности.
Разумеется, существуют другие варианты реализации такой функции:
javascript - Split array into chunks - Stack OverflowЕще кое-что:
You Might Not Need Lodash
Альтернативная реализация chunk (вроде тоже не очень эффективная?)dom1n1k
21.06.2024 09:10Внутри Lodash хорошая реализация. По вашей ссылке на SO топовый ответ тоже хороший (на самом деле там только цикл другой, а базовая схема та же).
Я немного поэкспериментировал, сравнил несколько вариаций с разными циклами и немного разными подходами - все более-менее близки, максимальный разбег примерно x3. Но вот та реализация с деструктуризациями на каждой итерации - катастрофа. Если массив более-менее большой, она в сотни и тысячи раз медленнее, на несколько минут(!) может залипнуть.
molnij
21.06.2024 09:10Даже любопытно стало, а вместо момента вы что предлагаете? Придуманный инопланетянами Intl?
daniluk4000 Автор
21.06.2024 09:10+1В целом что угодно современное. Мы используем Intl и натив, но если не подходит, сейчас date-fns очень популярен слышал. А так Moment просто официально умер, поэтому лучше бы слезать
sumdy-c
21.06.2024 09:10Меня возьмут в рай если я буду клонировать объект так:
const obj = { test: { test: { test: 'Hi!' } } }; const cloneObj = JSON.parse(JSON.stringify(obj));
Я бы взял. Ну лучше пока мир не придумал....
daniluk4000 Автор
21.06.2024 09:10+2Самый надежный метод из придуманных, конечно) Но да, structuredClone - наш выход
bromzh
21.06.2024 09:10Это ок только если ты прям точно уверен, то копируемый объект не будет содержать циклических ссылок.
Gibboustooth
21.06.2024 09:10Или NaN. Или BigInt. Или функций... В целом, прекрасный способ выстрелить себе в ногу.
zurzeropsi
21.06.2024 09:10+2Есть мнение, что программирование - оно все о переиспользовании. Переиспользовании проверенных, протестированных и доказавших свою надежность решений вместо написания своих велосипедов.
Я сталкивался с лодашными проектами: читать этот код больно, местами ты не понимаешь
А написать по 5-10 строк кода тут-там, забыть что оно уже написано (или не знать, что оно уже написано другим разрабом) и лежит воооон в том дальнем углу огромного проекта, и написать еще разок - весь этот замес будет более читаемым?
Если плохи дела со сборкой и 24kb gzipped - много, то просто создайте свой ПР, который позволит использовать более современный подход. Вродь же живая репа, мерджи есть. 50 млн только спасибо скажут https://npmtrends.com/lodash, а откажутся ли они после данной статьи - это врядли )
daniluk4000 Автор
21.06.2024 09:10+1Я очень ленивый разработчик, поэтому написали свой набор утилит, и локально в проектах переиспользуем composable (я вьюшник) в отдельных папках. И это не велосипеды, а небольшие методы. Велосипеды есть, но их минимум - если совсем выхода нет, подключаем библиотеку. Но ни разу за все время в АВ на куче проектов никто не сказал "это нельзя сделать без лодаша".
А писать PR и переписывать лодаш - вот опять же, смысл? Ради тех пяти методов? Им всем уже есть современные легковесные альтернативы (если уж нет в нативе), может за редкими исключениями.
Ares_ekb
21.06.2024 09:10+1Переиспользовании проверенных, протестированных и доказавших свою надежность решений вместо написания своих велосипедов
Другая крайность - это вместо велосипеда получить франкенштейна. Навтягивать из разных библиотек однострочных функций. На мой взгляд уж лучше написать у себя эти функции в едином стиле, чем тянуть лишние зависимости в проект
n0ne
21.06.2024 09:10+2Любая библиотека - это всего лишь инструмент, каждый сам себе решает, использовать или нет.
Если платят за написание собственных велосипедов - можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов, о которых ни вы, ни я даже не слышали.
В-третьих, люди не работают всю жизнь на одном месте. И никому неизвестные собственные велосипеды всегда будут проигрывать хорошо известным, типа, аналогам.
Не нравится теущий инструмент - поменял на другой. Со своим велосипедом придется "плакать, но всё равно продолжать есть кактус".
И такие библиотеки, как lodash, ramda, etc. являются прекрасным обучающим материалом, который просто необходимо просматривать иногда.
daniluk4000 Автор
21.06.2024 09:10можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов
Зависит от метода. Условный дебаунс тяжело написать плохо
можно и писать, но они заведомо будут хуже того, что было протестированно на таком количестве тест кейсов
Опять же - зависит от сложности и огромности проекта. В большинстве случаев, на больших проектах уже и так куча утилит, а тут просто +5 на замену лодаша, а на мелких часто вам многие методы и не нужны
Не нравится теущий инструмент - поменял на другой
Когда весь код покрыт лодашем - так не получится. Мы его относительно долго выпиливали с одного проекта, где он также использовался совершенно бысмысленно
И такие библиотеки, как lodash, ramda, etc. являются прекрасным обучающим материалом
Вот совершенно не соглашусь. Их часто используют, не вникая в то, как это работает - методы простые и решают задачу, зачем вникать, пока с ними нет проблем или непоняток?
В отличие от того же UnJS, которые местами написали сложные штуки, на которые хочется взглянуть - да и то не делают.
n0ne
21.06.2024 09:10Зависит от метода. Условный дебаунс тяжело написать плохо
Вот как раз дебаунс, хороший, с параметрами, отменой и т.п. написать совсем непросто. Он в библиотеках обычно десятки, а то и сотни LOC
В большинстве случаев, на больших проектах уже и так куча утилит, а тут просто +5 на замену лодаша, а на мелких часто вам многие методы и не нужны
Наверно я на недостаточно крупном проекте, в котором только девелоперов 1500 человек от Сиднея до Рейкьявика, но никому и в голову не придет выпиливать простой, удобный и надежный, как лом, lodash
Мы его относительно долго выпиливали с одного проекта, где он также использовался совершенно бысмысленно
Если вы на проекте бессмысленно используете инструмент - это разве говорит о том, что плохой инструмент?! Это говорит совсем о другом.
Вот совершенно не соглашусь.
Ваше право. Если Вы или Ваши сотрудники не смотрите хоть иногда в код того, что используете, не пытаетесь понять, что-то перенять, чему-то научиться - это очень и очень странно, в свою команду я бы не хотел таких разработчиков.
P.S. И да, про FP было смешно: я думал речь о разработчиках в статье идет, а не о просто обывателях-домохозяйках.
xemos
21.06.2024 09:101) structuredClone работает не эквивалентно deepClone, например он теряет конструктор класса, не копирует функции, не копирует геттеры и сеттеры
2) "Для чего нужен FP - никто не поймет", вы серьёзно?
daniluk4000 Автор
21.06.2024 09:10Верно, но на моей практике такие объекты нужно копировать весьма редко
Обыватель - вряд ли. Даже если он доберется для доки - она такая, что проще забить и не использовать (https://github.com/lodash/lodash/wiki/FP-Guide)
rtatarinov
21.06.2024 09:10Я понимаю, что лодаш создает проблемы с тришейкингом, но писать свои утилиты это огромная проблема! Своя утилита должна обрабатывать все кейсы, которые разработчик вряд ли покроет. Это первое! А второе, типы, граждане вывод типов. Все ваши собственные утилиты никогда нормально не умели в сужение типов. Сколько я этого насмотрелся в проектах! Я не понимаю почему все резко стали против библиотек. Я использую ramda, и не вижу ничего плохого. Она полность тришейкабл и может отлично работать с типами.
Dartess
21.06.2024 09:10Помню для тришейкабл нужны были какие-то костыли.
А вот с типами вообще не соглашусь. Это js-ная либа без типов, типы - сторонние, и у меня регулярно при обновлении зависимостей что-то отваливалось.
Ещё один минус того что либа js-ная - она обмазана проверками типов. Каждая функция будет проверять, что в неё передаётся, валидировать, приводить типы и стараться покрыть все их комбинации. Это ж сколько лишнего кода!
В итоге плюнул и пересел на remeda. Стало гораздо лучше. Тришейкается вообще без лишних движений, компактная, изначально ts. Советую посмотреть.
KLIJIN
21.06.2024 09:10Может современный мир развращает, но я предпочитаю вместе с chartGPT писать микроутилиты 10-ти строчные и кидать их в папку shared. Так я хоть полностью понимаю и контролирую что происходит в коде.
posledam
21.06.2024 09:10Вопрос автору. А какие минусы в использовании lodash? Понятно, что некоторые функции уже перекрываются нативной поддержкой. Опять же, не все, и не везде. Но вот зачем может понадобиться выпиливание? Что в итоге получим? "более лучший/чистый/правильный код"? Хотелось бы что-нибудь более существенного :)
daniluk4000 Автор
21.06.2024 09:10+1Вы простите, конечно, но я процитирую себя:
В правильный код я не верю. Скорее всего, код без лодаша будет работать быстрее - как минимум после замены на натив. Использование нативных методов также улучшит понимание того, что можно делать в чистом JS. Ну и код, скорее всего, правда будет чище - меньше импортов, меньше неймспейсов с методами.
daniluk4000 Автор
А, ну и бонусный комментарий: пожалейте разработчиков, которые не используют лодаш. Я сталкивался с лодашными проектами: читать этот код больно, местами ты не понимаешь, зачем нужно вот это, а что делает вот это. Документация у них не идеальная, и, опять же, новички могут его использовать совсем уж неправильно.
В отличие от всяких библиотек, интегрированных в фреймворк, лодаш совсем не выглядит нативно - особенно с этим чудесным андерскором.
KLIJIN
Я в 2024 году просто копирую кусок кода в chart GPT с запросом "объясни подробно этот код"
И gpt возвращает длинную простыню с человекочитаемым объяснением. Только это много времени отнимает
mazdai19
хорошая статья, нужна такая же про моментс
daniluk4000 Автор
Не нужна, потому что он официально устарел) Там нет особо дискуссии