Главная / Статьи / npm против pnpm: сравнение хранилища, скорости и практических недостатков

npm против pnpm: сравнение хранилища, скорости и практических недостатков

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

2380 слов

Если вы тратили время на разработку приложений с использованием React, Next.js, Node.js или любой другой современной базы кода на JavaScript, скорее всего, вы вводили эту команду больше раз, чем можете сосчитать:

npm install

Она просто работает. Все её знают. Почти все онлайн-учебники опираются на неё.

А потом кто-то говорит вам что-то вроде:

"Зачем вы всё ещё используете npm? Просто используйте pnpm."

И вы решаете попробовать pnpm.

Вскоре вы начинаете задаваться вопросом:

Является ли pnpm действительно улучшением, или это просто ещё одна из тех дискуссий о инструментах JavaScript, которые забываются через несколько месяцев?

Это вполне закономерный вопрос.

Npm ни в коем случае не сломан. Он знаком, надёжен и поставляется вместе с Node.js. Чтобы от него отказаться, нужны веские причины.

Как только вы узнаете, как на самом деле работают эти инструменты, вы поймёте, что настоящая проблема не сводится просто к npm versus pnpm.

Всё сводится к тому, как они управляют зависимостями, сколько дискового пространства они занимают, как происходит установка в разных условиях и какого типа проект вы создаёте.

И самый важный вопрос:

Какой из них действительно имеет смысл использовать?

npm и pnpm решают одну и ту же основную проблему

Начнём с очевидного.

И npm, и pnpm — это менеджеры пакетов, созданные для среды Node.js.

Оба загружают пакеты из реестра npm и используют одни и те же стандарты package.json.

Предположим, вашему проекту нужен React. Вы можете установить его с помощью npm:

npm install react

Или же вы можете воспользоваться pnpm:

pnpm add react

В любом случае React будет установлен.

Вы не перешли на совершенно другую экосистему JavaScript.

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

Именно здесь дизайн pnpm начинает выделяться.

В чем настоящее отличие: как хранятся зависимости

Представьте, что у вас на компьютере есть пять отдельных проектов на JavaScript.

Каждый из них зависит от React.

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

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

pnpm выбирает другой подход.

Он хранит пакеты в едином хранилище с адресным управлением, а затем создает ссылки из этого хранилища в каждый проект, который ими нуждается.

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

Представьте себе следующее:

Project A ──┐
Project B ──┤
Project C ──┼── Shared package store
Project D ──┤
Project E ──┘

Настоящий механизм, стоящий за этим, сложнее, чем показано на этой простой схеме, но именно основная концепция здесь имеет значение.

pnpm разработан для минимизации избыточных копий.

Если вы одновременно работаете над несколькими проектами, такой подход может значительно сократить использование диска.

Ускоряет ли pnpm установку файлов на самом деле?

Это обычно первый вопрос, который интересует разработчиков.

Честный ответ:

Зачастую — да, но не всегда.

Множество тестов показывает, что pnpm значительно быстрее npm при установке.

Однако скорость установки зависит от множества факторов.

Ваше интернет-соединение тоже играет важную роль.

То же самое касается аппаратного обеспечения диска.

Важен также количество пакетов, от которых зависит ваш проект.

То, находятся ли эти пакеты уже в локальной кэш-памяти, тоже имеет значение.

Учитывается также размер проекта.

Даже выполнение чистой установки вместо переустановки уже скачанного ранее контента может привести к совершенно разным результатам.

Архитектура pnpm основана на эффективном загрузке и связывании пакетов, и поскольку она поддерживает общий хранилище, пакеты, уже находящиеся у вас локально, можно просто повторно использовать вместо их повторной загрузки.

Именно в таких ситуациях её преимущества проявляются наиболее ярко.

Разница между «холодными» и «теплыми» установками действительно значительна

Это деталь, которую часто упускают из виду при сравнении менеджеров пакетов.

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

У вас нет другого выбора, кроме как скачать его заново.

По сути, это сценарий начала работы с чистого листа.

Теперь представьте, что точно такой же пакет уже находится в другом проекте на вашем компьютере.

Это совершенно другая отправная точка.

Именно здесь и проявляется польза общего хранилища pnpm.

Вместо того чтобы рассматривать каждый проект как полностью изолированную структуру, pnpm может использовать пакеты, уже сохраненные локально.

Поэтому, если вы регулярно создаете новые проекты, удаляете папки node_modules, часто переустанавливаете зависимости или работаете с несколькими репозиториями, эффективность pnpm со временем становится гораздо более заметной.

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

Вот почему я бы избегал утверждать:

«pnpm всегда является более быстрым вариантом».

Более точное формулирование будет таким:

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

npm сильно развился за пределы своей прежней репутации

Ещё один момент, который стоит упомянуть.

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

Такое описание на самом деле неточно.

npm сильно продвинулся вперед.

Текущая версия поддерживает такие функции, как рабочие пространства, файлы блокировки и команда npm ci для обеспечения воспроизводимых установок в CI-пайплайнах.

В качестве примера:

npm ci

Эта команда регулярно используется, когда нужна чистая установка на основе файла блокировки.

Поэтому называть npm медленным, устаревшим или плохо спроектированным не имеет оснований.

Он по-прежнему остается отличным выбором для огромного количества проектов.

Что отличает pnpm, так это то, что он был создан с упором на эффективность, и эти особенности становятся всё более заметными по мере роста проекта.

Ещё одно отличие, с которым сталкиваются разработчики: изоляция зависимостей

Это не всегда сразу становится очевидным, особенно если вы новичок в этой экосистеме.

