Что такое npm на самом деле
npm — это три вещи, объединённые под одним именем. Во-первых, это реестр — огромная публичная база JavaScript-пакетов на npmjs.com. Во-вторых, это утилита командной строки, которая идёт в комплекте с Node.js и отвечает за установку и управление пакетами. И в-третьих, это спецификация (формат package.json), описывающая, что нужно проекту.
Когда вы выполняете npm install express, CLI обращается к реестру, скачивает express вместе со всеми его зависимостями, складывает файлы в папку node_modules и записывает пакет с его версией в ваш package.json. Вот и весь цикл.
Если Node.js уже установлен, то npm у вас тоже есть. Проверим:
node --version
npm --version
Если обе команды вывели версию — всё готово, можно двигаться дальше.
Создание проекта: npm init
В любом проекте на Node.js должен быть файл package.json. Это манифест проекта: в нём указаны название, версия, скрипты и список зависимостей. Быстрее всего сгенерировать его командой npm init -y — она создаст файл со значениями по умолчанию:
mkdir my-app
cd my-app
npm init -y
Получится примерно такое:
{
"name": "my-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"keywords": [],
"author": "",
"license": "ISC"
}
Если убрать -y, npm проведёт вас по каждому полю в интерактивном режиме. В любом случае на выходе получится package.json — файл, к которому цепляется всё остальное. Подробно разберём его поля на следующей странице.
Установка пакета через npm install
Когда package.json уже есть, пакет ставится командой npm install (или коротко npm i):
npm install lodash
Происходит сразу три вещи:
- npm скачивает
lodashи все его зависимости в папкуnode_modules/. - В
package.jsonв разделdependenciesдобавляется строка"lodash": "^4.17.21"(или та версия, которая актуальна на момент установки). - Создаётся файл
package-lock.json, в котором зафиксированы точные версии всех пакетов из дерева зависимостей.
Теперь библиотеку можно использовать:
Вызов require (или import, если у вас ESM-проект) находит пакет, заглядывая в node_modules. Путь писать не нужно — за вас это делает модульный резолвер Node.
dependencies и devDependencies: в чём разница
Далеко не все пакеты нужны приложению в продакшене. Тестовые фреймворки, линтеры и сборщики работают только во время разработки. Такие зависимости ставятся с флагом --save-dev (или коротко -D):
npm install --save-dev jest
npm install -D eslint prettier
Такие пакеты попадают в devDependencies, а не в dependencies:
{
"dependencies": {
"lodash": "^4.17.21"
},
"devDependencies": {
"jest": "^29.7.0",
"eslint": "^8.57.0",
"prettier": "^3.2.5"
}
}
На продакшн-сервере команда npm install --omit=dev полностью пропускает dev-зависимости — установка получается легче и быстрее. Правильное разделение здесь важнее, чем кажется на первый взгляд: случайно забытый webpack в dependencies будет раздувать каждый продакшн-деплой.
Установка всех зависимостей разом
Когда вы клонируете репозиторий, в котором уже есть package.json, перечислять каждый пакет вручную не нужно. Достаточно выполнить:
npm install
Запустив npm install без аргументов, npm читает package.json (и чётко следует версиям из package-lock.json), после чего устанавливает всё дерево зависимостей в node_modules. Это первая команда, которую вы выполняете на любом свежем клоне репозитория.
Именно поэтому node_modules должен лежать в .gitignore. Эта папка воспроизводится из lock-файла, весит прилично и меняется при каждом запуске npm install у любого разработчика. В репозиторий коммитим только package.json и package-lock.json — node_modules каждый сгенерирует у себя сам.
Как обновлять пакеты через npm
Команда npm outdated покажет, что отстало от актуальных версий:
npm outdated
Вы увидите таблицу со столбцами Current, Wanted и Latest. Wanted — это самая свежая версия, которую разрешает диапазон в package.json (для ^4.17.21 это всё, что ниже 5.0.0). Latest — это самая новая опубликованная версия, и вполне возможно, что это мажорный релиз, на который вы ещё не перешли.
Чтобы обновиться в рамках разрешённого диапазона:
npm update
Чтобы обновиться до самой свежей версии, включая мажорные обновления, просто установите пакет заново с флагом @latest:
npm install lodash@latest
Смена мажорной версии может сломать ваш код — как раз об этом и сигнализирует номер версии. Прежде чем шагать через такую границу, обязательно загляните в changelog.
Как удалить пакет через npm uninstall
Удаление пакета работает симметрично установке:
npm uninstall lodash
Пакет удалится из node_modules, и запись в package.json тоже исчезнет. Если это была dev-зависимость, можно добавить -D — npm, конечно, разберётся и сам, но с явным флагом меньше сюрпризов в скриптах.
Глобальная установка против локальной
В подавляющем большинстве случаев пакеты ставятся локально — привязанными к одному проекту, внутрь его node_modules. Исключение — консольные утилиты, которые хочется вызывать из любой точки системы:
npm install -g typescript
npm install -g http-server
Глобальная установка кладёт инструмент в системное место и прописывает его bin в PATH, благодаря чему команды вроде tsc или http-server можно запускать из любой директории. Правда, такие пакеты не привязаны к конкретному проекту и легко расходятся по версиям между машинами.
Для разовых команд удобнее золотая середина — npx, который идёт в комплекте с npm:
npx create-react-app my-app
npx prettier --write .
npx позволяет запустить пакет без глобальной установки — он подтягивает его по требованию, выполняет, и на этом всё. Для утилит, которые нужны один раз, это гораздо аккуратнее, чем навсегда ставить их глобально.
Минимальная шпаргалка
Команды, которыми реально пользуешься каждый день:
npm init -y # создать package.json
npm install # установить всё из package.json
npm install <pkg> # добавить runtime-зависимость
npm install -D <pkg> # добавить dev-зависимость
npm install -g <pkg> # установить CLI-утилиту глобально
npm uninstall <pkg> # удалить зависимость
npm outdated # посмотреть, что устарело
npm update # обновить в пределах разрешённых диапазонов
npm install <pkg>@latest # перейти на самую свежую версию
npm run <script> # запустить скрипт из package.json
npx <pkg> # запустить пакет без установки
Вот, пожалуй, и всё главное про npm. Остальное — публикация пакетов, workspaces, scoped-пакеты — подтянете, когда реально понадобится.
Что на самом деле лежит в node_modules
Ещё одна полезная ментальная модель. node_modules — это почти плоская папка, в которой лежат все пакеты, от которых зависит ваш проект, плюс всё, от чего зависят они, и так по цепочке. Ставите один пакет — и вместе с ним может приехать ещё сотня. Это нормально. Там, где возможно, npm дедуплицирует зависимости: если два пакета хотят одну и ту же версию lodash, она будет лежать в одном экземпляре.
Lock-файл (package-lock.json) фиксирует точную версию каждого из этих пакетов — ту, которая была реально установлена. Именно благодаря ему сборки воспроизводимы: два разработчика, запустив npm install с одним и тем же lock-файлом, получат побайтово одинаковое дерево зависимостей — хоть сегодня, хоть через полгода.
Относитесь к node_modules как к сгенерированной папке. Никогда не правьте файлы внутри — ваши изменения исчезнут при первой же установке зависимостей.
Дальше: package.json
package.json — это тот самый файл, который npm постоянно читает и переписывает за кулисами. Как только вы разберётесь с его полями — scripts, main, type, диапазоны версий, engines — npm перестанет быть чёрным ящиком и превратится в понятный инструмент. Об этом и поговорим дальше.
Часто задаваемые вопросы
Что такое npm?
npm — это штатный пакетный менеджер Node.js. Он идёт в комплекте с самим Node, хостит огромный публичный реестр JavaScript-пакетов и даёт консольную утилиту для их установки, обновления и публикации. Когда вы пишете npm install lodash, npm скачивает lodash из реестра в папку node_modules и записывает его в package.json.
Чем dependencies отличаются от devDependencies?
dependencies — это пакеты, без которых приложение не запустится в проде: условные express или react. devDependencies нужны только на этапе разработки и сборки — тест-раннеры, бандлеры, линтеры. Ставятся они командой npm install --save-dev <pkg> (или коротко -D). В проде можно выполнить npm install --omit=dev, и dev-зависимости будут пропущены.
Стоит ли коммитить node_modules в git?
Нет, не надо. node_modules легко разрастается до сотен мегабайт, и при этом его можно в любой момент воспроизвести из package.json и package-lock.json. Добавьте папку в .gitignore, а в репозиторий коммитьте lock-файл. Любой, кто клонирует проект, запустит npm install и получит ровно то же дерево зависимостей.
В чём разница между глобальной и локальной установкой npm?
Локальная установка (npm install <pkg>) кладёт пакет внутрь node_modules вашего проекта и прописывает его в package.json. Глобальная (npm install -g <pkg>) ставит пакет в систему целиком — обычно так делают с CLI-утилитами, которые нужны везде. Для зависимостей конкретного проекта всегда лучше локальная установка, чтобы версии были зафиксированы отдельно под каждый проект.