Оптимизация бандла Electron: кейс Medal 40MB → 2.7MB
Кейс · Июль 2026

Оптимизация бандла Electron:
Как Medal уменьшила 40MB до 2.7MB

Технический разбор того, как команда Medal Engineering сократила бандл Electron-приложения с 40+MB до 2.7MB — с помощью Vite, Rolldown, code splitting и агрессивного удаления мёртвого кода. Реальные цифры, код и применимые техники для вашего Electron-проекта.

Олег Максимов 9 июля 2026 10 мин чтения

Проблема: бандл рендерера 40+MB

Medal — это десктопное приложение на Electron, позволяющее геймерам записывать, редактировать и делиться игровыми моментами. Когда Рик Чжан присоединился к фронтенд-команде Medal в 2024 году, бандл рендерера Electron весил более 40MB. Речь идёт только о JavaScript — не об ассетах, не о нативных модулях — а о скомпилированном фронтенд-коде, который каждый пользователь загружает и парсит при каждом запуске.

Бандл в 40MB означает медленный запуск, задержку time-to-interactive и плохой пользовательский опыт на среднебюджетном железе. К тому же это блокировало итерацию функций — каждое изменение требовало полной сборки, и команда не могла увидеть свой код на экране в процессе локальной разработки.

Генеральный директор Кен Колтон сделал первый решительный шаг: переключить локальную сборку на Vite для Hot Module Replacement. По словам Рика, это был «момент, когда мы открыли огонь».

Стратегия: сначала макро, потом микро

Вместо того чтобы атаковать раздутые компоненты в лоб, Рик подошёл к проблеме извне: сначала макро, потом микро. Сначала исправить developer experience и инструменты сборки, потом удалить мёртвый код, затем оптимизировать то, что осталось.

Шаг 1: PNPM монорепозиторий + Vite

Medal мигрировала с мульти-репозитория с версионированными библиотеками компонентов на PNPM монорепозиторий. Это устранило расхождение версий, конфликты слияния и проблему «что и когда было опубликовано». Всё — компоненты, хуки, утилиты, типы — оказалось в одном репозитории с чёткими границами пакетов.

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

Шаг 2: Удаление мёртвого кода с помощью AI

Команда систематически выявляла и удаляла неиспользуемый код. Рик использовал AI для массового исправления путей импорта, гарантируя, что каждый импорт явно указывает пакет, путь и расширение файла. Это позволило инструментам находить действительно мёртвый код с уверенностью.

// До: barrel-импорт, который собирает всё
import { Button, Input, Modal, Dropdown } from '@/components';

// После: прямые импорты с расширениями файлов
import { Button } from '@/components/Button/Button.tsx';
import { Input } from '@/components/Input/Input.tsx';

Прямые пути и явные расширения файлов позволили tree-shaking'у работать эффективно. Относительные импорты остались, но barrel-файлы были запрещены как политика. Консолидация кода между Electron и вебом (клиент-агностичные пакеты утилит, хуков, типов) дополнительно сократила дублирование.

Шаг 3: Vite в продакшене

Medal использовала Vite для локальной разработки, но Rollup для продакшен-сборок — две системы сборки, две конфигурации, две правды. Унификация на Vite означала один пайплайн и для разработки, и для продакшена. Что важнее, Glob Imports Vite сделали code splitting значительно проще.

Результаты: каждая оптимизация, байт за байтом

-2.6 MB
Удаление barrel-файлов
Замена реэкспортных индексов прямыми импортами
-3.4 MB
Вынесение .wav ассетов
Звуковые файлы загружаются по требованию
-4 MB
Tree-shaking через ESM
ESM вместо CJS для лучшего удаления мёртвого кода
-13 MB
Динамический импорт i18n
Файлы переводов загружаются только для активной локали
-5.4 MB
Code splitting по маршрутам
Каждый маршрут загружает только свои зависимости
22 MB → 2.7 MB
Миграция на Rolldown
Rust-бандлер как замена Rollup

1. Удаление barrel-файлов: -2.6 MB

Barrel-файлы — это index.ts, которые реэкспортируют всё из директории. Они удобны, но полностью блокируют tree-shaking: когда вы пишете import { Button } from '@/components', бандлер не знает, какие из десятков реэкспортированных модулей вы будете использовать, и включает все.