Представьте такую ситуацию: ваше приложение зависит от пакета A. Пакет A, в свою очередь, зависит от пакета B. Вы сами никогда не устанавливали B — вы даже не указывали его в своём манифесте. Тем не менее из-за способа организации папки node_modules ваш код всё равно может напрямую использовать B с помощью операторов require или import, и это будет работать.

Некоторое время всё кажется в порядке.

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

У этой ситуации есть название: фантомная зависимость. Вы полагаетесь на то, на что на самом деле не имели права полагаться.

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

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

Сценарий, в котором pnpm действительно проявляет себя лучше всего: монорепозитории

Вероятно, это самый убедительный аргумент в пользу использования pnpm.

Представьте кодовую базу компании, структурированную примерно так:

my-project/
│
├── apps/
│   ├── web/
│   └── admin/
│
├── packages/
│   ├── ui/
│   ├── utils/
│   └── config/
│
└── package.json

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

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

Если вы поддерживаете небольшой личный проект, всё это вам на самом деле не нужно.

Но если вы управляете репозиторием с несколькими приложениями и десятками внутренних общедоступных пакетов, эти инструменты становятся гораздо более полезными.

Переход с npm на pnpm — это небольшая замена

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

На самом деле это не так. Большинство повседневных команд соответствуют друг другу практически один к одному.

Установка зависимостей с помощью npm выглядит так:

npm install

С pnpm это выглядит так:

pnpm install

Добавление пакета в npm:

npm install axios

в pnpm превращается в это:

pnpm add axios

Добавление dev-зависимости в npm:

npm install -D typescript

становится:

pnpm add -D typescript

Удаление пакета в npm:

npm uninstall axios

становится:

pnpm remove axios

Запуск скрипта в npm:

npm run dev

может быть сокращён до:

pnpm dev

Если вы уже хорошо знакомы с npx, у pnpm есть свой аналог:

pnpm dlx

Поэтому уровень сложности обучения здесь минимален.

Ситуации, когда npm по-прежнему является лучшим выбором

Если вы впервые знакомите кого-то с JavaScript или Node.js, npm — естественный выбор для начала. Не потому, что он технически превосходнее во всех отношениях, а потому что он по умолчанию уже встроен, и новичкам и так предстоит многое усвоить, не добавляя к этому ещё и выбор менеджера пакетов.

Если в учебном пособии сказано запустить:

npm install express

вам следует просто ввести команду и продолжить изучение рассматриваемой концепции.

NPM также является правильным выбором, когда вы попадаете в кодовую базу, уже построенную на его основе. Нет смысла настаивать на чем-то другом:

«Команда всегда использовала npm, но здесь предпочтительнее pnpm, поэтому давайте преобразуем всю конфигурацию».

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

Ситуации, когда pnpm становится более целесообразным

pnpm становится более привлекательным по мере увеличения масштаба проекта или рабочего процесса.

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

Изоляция зависимостей — ещё одна причина для использования pnpm: если строгие границы между тем, что объявлено, и тем, что можно использовать, действительно важны для вашего проекта, pnpm автоматически обеспечивает их соблюдение по умолчанию.

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

Так кто же побеждает в скорости?

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

Тем не менее было бы некорректно утверждать что-то вроде:

"pnpm работает ровно в два раза быстрее, чем npm."

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

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

Что бы я выбрал

Для небольшого проекта на React npm отлично справляется со своей задачей без лишних проблем.

То же самое касается проектов для обучения — npm подходит идеально.

Если вы присоединяетесь к существующему кодовому репозиторию команды, используйте то, что уже стандартизировано в команде.

Однако для крупного монорепозитория pnpm становится серьезным конкурентом.

Если вы работаете на компьютере, где постоянно переключаетесь между различными проектами на JavaScript, pnpm, как правило, является более практичным выбором.

Все это объясняет, почему здесь нет единого универсального победителя.

Не меняйте инструмент только потому, что pnpm в моде

Это, возможно, самый важный момент во всем этом сравнении.

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

И уж тем более вам не нужно менять инструмент из-за настойчивости кого-то:

«npm мертв».

Это не так. Оба инструмента активно поддерживаются, у обоих есть развитые экосистемы, и оба в полной мере способны обрабатывать современные проекты на JavaScript.

Настоящий вопрос не в том, «какой менеджер пакетов объективно лучше». Вопрос в том, «какой менеджер пакетов лучше всего подходит для моей работы».

Если вы разрабатываете небольшие приложения, изучаете язык или работаете в команде, которая уже использует npm, то оставаться с npm — это совершенно разумный выбор.

Если же вы занимаетесь крупными проектами, несколькими репозиториями или монорепозиториями и хотите более эффективное хранение зависимостей и быстрее установку, тогда стоит попробовать pnpm.

Перед сравнением npm и pnpm казалось, что будет однозначный ответ: npm — устаревший вариант, а pnpm — более быстрое обновление.

На деле всё оказалось более сложным.

Основная сила npm — в его простоте и том, что почти все уже знакомы с ним. Он является стандартным выбором не просто так.

Основные преимущества pnpm — это его модель хранения зависимостей, высокая эффективность во время установки и инструменты, предоставляемые для крупномасштабных проектов.

Если вы только начинаете работать с JavaScript, не мучайтесь с выбором — просто используйте npm и начинайте разработку.

Если у вас уже есть прочные знания Node.js и ваши проекты растут в масштабах, попробуйте pnpm и посмотрите, улучшит ли он вашу повседневную работу.

В конечном итоге именно менеджер пакетов не делает приложение хорошим.

Это делает ваш код.

Связанные статьи

  • Node.js Streams Beyond the Basics: Memory, Backpressure, and Real Failures — Узнайте, как потоки Node.js взаимодействуют с Web Streams, оцените экономию памяти с помощью реальных тестов и ознакомьтесь с ошибками в производственной среде, которые проявляются только при высокой нагрузке. —