HTML Diff
0 added 0 removed
Original 2026-01-01
Modified 2026-02-26
1 <p>Поговорим об обновлении зависимостей. Для обновления всех зависимостей нужно выполнить команду npm update. Чтобы выполнить обновление конкретной зависимости - npm update name, где name - имя библиотеки. А как будет происходить обновление, зависит от того, что написано в package.json.</p>
1 <p>Поговорим об обновлении зависимостей. Для обновления всех зависимостей нужно выполнить команду npm update. Чтобы выполнить обновление конкретной зависимости - npm update name, где name - имя библиотеки. А как будет происходить обновление, зависит от того, что написано в package.json.</p>
2 <p>Рассмотрим все доступные варианты:</p>
2 <p>Рассмотрим все доступные варианты:</p>
3 <ul><li>* означает, что можно ставить любую версию библиотеки. После выполнения команды обновления в папке node_modules окажется последняя доступная версия package1</li>
3 <ul><li>* означает, что можно ставить любую версию библиотеки. После выполнения команды обновления в папке node_modules окажется последняя доступная версия package1</li>
4 <li>1.3.5 - конкретный номер. Если версия библиотеки жестко зафиксирована, никакая команда не сможет обновить ее</li>
4 <li>1.3.5 - конкретный номер. Если версия библиотеки жестко зафиксирована, никакая команда не сможет обновить ее</li>
5 </ul><p>Самый интересный сценарий происходит в случае использования тильды (~).</p>
5 </ul><p>Самый интересный сценарий происходит в случае использования тильды (~).</p>
6 <p>В семантическом версионировании считается, что patch, последняя цифра в версии, изменяется только в случае исправления ошибок, значит, обратная совместимость не должна теряться. При этом на практике это не всегда так. Код может работать с учетом ошибок в зависимостях.</p>
6 <p>В семантическом версионировании считается, что patch, последняя цифра в версии, изменяется только в случае исправления ошибок, значит, обратная совместимость не должна теряться. При этом на практике это не всегда так. Код может работать с учетом ошибок в зависимостях.</p>
7 <p>Как правило, в проектах десятки, а то и сотни зависимостей, причем обновляются они часто. С одной стороны, можно всегда писать *, но тогда обновления нередко могут ломать систему из-за мажорных обновлений библиотек. С другой стороны, можно зафиксировать все версии, и тогда обновлять все придется вручную.</p>
7 <p>Как правило, в проектах десятки, а то и сотни зависимостей, причем обновляются они часто. С одной стороны, можно всегда писать *, но тогда обновления нередко могут ломать систему из-за мажорных обновлений библиотек. С другой стороны, можно зафиксировать все версии, и тогда обновлять все придется вручную.</p>
8 <p>Поэтому появился третий вариант. Добавление тильды приводит к тому, что в автоматическом режиме обновляются только патчи. Предположим что после добавления зависимости в проект, версия была установлена в ~2.10.3. Если после нее в npm репозитории появилась 2.10.5, то она будет установлена командой обновления.</p>
8 <p>Поэтому появился третий вариант. Добавление тильды приводит к тому, что в автоматическом режиме обновляются только патчи. Предположим что после добавления зависимости в проект, версия была установлена в ~2.10.3. Если после нее в npm репозитории появилась 2.10.5, то она будет установлена командой обновления.</p>
9 <p>То же самое произойдет, если потом будет выпущена версия 2.10.15. Но если создатель библиотеки опубликует изменения в мажорной и минорной версии, например, 2.11.5 или 3.0.0, то npm их проигнорирует.</p>
9 <p>То же самое произойдет, если потом будет выпущена версия 2.10.15. Но если создатель библиотеки опубликует изменения в мажорной и минорной версии, например, 2.11.5 или 3.0.0, то npm их проигнорирует.</p>
10 <p>Примерно то же самое происходит и при использовании ^. Только в отличие от тильды она фиксирует мажорную версию, а минорная обновляется наравне с патчем.</p>
10 <p>Примерно то же самое происходит и при использовании ^. Только в отличие от тильды она фиксирует мажорную версию, а минорная обновляется наравне с патчем.</p>
11 <h3>Lock-файл</h3>
11 <h3>Lock-файл</h3>
12 <p>На предыдущем шаге каждая новая установка зависимостей приводила сначала к созданию, а потом - к обновлению файла package-lock.json.</p>
12 <p>На предыдущем шаге каждая новая установка зависимостей приводила сначала к созданию, а потом - к обновлению файла package-lock.json.</p>
13 <p>В package.json указываются зависимости. Мы уже рассмотрели, как их устанавливать и обновлять. Но еще мы помним, что у каждой зависимости могут быть свои зависимости, которые так же обновляются, и так до бесконечности.</p>
13 <p>В package.json указываются зависимости. Мы уже рассмотрели, как их устанавливать и обновлять. Но еще мы помним, что у каждой зависимости могут быть свои зависимости, которые так же обновляются, и так до бесконечности.</p>
14 <p>Зависимости зависимостей называются<strong>транзитивными</strong>, и с ними не все так просто. Настолько не просто, что существует понятие dependency hell - ад зависимостей.</p>
14 <p>Зависимости зависимостей называются<strong>транзитивными</strong>, и с ними не все так просто. Настолько не просто, что существует понятие dependency hell - ад зависимостей.</p>
15 <p>Проблема заключается в том, что мы не фиксируем версии транзитивных зависимостей. Предположим, что в нашем пакете есть зависимость A с зафиксированной версией 1.3.2, у которой в зависимостях стоит пакет B с версией *. В такой ситуации и npm install поставил бы версию зависимости A, указанной версии, но так нельзя сказать про пакет B. Npm поставит последнюю доступную версию из репозитория.</p>
15 <p>Проблема заключается в том, что мы не фиксируем версии транзитивных зависимостей. Предположим, что в нашем пакете есть зависимость A с зафиксированной версией 1.3.2, у которой в зависимостях стоит пакет B с версией *. В такой ситуации и npm install поставил бы версию зависимости A, указанной версии, но так нельзя сказать про пакет B. Npm поставит последнюю доступную версию из репозитория.</p>
16 <p>Такое поведение не детерминировано. Если создатель обновит B и нарушится обратная совместимость, проект сломается, так как перестанет работать A.</p>
16 <p>Такое поведение не детерминировано. Если создатель обновит B и нарушится обратная совместимость, проект сломается, так как перестанет работать A.</p>
17 <p>Если бы мы полгода не заходили в проект, а затем зашли и поставили зависимости заново (удалив папку node_modules или выполнив новое клонирование), то с почти 100% вероятностью ничего не заработает. Как правило, пакеты обновляются часто и какой-нибудь из них обязательно изменит мажорную версию за такой большой срок.</p>
17 <p>Если бы мы полгода не заходили в проект, а затем зашли и поставили зависимости заново (удалив папку node_modules или выполнив новое клонирование), то с почти 100% вероятностью ничего не заработает. Как правило, пакеты обновляются часто и какой-нибудь из них обязательно изменит мажорную версию за такой большой срок.</p>
18 <p>Очевидный, но не рабочий выход из данной ситуации - отслеживать зависимости всех зависимостей руками и явно прописывать их версии в package.json. Такой способ сработает, но даже в проекте, который содержит, например, пять зависимостей, транзитивных зависимостей будет сотни.</p>
18 <p>Очевидный, но не рабочий выход из данной ситуации - отслеживать зависимости всех зависимостей руками и явно прописывать их версии в package.json. Такой способ сработает, но даже в проекте, который содержит, например, пять зависимостей, транзитивных зависимостей будет сотни.</p>
19 <p>И не стоит забывать, что пакеты обновляются и меняются. Такую ситуацию невозможно контролировать, и в итоге зависимости перестанут обновляться.</p>
19 <p>И не стоит забывать, что пакеты обновляются и меняются. Такую ситуацию невозможно контролировать, и в итоге зависимости перестанут обновляться.</p>
20 <p>Другой выход - требовать, чтобы создатели всех библиотек всегда жестко указывали версии. Из-за человеческих факторов это не сработает. При этом автоматизация такого процесса привела бы к полному параличу системы пакетов, библиотек и разработки.</p>
20 <p>Другой выход - требовать, чтобы создатели всех библиотек всегда жестко указывали версии. Из-за человеческих факторов это не сработает. При этом автоматизация такого процесса привела бы к полному параличу системы пакетов, библиотек и разработки.</p>
21 <p>В таком случае нас спасает<strong>lock-файл</strong>- автоматизированное решение первого способа. Его содержимое выглядит примерно так:</p>
21 <p>В таком случае нас спасает<strong>lock-файл</strong>- автоматизированное решение первого способа. Его содержимое выглядит примерно так:</p>
22 <p>Первый запуск установки зависимостей формирует этот файл. Туда записываются все установленные зависимости, в том числе транзитивные со своими версиями. При дальнейших запусках npm install всегда ставится то, что указано в lock-файле, даже если стереть папку node_modules, а в npm репозитории добавятся новые версии пакетов. Повторный запуск через любой промежуток времени приведет к тому же результату.</p>
22 <p>Первый запуск установки зависимостей формирует этот файл. Туда записываются все установленные зависимости, в том числе транзитивные со своими версиями. При дальнейших запусках npm install всегда ставится то, что указано в lock-файле, даже если стереть папку node_modules, а в npm репозитории добавятся новые версии пакетов. Повторный запуск через любой промежуток времени приведет к тому же результату.</p>
23 <p>Теперь всегда можно быть уверенным, что если заработало сейчас, то и заработает потом не только у нас.</p>
23 <p>Теперь всегда можно быть уверенным, что если заработало сейчас, то и заработает потом не только у нас.</p>
24 <p>Наличие lock-файла не влияет на поведение команды update для прямых зависимостей. Если пакет, указанный в package.json, обновился и может быть обновлен в соответствии с тем, как указана его версия, то загрузится новая версия, а файл lock обновится автоматически. После чего нужно не забыть залить его в репозиторий.</p>
24 <p>Наличие lock-файла не влияет на поведение команды update для прямых зависимостей. Если пакет, указанный в package.json, обновился и может быть обновлен в соответствии с тем, как указана его версия, то загрузится новая версия, а файл lock обновится автоматически. После чего нужно не забыть залить его в репозиторий.</p>
25 <p>Поведение lock-файла чуть сложнее, и количество различных ситуаций тоже больше. Но для понимания схемы работы достаточно описанного выше. Если хочется разобраться в теме от и до, стоит обратить внимание на<a>официальную документацию</a>.</p>
25 <p>Поведение lock-файла чуть сложнее, и количество различных ситуаций тоже больше. Но для понимания схемы работы достаточно описанного выше. Если хочется разобраться в теме от и до, стоит обратить внимание на<a>официальную документацию</a>.</p>
26 <p>Как и любой автоматически генерируемый файл, lock-файл не может правиться руками, потому что наши изменения сразу будут сброшены при следующей генерации.</p>
26 <p>Как и любой автоматически генерируемый файл, lock-файл не может правиться руками, потому что наши изменения сразу будут сброшены при следующей генерации.</p>