Главная / Статьи

Перенос проекта Node.js на Bun: внутренние механизмы выполнения, Lambda и процесс миграции

Понять, что заменяет Bun в среде разработки Node.js, почему он запускается быстро, как он обрабатывает TypeScript и работает в AWS Lambda, а также как пошагово мигрировать проект.

Издатель: REACTAPP.TOP6 мин чтения1493 слов

Источники: Everything You Need to Know About Bun: A Beginner’s Guide, Migration, and Architecture

Типичный сервис на Node.js зависит от четырех или пяти отдельных инструментов до того, как сможет выполнить хотя бы одну строку бизнес-логики: менеджера пакетов, запускача TypeScript, фреймворка тестирования и инструмента для сборки. Bun объединяет все эти компоненты в один исполняемый файл, который выполняет функции среды выполнения, менеджера пакетов, инструмента для сборки и запуска тестов, при этом он предназначен стать прямой заменой Node.js. В этом руководстве объясняется, что именно заменяет Bun, откуда берется его скорость, как он обрабатывает TypeScript и развертывание без сервера, а также как перенести существующий проект на Bun без неожиданностей. Для более полного сравнения на основе тестов, включающего также Deno, см. Сравнение Node.js, Deno и Bun.

Что заменяет Bun в типичной цепочке инструментов

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

  • npm или Yarn для установки и управления пакетами;
  • tsc, ts-node или tsx для компиляции или запуска TypeScript;
  • Jest или Vitest для тестирования на единицы;
  • esbuild, webpack или Rollup для формирования готовых к использованию бандлов.

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

Сравнение преимуществ и недостатков по сравнению с Node.js

Решение в основном зависит от скорости и интеграции с одной стороны, а также от зрелости экосистемы — с другой.

