- PVSM.RU - https://www.pvsm.ru -
Всем, привет. Меня зовут Виталий Киреев и я руковожу исследованиями и разработкой в IT-компании. Компания существует уже много лет и в разработке накопилось довольно много Legacy-кода. Мы регулярно проводим аудит на предмет использования устаревших библиотек и меняем их, если в этом есть необходимость. В этой статье я расскажу о практических кейсах, с которыми мы столкнулись при замене широко известной библиотеки Lodash [1] для Javascript.
Сразу оговорюсь, что на Хабре есть очень хорошая статья на эту тему "Вам не нужен Lodash" [2]. Очень рекомендую её прочитать и не буду писать спойлеры, скажу только, что для нас довольно критичными стали следующие моменты:
отсутствие обновлений библиотеки с 2021 года (v4.17.21). Сам релиз v4 от 2016 года;
критические уязвимости безопасности при проверке OWASP Dependency check.
Есть ещё несколько некритичных моментов, связанных с неочевидной работой некоторых методов и увеличением зависимостей в проекте.
Не смотря но то, что статья очень хорошо и подробно описывает минусы использования этой библиотеки, в ней совсем вскользь упоминаются практические советы, как и на что менять Lodash в различных кейсах. Именно этот пробел я и попробую закрыть в этой статье, а помог мне в этом Александр Долженко, наш frontend разработчик.
Сама библиотека довольно большая и кейсов, в которых её используют тоже много, но здесь я бы хотел обсудить те из них, с которыми мы столкнулись в нашем проекте Личного Кабинета пользователя на React.js. Вероятно у вас будут и другие варианты использования, но, надеюсь, что наши советы вам тоже будут полезны. Итак, после аудита, нашего проекта мы определили кейсы, в которых использовался Lodash:
cloneDeep (клонированиe объектов);
isEmpty (проверка на пустое значение);
isEqual (проверка на эквивалентность двух объектов);
every (проверка всех элементов коллекции на соответствие условию);
get (безопасное извлечение свойства из объекта);
round (округление).
Начнем с того, что многие методы библиотеки можно заменить на нативные, и наши не исключение.
cloneDeep() -> structuredClone()
Для большинства случаев копирования нативный structureClone прекрасно справится, но нужно учитывать следующие особенности:
при попытке склонировать функции получим ошибку;
не получится склонировать DOM-элементы;
прототипы при клонировании потеряются.
В нашем проекте таких кейсов не было, поэтому замена прошла гладко.
every() -> Object.values().every()
Мы использовали проверки соответствия элементов коллекции условиям, причем часто это были, например, коллекции валидаторов, которые было удобно проверять таким образом
import _every from "lodash/every";
// в коллекции isValid валидаторы проверки данных
if (_every(isValid, Boolean)) {
// все данные валидны
}
в таком случае мы заменяем на нативный метод every, который применяем для значений коллекции:
// в коллекции isValid валидаторы проверки данных
if (Object.values(isValid).every(Boolean)) {
// все данные валидны
}
get() -> опциональные операторы (?.) и nullish coalescing (??)
Действительно, безопасное получение значения свойства возможна и нативными средствами Javascript. Например, пытаемся получить свойство key из values, и если его нет возвращаем пустую строку
import get from "lodash/get";
export const replaceData = (string = "", values = {}) => {
return string.replace(/{{s*(.*?)s*}}/g, (s, key) =>
get(values, key, ""),
);
};
заменим на опциональный оператор и nullish coalescing
export const replaceData = (string = "", values = {}) => {
return string.replace(/{{s*(.*?)s*}}/g, (s, key) =>
values?.[key] ?? "",
);
};
round() -> Math.round()
Здесь без комментариев, как и в примерах работы с массивами из оригинальной статьи о бесполезности Lodash, нет никакого смысла увеличивать число зависимостей, если можно этого не делать.
Это наиболее спорная часть рефакторинга, т.к. кто-то может решить использовать сторонние библиотеки, вместо написания собственного кода, и он имеет на это полное право. Для наших случаев использования командой было принято решение написать собственные реализации, которые я бы хотел показать, как пример решения проблемы.
isEmpty()
Например, в нашей frontend части строка из одних пробелов также является пустой, поэтому реализация метода получилась такой:
export const isEmpty = (value) => {
return (
value === undefined ||
value === null ||
(typeof value === "object" && Object.keys(value).length === 0) ||
(typeof value === "string" && value.trim().length === 0)
// уберите trim, если строка из одних пробелов для вас не пустая
);
};
isEqual()
При проверке на эквивалентность двух объектов мы учитываем нюансы сравнения типов Date, Array, Object:
export const isEqual = (a, b) => {
if (typeof a !== "object" && typeof b !== "object") {
return Object.is(a, b);
}
// Хотя null — примитивный тип в JavaScript, из-за некоторых
// исторических особенностей тип null — object, поэтому нам требуется
// дополнительная обработка для null.
if (a === null && b === null) {
return true;
}
if (typeof a !== typeof b) {
return false;
}
// Сначала пробуем строгое сравнение.
if (a === b) {
return true;
}
// Отдельно сравниваем объекты типа Date
if (a instanceof Date && b instanceof Date) {
return a.getTime() === b.getTime();
}
// Проверка отдельно элеменов в массивах
if (Array.isArray(a) && Array.isArray(b)) {
return a.length === b.length && a.every((value, index) => isEqual(value, b[index]));
}
// Убедимся, что переданные значения точно объекты
if (!a || !b || typeof a !== "object" || typeof b !== "object") return false;
let keysA = Object.keys(a),
keysB = Object.keys(b);
// Проверка свойств в объектах
return keysA.every((key) => keysB.includes(key) && isEqual(a[key], b[key]));
};
Возможно вам потребуется учесть какие-то свои особенности, и вы можете дополнить этот метод.
В заключение своей статьи я бы хотел подвести некоторые итоги процесса отказа от Lodash и профит, который мы получили:
в большинстве кейсов у нас получилось успешно заменить использование устаревшей библиотеки с уязвимостями на нативный код;
в кейсах, где это не представлялось возможным, мы написали собственные реализации методов, которые прозрачно работают и учитывают нюансы нашего проекта;
уменьшили число зависимостей в проекте.
Отказываться или нет от Lodash решать безусловно нужно индивидуально, учитывая особенности каждого проекта, но если вы приняли такое решение, то я надеюсь, что наш опыт окажется для вас полезным. Спасибо за то, что дочитали статью до конца.
Автор: vk15work
Источник [3]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/427251
Ссылки в тексте:
[1] Lodash: https://github.com/lodash/lodash
[2] "Вам не нужен Lodash": https://habr.com/ru/articles/823484/
[3] Источник: https://habr.com/ru/articles/934298/?utm_source=habrahabr&utm_medium=rss&utm_campaign=934298
Нажмите здесь для печати.