// Типичный barrel-файл — собирает всё
export { Button } from './Button';
export { Input } from './Input';
export { Modal } from './Modal';
export { Dropdown } from './Dropdown';
export { Tooltip } from './Tooltip';
// ... ещё 30+ экспортов

Medal ввела строгую политику «смерть barrel-файлам». Каждый импорт должен указывать точный путь к файлу. Это само по себе сэкономило 2.6 MB — больше, чем полный размер многих небольших приложений.

2. Вынесение .wav ассетов: -3.4 MB

Звуковые файлы SoundAlertsData в формате .wav встраивались в бандл рендерера. Medal вынесла эти файлы — теперь они загружаются в рантайме, а не встраиваются в JavaScript. Это сэкономило 3.4 MB без каких-либо изменений в логике воспроизведения.

// Конфиг Vite — выносим ассеты
export default defineConfig({
  build: {
    rollupOptions: {
      external: [/\.wav$/],
    },
  },
});

3. ESM для лучшего tree-shaking: -4 MB

Rollup (и Rolldown) могут эффективно tree-shake'ить ESM-импорты в отличие от CommonJS. CJS require() динамический и непрозрачный — бандлер не может статически проанализировать, что используется. ESM-декларации import статичны и анализируемы. Переход Medal на ESM по всему коду дал дополнительные 4 MB экономии.

// CJS — бандлер не может tree-shake'ить
const lodash = require('lodash');

// ESM — бандлер может удалить неиспользуемые экспорты
import { debounce } from 'lodash-es';

4. Динамический импорт i18n: -13 MB

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

// Динамический импорт — загружаем переводы только для активной локали
async function loadTranslations(locale: string) {
  const messages = await import(`./locales/${locale}.json`);
  i18n.global.setLocaleMessage(locale, messages.default);
}

Замена статических импортов на динамические import() для файлов i18n сэкономила 13 MB — почти треть исходного бандла. Теперь каждый пользователь загружает только строки своего языка. Это техника, которую стоит внедрить любому многоязычному приложению.

5. Code splitting по маршрутам: -5.4 MB

Medal мигрировала с React Router v5 на v7 для поддержки ленивой загрузки маршрутов. С React Router v7 каждая страница загружает только свои компоненты, хуки и зависимости — ничего из других страниц не попадает в начальный бандл.

import { createBrowserRouter } from 'react-router-dom';

const router = createBrowserRouter([
  {
    path: '/',
    lazy: () => import('./pages/Home'),  // ленивая загрузка
  },
  {
    path: '/clips',
    lazy: () => import('./pages/Clips'),  // отдельный чанк
  },
  {
    path: '/settings',
    lazy: () => import('./pages/Settings'),  // отдельный чанк
  },
]);

Миграция с React Router v5 на v7 была масштабным рефакторингом — она затронула почти каждый файл в слое маршрутизации — но стала обязательным условием для разделения по маршрутам. В сочетании с автоматическим разделением чанков Vite это сэкономило 5.4 MB.

6. Rolldown: 22 MB → 2.7 MB

Когда бандл сократился до ~22 MB, Рик сосредоточился на Rolldown — бандлере на Rust, спроектированном как замена Rollup. Rolldown обеспечивает значительно более быструю сборку и лучший tree-shaking благодаря нативной компиляции.

Миграция не была безболезненной. Некоторые зависимости конфликтовали с системой сборки Rolldown и их пришлось предварительно удалить. Но после этой очистки переход на Rolldown сократил бандл с 22 MB до 2.7 MB — на 88% поверх всех предыдущих оптимизаций.

// rolldown.config.js
import { defineConfig } from 'rolldown';

export default defineConfig({
  input: 'src/main.tsx',
  output: {
    format: 'esm',
    dir: 'dist/renderer',
  },
  // Rolldown автоматически tree-shake'ит лучше, чем Rollup
  resolve: {
    extensions: ['.ts', '.tsx', '.js', '.jsx'],
  },
});

Финальный стек

После оптимизации тулчейн Medal выглядит так:

Vite

Локальная разработка с HMR

🦀

Rolldown

Продакшен-сборка (Rust)

📦

PNPM

Монорепозиторий

🧪

Vitest

Юнит и интеграционные тесты

🎨

Tailwind + Base UI

Библиотека компонентов (миграция с Grommet)

🔄

React Router v7