Чем превосходит Bun

  • Один интегрированный набор инструментов. Не требуется настройка отдельного запускателя тестов, упаковщика или транспилятора TypeScript.
  • Быстрая установка. По данным самого Bun, команда bun install работает в до 30 раз быстрее, чем npm; скорость будет зависеть от проекта и состояния кэша.
  • Быстрый старт без подготовки. Процессы запускаются за миллисекунды, что особенно важно в безсерверных средах, где задержка старта взимается и ощущается пользователями.
  • TypeScript без настройки. Файлы .ts и .tsx запускаются напрямую, без этапа сборки или скриптов загрузчика.
  • Меньшее потребление памяти. По имеющимся данным, HTTP-серверы потребляют на 25%–40% меньше оперативной памяти, чем Node.js; перед тем как полагаться на эти цифры, измерьте нагрузку на своем оборудовании.
  • Встроенные батареи. Файлы .env загружаются автоматически без необходимости использования dotenv, а среда выполнения включает встроенные клиенты для SQLite, Postgres, S3 и Redis.
  • В чем Node.js всё ещё превосходит

    • Зрелость. Node.js имеет более 15 лет опыта использования в производстве и официальные версии с долгосрочной поддержкой, на которые рассчитывают предприятия. Bun ещё моложе и по-прежнему устраняет проблемы совместимости в редких случаях.
    • Нативные дополнения. Подавляющее большинство пакетов npm (по некоторым оценкам более 95%) работают без изменений, однако старые пакеты, компилирующие код на C++ с помощью node-gyp, могут всё ещё сталкиваться с проблемами.

    Почему Bun запускается и работает быстро

    Дело не просто в том, что «Zig быстрее C++»

    Bun написан на Zig, и кажется логичным приписывать его скорость самому языку. На практике компилированный Zig и компилированный C++ работают примерно с одинаковой скоростью на процессоре. Разница заключается в архитектуре. Node.js использует многолетние накопленные абстракции, тогда как Bun заново реализовал операции ввода-вывода файлов, обработку строк и сетевые функции, уделяя особое внимание избеганию ненужных выделений памяти.

    Другой движок JavaScript

    Node.js работает на движке V8, созданном Google для Chrome. Bun использует JavaScriptCore — движок, разработанный Apple для Safari. JavaScriptCore настроен для быстрого запуска и интенсивной оптимизации памяти, что объясняет лучшие показатели быстрого запуска у Bun. У каждого движка есть задачи, в которых он работает быстрее, поэтому для долгосрочных сервисов с высокой нагрузкой на обработку данных следует проводить тестирование, а не полагаться на автоматическое улучшение производительности.

    Преимущества не ограничиваются macOS

    Хотя JavaScriptCore был создан для Safari, как он, так и Bun компилируются нативно для Linux на архитектурах x86_64 и ARM64, а также для Windows. Характеристики производительности сохраняются на обычных облачных серверах независимо от операционной системы.

    Запуск Bun на AWS Lambda

    Bun может обслуживать HTTP-бэкенды с помощью своего встроенного API Bun.serve() или с использованием известных фреймворков, таких как Express, Fastify и Hono. В среде AWS Lambda его быстрая загрузка при первом запуске позволяет снизить как задержку, так и стоимость выполнения. Lambda не предоставляет управляемой среды выполнения Bun для выбора в консоли, поэтому команды обычно выбирают один из трех подходов:

    • Собственный слой среды выполнения. Опубликуйте бинарник bun в качестве слоя Lambda и запустите его на базовом образе Linux provided.al2023.
    • Образ контейнера. Соберите функцию на основе официального Docker-образа oven/bun, загрузите его в Amazon ECR и разверните в виде контейнера.
    • Сборка с помощью Bun, выполнение на Node. Используйте команду bun build, чтобы собрать код в один файл .js, затем запустите этот файл на управляемой среде выполнения Node.js в Lambda.

    Третий вариант является наиболее консервативным: вы получаете быструю обработку кода с помощью Bun, сохраняя при этом поддерживаемую AWS среду выполнения в производстве. Вы теряете API, специфичные для среды выполнения Bun, и преимущество быстрого запуска, поэтому этот вариант подходит командам, которые хотят постепенно переходить на Bun.

    Как Bun выполняет TypeScript

    Файлы можно запускать непосредственно с помощью команды bun, независимо от того, являются ли они JavaScript или TypeScript: bun index.js, bun index.ts или bun --watch index.ts для автоматического перезапуска при изменении файлов.

    При запуске файла с расширением .ts транспилятор Bun удаляет аннотации типов в реальном времени полностью в памяти. Он не создает папку dist/, каталог .build/ или временные файлы .js на диске.

    Важно понимать, что удаление информации о типах — это не то же самое, что их проверка. Чтобы запуск происходил мгновенно, Bun не выполняет статическую проверку типов во время выполнения, поэтому код с ошибками типов всё равно будет работать. Проверка типов происходит в вашем редакторе, таком как VS Code или Cursor, а также в системах CI, где можно запустить команду bunx tsc --noEmit. Считайте этот шаг в CI обязательным.

    Пошаговая миграция существующего проекта Node.js

    Поскольку Bun следует API Node и конвенциям npm, обычно достаточно нескольких изменений для миграции простого проекта. Выполняйте эти изменения на отдельной ветке и в конце протестируйте весь набор тестов.

    Шаг 1: Удаление старых модулей и файлов lockfile

    Начните с чистой иерархии зависимостей, чтобы Bun мог сам решить все вопросы: удалите папку node_modules вместе с файлом фиксации версий, который использовал проект — будь то package-lock.json, yarn.lock или pnpm-lock.yaml.

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

    Шаг 2: Установка с помощью Bun

    Запустите команду bun install. Bun считывает ваш существующий файл package.json, загружает пакеты и создает файл bun.lock, который следует сохранить в репозитории.

    Шаг 3: Обновление скриптов в package.json

    Замените упоминания node, ts-node или tsx в разделе scripts:

    • start: вместо "node src/index.js" используется "bun src/index.js";
    • dev: вместо "ts-node-dev src/index.ts" используется "bun --watch src/index.ts";
    • test: вместо "jest" или "vitest" используется "bun test".

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

    Шаг 4: Замена определений TypeScript

    Для проектов на TypeScript замените определения типов Node на определения Bun, выполнив команды bun remove @types/node затем bun add -d @types/bun.

    Затем обновите запись types в поле compilerOptions файла tsconfig.json. В старых руководствах там упоминается "bun-types"; однако с текущей версией пакета @types/bun эта запись обычно имеет значение "bun", поэтому ознакомьтесь с документацией Bun для TypeScript, соответствующей установленной вами версии.

    Шаг 5: Замена несовместимых модулей

    Стандартные модули Node, такие как node:fs, node:path и node:http, работают без изменений. Проблемы обычно возникают с пакетами, зависящими от нативных биндингов на C++, и для некоторых из них существуют простые замены:

    • замените bcrypt на bcryptjs или @node-rs/bcrypt;
    • замените sqlite3 на встроенный в Bun модуль bun:sqlite;
  • Удалите вызовы dotenv, поскольку Bun автоматически загружает файл .env.
  • Тщательно обдумайте эту последнюю изменение. Если тот же код всё равно должен работать в Node, например в описанной выше конфигурации Lambda только для сборки, сохраните dotenv или явно загружайте конфигурацию, чтобы поведение не отличалось между средами выполнения.

    Основные выводы

    • Основная ценность Bun — объединение: один бинарный файл заменяет менеджер пакетов, запускач TypeScript, фреймворк тестирования и инструмент для сборки кода.
    • Его скорость обусловлена оптимизированной реализацией и быстрым запуском JavaScriptCore, а не тем, что Zig по своей природе быстрее C++.
    • Bun удаляет типы TypeScript из памяти, но никогда их не проверяет, поэтому в CI сохраняйте параметр tsc --noEmit.
    • В Lambda вы можете выбрать между пользовательским слоем, образом контейнера или сборкой кода с помощью Bun и его запуском на управляемом Node.js.
    • Миграция в основном является механической, но следите за повторным решением проблем с файлом блокировки, нативными дополнениями и любым поведением, зависящим от автоматической загрузки файла .env.