Всем привет!
Я уверен, что вы видели проекты Node.js, использующие разные менеджеры пакетов, например:
Я видел это сам и работал со всем вышеперечисленным, но у меня всегда был вопрос: что заставляет людей/команды использовать Yarn или pnpm вместо npm ? Каковы плюсы? Есть ли минусы?
Что ж… Давайте узнаем!
Я решил сравнить npm , Yarn и pnpm с точки зрения их «скорости»…
Ниже вы увидите 3 меры:
Создайте файл блокировки без кэша.
Установите зависимости из существующих файлов блокировки без кэша.
Установите зависимости из существующих файлов блокировки с глобальным кешем.
Существует два типа кэша:
Глобальный.
Обычно хранится в домашнем каталоге пользователя (fe, ~/.yarn/berry/cache
).
Местный.
Хранится в каталоге проекта (fe, <project-dir>/.yarn
).
Хотя, по моему опыту, наиболее распространенными вариантами использования являются № 2 и № 3 , на всякий случай я также выбрал № 1 (хотя это очень редкий случай).
В качестве примера для тестов я использовал пример проекта из create-react-app .
Это менеджер пакетов по умолчанию для экосистемы Node.js — что еще сказать? Он поставляется с установочным пакетом, поэтому он практически готов к использованию при установке Node.js на свой компьютер (или в любом поставщике CI , если вы там настроили Node.js ).
На мой взгляд, это огромный плюс – вам не нужно устанавливать его отдельно!
Ничего выдающегося, просто… работает! И за прошедшие годы я не заметил каких-либо серьезных ошибок — он кажется довольно стабильным и выполняет свою работу.
Возможности npm, которые я использовал до сих пор:
npm хранит зависимости в папке node_modules
в корне вашего проекта. Довольно просто.
ℹ️ package-lock.json
хранит информацию о реестрах для перечисленных пакетов — это ОЧЕНЬ удобно, если у вас есть пакеты из одной области, т.е. @example-company
в разных реестрах (например — npm & GitHub Packages ):
Теперь давайте посмотрим, как он работает с точки зрения скорости установки…
Потребовалосьpackage-lock.json
и установил зависимости без кеша.
Используемая команда:
npm i
Потребовалосьpackage-lock.json
без кеша.
Используемая команда:
npm ci
Потребовалосьpackage-lock.json
с глобальным кешем.
Используемая команда:
npm ci
Мне удалось создать рабочее пространство и управлять зависимостями для всего рабочего пространства сразу и для конкретных проектов отдельно.
Другими словами, он выполняет свою работу без каких-либо ошибок/проблем, а официальная документация довольно проста.
Функции рабочей области, которые я использовал до сих пор:
Честно говоря, некоторые особенности пряжи я особо не пробовала. Я имею в виду, что я много использовал его в плане «установки зависимостей» при работе над некоторыми проектами, и все.
В комплект поставки Yarn не входит установщик Node.js , поэтому вам придется установить его отдельно. Это означает, что в ваших конвейерах CI будет дополнительный шаг — вам придется настроить пряжу перед установкой зависимостей проекта.
В Yarn есть два подхода к установке зависимостей:
« Нулевые установки » (по умолчанию) — создает папку .yarn
и перечисляет пакеты в файлах yarn.lock
и .pnp.cjs
.
Обычный, аналогичный npm , сохраняет зависимости в node_modules
и перечисляет их в файле yarn.lock
.
ℹ️ файлы блокировки пряжи хранят информацию о реестрах для всех перечисленных пакетов ТОЛЬКО если вы используете старый (обычный) подход к установке.
⚠️ Имейте в виду, что « Нулевые установки », похоже, хранят пакеты в локальном кеше и предоставляют ссылки на ваши файлы блокировки:
Это может быть важно для вас, если у вас есть Dockerfile или CI -конвейер, в котором вы устанавливаете зависимости в одну чистую среду, а затем хотите переместить их в другую (вам придется скопировать и папку .yarn
, и локальный кеш).
Поскольку подход по умолчанию для Yarn сейчас — « Нулевые установки » и имеет лучшую производительность, чем старый подход — мы собираемся записывать тесты только с этим подходом.
Потребовалосьyarn.lock
и установил зависимости без кеша.
Используемая команда:
yarn install
Потребовалось
Используемая команда:
yarn install --frozen-lockfile
Потребовалось
Используемая команда:
yarn install --frozen-lockfile
Мне удалось создать рабочее пространство и управлять зависимостями для всех проектов сразу и для конкретных проектов отдельно.
Функции рабочей области, которые я использовал до сих пор:
Документация хорошая, но имена и флаги команд несколько сбивают с толку.
Например, мне нужно выполнить это, чтобы запустить test
скрипт в корневом ( . ) и вложенном проекте b2b
:
yarn workspaces foreach -A --include '{.,b2b}' run test
По сравнению с npm :
npm run test --workspace=b2b --include-workspace-root
pnpm сейчас на хайпе — его используют множество компаний и проектов с открытым исходным кодом .
Как и в случае с Yarn , pnpm не поставляется с установщиком Node.js , поэтому его придется устанавливать отдельно. Это означает, что в ваших конвейерах CI будет дополнительный шаг — вам придется настроить pnpm перед установкой зависимостей проекта.
pnpm считается « быстрым менеджером пакетов, эффективно использующим дисковое пространство » …
Действительно, я согласен с утверждением об «эффективном дисковом пространстве» с точки зрения локального управления зависимостями.
По умолчанию pnpm устраняет дубликаты общих зависимостей. pnpm создает символические ссылки для пакетов, которые используются в нескольких зависимостях. т. е. если пакеты a
и b
используют пакет c
в качестве зависимости, pnpm сохранит пакет c
как одну копию и создаст символические ссылки для пакетов a
и b
. Таким образом, менеджер пакетов не создает бумажные копии и экономит память на вашем SSD/HDD.
ℹ️ pnpm-lock.yaml
не хранит информацию о реестрах для перечисленных пакетов.
⚠️ Имейте в виду, что pnpm иногда хранит зависимости в глобальном кеше, а не как проект.
Потребовалосьpnpm-lock.yaml
и установил зависимости без кэша.
Используемая команда:
pnpm install
Потребовалосьpnpm-lock.yaml
без кеша.
Используемая команда:
pnpm i --frozen-lockfile
Потребовалосьpnpm-lock.yaml
с глобальным кешем.
Используемая команда:
pnpm i --frozen-lockfile
Вот тут-то всё и становится по-настоящему интересным…
pnpm имеет множество опций конфигурации, но некоторые основные функции просто не работают!
Давайте рассмотрим пару ошибок, с которыми я столкнулся:
Важно иметь возможность устанавливать зависимости только для конкретных проектов — это весьма полезно для монорепозиториев, когда вы создаете конвейеры, связанные с конкретными проектами в рабочей области.
т. е. представьте, что в вашем рабочем пространстве есть:
Все это отдельные проекты npm , но они являются частью одного репо ☝️
Теперь вам нужно, чтобы конвейер запускал только сквозные тесты. Итак, вам нужны только сквозные тестовые зависимости, верно?
Ну, вы не сможете этого сделать — pnpm заставляет вас устанавливать зависимости для всей рабочей области!
pnpm install --filter <project-name>
должен был устанавливать зависимости только для выбранных проектов, но это не работает.
Это ошибка годичной давности, и недавно она была закрыта с нерабочим исправлением.
pnpm по умолчанию устанавливает зависимости для всей рабочей области (всех проектов) при запуске pnpm install
Вы можете изменить это поведение, если установите recursive-install=false
в .npmrc
в корне вашего рабочего пространства.
НО это вводит еще одну ошибку, которой уже почти 2 года .
pnpm по умолчанию хранит список зависимостей в одном файле блокировки (так же, как npm и Yarn ).
Вы также можете изменить это поведение, если shared-workspace-lockfile=false
в .npmrc
в корне вашего рабочего пространства.
Это позволило бы нам сохранить функцию рабочей области и использовать флаг --ignore-workspace
для установки зависимостей для конкретного проекта.
В любом случае, этот параметр создает еще несколько проблем:
eslint
и tsc --noEmit
выдают ошибку «JavaScript Heap Out of Memory» в моих конвейерах действий GitHub .
Некоторые зависимости хранятся в глобальном кеше и имеют символические ссылки в node_modules/.pnpm
.
# | НПМ | пряжа | пнпм |
---|---|---|---|
Создать файл блокировки | 60 секунд | 16,5 сек. | 31 секунда |
Установить зависимости без кеша | 18 секунд | 11 секунд | 8 секунд |
Установите зависимости с глобальным кешем | 8 секунд | 8 секунд | 5 секунд |
Согласно приведенному выше тесту, npm — самый медленный менеджер пакетов ☝️
В любом случае, давайте интерпретируем эти результаты…
Это редкий случай. Обычно файл блокировки создается при инициализации проекта, а затем расширяется при установке/обновлении пакетов.
Учитывая это, кажется, что при выборе менеджера пакетов не так уж важно полагаться на него.
В большинстве случаев ваши проекты хранят определенный список зависимостей, и вы редко что-то добавляете/удаляете.
Скорее всего, вы будете время от времени обновлять версии своих пакетов — эти изменения невелики, и вы будете повторно использовать остальные пакеты из кеша.
Другими словами, общий вариант использования — получение новых пакетов из реестра пакетов и получение остальных из кеша.
pnpm (5-8 сек) почти в два раза быстрее, чем npm (8-18 сек) с пряжей (8-11 сек) посередине.
Я думаю, что pnpm лучше всего справляется со своей задачей, если ваши требования к менеджеру пакетов такие же простые, как «устанавливать только зависимости».
Несмотря на то, что pnpm не поставляется с готовым установщиком Node.js , его легко настроить в конвейерах CI с помощью Corepack или существующих действий .
Я предпочитаю npm , потому что:
package-lock.json
, поэтому вы можете устанавливать зависимости с одной областью из разных реестров.
Эти плюсы перевешивают секунды скорости и дискового пространства, которые я бы сэкономил с помощью Yarn или pnpm .
Каковы ваши критерии выбора пакетного менеджера? Не стесняйтесь и дайте мне знать свои мысли в разделе комментариев ниже! 👇😊