Bundle анализ и оптимизация размера

Bundle-анализ — это процесс изучения итогового JavaScript-файла (или набора файлов), который получается после сборки приложения. Для Inferno, как и для других UI-фреймворков, критично понимать, какие модули попадают в бандл, почему они там оказываются и каков их реальный вклад в размер.

Inferno изначально спроектирован как минималистичный фреймворк, но даже при этом итоговый размер может быстро расти за счёт:

  • неявных зависимостей,
  • неправильного импорта API,
  • отключённого tree shaking,
  • избыточных полифиллов,
  • дублирующихся библиотек.

Анализ начинается не с оптимизаций, а с прозрачности структуры сборки.


Инструменты анализа бандла

На практике Inferno почти всегда используется вместе с Webpack, Rollup или Vite. Каждый из этих сборщиков имеет собственные инструменты визуализации.

Webpack

  • webpack-bundle-analyzer
  • stats.json + анализ через сторонние сервисы

Подключение анализатора:

const BundleAnalyzerPlugin = require('webpack-bundle-analyzer').BundleAnalyzerPlugin;

module.exports = {
  plugins: [
    new BundleAnalyzerPlugin()
  ]
};

Результат — интерактивная карта бандла, показывающая:

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

Rollup

  • rollup-plugin-visualizer

Пример:

import visualizer from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({ open: true })
  ]
};

Vite Использует Rollup под капотом, поэтому совместим с теми же плагинами визуализации.


Особенности Inferno, влияющие на размер

Inferno предоставляет несколько пакетов, каждый из которых решает конкретную задачу:

  • inferno
  • inferno-create-element
  • inferno-component
  • inferno-compat
  • inferno-router

Неправильный выбор пакета приводит к резкому увеличению размера.

Критически важно: inferno-compat добавляет слой совместимости с React и тянет за собой большое количество лишнего кода. Его использование оправдано только при миграции.


Импорт API и tree shaking

Inferno хорошо поддерживает tree shaking, но только при корректных ES-модулях.

Неправильный импорт:

import Inferno from 'inferno';

Правильный импорт:

import { render } from 'inferno';

Или:

import { Component } from 'inferno-component';

Причины:

  • default-импорт часто отключает эффективное удаление неиспользуемого кода;
  • named-импорты позволяют сборщику исключить лишние части.

Разделение кода (Code Splitting)

Даже небольшой Inferno-проект выигрывает от ленивой загрузки.

Динамический импорт

const LazyPage = () =>
  import('./pages/HeavyPage');

Webpack и Rollup автоматически создают отдельный chunk.

Типичные кандидаты на разделение:

  • роуты,
  • административные панели,
  • визуально тяжёлые компоненты,
  • сторонние библиотеки (графики, редакторы).

Оптимизация inferno-router

inferno-router относительно лёгкий, но неправильная архитектура маршрутов может свести выгоду на нет.

Распространённая ошибка:

  • импорт всех страниц сразу в корневом файле маршрутизации.

Предпочтительная схема:

  • каждый маршрут загружается через import().

Это позволяет:

  • уменьшить initial bundle,
  • ускорить Time To Interactive.

Минификация и режим сборки

Inferno активно использует проверки окружения.

Обязательное условие:

process.env.NODE_ENV === 'production'

В production-режиме:

  • отключаются dev-assertions,
  • удаляются проверки типов,
  • упрощается внутренний код.

Для Webpack:

mode: 'production'

Для Rollup:

replace({
  'process.env.NODE_ENV': JSON.stringify('production')
})

Dead Code Elimination

Inferno хорошо оптимизируется при использовании:

  • ES-модулей,
  • production-режима,
  • минификатора с поддержкой DCE (Terser).

Пример настройки Terser:

terserOptions: {
  compress: {
    pure_getters: true,
    passes: 2
  }
}

Это особенно эффективно для удаления:

  • неиспользуемых хелперов,
  • dev-веток,
  • альтернативных путей выполнения.

Исключение полифиллов

Автоматическое подключение полифиллов часто становится главным источником лишнего веса.

Типичные проблемы:

  • core-js целиком,
  • полифиллы для устаревших браузеров без реальной необходимости.

Решение:

  • использовать @babel/preset-env с useBuiltIns: 'usage',
  • явно задавать target-браузеры.
presets: [
  ['@babel/preset-env', {
    useBuiltIns: 'usage',
    corejs: 3
  }]
]

Дублирование зависимостей

При анализе бандла часто обнаруживается:

  • несколько версий одной библиотеки,
  • вложенные зависимости с одинаковым функционалом.

Причины:

  • неконсистентные версии в package.json,
  • транзитивные зависимости.

Методы борьбы:

  • resolutions (Yarn),
  • dedupe в Rollup,
  • alias в Webpack.

Пример alias:

resolve: {
  alias: {
    'inferno': path.resolve('./node_modules/inferno')
  }
}

Замена тяжёлых библиотек

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

Примеры замены:

  • momentdayjs
  • lodash → точечные импорты или нативные методы
  • большие UI-киты → изолированные компоненты

Inferno не требует обёрток для большинства утилит, что упрощает замену.


Анализ initial bundle

Ключевая метрика — размер первого загружаемого чанка.

Оптимальный состав:

  • Inferno core,
  • корневой компонент,
  • минимальный CSS,
  • базовая логика роутинга.

Всё остальное должно загружаться позже.

Практика:

  • жёсткий лимит на initial bundle (например, 50–70 KB gzip),
  • проверка размера на CI,
  • fail-сборка при превышении лимита.

Кэширование и хеши

Оптимизация размера теряет смысл без правильного кэширования.

Рекомендуется:

  • использовать content-hash в именах файлов,
  • разделять vendor-chunk и application-chunk.

Webpack:

output: {
  filename: '[name].[contenthash].js'
}

Это снижает повторную загрузку Inferno-кода при изменении бизнес-логики.


Контроль регрессий размера

Размер бандла должен рассматриваться как регрессионная метрика.

Подходы:

  • хранение отчётов анализатора,
  • сравнение размеров между коммитами,
  • автоматизированные проверки.

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