Ленивая загрузка маршрутов

Что это значит для вашего Electron-приложения

Путь Medal — это не просто вдохновляющая история. Каждая техника применима к существующим Electron-проектам. Вот практический чеклист для аудита вашего бандла:

  1. Сначала измерьте. Запустите анализ бандла (vite build --analyze или source-map-explorer), чтобы найти крупнейших потребителей.
  2. Убейте barrel-файлы. Проверьте index.ts и замените barrel-импорты прямыми импортами файлов.
  3. Вынесите ассеты. Переместите большие статические файлы (аудио, видео, шрифты) из бандла в рантайм-загрузку.
  4. Переходите на ESM. Убедитесь, что ваши зависимости совместимы с ESM. Замените CJS-пакеты там, где есть альтернативы.
  5. Лениво загружайте i18n. Если приложение поддерживает несколько языков, используйте динамический импорт файлов локалей. Это почти всегда самая лёгкая победа.
  6. Разделяйте по маршрутам. Мигрируйте на ленивые маршруты. Каждый маршрут должен загружать свой собственный чанк.
  7. Оцените Rolldown. Если вы на Rollup, Rolldown может сократить бандл вдвое без изменения кода.

Для общего обзора того, как развиваются современные инструменты сборки, смотрите моё практическое руководство по Vite+ Beta — единому тулчейну, объединяющему Vite, Vitest, Oxlint и Rolldown в один CLI. А о новых возможностях производительности самой платформы Electron читайте в Electron 43 beta: улучшения запуска, кэширования и LTO.

Часто задаваемые вопросы

Какие техники использовала Medal для сокращения бандла Electron с 40MB до 2.7MB?
Medal применила шесть основных оптимизаций: удаление barrel-файлов (-2.6MB), вынесение .wav-ассетов (-3.4MB), переход на ESM для лучшего tree-shaking (-4MB), динамический импорт i18n (-13MB), code splitting по маршрутам (-5.4MB) и миграцию с Rollup на Rolldown (22MB → 2.7MB). Общее сокращение — более 93%.
Как Vite помог Medal оптимизировать Electron-приложение?
Vite обеспечил Hot Module Replacement для быстрой разработки и упростил code splitting через Glob Imports. Переход на Vite для продакшен-сборок объединил пайплайн разработки, а его ESM-нативный dev-сервер подтолкнул команду к использованию ESM-паттернов, улучшив tree-shaking.
Что такое Rolldown и как он помог уменьшить размер бандла?
Rolldown — это Rust-бандлер, замена Rollup с лучшим tree-shaking'ом за счёт нативной компиляции. Medal перешла на Rolldown после очистки конфликтующих зависимостей. Миграция сократила бандл с 22MB до 2.7MB и значительно ускорила сборку.
Что такое barrel-файлы и почему они вредят размеру бандла?
Barrel-файлы — это index-файлы, реэкспортирующие модули из многих источников. Они мешают tree-shaking'у, потому что бандлер не может определить, какие именно экспорты используются. Medal избавилась от них, сэкономив 2.6MB, и ввела строгий запрет на их использование.
Сколько сэкономил динамический импорт i18n?
Динамический импорт файлов переводов сэкономил 13MB — самое крупное сокращение. Вместо того чтобы собирать все переводы заранее, Medal загружает только строки активной локали. Англоязычные пользователи больше не загружают десятки других языков.
Можно ли применить эти техники к любому Electron-приложению?
Да — большинство техник универсальны: удалите barrel-файлы, переходите на ESM, выносите ассеты, используйте динамический импорт для i18n, внедряйте code splitting по маршрутам и рассмотрите Rolldown для продакшен-сборок. Всё это работает с любой связкой Electron + Vite или Electron + Rolldown.
Какие инструменты сборки Medal использует после оптимизации?
Medal использует Vite для разработки и Rolldown для продакшена в PNPM-монорепозитории. Тесты на Vitest. UI-стек: Tailwind + Base UI (миграция с Grommet + Styled Components). Каждая миграция уменьшала вес зависимостей и улучшала производительность сборки.

Источник: "W-Key in Frontend: Synergizing Technology and Product" — Рик Чжан, Medal Engineering (23 июня 2026). Опубликовано в JS Weekly #793 (7 июля 2026).

Связаться

Нужна помощь с оптимизацией Electron-приложения?

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